1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

updated liteserver

- new methods for liteserver/liteclient
- added ADNL/DHT client-only work mode
- fixed crash in ADNL
This commit is contained in:
ton 2020-02-02 16:53:37 +04:00
parent acf16718e6
commit 53ec9684bd
70 changed files with 816 additions and 322 deletions

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "lite-client.h"
@ -800,7 +800,7 @@ 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 <addr> [<block-id-ext>] <method-id> <params>...\tRuns GET method <method-id> of account <addr> "
"runmethod[x] <addr> [<block-id-ext>] <method-id> <params>...\tRuns GET method <method-id> of account <addr> "
"with specified parameters\n"
"allshards [<block-id-ext>]\tShows shard configuration from the most recent masterchain "
"state or from masterchain state corresponding to <block-id-ext>\n"
@ -871,11 +871,11 @@ 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") {
} else if (word == "runmethod" || word == "runmethodx") {
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);
parse_run_method(workchain, addr, blkid, method, word.size() > 9);
} else if (word == "allshards") {
return eoln() ? get_all_shards() : (parse_block_id_ext(blkid) && seekeoln() && get_all_shards(false, blkid));
} else if (word == "saveconfig") {
@ -1015,8 +1015,16 @@ bool TestNode::get_account_state(ton::WorkchainId workchain, ton::StdSmcAddress
});
}
td::int64 TestNode::compute_method_id(std::string method) {
td::int64 method_id;
if (!convert_int64(method, method_id)) {
method_id = (td::crc16(td::Slice{method}) & 0xffff) | 0x10000;
}
return method_id;
}
bool TestNode::parse_run_method(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt ref_blkid,
std::string method_name) {
std::string method_name, bool ext_mode) {
auto R = vm::parse_stack_entries(td::Slice(parse_ptr_, parse_end_));
if (R.is_error()) {
return set_error(R.move_as_error().to_string());
@ -1030,28 +1038,69 @@ 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);
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, 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));
}
});
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);
}
});
} else {
td::int64 method_id = compute_method_id(method_name);
// serialize parameters
vm::CellBuilder cb;
Ref<vm::Cell> cell;
if (!(vm::Stack{params}.serialize(cb) && cb.finalize_to(cell))) {
return set_error("cannot serialize stack with get-method parameters");
}
auto stk = vm::std_boc_serialize(std::move(cell));
if (stk.is_error()) {
return set_error("cannot serialize stack with get-method parameters : "s + stk.move_as_error().to_string());
}
auto b = ton::serialize_tl_object(
ton::create_tl_object<ton::lite_api::liteServer_runSmcMethod>(mode, ton::create_tl_lite_block_id(ref_blkid),
std::move(a), method_id, stk.move_as_ok()),
true);
LOG(INFO) << "requesting remote get-method execution 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, mode, 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_runMethodResult>(R.move_as_ok(), true);
if (F.is_error()) {
LOG(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_);
}
});
}
}
bool TestNode::get_one_transaction(ton::BlockIdExt blkid, ton::WorkchainId workchain, ton::StdSmcAddress addr,
@ -1212,87 +1261,147 @@ void TestNode::got_account_state(ton::BlockIdExt ref_blk, ton::BlockIdExt blk, t
}
}
void TestNode::run_smc_method(ton::BlockIdExt ref_blk, ton::BlockIdExt blk, ton::BlockIdExt shard_blk,
void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt blk, ton::BlockIdExt shard_blk,
td::BufferSlice shard_proof, td::BufferSlice proof, td::BufferSlice state,
ton::WorkchainId workchain, ton::StdSmcAddress addr, std::string method,
std::vector<vm::StackEntry> params) {
LOG(INFO) << "got account state for " << workchain << ":" << addr.to_hex() << " with respect to blocks "
<< blk.to_str() << (shard_blk == blk ? "" : std::string{" and "} + shard_blk.to_str());
block::AccountState account_state;
account_state.blk = blk;
account_state.shard_blk = shard_blk;
account_state.shard_proof = std::move(shard_proof);
account_state.proof = std::move(proof);
account_state.state = std::move(state);
auto r_info = account_state.validate(ref_blk, block::StdAddress(workchain, addr));
if (r_info.is_error()) {
LOG(ERROR) << r_info.error().message();
return;
}
std::vector<vm::StackEntry> params, td::BufferSlice remote_c7,
td::BufferSlice remote_libs, td::BufferSlice remote_result, int remote_exit_code) {
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());
auto out = td::TerminalIO::out();
auto info = r_info.move_as_ok();
if (info.root.is_null()) {
LOG(ERROR) << "account state of " << workchain << ":" << addr.to_hex() << " is empty (cannot run method `" << method
<< "`)";
return;
}
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))) {
LOG(ERROR) << "error unpacking account state";
return;
}
int tag = block::gen::t_AccountState.get_tag(*store.state);
switch (tag) {
case block::gen::AccountState::account_uninit:
LOG(ERROR) << "account " << workchain << ":" << addr.to_hex() << " not initialized yet (cannot run any methods)";
try {
block::AccountState account_state;
account_state.blk = blk;
account_state.shard_blk = shard_blk;
account_state.shard_proof = std::move(shard_proof);
account_state.proof = std::move(proof);
LOG(DEBUG) << "serialized state is " << state.size() << " bytes";
LOG(DEBUG) << "serialized remote c7 is " << remote_c7.size() << " bytes";
account_state.state = std::move(state);
account_state.is_virtualized = (mode > 0);
auto r_info = account_state.validate(ref_blk, block::StdAddress(workchain, addr));
if (r_info.is_error()) {
LOG(ERROR) << r_info.error().message();
return;
case block::gen::AccountState::account_frozen:
LOG(ERROR) << "account " << workchain << ":" << addr.to_hex() << " frozen (cannot run any methods)";
}
auto out = td::TerminalIO::out();
auto info = r_info.move_as_ok();
if (info.root.is_null()) {
LOG(ERROR) << "account state of " << workchain << ":" << addr.to_hex() << " is empty (cannot run method `"
<< method << "`)";
return;
}
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;
if (!convert_int64(method, method_id)) {
method_id = (td::crc16(td::Slice{method}) & 0xffff) | 0x10000;
}
stack.write().push_smallint(method_id);
{
std::ostringstream os;
os << "arguments: ";
stack->dump(os, 3);
out << os.str();
}
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(liteclient::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
LOG(INFO) << "starting VM to run method `" << method << "` (" << method_id << ") of smart contract " << workchain
<< ":" << addr.to_hex();
int exit_code = ~vm.run();
LOG(DEBUG) << "VM terminated with exit code " << exit_code;
if (exit_code != 0) {
LOG(ERROR) << "VM terminated with error code " << exit_code;
out << "result: error " << exit_code << std::endl;
return;
}
stack = vm.get_stack_ref();
{
std::ostringstream os;
os << "result: ";
stack->dump(os, 3);
out << os.str();
}
if (false) {
// DEBUG (dump state)
std::ostringstream os;
vm::CellSlice{vm::NoVm(), info.true_root}.print_rec(os);
out << "dump of account state (proof): " << os.str() << std::endl;
}
if (false && remote_c7.size()) {
// DEBUG (dump remote_c7)
auto r_c7 = vm::std_boc_deserialize(remote_c7).move_as_ok();
std::ostringstream os;
vm::StackEntry val;
bool ok = val.deserialize(r_c7);
val.dump(os);
// os << std::endl;
// block::gen::t_VmStackValue.print_ref(os, r_c7);
// os << std::endl;
// vm::CellSlice{vm::NoVmOrd(), r_c7}.print_rec(os);
out << "remote_c7 (deserialized=" << ok << "): " << os.str() << std::endl;
}
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))) {
LOG(ERROR) << "error unpacking account state";
return;
}
int tag = block::gen::t_AccountState.get_tag(*store.state);
switch (tag) {
case block::gen::AccountState::account_uninit:
LOG(ERROR) << "account " << workchain << ":" << addr.to_hex()
<< " not initialized yet (cannot run any methods)";
return;
case block::gen::AccountState::account_frozen:
LOG(ERROR) << "account " << workchain << ":" << addr.to_hex() << " frozen (cannot run any methods)";
return;
}
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 = compute_method_id(method);
stack.write().push_smallint(method_id);
{
std::ostringstream os;
os << "arguments: ";
stack->dump(os, 3);
out << os.str();
}
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(liteclient::prepare_vm_c7(info.gen_utime, info.gen_lt, td::make_ref<vm::CellSlice>(acc.addr->clone()),
balance)); // tuple with SmartContractInfo
// vm.incr_stack_trace(1); // enable stack dump after each step
LOG(INFO) << "starting VM to run method `" << method << "` (" << method_id << ") of smart contract " << workchain
<< ":" << addr.to_hex();
int exit_code = ~vm.run();
LOG(DEBUG) << "VM terminated with exit code " << exit_code;
if (mode > 0) {
LOG(DEBUG) << "remote VM exit code is " << remote_exit_code;
}
if (exit_code != 0) {
LOG(ERROR) << "VM terminated with error code " << exit_code;
out << "result: error " << exit_code << std::endl;
return;
}
stack = vm.get_stack_ref();
{
std::ostringstream os;
os << "result: ";
stack->dump(os, 3);
out << os.str();
}
if (mode & 4) {
if (remote_result.empty()) {
out << "remote result: <none>, exit code " << remote_exit_code;
} else {
auto res = vm::std_boc_deserialize(std::move(remote_result));
if (res.is_error()) {
LOG(ERROR) << "cannot deserialize remote VM result boc: " << res.move_as_error();
return;
}
auto cs = vm::load_cell_slice(res.move_as_ok());
Ref<vm::Stack> remote_stack;
if (!(vm::Stack::deserialize_to(cs, remote_stack, 0) && cs.empty_ext())) {
LOG(ERROR) << "remote VM result boc cannot be deserialized as a VmStack";
return;
}
std::ostringstream os;
os << "remote result (not to be trusted): ";
remote_stack->dump(os, 3);
out << os.str();
}
}
if (0) { // DEBUG
std::ostringstream os;
LOG(DEBUG) << "dumping constructed proof";
//vm::CellSlice{vm::NoVm(), pb.extract_proof()}.print_rec(os);
out << "constructed state proof: " << os.str();
}
} catch (vm::VmVirtError& err) {
out << "virtualization error while parsing runSmcMethod result: " << err.get_msg();
} catch (vm::VmError& err) {
out << "error while parsing runSmcMethod result: " << err.get_msg();
}
}
@ -2062,7 +2171,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};
vm::CellSlice cs{vm::NoVm(), root};
cs.print_rec(outp);
td::TerminalIO::out() << outp.str();
try {