mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
updated vm (breaking compatibility)
- updated vm - new actor scheduler - updated tonlib - updated DNS smartcontract
This commit is contained in:
parent
9e4816e7f6
commit
e27fb1e09c
100 changed files with 3692 additions and 1299 deletions
|
@ -61,6 +61,7 @@
|
|||
#include "ton/ton-shard.h"
|
||||
#include "openssl/rand.hpp"
|
||||
#include "crypto/vm/utils.h"
|
||||
#include "crypto/common/util.h"
|
||||
|
||||
#if TD_DARWIN || TD_LINUX
|
||||
#include <unistd.h>
|
||||
|
@ -142,6 +143,8 @@ void TestNode::got_result() {
|
|||
parse_line(std::move(data));
|
||||
}
|
||||
if (ex_mode_ && !running_queries_ && ex_queries_.size() == 0) {
|
||||
std::cout.flush();
|
||||
std::cerr.flush();
|
||||
std::_Exit(0);
|
||||
}
|
||||
}
|
||||
|
@ -179,6 +182,14 @@ bool TestNode::envelope_send_query(td::BufferSlice query, td::Promise<td::Buffer
|
|||
return true;
|
||||
}
|
||||
|
||||
td::Promise<td::Unit> TestNode::trivial_promise() {
|
||||
return td::PromiseCreator::lambda([Self = actor_id(this)](td::Result<td::Unit> res) {
|
||||
if (res.is_error()) {
|
||||
LOG(ERROR) << "error: " << res.move_as_error();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool TestNode::register_blkid(const ton::BlockIdExt& blkid) {
|
||||
for (const auto& id : known_blk_ids_) {
|
||||
if (id == blkid) {
|
||||
|
@ -570,8 +581,13 @@ bool TestNode::seekeoln() {
|
|||
return eoln();
|
||||
}
|
||||
|
||||
bool TestNode::parse_account_addr(ton::WorkchainId& wc, ton::StdSmcAddress& addr) {
|
||||
return block::parse_std_account_addr(get_word(), wc, addr) || set_error("cannot parse account address");
|
||||
bool TestNode::parse_account_addr(ton::WorkchainId& wc, ton::StdSmcAddress& addr, bool allow_none) {
|
||||
auto word = get_word();
|
||||
if (allow_none && (word == "none" || word == "root")) {
|
||||
wc = ton::workchainInvalid;
|
||||
return true;
|
||||
}
|
||||
return block::parse_std_account_addr(word, wc, addr) || set_error("cannot parse account address");
|
||||
}
|
||||
|
||||
bool TestNode::convert_uint64(td::Slice word, td::uint64& val) {
|
||||
|
@ -634,6 +650,14 @@ bool TestNode::parse_uint32(td::uint32& val) {
|
|||
return convert_uint32(get_word(), val) || set_error("cannot parse 32-bit unsigned integer");
|
||||
}
|
||||
|
||||
bool TestNode::parse_int32(td::int32& val) {
|
||||
return convert_int32(get_word(), val) || set_error("cannot parse 32-bit integer");
|
||||
}
|
||||
|
||||
bool TestNode::parse_int16(int& val) {
|
||||
return (convert_int32(get_word(), val) && val == (td::int16)val) || set_error("cannot parse 16-bit integer");
|
||||
}
|
||||
|
||||
bool TestNode::set_error(td::Status error) {
|
||||
if (error.is_ok()) {
|
||||
return true;
|
||||
|
@ -801,8 +825,12 @@ bool TestNode::show_help(std::string command) {
|
|||
"saveaccount[code|data] <filename> <addr> [<block-id-ext>]\tSaves into specified file the most recent state "
|
||||
"(StateInit) or just the code or data of specified account; <addr> is in "
|
||||
"[<workchain>:]<hex-or-base64-addr> format\n"
|
||||
"runmethod[x] <addr> [<block-id-ext>] <method-id> <params>...\tRuns GET method <method-id> of account <addr> "
|
||||
"runmethod[full] <addr> [<block-id-ext>] <method-id> <params>...\tRuns GET method <method-id> of account "
|
||||
"<addr> "
|
||||
"with specified parameters\n"
|
||||
"dnsresolve [<block-id-ext>] <domain> [<category>]\tResolves a domain starting from root dns smart contract\n"
|
||||
"dnsresolvestep <addr> [<block-id-ext>] <domain> [<category>]\tResolves a subdomain using dns smart contract "
|
||||
"<addr>\n"
|
||||
"allshards [<block-id-ext>]\tShows shard configuration from the most recent masterchain "
|
||||
"state or from masterchain state corresponding to <block-id-ext>\n"
|
||||
"getconfig [<param>...]\tShows specified or all configuration parameters from the latest masterchain state\n"
|
||||
|
@ -872,21 +900,30 @@ bool TestNode::do_parse_line() {
|
|||
(seekeoln()
|
||||
? get_account_state(workchain, addr, mc_last_id_, filename, mode)
|
||||
: parse_block_id_ext(blkid) && seekeoln() && get_account_state(workchain, addr, blkid, filename, mode));
|
||||
} else if (word == "runmethod" || word == "runmethodx") {
|
||||
} else if (word == "runmethod" || word == "runmethodx" || word == "runmethodfull") {
|
||||
std::string method;
|
||||
return parse_account_addr(workchain, addr) && get_word_to(method) &&
|
||||
(parse_block_id_ext(method, blkid) ? get_word_to(method) : (blkid = mc_last_id_).is_valid()) &&
|
||||
parse_run_method(workchain, addr, blkid, method, word.size() > 9);
|
||||
parse_run_method(workchain, addr, blkid, method, word.size() <= 10);
|
||||
} else if (word == "dnsresolve" || word == "dnsresolvestep") {
|
||||
workchain = ton::workchainInvalid;
|
||||
bool step = (word.size() > 10);
|
||||
std::string domain;
|
||||
int cat = 0;
|
||||
return (!step || parse_account_addr(workchain, addr)) && get_word_to(domain) &&
|
||||
(parse_block_id_ext(domain, blkid) ? get_word_to(domain) : (blkid = mc_last_id_).is_valid()) &&
|
||||
(seekeoln() || parse_int16(cat)) && seekeoln() &&
|
||||
dns_resolve_start(workchain, addr, blkid, domain, cat, step);
|
||||
} else if (word == "allshards") {
|
||||
return eoln() ? get_all_shards() : (parse_block_id_ext(blkid) && seekeoln() && get_all_shards(false, blkid));
|
||||
} else if (word == "saveconfig") {
|
||||
blkid = mc_last_id_;
|
||||
std::string filename;
|
||||
return get_word_to(filename) && (seekeoln() || parse_block_id_ext(blkid)) && seekeoln() &&
|
||||
get_config_params(blkid, -1, filename);
|
||||
get_config_params(blkid, trivial_promise(), -1, filename);
|
||||
} else if (word == "getconfig" || word == "getconfigfrom") {
|
||||
blkid = mc_last_id_;
|
||||
return (word == "getconfig" || parse_block_id_ext(blkid)) && get_config_params(blkid, 0);
|
||||
return (word == "getconfig" || parse_block_id_ext(blkid)) && get_config_params(blkid, trivial_promise(), 0);
|
||||
} else if (word == "getblock") {
|
||||
return parse_block_id_ext(blkid) && seekeoln() && get_block(blkid, false);
|
||||
} else if (word == "dumpblock") {
|
||||
|
@ -1031,7 +1068,17 @@ bool TestNode::parse_run_method(ton::WorkchainId workchain, ton::StdSmcAddress a
|
|||
return set_error(R.move_as_error().to_string());
|
||||
}
|
||||
parse_ptr_ = parse_end_;
|
||||
auto params = R.move_as_ok();
|
||||
auto P = td::PromiseCreator::lambda([](td::Result<std::vector<vm::StackEntry>> R) {
|
||||
if (R.is_error()) {
|
||||
LOG(ERROR) << R.move_as_error();
|
||||
}
|
||||
});
|
||||
return start_run_method(workchain, addr, ref_blkid, method_name, R.move_as_ok(), ext_mode ? 0x1f : 0, std::move(P));
|
||||
}
|
||||
|
||||
bool TestNode::start_run_method(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt ref_blkid,
|
||||
std::string method_name, std::vector<vm::StackEntry> params, int mode,
|
||||
td::Promise<std::vector<vm::StackEntry>> promise) {
|
||||
if (!ref_blkid.is_valid()) {
|
||||
return set_error("must obtain last block information before making other queries");
|
||||
}
|
||||
|
@ -1039,31 +1086,33 @@ bool TestNode::parse_run_method(ton::WorkchainId workchain, ton::StdSmcAddress a
|
|||
return set_error("server connection not ready");
|
||||
}
|
||||
auto a = ton::create_tl_object<ton::lite_api::liteServer_accountId>(workchain, addr);
|
||||
int mode = (ext_mode ? 0x1f : 0);
|
||||
if (!mode) {
|
||||
auto b = ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getAccountState>(
|
||||
ton::create_tl_lite_block_id(ref_blkid), std::move(a)),
|
||||
true);
|
||||
LOG(INFO) << "requesting account state for " << workchain << ":" << addr.to_hex() << " with respect to "
|
||||
<< ref_blkid.to_str() << " to run method " << method_name << " with " << params.size() << " parameters";
|
||||
return envelope_send_query(
|
||||
std::move(b), [ Self = actor_id(this), workchain, addr, ref_blkid, method_name,
|
||||
params = std::move(params) ](td::Result<td::BufferSlice> R) mutable {
|
||||
if (R.is_error()) {
|
||||
return;
|
||||
}
|
||||
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_accountState>(R.move_as_ok(), true);
|
||||
if (F.is_error()) {
|
||||
LOG(ERROR) << "cannot parse answer to liteServer.getAccountState";
|
||||
} else {
|
||||
auto f = F.move_as_ok();
|
||||
td::actor::send_closure_later(Self, &TestNode::run_smc_method, 0, ref_blkid, ton::create_block_id(f->id_),
|
||||
ton::create_block_id(f->shardblk_), std::move(f->shard_proof_),
|
||||
std::move(f->proof_), std::move(f->state_), workchain, addr, method_name,
|
||||
std::move(params), td::BufferSlice(), td::BufferSlice(), td::BufferSlice(),
|
||||
-0x10000);
|
||||
}
|
||||
});
|
||||
return envelope_send_query(std::move(b), [
|
||||
Self = actor_id(this), workchain, addr, ref_blkid, method_name, params = std::move(params),
|
||||
promise = std::move(promise)
|
||||
](td::Result<td::BufferSlice> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
return;
|
||||
}
|
||||
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_accountState>(R.move_as_ok(), true);
|
||||
if (F.is_error()) {
|
||||
LOG(ERROR) << "cannot parse answer to liteServer.getAccountState";
|
||||
promise.set_error(td::Status::Error("cannot parse answer to liteServer.getAccountState"));
|
||||
} else {
|
||||
auto f = F.move_as_ok();
|
||||
td::actor::send_closure_later(Self, &TestNode::run_smc_method, 0, ref_blkid, ton::create_block_id(f->id_),
|
||||
ton::create_block_id(f->shardblk_), std::move(f->shard_proof_),
|
||||
std::move(f->proof_), std::move(f->state_), workchain, addr, method_name,
|
||||
std::move(params), td::BufferSlice(), td::BufferSlice(), td::BufferSlice(),
|
||||
-0x10000, std::move(promise));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
td::int64 method_id = compute_method_id(method_name);
|
||||
// set serialization limits
|
||||
|
@ -1087,26 +1136,260 @@ bool TestNode::parse_run_method(ton::WorkchainId workchain, ton::StdSmcAddress a
|
|||
<< " with respect to " << ref_blkid.to_str() << " to run method " << method_name << " with "
|
||||
<< params.size() << " parameters";
|
||||
return envelope_send_query(std::move(b), [
|
||||
Self = actor_id(this), workchain, addr, ref_blkid, method_name, mode, params = std::move(params)
|
||||
Self = actor_id(this), workchain, addr, ref_blkid, method_name, mode, params = std::move(params),
|
||||
promise = std::move(promise)
|
||||
](td::Result<td::BufferSlice> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_error(R.move_as_error());
|
||||
return;
|
||||
}
|
||||
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_runMethodResult>(R.move_as_ok(), true);
|
||||
if (F.is_error()) {
|
||||
LOG(ERROR) << "cannot parse answer to liteServer.runSmcMethod";
|
||||
promise.set_error(td::Status::Error("cannot parse answer to liteServer.runSmcMethod"));
|
||||
} else {
|
||||
auto f = F.move_as_ok();
|
||||
td::actor::send_closure_later(Self, &TestNode::run_smc_method, mode, ref_blkid, ton::create_block_id(f->id_),
|
||||
ton::create_block_id(f->shardblk_), std::move(f->shard_proof_),
|
||||
std::move(f->proof_), std::move(f->state_proof_), workchain, addr, method_name,
|
||||
std::move(params), std::move(f->init_c7_), std::move(f->lib_extras_),
|
||||
std::move(f->result_), f->exit_code_);
|
||||
std::move(f->result_), f->exit_code_, std::move(promise));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool TestNode::dns_resolve_start(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt blkid,
|
||||
std::string domain, int cat, int mode) {
|
||||
if (domain.size() > 1023) {
|
||||
return set_error("domain name too long");
|
||||
}
|
||||
if (domain.size() >= 2 && domain[0] == '"' && domain.back() == '"') {
|
||||
domain.erase(0, 1);
|
||||
domain.pop_back();
|
||||
}
|
||||
std::vector<std::string> components;
|
||||
std::size_t i, p = 0;
|
||||
for (i = 0; i < domain.size(); i++) {
|
||||
if (!domain[i] || (unsigned char)domain[i] >= 0xfe || (unsigned char)domain[i] <= ' ') {
|
||||
return set_error("invalid characters in a domain name");
|
||||
}
|
||||
if (domain[i] == '.') {
|
||||
if (i == p) {
|
||||
return set_error("domain name cannot have an empty component");
|
||||
}
|
||||
components.emplace_back(domain, p, i - p);
|
||||
p = i + 1;
|
||||
}
|
||||
}
|
||||
if (i > p) {
|
||||
components.emplace_back(domain, p, i - p);
|
||||
}
|
||||
std::string qdomain, qdomain0;
|
||||
while (!components.empty()) {
|
||||
qdomain += components.back();
|
||||
qdomain += '\0';
|
||||
components.pop_back();
|
||||
}
|
||||
|
||||
if (!(ready_ && !client_.empty())) {
|
||||
return set_error("server connection not ready");
|
||||
}
|
||||
|
||||
if (workchain == ton::workchainInvalid) {
|
||||
if (dns_root_queried_) {
|
||||
workchain = ton::masterchainId;
|
||||
addr = dns_root_;
|
||||
} else {
|
||||
auto P = td::PromiseCreator::lambda([this, blkid, domain, cat, mode](td::Result<td::Unit> R) {
|
||||
if (R.is_error()) {
|
||||
LOG(ERROR) << "cannot obtain root dns address from configuration: " << R.move_as_error();
|
||||
} else if (dns_root_queried_) {
|
||||
dns_resolve_start(ton::masterchainId, dns_root_, blkid, domain, cat, mode);
|
||||
} else {
|
||||
LOG(ERROR) << "cannot obtain root dns address from configuration parameter #4";
|
||||
}
|
||||
});
|
||||
return get_config_params(mc_last_id_, std::move(P), 0x5000, "", {4});
|
||||
}
|
||||
}
|
||||
return dns_resolve_send(workchain, addr, blkid, domain, qdomain, cat, mode);
|
||||
}
|
||||
|
||||
bool TestNode::dns_resolve_send(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt blkid,
|
||||
std::string domain, std::string qdomain, int cat, int mode) {
|
||||
LOG(INFO) << "dns_resolve for '" << domain << "' category=" << cat << " mode=" << mode
|
||||
<< " starting from smart contract " << workchain << ":" << addr.to_hex() << " with respect to block "
|
||||
<< blkid.to_str();
|
||||
std::string qdomain0;
|
||||
if (qdomain.size() <= 127) {
|
||||
qdomain0 = qdomain;
|
||||
} else {
|
||||
qdomain0 = std::string{qdomain, 0, 127};
|
||||
qdomain[125] = '\xff';
|
||||
qdomain[126] = '\x0';
|
||||
}
|
||||
vm::CellBuilder cb;
|
||||
Ref<vm::Cell> cell;
|
||||
if (!(cb.store_bytes_bool(td::Slice(qdomain0)) && cb.finalize_to(cell))) {
|
||||
return set_error("cannot store domain name into slice");
|
||||
}
|
||||
std::vector<vm::StackEntry> params;
|
||||
params.emplace_back(vm::load_cell_slice_ref(std::move(cell)));
|
||||
params.emplace_back(td::make_refint(cat));
|
||||
auto P = td::PromiseCreator::lambda([this, workchain, addr, blkid, domain, qdomain, cat,
|
||||
mode](td::Result<std::vector<vm::StackEntry>> R) {
|
||||
if (R.is_error()) {
|
||||
LOG(ERROR) << R.move_as_error();
|
||||
return;
|
||||
}
|
||||
auto S = R.move_as_ok();
|
||||
if (S.size() < 2 || !S[S.size() - 2].is_int() || !(S.back().is_cell() || S.back().is_null())) {
|
||||
LOG(ERROR) << "dnsresolve did not return a value of type (int,cell)";
|
||||
return;
|
||||
}
|
||||
auto cell = S.back().as_cell();
|
||||
S.pop_back();
|
||||
auto x = S.back().as_int();
|
||||
S.clear();
|
||||
if (!x->signed_fits_bits(32)) {
|
||||
LOG(ERROR) << "invalid integer result of dnsresolve (" << x << ")";
|
||||
return;
|
||||
}
|
||||
return dns_resolve_finish(workchain, addr, blkid, domain, qdomain, cat, mode, (int)x->to_long(), std::move(cell));
|
||||
});
|
||||
return start_run_method(workchain, addr, blkid, "dnsresolve", std::move(params), 0x1f, std::move(P));
|
||||
}
|
||||
|
||||
bool TestNode::show_dns_record(std::ostream& os, int cat, Ref<vm::Cell> value, bool raw_dump) {
|
||||
if (raw_dump) {
|
||||
bool ok = show_dns_record(os, cat, value, false);
|
||||
if (!ok) {
|
||||
os << "cannot parse dns record; raw value: ";
|
||||
vm::load_cell_slice(value).print_rec(print_limit_, os);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
if (value.is_null()) {
|
||||
os << "(null)";
|
||||
return true;
|
||||
}
|
||||
// block::gen::t_DNSRecord.print_ref(print_limit_, os, value);
|
||||
if (!block::gen::t_DNSRecord.validate_ref(value)) {
|
||||
return false;
|
||||
}
|
||||
block::gen::t_DNSRecord.print_ref(print_limit_, os, value);
|
||||
auto cs = vm::load_cell_slice(value);
|
||||
auto tag = block::gen::t_DNSRecord.get_tag(cs);
|
||||
ton::WorkchainId wc;
|
||||
ton::StdSmcAddress addr;
|
||||
switch (tag) {
|
||||
case block::gen::DNSRecord::dns_adnl_address: {
|
||||
block::gen::DNSRecord::Record_dns_adnl_address rec;
|
||||
if (tlb::unpack_exact(cs, rec)) {
|
||||
os << "\n\tadnl address " << rec.adnl_addr.to_hex() << " = " << td::adnl_id_encode(rec.adnl_addr, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case block::gen::DNSRecord::dns_smc_address: {
|
||||
block::gen::DNSRecord::Record_dns_smc_address rec;
|
||||
if (tlb::unpack_exact(cs, rec) && block::tlb::t_MsgAddressInt.extract_std_address(rec.smc_addr, wc, addr)) {
|
||||
os << "\tsmart contract " << wc << ":" << addr.to_hex() << " = " << block::StdAddress{wc, addr}.rserialize(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case block::gen::DNSRecord::dns_next_resolver: {
|
||||
block::gen::DNSRecord::Record_dns_next_resolver rec;
|
||||
if (tlb::unpack_exact(cs, rec) && block::tlb::t_MsgAddressInt.extract_std_address(rec.resolver, wc, addr)) {
|
||||
os << "\tnext resolver " << wc << ":" << addr.to_hex() << " = " << block::StdAddress{wc, addr}.rserialize(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TestNode::dns_resolve_finish(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt blkid,
|
||||
std::string domain, std::string qdomain, int cat, int mode, int used_bits,
|
||||
Ref<vm::Cell> value) {
|
||||
if (used_bits <= 0) {
|
||||
td::TerminalIO::out() << "domain '" << domain << "' not found" << std::endl;
|
||||
return;
|
||||
}
|
||||
if ((used_bits & 7) || (unsigned)used_bits > 8 * std::min<std::size_t>(qdomain.size(), 126)) {
|
||||
LOG(ERROR) << "too many bits used (" << used_bits << " out of " << qdomain.size() * 8 << ")";
|
||||
return;
|
||||
}
|
||||
int pos = (used_bits >> 3);
|
||||
if (qdomain[pos - 1]) {
|
||||
LOG(ERROR) << "domain split not at a component boundary";
|
||||
return;
|
||||
}
|
||||
bool end = ((std::size_t)pos == qdomain.size());
|
||||
if (!end) {
|
||||
LOG(INFO) << "partial information obtained";
|
||||
if (value.is_null()) {
|
||||
td::TerminalIO::out() << "domain '" << domain << "' not found: no next resolver" << std::endl;
|
||||
return;
|
||||
}
|
||||
Ref<vm::CellSlice> nx_address;
|
||||
ton::WorkchainId nx_wc;
|
||||
ton::StdSmcAddress nx_addr;
|
||||
if (!(block::gen::t_DNSRecord.cell_unpack_dns_next_resolver(value, nx_address) &&
|
||||
block::tlb::t_MsgAddressInt.extract_std_address(std::move(nx_address), nx_wc, nx_addr))) {
|
||||
LOG(ERROR) << "cannot parse next resolver info for " << domain.substr(qdomain.size() - pos);
|
||||
std::ostringstream out;
|
||||
vm::load_cell_slice(value).print_rec(print_limit_, out);
|
||||
td::TerminalIO::err() << out.str() << std::endl;
|
||||
return;
|
||||
}
|
||||
LOG(INFO) << "next resolver is " << nx_wc << ":" << nx_addr.to_hex();
|
||||
if ((mode & 1)) {
|
||||
return; // no recursive resolving
|
||||
}
|
||||
if (!(dns_resolve_send(nx_wc, nx_addr, blkid, domain, qdomain.substr(pos), cat, mode))) {
|
||||
LOG(ERROR) << "cannot send next dns query";
|
||||
return;
|
||||
}
|
||||
LOG(INFO) << "recursive dns query to '" << domain.substr(qdomain.size() - pos) << "' sent";
|
||||
return;
|
||||
}
|
||||
auto out = td::TerminalIO::out();
|
||||
out << "Result for domain '" << domain << "' category " << cat << (cat ? "" : " (all categories)") << std::endl;
|
||||
try {
|
||||
if (value.not_null()) {
|
||||
std::ostringstream os0;
|
||||
vm::load_cell_slice(value).print_rec(print_limit_, os0);
|
||||
out << "raw data: " << os0.str() << std::endl;
|
||||
}
|
||||
if (!cat) {
|
||||
vm::Dictionary dict{value, 16};
|
||||
if (!dict.check_for_each([this, &out](Ref<vm::CellSlice> cs, td::ConstBitPtr key, int n) {
|
||||
CHECK(n == 16);
|
||||
int x = (int)key.get_int(16);
|
||||
if (cs.is_null() || cs->size_ext() != 0x10000) {
|
||||
out << "category #" << x << " : value is not a reference" << std::endl;
|
||||
return false;
|
||||
}
|
||||
std::ostringstream os;
|
||||
(void)show_dns_record(os, x, cs->prefetch_ref(), true);
|
||||
out << "category #" << x << " : " << os.str() << std::endl;
|
||||
return true;
|
||||
})) {
|
||||
out << "invalid dns record dictionary" << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::ostringstream os;
|
||||
(void)show_dns_record(os, cat, value, true);
|
||||
out << "category #" << cat << " : " << os.str() << std::endl;
|
||||
}
|
||||
} catch (vm::VmError& err) {
|
||||
LOG(ERROR) << "vm error while traversing dns resolve result: " << err.get_msg();
|
||||
} catch (vm::VmVirtError& err) {
|
||||
LOG(ERROR) << "vm virtualization error while traversing dns resolve result: " << err.get_msg();
|
||||
}
|
||||
}
|
||||
|
||||
bool TestNode::get_one_transaction(ton::BlockIdExt blkid, ton::WorkchainId workchain, ton::StdSmcAddress addr,
|
||||
ton::LogicalTime lt, bool dump) {
|
||||
if (!blkid.is_valid_full()) {
|
||||
|
@ -1192,8 +1475,8 @@ void TestNode::got_account_state(ton::BlockIdExt ref_blk, ton::BlockIdExt blk, t
|
|||
if (info.root.not_null()) {
|
||||
out << "account state is ";
|
||||
std::ostringstream outp;
|
||||
block::gen::t_Account.print_ref(outp, info.root);
|
||||
vm::load_cell_slice(info.root).print_rec(outp);
|
||||
block::gen::t_Account.print_ref(print_limit_, outp, info.root);
|
||||
vm::load_cell_slice(info.root).print_rec(print_limit_, outp);
|
||||
out << outp.str();
|
||||
out << "last transaction lt = " << info.last_trans_lt << " hash = " << info.last_trans_hash.to_hex() << std::endl;
|
||||
block::gen::Account::Record_account acc;
|
||||
|
@ -1269,7 +1552,8 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
|
|||
td::BufferSlice shard_proof, td::BufferSlice proof, td::BufferSlice state,
|
||||
ton::WorkchainId workchain, ton::StdSmcAddress addr, std::string method,
|
||||
std::vector<vm::StackEntry> params, td::BufferSlice remote_c7,
|
||||
td::BufferSlice remote_libs, td::BufferSlice remote_result, int remote_exit_code) {
|
||||
td::BufferSlice remote_libs, td::BufferSlice remote_result, int remote_exit_code,
|
||||
td::Promise<std::vector<vm::StackEntry>> promise) {
|
||||
LOG(INFO) << "got (partial) account state with mode=" << mode << " for " << workchain << ":" << addr.to_hex()
|
||||
<< " with respect to blocks " << blk.to_str()
|
||||
<< (shard_blk == blk ? "" : std::string{" and "} + shard_blk.to_str());
|
||||
|
@ -1287,6 +1571,7 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
|
|||
auto r_info = account_state.validate(ref_blk, block::StdAddress(workchain, addr));
|
||||
if (r_info.is_error()) {
|
||||
LOG(ERROR) << r_info.error().message();
|
||||
promise.set_error(r_info.move_as_error());
|
||||
return;
|
||||
}
|
||||
auto out = td::TerminalIO::out();
|
||||
|
@ -1294,12 +1579,14 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
|
|||
if (info.root.is_null()) {
|
||||
LOG(ERROR) << "account state of " << workchain << ":" << addr.to_hex() << " is empty (cannot run method `"
|
||||
<< method << "`)";
|
||||
promise.set_error(td::Status::Error(PSLICE() << "account state of " << workchain << ":" << addr.to_hex()
|
||||
<< " is empty (cannot run method `" << method << "`)"));
|
||||
return;
|
||||
}
|
||||
if (false) {
|
||||
// DEBUG (dump state)
|
||||
std::ostringstream os;
|
||||
vm::CellSlice{vm::NoVm(), info.true_root}.print_rec(os);
|
||||
vm::CellSlice{vm::NoVm(), info.true_root}.print_rec(print_limit_, os);
|
||||
out << "dump of account state (proof): " << os.str() << std::endl;
|
||||
}
|
||||
// set deserialization limits
|
||||
|
@ -1313,9 +1600,9 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
|
|||
bool ok = val.deserialize(r_c7);
|
||||
val.dump(os);
|
||||
// os << std::endl;
|
||||
// block::gen::t_VmStackValue.print_ref(os, r_c7);
|
||||
// block::gen::t_VmStackValue.print_ref(print_limit_, os, r_c7);
|
||||
// os << std::endl;
|
||||
// vm::CellSlice{vm::NoVmOrd(), r_c7}.print_rec(os);
|
||||
// vm::CellSlice{vm::NoVmOrd(), r_c7}.print_rec(print_limit_, os);
|
||||
out << "remote_c7 (deserialized=" << ok << "): " << os.str() << std::endl;
|
||||
}
|
||||
block::gen::Account::Record_account acc;
|
||||
|
@ -1324,6 +1611,7 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
|
|||
if (!(tlb::unpack_cell(info.root, acc) && tlb::csr_unpack(acc.storage, store) &&
|
||||
balance.validate_unpack(store.balance))) {
|
||||
LOG(ERROR) << "error unpacking account state";
|
||||
promise.set_error(td::Status::Error("error unpacking account state"));
|
||||
return;
|
||||
}
|
||||
int tag = block::gen::t_AccountState.get_tag(*store.state);
|
||||
|
@ -1369,6 +1657,7 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
|
|||
if (exit_code != 0) {
|
||||
LOG(ERROR) << "VM terminated with error code " << exit_code;
|
||||
out << "result: error " << exit_code << std::endl;
|
||||
promise.set_error(td::Status::Error(PSLICE() << "VM terminated with non-zero exit code " << exit_code));
|
||||
return;
|
||||
}
|
||||
stack = vm.get_stack_ref();
|
||||
|
@ -1399,6 +1688,8 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
|
|||
out << os.str();
|
||||
}
|
||||
}
|
||||
out.flush();
|
||||
promise.set_result(stack->extract_contents());
|
||||
} catch (vm::VmVirtError& err) {
|
||||
out << "virtualization error while parsing runSmcMethod result: " << err.get_msg();
|
||||
} catch (vm::VmError& err) {
|
||||
|
@ -1485,8 +1776,8 @@ void TestNode::got_one_transaction(ton::BlockIdExt req_blkid, ton::BlockIdExt bl
|
|||
} else {
|
||||
out << "transaction is ";
|
||||
std::ostringstream outp;
|
||||
block::gen::t_Transaction.print_ref(outp, root);
|
||||
vm::load_cell_slice(root).print_rec(outp);
|
||||
block::gen::t_Transaction.print_ref(print_limit_, outp, root, 0);
|
||||
vm::load_cell_slice(root).print_rec(print_limit_, outp);
|
||||
out << outp.str();
|
||||
}
|
||||
}
|
||||
|
@ -1611,8 +1902,8 @@ void TestNode::got_last_transactions(std::vector<ton::BlockIdExt> blkids, td::Bu
|
|||
out << "transaction #" << c << " from block " << blkid.to_str() << (dump ? " is " : "\n");
|
||||
if (dump) {
|
||||
std::ostringstream outp;
|
||||
block::gen::t_Transaction.print_ref(outp, info.transaction);
|
||||
vm::load_cell_slice(info.transaction).print_rec(outp);
|
||||
block::gen::t_Transaction.print_ref(print_limit_, outp, info.transaction);
|
||||
vm::load_cell_slice(info.transaction).print_rec(print_limit_, outp);
|
||||
out << outp.str();
|
||||
}
|
||||
block::gen::Transaction::Record trans;
|
||||
|
@ -1740,8 +2031,8 @@ void TestNode::got_all_shards(ton::BlockIdExt blk, td::BufferSlice proof, td::Bu
|
|||
auto out = td::TerminalIO::out();
|
||||
out << "shard configuration is ";
|
||||
std::ostringstream outp;
|
||||
block::gen::t_ShardHashes.print_ref(outp, root);
|
||||
vm::load_cell_slice(root).print_rec(outp);
|
||||
block::gen::t_ShardHashes.print_ref(print_limit_, outp, root);
|
||||
vm::load_cell_slice(root).print_rec(print_limit_, outp);
|
||||
out << outp.str();
|
||||
block::ShardConfig sh_conf;
|
||||
if (!sh_conf.unpack(vm::load_cell_slice_ref(root))) {
|
||||
|
@ -1764,26 +2055,35 @@ void TestNode::got_all_shards(ton::BlockIdExt blk, td::BufferSlice proof, td::Bu
|
|||
show_new_blkids();
|
||||
}
|
||||
|
||||
bool TestNode::get_config_params(ton::BlockIdExt blkid, int mode, std::string filename) {
|
||||
std::vector<int> params;
|
||||
if (mode >= 0 && !seekeoln()) {
|
||||
bool TestNode::get_config_params(ton::BlockIdExt blkid, td::Promise<td::Unit> do_after, int mode, std::string filename,
|
||||
std::vector<int> params) {
|
||||
if (mode < 0) {
|
||||
mode = 0x8000;
|
||||
}
|
||||
if (!(mode & 0x9000) && !seekeoln()) {
|
||||
mode |= 0x1000;
|
||||
while (!seekeoln()) {
|
||||
int x;
|
||||
if (!convert_int32(get_word(), x)) {
|
||||
do_after.set_error(td::Status::Error("integer configuration parameter id expected"));
|
||||
return set_error("integer configuration parameter id expected");
|
||||
}
|
||||
params.push_back(x);
|
||||
}
|
||||
}
|
||||
if (!(ready_ && !client_.empty())) {
|
||||
do_after.set_error(td::Status::Error("integer configuration parameter id expected"));
|
||||
return set_error("server connection not ready");
|
||||
}
|
||||
if (!blkid.is_masterchain_ext()) {
|
||||
do_after.set_error(td::Status::Error("integer configuration parameter id expected"));
|
||||
return set_error("only masterchain blocks contain configuration");
|
||||
}
|
||||
if (blkid == mc_last_id_) {
|
||||
mode |= 0x2000;
|
||||
}
|
||||
auto params_copy = params;
|
||||
auto b = (mode & 0x3000) == 0x1000
|
||||
auto b = (mode & 0x1000)
|
||||
? ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getConfigParams>(
|
||||
0, ton::create_tl_lite_block_id(blkid), std::move(params_copy)),
|
||||
true)
|
||||
|
@ -1792,9 +2092,11 @@ bool TestNode::get_config_params(ton::BlockIdExt blkid, int mode, std::string fi
|
|||
true);
|
||||
LOG(INFO) << "requesting " << params.size() << " configuration parameters with respect to masterchain block "
|
||||
<< blkid.to_str();
|
||||
return envelope_send_query(std::move(b), [ Self = actor_id(this), mode, filename, blkid,
|
||||
params = std::move(params) ](td::Result<td::BufferSlice> R) mutable {
|
||||
return envelope_send_query(std::move(b), [
|
||||
Self = actor_id(this), mode, filename, blkid, params = std::move(params), do_after = std::move(do_after)
|
||||
](td::Result<td::BufferSlice> R) mutable {
|
||||
if (R.is_error()) {
|
||||
do_after.set_error(R.move_as_error());
|
||||
return;
|
||||
}
|
||||
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_configInfo>(R.move_as_ok(), true);
|
||||
|
@ -1804,13 +2106,14 @@ bool TestNode::get_config_params(ton::BlockIdExt blkid, int mode, std::string fi
|
|||
auto f = F.move_as_ok();
|
||||
td::actor::send_closure_later(Self, &TestNode::got_config_params, blkid, ton::create_block_id(f->id_),
|
||||
std::move(f->state_proof_), std::move(f->config_proof_), mode, filename,
|
||||
std::move(params));
|
||||
std::move(params), std::move(do_after));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void TestNode::got_config_params(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, td::BufferSlice state_proof,
|
||||
td::BufferSlice cfg_proof, int mode, std::string filename, std::vector<int> params) {
|
||||
td::BufferSlice cfg_proof, int mode, std::string filename, std::vector<int> params,
|
||||
td::Promise<td::Unit> do_after) {
|
||||
LOG(INFO) << "got configuration parameters";
|
||||
if (!blkid.is_masterchain_ext()) {
|
||||
LOG(ERROR) << "reference block " << blkid.to_str() << " for the configuration is not a valid masterchain block";
|
||||
|
@ -1833,7 +2136,7 @@ void TestNode::got_config_params(ton::BlockIdExt req_blkid, ton::BlockIdExt blki
|
|||
return;
|
||||
}
|
||||
auto config = res.move_as_ok();
|
||||
if (mode < 0) {
|
||||
if (mode & 0x8000) {
|
||||
auto F = vm::std_boc_serialize(config->get_root_cell(), 2);
|
||||
if (F.is_error()) {
|
||||
LOG(ERROR) << "cannot serialize configuration: " << F.move_as_error().to_string();
|
||||
|
@ -1859,26 +2162,32 @@ void TestNode::got_config_params(ton::BlockIdExt req_blkid, ton::BlockIdExt blki
|
|||
} else {
|
||||
std::ostringstream os;
|
||||
if (i >= 0) {
|
||||
block::gen::ConfigParam{i}.print_ref(os, value);
|
||||
block::gen::ConfigParam{i}.print_ref(print_limit_, os, value);
|
||||
os << std::endl;
|
||||
}
|
||||
vm::load_cell_slice(value).print_rec(os);
|
||||
vm::load_cell_slice(value).print_rec(print_limit_, os);
|
||||
out << os.str() << std::endl;
|
||||
if (i == 4 && (mode & 0x2000)) {
|
||||
register_config_param4(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
config->foreach_config_param([&out](int i, Ref<vm::Cell> value) {
|
||||
config->foreach_config_param([this, &out, mode](int i, Ref<vm::Cell> value) {
|
||||
out << "ConfigParam(" << i << ") = ";
|
||||
if (value.is_null()) {
|
||||
out << "(null)\n";
|
||||
} else {
|
||||
std::ostringstream os;
|
||||
if (i >= 0) {
|
||||
block::gen::ConfigParam{i}.print_ref(os, value);
|
||||
block::gen::ConfigParam{i}.print_ref(print_limit_, os, value);
|
||||
os << std::endl;
|
||||
}
|
||||
vm::load_cell_slice(value).print_rec(os);
|
||||
vm::load_cell_slice(value).print_rec(print_limit_, os);
|
||||
out << os.str() << std::endl;
|
||||
if (i == 4 && (mode & 0x2000)) {
|
||||
register_config_param4(value);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
@ -1888,6 +2197,25 @@ void TestNode::got_config_params(ton::BlockIdExt req_blkid, ton::BlockIdExt blki
|
|||
} catch (vm::VmVirtError& err) {
|
||||
LOG(ERROR) << "virtualization error while traversing configuration: " << err.get_msg();
|
||||
}
|
||||
do_after.set_result(td::Unit());
|
||||
}
|
||||
|
||||
bool TestNode::register_config_param4(Ref<vm::Cell> value) {
|
||||
if (value.is_null()) {
|
||||
return false;
|
||||
}
|
||||
vm::CellSlice cs{vm::NoVmOrd(), std::move(value)};
|
||||
ton::StdSmcAddress addr;
|
||||
if (cs.size_ext() == 256 && cs.fetch_bits_to(addr)) {
|
||||
dns_root_queried_ = true;
|
||||
if (dns_root_ != addr) {
|
||||
dns_root_ = addr;
|
||||
LOG(INFO) << "dns root set to -1:" << addr.to_hex();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool TestNode::get_block(ton::BlockIdExt blkid, bool dump) {
|
||||
|
@ -1981,8 +2309,8 @@ void TestNode::got_block(ton::BlockIdExt blkid, td::BufferSlice data, bool dump)
|
|||
auto out = td::TerminalIO::out();
|
||||
out << "block contents is ";
|
||||
std::ostringstream outp;
|
||||
block::gen::t_Block.print_ref(outp, root);
|
||||
vm::load_cell_slice(root).print_rec(outp);
|
||||
block::gen::t_Block.print_ref(print_limit_, outp, root);
|
||||
vm::load_cell_slice(root).print_rec(print_limit_, outp);
|
||||
out << outp.str();
|
||||
show_block_header(blkid, std::move(root), 0xffff);
|
||||
} else {
|
||||
|
@ -2037,8 +2365,8 @@ void TestNode::got_state(ton::BlockIdExt blkid, ton::RootHash root_hash, ton::Fi
|
|||
auto out = td::TerminalIO::out();
|
||||
out << "shard state contents is ";
|
||||
std::ostringstream outp;
|
||||
block::gen::t_ShardState.print_ref(outp, root);
|
||||
vm::load_cell_slice(root).print_rec(outp);
|
||||
block::gen::t_ShardState.print_ref(print_limit_, outp, root);
|
||||
vm::load_cell_slice(root).print_rec(print_limit_, outp);
|
||||
out << outp.str();
|
||||
show_state_header(blkid, std::move(root), 0xffff);
|
||||
} else {
|
||||
|
@ -2173,7 +2501,7 @@ void TestNode::got_block_header(ton::BlockIdExt blkid, td::BufferSlice data, int
|
|||
auto root = res.move_as_ok();
|
||||
std::ostringstream outp;
|
||||
vm::CellSlice cs{vm::NoVm(), root};
|
||||
cs.print_rec(outp);
|
||||
cs.print_rec(print_limit_, outp);
|
||||
td::TerminalIO::out() << outp.str();
|
||||
try {
|
||||
auto virt_root = vm::MerkleProof::virtualize(root, 1);
|
||||
|
@ -2405,6 +2733,11 @@ int main(int argc, char* argv[]) {
|
|||
td::actor::send_closure(x, &TestNode::set_db_root, fname.str());
|
||||
return td::Status::OK();
|
||||
});
|
||||
p.add_option('L', "print-limit", "sets maximum count of recursively printed objects", [&](td::Slice arg) {
|
||||
auto plimit = td::to_integer<int>(arg);
|
||||
td::actor::send_closure(x, &TestNode::set_print_limit, plimit);
|
||||
return plimit >= 0 ? td::Status::OK() : td::Status::Error("printing limit must be non-negative");
|
||||
});
|
||||
p.add_option('v', "verbosity", "set verbosity level", [&](td::Slice arg) {
|
||||
verbosity = td::to_integer<int>(arg);
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL) + verbosity);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue