1
0
Fork 0
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:
ton 2019-11-28 18:44:14 +04:00
parent 7f3a22a217
commit 090e0c16eb
82 changed files with 1852 additions and 391 deletions

View file

@ -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;

View file

@ -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()};
}

View file

@ -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)) {
}

View file

@ -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);

View file

@ -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"); });

View file

@ -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 {