mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
slightly changed block format
- small change in block format - added config in blockchain explorer - bugfixes
This commit is contained in:
parent
7f3a22a217
commit
090e0c16eb
82 changed files with 1852 additions and 391 deletions
|
@ -379,6 +379,23 @@ HttpAnswer& HttpAnswer::operator<<(AccountCell acc_c) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
*this << "<form class=\"container\" action=\"" << prefix_ << "runmethod\" method=\"get\">"
|
||||
<< "<div class=\"row\">"
|
||||
<< "<p>Run get method<p>"
|
||||
<< "<div class=\"form-group col-lg-3 col-md-4\">"
|
||||
<< "<input type=\"text\" class=\"form-control mr-2\" name=\"method\" placeholder=\"method\">"
|
||||
<< "</div>\n"
|
||||
<< "<div class=\"form-group col-lg-4 col-md-6\">"
|
||||
<< "<input type=\"text\" class=\"form-control mr-2\" name=\"params\" placeholder=\"paramerers\"></div>"
|
||||
<< "<input type=\"hidden\" name=\"account\" value=\"" << acc_c.addr.rserialize(true) << "\">"
|
||||
<< "<input type=\"hidden\" name=\"workchain\" value=\"" << block_id.id.workchain << "\">"
|
||||
<< "<input type=\"hidden\" name=\"shard\" value=\"" << ton::shard_to_str(block_id.id.shard) << "\">"
|
||||
<< "<input type=\"hidden\" name=\"seqno\" value=\"" << block_id.id.seqno << "\">"
|
||||
<< "<input type=\"hidden\" name=\"roothash\" value=\"" << block_id.root_hash.to_hex() << "\">"
|
||||
<< "<input type=\"hidden\" name=\"filehash\" value=\"" << block_id.file_hash.to_hex() << "\">"
|
||||
<< "<div><button type=\"submit\" class=\"btn btn-primary mr-2\">Run!</button></div>"
|
||||
<< "</div></form>\n";
|
||||
|
||||
*this << "<div class=\"table-responsive my-3\">\n"
|
||||
<< "<table class=\"table-sm table-striped\">\n";
|
||||
*this << "<tr><th>block</th><td><a href=\"" << BlockLink{acc_c.block_id} << "\">" << block_id.id.to_str()
|
||||
|
@ -480,10 +497,13 @@ HttpAnswer& HttpAnswer::operator<<(BlockHeaderCell head_c) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
return *this << "<p><a class=\"btn btn-primary mr-2\" href=\"" << BlockDownloadLink{block_id} << "\" download=\""
|
||||
<< block_id.file_hash << ".boc\">download block</a>"
|
||||
<< "<a class=\"btn btn-primary\" href=\"" << BlockViewLink{block_id} << "\">view block</a>\n"
|
||||
<< "</p></div>";
|
||||
*this << "<p><a class=\"btn btn-primary mr-2\" href=\"" << BlockDownloadLink{block_id} << "\" download=\""
|
||||
<< block_id.file_hash << ".boc\">download block</a>"
|
||||
<< "<a class=\"btn btn-primary\" href=\"" << BlockViewLink{block_id} << "\">view block</a>\n";
|
||||
if (block_id.is_masterchain()) {
|
||||
*this << "<a class=\"btn btn-primary\" href=\"" << ConfigViewLink{block_id} << "\">view config</a>\n";
|
||||
}
|
||||
return *this << "</p></div>";
|
||||
}
|
||||
|
||||
HttpAnswer& HttpAnswer::operator<<(BlockShardsCell shards_c) {
|
||||
|
@ -571,6 +591,12 @@ HttpAnswer& HttpAnswer::operator<<(BlockViewLink block) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
HttpAnswer& HttpAnswer::operator<<(ConfigViewLink block) {
|
||||
*this << prefix_ << "config?";
|
||||
block_id_link(block.block_id);
|
||||
return *this;
|
||||
}
|
||||
|
||||
HttpAnswer& HttpAnswer::operator<<(BlockDownloadLink block) {
|
||||
*this << prefix_ << "download?";
|
||||
block_id_link(block.block_id);
|
||||
|
@ -606,10 +632,26 @@ HttpAnswer& HttpAnswer::operator<<(TransactionList trans) {
|
|||
return *this << "</tbody></table></div>";
|
||||
}
|
||||
|
||||
HttpAnswer& HttpAnswer::operator<<(ConfigParam conf) {
|
||||
std::ostringstream os;
|
||||
*this << "<div id=\"configparam" << conf.idx << "\"><h3>param " << conf.idx << "</h3>";
|
||||
if (conf.idx >= 0) {
|
||||
*this << RawData<block::gen::ConfigParam>{conf.root, conf.idx};
|
||||
} else {
|
||||
*this << RawData<void>{conf.root};
|
||||
}
|
||||
*this << "</div>\n";
|
||||
return *this;
|
||||
}
|
||||
|
||||
HttpAnswer& HttpAnswer::operator<<(Error error) {
|
||||
return *this << "<div class=\"alert alert-danger\">" << error.error.to_string() << "</div>";
|
||||
}
|
||||
|
||||
HttpAnswer& HttpAnswer::operator<<(Notification n) {
|
||||
return *this << "<div class=\"alert alert-success\">" << n.text << "</div>";
|
||||
}
|
||||
|
||||
void HttpAnswer::block_id_link(ton::BlockIdExt block_id) {
|
||||
*this << "workchain=" << block_id.id.workchain << "&shard=" << ton::shard_to_str(block_id.id.shard)
|
||||
<< "&seqno=" << block_id.id.seqno << "&roothash=" << block_id.root_hash << "&filehash=" << block_id.file_hash;
|
||||
|
|
|
@ -84,6 +84,9 @@ class HttpAnswer {
|
|||
struct BlockViewLink {
|
||||
ton::BlockIdExt block_id;
|
||||
};
|
||||
struct ConfigViewLink {
|
||||
ton::BlockIdExt block_id;
|
||||
};
|
||||
struct BlockDownloadLink {
|
||||
ton::BlockIdExt block_id;
|
||||
};
|
||||
|
@ -116,9 +119,16 @@ class HttpAnswer {
|
|||
struct CodeBlock {
|
||||
std::string data;
|
||||
};
|
||||
struct ConfigParam {
|
||||
td::int32 idx;
|
||||
td::Ref<vm::Cell> root;
|
||||
};
|
||||
struct Error {
|
||||
td::Status error;
|
||||
};
|
||||
struct Notification {
|
||||
std::string text;
|
||||
};
|
||||
template <class T>
|
||||
struct RawData {
|
||||
td::Ref<vm::Cell> root;
|
||||
|
@ -189,14 +199,17 @@ class HttpAnswer {
|
|||
HttpAnswer &operator<<(TransactionLinkShort trans);
|
||||
HttpAnswer &operator<<(BlockLink block);
|
||||
HttpAnswer &operator<<(BlockViewLink block);
|
||||
HttpAnswer &operator<<(ConfigViewLink block);
|
||||
HttpAnswer &operator<<(BlockDownloadLink block);
|
||||
|
||||
HttpAnswer &operator<<(Error error);
|
||||
HttpAnswer &operator<<(Notification notification);
|
||||
|
||||
HttpAnswer &operator<<(TransactionList trans);
|
||||
HttpAnswer &operator<<(CodeBlock block) {
|
||||
return *this << "<pre><code>" << block.data << "</code></pre>";
|
||||
}
|
||||
HttpAnswer &operator<<(ConfigParam conf);
|
||||
|
||||
template <class T>
|
||||
HttpAnswer &operator<<(RawData<T> data) {
|
||||
|
@ -220,3 +233,16 @@ class HttpAnswer {
|
|||
std::unique_ptr<td::StringBuilder> sb_;
|
||||
td::BufferSlice buf_;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HttpAnswer::RawData<void> {
|
||||
td::Ref<vm::Cell> root;
|
||||
RawData(td::Ref<vm::Cell> root) : root(std::move(root)) {
|
||||
}
|
||||
};
|
||||
template <>
|
||||
inline HttpAnswer &HttpAnswer::operator<<(RawData<void> data) {
|
||||
std::ostringstream outp;
|
||||
vm::load_cell_slice(data.root).print_rec(outp);
|
||||
return *this << CodeBlock{outp.str()};
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
*/
|
||||
#include "blockchain-explorer-query.hpp"
|
||||
#include "blockchain-explorer-http.hpp"
|
||||
#include "block/mc-config.h"
|
||||
#include "crypto/block/check-proof.h"
|
||||
|
||||
#include "auto/tl/lite_api.h"
|
||||
|
||||
|
@ -39,6 +41,40 @@
|
|||
|
||||
#include "common/errorcode.h"
|
||||
#include "block/block-auto.h"
|
||||
#include "crypto/vm/utils.h"
|
||||
#include "td/utils/crypto.h"
|
||||
|
||||
#include "vm/boc.h"
|
||||
#include "vm/cellops.h"
|
||||
#include "vm/cells/MerkleProof.h"
|
||||
#include "vm/continuation.h"
|
||||
#include "vm/cp0.h"
|
||||
|
||||
namespace {
|
||||
|
||||
td::Ref<vm::Tuple> prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, td::Ref<vm::CellSlice> my_addr,
|
||||
const block::CurrencyCollection &balance) {
|
||||
td::BitArray<256> rand_seed;
|
||||
td::RefInt256 rand_seed_int{true};
|
||||
td::Random::secure_bytes(rand_seed.as_slice());
|
||||
if (!rand_seed_int.unique_write().import_bits(rand_seed.cbits(), 256, false)) {
|
||||
return {};
|
||||
}
|
||||
auto tuple = vm::make_tuple_ref(td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea
|
||||
td::make_refint(0), // actions:Integer
|
||||
td::make_refint(0), // msgs_sent:Integer
|
||||
td::make_refint(now), // unixtime:Integer
|
||||
td::make_refint(lt), // block_lt:Integer
|
||||
td::make_refint(lt), // trans_lt:Integer
|
||||
std::move(rand_seed_int), // rand_seed:Integer
|
||||
balance.as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)]
|
||||
my_addr, // myself:MsgAddressInt
|
||||
vm::StackEntry()); // global_config:(Maybe Cell) ] = SmartContractInfo;
|
||||
LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string();
|
||||
return vm::make_tuple_ref(std::move(tuple));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
td::Result<ton::BlockIdExt> parse_block_id(std::map<std::string, std::string> &opts, bool allow_empty) {
|
||||
if (allow_empty) {
|
||||
|
@ -990,6 +1026,419 @@ void HttpQueryViewLastBlock::finish_query() {
|
|||
stop();
|
||||
}
|
||||
|
||||
HttpQueryConfig::HttpQueryConfig(std::string prefix, ton::BlockIdExt block_id, std::vector<td::int32> params,
|
||||
td::Promise<MHD_Response *> promise)
|
||||
: HttpQueryCommon(prefix, std::move(promise)), block_id_(block_id), params_(std::move(params)) {
|
||||
}
|
||||
|
||||
HttpQueryConfig::HttpQueryConfig(std::map<std::string, std::string> opts, std::string prefix,
|
||||
td::Promise<MHD_Response *> promise)
|
||||
: HttpQueryCommon(prefix, std::move(promise)) {
|
||||
auto R = parse_block_id(opts, true);
|
||||
if (R.is_error()) {
|
||||
error_ = R.move_as_error();
|
||||
return;
|
||||
}
|
||||
block_id_ = R.move_as_ok();
|
||||
|
||||
auto it = opts.find("param");
|
||||
if (it != opts.end()) {
|
||||
auto R2 = td::to_integer_safe<int>(it->second);
|
||||
if (R2.is_error()) {
|
||||
error_ = R2.move_as_error();
|
||||
return;
|
||||
}
|
||||
params_.push_back(R2.move_as_ok());
|
||||
}
|
||||
}
|
||||
|
||||
void HttpQueryConfig::start_up() {
|
||||
if (error_.is_error()) {
|
||||
abort_query(std::move(error_));
|
||||
return;
|
||||
}
|
||||
if (block_id_.is_valid()) {
|
||||
send_main_query();
|
||||
} else {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(SelfId, &HttpQueryConfig::abort_query, R.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &HttpQueryConfig::got_block, R.move_as_ok());
|
||||
}
|
||||
});
|
||||
|
||||
auto query = ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getMasterchainInfo>(), true);
|
||||
td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
|
||||
std::move(query), std::move(P));
|
||||
}
|
||||
}
|
||||
|
||||
void HttpQueryConfig::got_block(td::BufferSlice data) {
|
||||
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_masterchainInfo>(std::move(data), true);
|
||||
if (F.is_error()) {
|
||||
abort_query(F.move_as_error());
|
||||
return;
|
||||
}
|
||||
auto f = F.move_as_ok();
|
||||
block_id_ = ton::create_block_id(f->last_);
|
||||
|
||||
send_main_query();
|
||||
}
|
||||
|
||||
void HttpQueryConfig::send_main_query() {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(SelfId, &HttpQueryConfig::abort_query, R.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &HttpQueryConfig::got_result, R.move_as_ok());
|
||||
}
|
||||
});
|
||||
auto query =
|
||||
params_.size() > 0
|
||||
? ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getConfigParams>(
|
||||
0, ton::create_tl_lite_block_id(block_id_), std::vector<int>(params_)),
|
||||
true)
|
||||
: ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getConfigAll>(
|
||||
0, ton::create_tl_lite_block_id(block_id_)),
|
||||
true);
|
||||
td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
|
||||
std::move(query), std::move(P));
|
||||
}
|
||||
|
||||
void HttpQueryConfig::got_result(td::BufferSlice data) {
|
||||
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_configInfo>(std::move(data), true);
|
||||
if (F.is_error()) {
|
||||
abort_query(F.move_as_error());
|
||||
return;
|
||||
}
|
||||
auto f = F.move_as_ok();
|
||||
|
||||
state_proof_ = std::move(f->state_proof_);
|
||||
config_proof_ = std::move(f->config_proof_);
|
||||
|
||||
finish_query();
|
||||
}
|
||||
|
||||
void HttpQueryConfig::finish_query() {
|
||||
if (promise_) {
|
||||
auto page = [&]() -> std::string {
|
||||
HttpAnswer A{"config", prefix_};
|
||||
A.set_block_id(block_id_);
|
||||
auto R = block::check_extract_state_proof(block_id_, state_proof_.as_slice(), config_proof_.as_slice());
|
||||
if (R.is_error()) {
|
||||
A.abort(PSTRING() << "masterchain state proof for " << block_id_.to_str()
|
||||
<< " is invalid : " << R.move_as_error());
|
||||
return A.finish();
|
||||
}
|
||||
try {
|
||||
auto res = block::Config::extract_from_state(R.move_as_ok(), 0);
|
||||
if (res.is_error()) {
|
||||
A.abort(PSTRING() << "cannot unpack configuration: " << res.move_as_error());
|
||||
return A.finish();
|
||||
}
|
||||
auto config = res.move_as_ok();
|
||||
if (params_.size() > 0) {
|
||||
A << "<p>params: ";
|
||||
for (int i : params_) {
|
||||
auto value = config->get_config_param(i);
|
||||
if (value.not_null()) {
|
||||
A << "<a href=\"#configparam" << i << "\">" << i << "</a> ";
|
||||
}
|
||||
}
|
||||
A << "</p>";
|
||||
for (int i : params_) {
|
||||
auto value = config->get_config_param(i);
|
||||
if (value.not_null()) {
|
||||
A << HttpAnswer::ConfigParam{i, value};
|
||||
} else {
|
||||
A << HttpAnswer::Error{td::Status::Error(404, PSTRING() << "empty param " << i)};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
A << "<p>params: ";
|
||||
config->foreach_config_param([&](int i, td::Ref<vm::Cell> value) {
|
||||
if (value.not_null()) {
|
||||
A << "<a href=\"#configparam" << i << "\">" << i << "</a> ";
|
||||
}
|
||||
return true;
|
||||
});
|
||||
A << "</p>";
|
||||
config->foreach_config_param([&](int i, td::Ref<vm::Cell> value) {
|
||||
if (value.not_null()) {
|
||||
A << HttpAnswer::ConfigParam{i, value};
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
} catch (vm::VmError &err) {
|
||||
A.abort(PSTRING() << "error while traversing configuration: " << err.get_msg());
|
||||
} catch (vm::VmVirtError &err) {
|
||||
A.abort(PSTRING() << "virtualization error while traversing configuration: " << err.get_msg());
|
||||
}
|
||||
return A.finish();
|
||||
}();
|
||||
promise_.set_value(
|
||||
MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY));
|
||||
}
|
||||
stop();
|
||||
}
|
||||
|
||||
HttpQuerySendForm::HttpQuerySendForm(std::string prefix, td::Promise<MHD_Response *> promise)
|
||||
: HttpQueryCommon(prefix, std::move(promise)) {
|
||||
}
|
||||
|
||||
HttpQuerySendForm::HttpQuerySendForm(std::map<std::string, std::string> opts, std::string prefix,
|
||||
td::Promise<MHD_Response *> promise)
|
||||
: HttpQueryCommon(prefix, std::move(promise)) {
|
||||
}
|
||||
|
||||
void HttpQuerySendForm::start_up() {
|
||||
finish_query();
|
||||
}
|
||||
|
||||
void HttpQuerySendForm::finish_query() {
|
||||
if (promise_) {
|
||||
auto page = [&]() -> std::string {
|
||||
HttpAnswer A{"send", prefix_};
|
||||
A << "<div class=\"row\"><form action=\"" << prefix_
|
||||
<< "send\" method=\"post\" enctype=\"multipart/form-data\"><div class=\"form-group-row\">"
|
||||
<< "<label for=\"filedata\">bag of cells</label>"
|
||||
<< "<input type=\"file\" class=\"form-control-file\" id=\"filedata\" name=\"filedata\">"
|
||||
<< "<button type=\"submit\" class=\"btn btn-primary\">send</button>"
|
||||
<< "</div></form></div>";
|
||||
return A.finish();
|
||||
}();
|
||||
promise_.set_value(
|
||||
MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY));
|
||||
}
|
||||
stop();
|
||||
}
|
||||
|
||||
HttpQuerySend::HttpQuerySend(std::string prefix, td::BufferSlice data, td::Promise<MHD_Response *> promise)
|
||||
: HttpQueryCommon(prefix, std::move(promise)), data_(std::move(data)) {
|
||||
}
|
||||
|
||||
HttpQuerySend::HttpQuerySend(std::map<std::string, std::string> opts, std::string prefix,
|
||||
td::Promise<MHD_Response *> promise)
|
||||
: HttpQueryCommon(prefix, std::move(promise)) {
|
||||
auto it = opts.find("filedata");
|
||||
if (it != opts.end()) {
|
||||
data_ = td::BufferSlice{it->second};
|
||||
} else {
|
||||
error_ = td::Status::Error("no file data");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void HttpQuerySend::start_up() {
|
||||
if (error_.is_error()) {
|
||||
abort_query(std::move(error_));
|
||||
return;
|
||||
}
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(SelfId, &HttpQuerySend::abort_query, R.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &HttpQuerySend::got_result, R.move_as_ok());
|
||||
}
|
||||
});
|
||||
auto query =
|
||||
ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_sendMessage>(std::move(data_)), true);
|
||||
td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
|
||||
std::move(query), std::move(P));
|
||||
}
|
||||
|
||||
void HttpQuerySend::got_result(td::BufferSlice data) {
|
||||
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_sendMsgStatus>(std::move(data), true);
|
||||
if (F.is_error()) {
|
||||
abort_query(F.move_as_error());
|
||||
} else {
|
||||
status_ = F.move_as_ok()->status_;
|
||||
}
|
||||
finish_query();
|
||||
}
|
||||
|
||||
void HttpQuerySend::finish_query() {
|
||||
if (promise_) {
|
||||
auto page = [&]() -> std::string {
|
||||
HttpAnswer A{"send", prefix_};
|
||||
if (status_ >= 0) {
|
||||
A << HttpAnswer::Notification{"success"};
|
||||
} else {
|
||||
A << HttpAnswer::Error{td::Status::Error(status_, "failed")};
|
||||
}
|
||||
return A.finish();
|
||||
}();
|
||||
promise_.set_value(
|
||||
MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY));
|
||||
}
|
||||
stop();
|
||||
}
|
||||
|
||||
HttpQueryRunMethod::HttpQueryRunMethod(ton::BlockIdExt block_id, block::StdAddress addr, std::string method_name,
|
||||
std::vector<vm::StackEntry> params, std::string prefix,
|
||||
td::Promise<MHD_Response *> promise)
|
||||
: HttpQueryCommon(std::move(prefix), std::move(promise))
|
||||
, block_id_(block_id)
|
||||
, addr_(addr)
|
||||
, method_name_(std::move(method_name))
|
||||
, params_(std::move(params)) {
|
||||
}
|
||||
|
||||
HttpQueryRunMethod::HttpQueryRunMethod(std::map<std::string, std::string> opts, std::string prefix,
|
||||
td::Promise<MHD_Response *> promise)
|
||||
: HttpQueryCommon(std::move(prefix), std::move(promise)) {
|
||||
auto R = parse_block_id(opts, true);
|
||||
if (R.is_ok()) {
|
||||
block_id_ = R.move_as_ok();
|
||||
if (!block_id_.is_valid()) {
|
||||
block_id_.id.workchain = ton::masterchainId;
|
||||
block_id_.id.shard = ton::shardIdAll;
|
||||
block_id_.id.seqno = static_cast<td::uint32>(0xffffffff);
|
||||
block_id_.root_hash.set_zero();
|
||||
block_id_.file_hash.set_zero();
|
||||
}
|
||||
} else {
|
||||
error_ = R.move_as_error();
|
||||
return;
|
||||
}
|
||||
auto R2 = parse_account_addr(opts);
|
||||
if (R2.is_ok()) {
|
||||
addr_ = R2.move_as_ok();
|
||||
} else {
|
||||
error_ = R2.move_as_error();
|
||||
return;
|
||||
}
|
||||
auto it = opts.find("method");
|
||||
if (it == opts.end()) {
|
||||
error_ = td::Status::Error("no method");
|
||||
return;
|
||||
} else {
|
||||
method_name_ = it->second;
|
||||
}
|
||||
it = opts.find("params");
|
||||
if (it != opts.end()) {
|
||||
auto R3 = vm::parse_stack_entries(it->second);
|
||||
if (R3.is_error()) {
|
||||
error_ = R3.move_as_error();
|
||||
return;
|
||||
}
|
||||
params_ = R3.move_as_ok();
|
||||
}
|
||||
}
|
||||
|
||||
void HttpQueryRunMethod::start_up_query() {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(SelfId, &HttpQueryRunMethod::abort_query, R.move_as_error_prefix("litequery failed: "));
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &HttpQueryRunMethod::got_account, R.move_as_ok());
|
||||
}
|
||||
});
|
||||
auto a = ton::create_tl_object<ton::lite_api::liteServer_accountId>(addr_.workchain, addr_.addr);
|
||||
auto query = ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getAccountState>(
|
||||
ton::create_tl_lite_block_id(block_id_), std::move(a)),
|
||||
true);
|
||||
td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
|
||||
std::move(query), std::move(P));
|
||||
}
|
||||
|
||||
void HttpQueryRunMethod::got_account(td::BufferSlice data) {
|
||||
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_accountState>(std::move(data), true);
|
||||
if (F.is_error()) {
|
||||
abort_query(F.move_as_error());
|
||||
return;
|
||||
}
|
||||
|
||||
auto f = F.move_as_ok();
|
||||
data_ = std::move(f->state_);
|
||||
proof_ = std::move(f->proof_);
|
||||
shard_proof_ = std::move(f->shard_proof_);
|
||||
block_id_ = ton::create_block_id(f->id_);
|
||||
res_block_id_ = ton::create_block_id(f->shardblk_);
|
||||
|
||||
finish_query();
|
||||
}
|
||||
|
||||
void HttpQueryRunMethod::finish_query() {
|
||||
if (promise_) {
|
||||
auto page = [&]() -> std::string {
|
||||
HttpAnswer A{"account", prefix_};
|
||||
A.set_account_id(addr_);
|
||||
A.set_block_id(res_block_id_);
|
||||
|
||||
block::AccountState account_state;
|
||||
account_state.blk = block_id_;
|
||||
account_state.shard_blk = res_block_id_;
|
||||
account_state.shard_proof = std::move(shard_proof_);
|
||||
account_state.proof = std::move(proof_);
|
||||
account_state.state = std::move(data_);
|
||||
auto r_info = account_state.validate(block_id_, addr_);
|
||||
if (r_info.is_error()) {
|
||||
A.abort(r_info.move_as_error());
|
||||
return A.finish();
|
||||
}
|
||||
auto info = r_info.move_as_ok();
|
||||
if (info.root.is_null()) {
|
||||
A.abort(PSTRING() << "account state of " << addr_ << " is empty (cannot run method `" << method_name_ << "`)");
|
||||
return A.finish();
|
||||
}
|
||||
block::gen::Account::Record_account acc;
|
||||
block::gen::AccountStorage::Record store;
|
||||
block::CurrencyCollection balance;
|
||||
if (!(tlb::unpack_cell(info.root, acc) && tlb::csr_unpack(acc.storage, store) &&
|
||||
balance.validate_unpack(store.balance))) {
|
||||
A.abort("error unpacking account state");
|
||||
return A.finish();
|
||||
}
|
||||
int tag = block::gen::t_AccountState.get_tag(*store.state);
|
||||
switch (tag) {
|
||||
case block::gen::AccountState::account_uninit:
|
||||
A.abort(PSTRING() << "account " << addr_ << " not initialized yet (cannot run any methods)");
|
||||
return A.finish();
|
||||
case block::gen::AccountState::account_frozen:
|
||||
A.abort(PSTRING() << "account " << addr_ << " frozen (cannot run any methods)");
|
||||
return A.finish();
|
||||
}
|
||||
|
||||
CHECK(store.state.write().fetch_ulong(1) == 1); // account_init$1 _:StateInit = AccountState;
|
||||
block::gen::StateInit::Record state_init;
|
||||
CHECK(tlb::csr_unpack(store.state, state_init));
|
||||
auto code = state_init.code->prefetch_ref();
|
||||
auto data = state_init.data->prefetch_ref();
|
||||
auto stack = td::make_ref<vm::Stack>(std::move(params_));
|
||||
td::int64 method_id = (td::crc16(td::Slice{method_name_}) & 0xffff) | 0x10000;
|
||||
stack.write().push_smallint(method_id);
|
||||
long long gas_limit = vm::GasLimits::infty;
|
||||
// OstreamLogger ostream_logger(ctx.error_stream);
|
||||
// auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr);
|
||||
vm::GasLimits gas{gas_limit};
|
||||
LOG(DEBUG) << "creating VM";
|
||||
vm::VmState vm{code, std::move(stack), gas, 1, data, vm::VmLog()};
|
||||
vm.set_c7(prepare_vm_c7(info.gen_utime, info.gen_lt, acc.addr, balance)); // tuple with SmartContractInfo
|
||||
// vm.incr_stack_trace(1); // enable stack dump after each step
|
||||
int exit_code = ~vm.run();
|
||||
if (exit_code != 0) {
|
||||
A.abort(PSTRING() << "VM terminated with error code " << exit_code);
|
||||
return A.finish();
|
||||
}
|
||||
stack = vm.get_stack_ref();
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "result: ";
|
||||
stack->dump(os, 3);
|
||||
|
||||
A << HttpAnswer::CodeBlock{os.str()};
|
||||
}
|
||||
|
||||
return A.finish();
|
||||
}();
|
||||
promise_.set_value(
|
||||
MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY));
|
||||
}
|
||||
stop();
|
||||
}
|
||||
HttpQueryStatus::HttpQueryStatus(std::string prefix, td::Promise<MHD_Response *> promise)
|
||||
: HttpQueryCommon(std::move(prefix), std::move(promise)) {
|
||||
}
|
||||
|
|
|
@ -258,6 +258,77 @@ class HttpQueryViewLastBlock : public HttpQueryCommon {
|
|||
ton::BlockIdExt res_block_id_;
|
||||
};
|
||||
|
||||
class HttpQueryConfig : public HttpQueryCommon {
|
||||
public:
|
||||
HttpQueryConfig(std::string prefix, ton::BlockIdExt block_id, std::vector<td::int32> params,
|
||||
td::Promise<MHD_Response *> promise);
|
||||
HttpQueryConfig(std::map<std::string, std::string> opts, std::string prefix, td::Promise<MHD_Response *> promise);
|
||||
|
||||
void finish_query();
|
||||
|
||||
void start_up() override;
|
||||
void got_block(td::BufferSlice result);
|
||||
void send_main_query();
|
||||
void got_result(td::BufferSlice result);
|
||||
|
||||
private:
|
||||
ton::BlockIdExt block_id_;
|
||||
std::vector<td::int32> params_;
|
||||
|
||||
td::BufferSlice state_proof_;
|
||||
td::BufferSlice config_proof_;
|
||||
};
|
||||
|
||||
class HttpQuerySendForm : public HttpQueryCommon {
|
||||
public:
|
||||
HttpQuerySendForm(std::string prefix, td::Promise<MHD_Response *> promise);
|
||||
HttpQuerySendForm(std::map<std::string, std::string> opts, std::string prefix, td::Promise<MHD_Response *> promise);
|
||||
|
||||
void start_up() override;
|
||||
void finish_query();
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
class HttpQuerySend : public HttpQueryCommon {
|
||||
public:
|
||||
HttpQuerySend(std::string prefix, td::BufferSlice data, td::Promise<MHD_Response *> promise);
|
||||
HttpQuerySend(std::map<std::string, std::string> opts, std::string prefix, td::Promise<MHD_Response *> promise);
|
||||
|
||||
void finish_query();
|
||||
|
||||
void start_up() override;
|
||||
void got_result(td::BufferSlice result);
|
||||
|
||||
private:
|
||||
td::BufferSlice data_;
|
||||
td::int32 status_;
|
||||
};
|
||||
|
||||
class HttpQueryRunMethod : public HttpQueryCommon {
|
||||
public:
|
||||
HttpQueryRunMethod(ton::BlockIdExt block_id, block::StdAddress addr, std::string method_name,
|
||||
std::vector<vm::StackEntry> params, std::string prefix, td::Promise<MHD_Response *> promise);
|
||||
HttpQueryRunMethod(std::map<std::string, std::string> opts, std::string prefix, td::Promise<MHD_Response *> promise);
|
||||
|
||||
void finish_query();
|
||||
|
||||
void start_up_query() override;
|
||||
void got_account(td::BufferSlice result);
|
||||
|
||||
private:
|
||||
ton::BlockIdExt block_id_;
|
||||
block::StdAddress addr_;
|
||||
|
||||
std::string method_name_;
|
||||
std::vector<vm::StackEntry> params_;
|
||||
|
||||
td::BufferSlice data_;
|
||||
td::BufferSlice proof_;
|
||||
td::BufferSlice shard_proof_;
|
||||
ton::BlockIdExt res_block_id_;
|
||||
};
|
||||
|
||||
class HttpQueryStatus : public HttpQueryCommon {
|
||||
public:
|
||||
HttpQueryStatus(std::string prefix, td::Promise<MHD_Response *> promise);
|
||||
|
|
|
@ -49,6 +49,12 @@
|
|||
#include "blockchain-explorer-http.hpp"
|
||||
#include "blockchain-explorer-query.hpp"
|
||||
|
||||
#include "vm/boc.h"
|
||||
#include "vm/cellops.h"
|
||||
#include "vm/cells/MerkleProof.h"
|
||||
#include "vm/continuation.h"
|
||||
#include "vm/cp0.h"
|
||||
|
||||
#include "auto/tl/lite_api.h"
|
||||
#include "ton/lite-tl.hpp"
|
||||
#include "tl-utils/lite-utils.hpp"
|
||||
|
@ -263,22 +269,80 @@ class CoreActor : public CoreActorInterface {
|
|||
return MHD_YES;
|
||||
}
|
||||
|
||||
struct HttpRequestExtra {
|
||||
HttpRequestExtra(MHD_Connection* connection, bool is_post) {
|
||||
if (is_post) {
|
||||
postprocessor = MHD_create_post_processor(connection, 1 << 14, iterate_post, static_cast<void*>(this));
|
||||
}
|
||||
}
|
||||
~HttpRequestExtra() {
|
||||
MHD_destroy_post_processor(postprocessor);
|
||||
}
|
||||
static int iterate_post(void* coninfo_cls, enum MHD_ValueKind kind, const char* key, const char* filename,
|
||||
const char* content_type, const char* transfer_encoding, const char* data, uint64_t off,
|
||||
size_t size) {
|
||||
auto ptr = static_cast<HttpRequestExtra*>(coninfo_cls);
|
||||
ptr->total_size += strlen(key) + size;
|
||||
if (ptr->total_size > MAX_POST_SIZE) {
|
||||
return MHD_NO;
|
||||
}
|
||||
std::string k = key;
|
||||
if (ptr->opts[k].size() < off + size) {
|
||||
ptr->opts[k].resize(off + size);
|
||||
}
|
||||
td::MutableSlice(ptr->opts[k]).remove_prefix(off).copy_from(td::Slice(data, size));
|
||||
return MHD_YES;
|
||||
}
|
||||
MHD_PostProcessor* postprocessor;
|
||||
std::map<std::string, std::string> opts;
|
||||
td::uint64 total_size = 0;
|
||||
};
|
||||
|
||||
static void request_completed(void* cls, struct MHD_Connection* connection, void** ptr,
|
||||
enum MHD_RequestTerminationCode toe) {
|
||||
auto e = static_cast<HttpRequestExtra*>(*ptr);
|
||||
if (e) {
|
||||
delete e;
|
||||
}
|
||||
}
|
||||
|
||||
static int process_http_request(void* cls, struct MHD_Connection* connection, const char* url, const char* method,
|
||||
const char* version, const char* upload_data, size_t* upload_data_size, void** ptr) {
|
||||
static int dummy;
|
||||
struct MHD_Response* response = nullptr;
|
||||
int ret;
|
||||
|
||||
if (0 != std::strcmp(method, "GET"))
|
||||
bool is_post = false;
|
||||
if (std::strcmp(method, "GET") == 0) {
|
||||
is_post = false;
|
||||
} else if (std::strcmp(method, "POST") == 0) {
|
||||
is_post = true;
|
||||
} else {
|
||||
return MHD_NO; /* unexpected method */
|
||||
if (&dummy != *ptr) {
|
||||
/* The first time only the headers are valid,
|
||||
do not respond in the first round... */
|
||||
*ptr = &dummy;
|
||||
return MHD_YES;
|
||||
}
|
||||
if (0 != *upload_data_size)
|
||||
return MHD_NO; /* upload data in a GET!? */
|
||||
std::map<std::string, std::string> opts;
|
||||
if (!is_post) {
|
||||
if (!*ptr) {
|
||||
*ptr = static_cast<void*>(new HttpRequestExtra{connection, false});
|
||||
return MHD_YES;
|
||||
}
|
||||
if (0 != *upload_data_size)
|
||||
return MHD_NO; /* upload data in a GET!? */
|
||||
} else {
|
||||
if (!*ptr) {
|
||||
*ptr = static_cast<void*>(new HttpRequestExtra{connection, true});
|
||||
return MHD_YES;
|
||||
}
|
||||
auto e = static_cast<HttpRequestExtra*>(*ptr);
|
||||
if (0 != *upload_data_size) {
|
||||
CHECK(e->postprocessor);
|
||||
MHD_post_process(e->postprocessor, upload_data, *upload_data_size);
|
||||
*upload_data_size = 0;
|
||||
return MHD_YES;
|
||||
}
|
||||
for (auto& o : e->opts) {
|
||||
opts[o.first] = std::move(o.second);
|
||||
}
|
||||
}
|
||||
|
||||
std::string url_s = url;
|
||||
|
||||
|
@ -295,7 +359,6 @@ class CoreActor : public CoreActorInterface {
|
|||
command = url_s.substr(pos + 1);
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> opts;
|
||||
MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, get_arg_iterate, static_cast<void*>(&opts));
|
||||
|
||||
if (command == "status") {
|
||||
|
@ -352,6 +415,26 @@ class CoreActor : public CoreActorInterface {
|
|||
.release();
|
||||
}};
|
||||
response = g.wait();
|
||||
} else if (command == "config") {
|
||||
HttpQueryRunner g{[&](td::Promise<MHD_Response*> promise) {
|
||||
td::actor::create_actor<HttpQueryConfig>("getconfig", opts, prefix, std::move(promise)).release();
|
||||
}};
|
||||
response = g.wait();
|
||||
} else if (command == "send") {
|
||||
HttpQueryRunner g{[&](td::Promise<MHD_Response*> promise) {
|
||||
td::actor::create_actor<HttpQuerySend>("send", opts, prefix, std::move(promise)).release();
|
||||
}};
|
||||
response = g.wait();
|
||||
} else if (command == "sendform") {
|
||||
HttpQueryRunner g{[&](td::Promise<MHD_Response*> promise) {
|
||||
td::actor::create_actor<HttpQuerySendForm>("sendform", opts, prefix, std::move(promise)).release();
|
||||
}};
|
||||
response = g.wait();
|
||||
} else if (command == "runmethod") {
|
||||
HttpQueryRunner g{[&](td::Promise<MHD_Response*> promise) {
|
||||
td::actor::create_actor<HttpQueryRunMethod>("runmethod", opts, prefix, std::move(promise)).release();
|
||||
}};
|
||||
response = g.wait();
|
||||
} else {
|
||||
ret = MHD_NO;
|
||||
}
|
||||
|
@ -394,7 +477,8 @@ class CoreActor : public CoreActorInterface {
|
|||
remote_addr_, make_callback(0)));
|
||||
}
|
||||
daemon_ = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, static_cast<td::uint16>(http_port_), nullptr, nullptr,
|
||||
&process_http_request, nullptr, MHD_OPTION_THREAD_POOL_SIZE, 16, MHD_OPTION_END);
|
||||
&process_http_request, nullptr, MHD_OPTION_NOTIFY_COMPLETED, request_completed, nullptr,
|
||||
MHD_OPTION_THREAD_POOL_SIZE, 16, MHD_OPTION_END);
|
||||
CHECK(daemon_ != nullptr);
|
||||
}
|
||||
};
|
||||
|
@ -567,6 +651,8 @@ int main(int argc, char* argv[]) {
|
|||
});
|
||||
#endif
|
||||
|
||||
vm::init_op_cp0();
|
||||
|
||||
td::actor::Scheduler scheduler({2});
|
||||
scheduler_ptr = &scheduler;
|
||||
scheduler.run_in_context([&] { x = td::actor::create_actor<CoreActor>("testnode"); });
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include "ton/ton-types.h"
|
||||
#include "td/utils/port/IPAddress.h"
|
||||
|
||||
#define MAX_POST_SIZE (64 << 10)
|
||||
|
||||
class CoreActorInterface : public td::actor::Actor {
|
||||
public:
|
||||
struct RemoteNodeStatus {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue