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

Emulator improvements (#687)

* ticktock, unixtime, optional config

* documentation

* fix account.now

* emulate tick/tock for emscripten

* remove excessive check

* limit stack serialization calls
This commit is contained in:
Marat 2023-05-24 10:40:04 +01:00 committed by GitHub
parent 86623b4cea
commit c527bfeceb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 154 additions and 20 deletions

View file

@ -8,6 +8,7 @@
#include "transaction-emulator.h"
#include "tvm-emulator.hpp"
#include "crypto/vm/stack.hpp"
#include "crypto/vm/memo.h"
td::Result<td::Ref<vm::Cell>> boc_b64_to_cell(const char *boc) {
TRY_RESULT_PREFIX(boc_decoded, td::base64_decode(td::Slice(boc)), "Can't decode base64 boc: ");
@ -42,6 +43,7 @@ const char *error_response(std::string&& error) {
auto json_obj = jb.enter_object();
json_obj("success", td::JsonFalse());
json_obj("error", std::move(error));
json_obj("external_not_accepted", td::JsonFalse());
json_obj.leave();
return strdup(jb.string_builder().as_cslice().c_str());
}
@ -51,6 +53,7 @@ const char *external_not_accepted_response(std::string&& vm_log, int vm_exit_cod
auto json_obj = jb.enter_object();
json_obj("success", td::JsonFalse());
json_obj("error", "External message not accepted by smart contract");
json_obj("external_not_accepted", td::JsonTrue());
json_obj("vm_log", std::move(vm_log));
json_obj("vm_exit_code", vm_exit_code);
json_obj("elapsed_time", elapsed_time);
@ -131,13 +134,16 @@ const char *transaction_emulator_emulate_transaction(void *transaction_emulator,
}
auto account = block::Account(wc, addr.bits());
ton::UnixTime now = (unsigned)std::time(nullptr);
ton::UnixTime now = emulator->get_unixtime();
if (!now) {
now = (unsigned)std::time(nullptr);
}
bool is_special = wc == ton::masterchainId && emulator->get_config().is_special_smartcontract(addr);
if (!account.unpack(vm::load_cell_slice_ref(shard_account_cell.move_as_ok()), td::Ref<vm::CellSlice>(), now, is_special)) {
ERROR_RESPONSE(PSTRING() << "Can't unpack shard account");
}
auto result = emulator->emulate_transaction(std::move(account), message_cell, 0, 0, block::transaction::Transaction::tr_ord);
auto result = emulator->emulate_transaction(std::move(account), message_cell, now, 0, block::transaction::Transaction::tr_ord);
if (result.is_error()) {
ERROR_RESPONSE(PSTRING() << "Emulate transaction failed: " << result.move_as_error());
}
@ -176,6 +182,79 @@ const char *transaction_emulator_emulate_transaction(void *transaction_emulator,
std::move(actions_boc_b64), emulation_success.elapsed_time);
}
const char *transaction_emulator_emulate_tick_tock_transaction(void *transaction_emulator, const char *shard_account_boc, bool is_tock) {
auto emulator = static_cast<emulator::TransactionEmulator *>(transaction_emulator);
auto shard_account_cell = boc_b64_to_cell(shard_account_boc);
if (shard_account_cell.is_error()) {
ERROR_RESPONSE(PSTRING() << "Can't deserialize shard account boc: " << shard_account_cell.move_as_error());
}
auto shard_account_slice = vm::load_cell_slice(shard_account_cell.ok_ref());
block::gen::ShardAccount::Record shard_account;
if (!tlb::unpack(shard_account_slice, shard_account)) {
ERROR_RESPONSE(PSTRING() << "Can't unpack shard account cell");
}
td::Ref<vm::CellSlice> addr_slice;
auto account_slice = vm::load_cell_slice(shard_account.account);
if (block::gen::t_Account.get_tag(account_slice) == block::gen::Account::account_none) {
ERROR_RESPONSE(PSTRING() << "Can't run tick/tock transaction on account_none");
}
block::gen::Account::Record_account account_record;
if (!tlb::unpack(account_slice, account_record)) {
ERROR_RESPONSE(PSTRING() << "Can't unpack account cell");
}
addr_slice = std::move(account_record.addr);
ton::WorkchainId wc;
ton::StdSmcAddress addr;
if (!block::tlb::t_MsgAddressInt.extract_std_address(addr_slice, wc, addr)) {
ERROR_RESPONSE(PSTRING() << "Can't extract account address");
}
auto account = block::Account(wc, addr.bits());
ton::UnixTime now = emulator->get_unixtime();
if (!now) {
now = (unsigned)std::time(nullptr);
}
bool is_special = wc == ton::masterchainId && emulator->get_config().is_special_smartcontract(addr);
if (!account.unpack(vm::load_cell_slice_ref(shard_account_cell.move_as_ok()), td::Ref<vm::CellSlice>(), now, is_special)) {
ERROR_RESPONSE(PSTRING() << "Can't unpack shard account");
}
auto trans_type = is_tock ? block::transaction::Transaction::tr_tock : block::transaction::Transaction::tr_tick;
auto result = emulator->emulate_transaction(std::move(account), {}, now, 0, trans_type);
if (result.is_error()) {
ERROR_RESPONSE(PSTRING() << "Emulate transaction failed: " << result.move_as_error());
}
auto emulation_result = result.move_as_ok();
auto emulation_success = dynamic_cast<emulator::TransactionEmulator::EmulationSuccess&>(*emulation_result);
auto trans_boc_b64 = cell_to_boc_b64(std::move(emulation_success.transaction));
if (trans_boc_b64.is_error()) {
ERROR_RESPONSE(PSTRING() << "Can't serialize Transaction to boc " << trans_boc_b64.move_as_error());
}
auto new_shard_account_cell = vm::CellBuilder().store_ref(emulation_success.account.total_state)
.store_bits(emulation_success.account.last_trans_hash_.as_bitslice())
.store_long(emulation_success.account.last_trans_lt_).finalize();
auto new_shard_account_boc_b64 = cell_to_boc_b64(std::move(new_shard_account_cell));
if (new_shard_account_boc_b64.is_error()) {
ERROR_RESPONSE(PSTRING() << "Can't serialize ShardAccount to boc " << new_shard_account_boc_b64.move_as_error());
}
td::optional<td::string> actions_boc_b64;
if (emulation_success.actions.not_null()) {
auto actions_boc_b64_result = cell_to_boc_b64(std::move(emulation_success.actions));
if (actions_boc_b64_result.is_error()) {
ERROR_RESPONSE(PSTRING() << "Can't serialize actions list cell to boc " << actions_boc_b64_result.move_as_error());
}
actions_boc_b64 = actions_boc_b64_result.move_as_ok();
}
return success_response(trans_boc_b64.move_as_ok(), new_shard_account_boc_b64.move_as_ok(), std::move(emulation_success.vm_log),
std::move(actions_boc_b64), emulation_success.elapsed_time);
}
bool transaction_emulator_set_unixtime(void *transaction_emulator, uint32_t unixtime) {
auto emulator = static_cast<emulator::TransactionEmulator *>(transaction_emulator);
@ -309,16 +388,19 @@ bool tvm_emulator_set_c7(void *tvm_emulator, const char *address, uint32_t unixt
return false;
}
auto config_params_cell = boc_b64_to_cell(config_boc);
if (config_params_cell.is_error()) {
LOG(ERROR) << "Can't deserialize config params boc: " << config_params_cell.move_as_error();
return false;
}
auto global_config = std::make_shared<block::Config>(config_params_cell.move_as_ok(), td::Bits256::zero(), block::Config::needWorkchainInfo | block::Config::needSpecialSmc);
auto unpack_res = global_config->unpack();
if (unpack_res.is_error()) {
LOG(ERROR) << "Can't unpack config params";
return false;
std::shared_ptr<block::Config> global_config;
if (config_boc != nullptr) {
auto config_params_cell = boc_b64_to_cell(config_boc);
if (config_params_cell.is_error()) {
LOG(ERROR) << "Can't deserialize config params boc: " << config_params_cell.move_as_error();
return false;
}
global_config = std::make_shared<block::Config>(config_params_cell.move_as_ok(), td::Bits256::zero(), block::Config::needWorkchainInfo | block::Config::needSpecialSmc);
auto unpack_res = global_config->unpack();
if (unpack_res.is_error()) {
LOG(ERROR) << "Can't unpack config params";
return false;
}
}
auto rand_seed_hex_slice = td::Slice(rand_seed_hex);
@ -365,6 +447,9 @@ const char *tvm_emulator_run_get_method(void *tvm_emulator, int method_id, const
auto emulator = static_cast<emulator::TvmEmulator *>(tvm_emulator);
auto result = emulator->run_get_method(method_id, stack);
vm::FakeVmStateLimits fstate(1000); // limit recursive (de)serialization calls
vm::VmStateInterface::Guard guard(&fstate);
vm::CellBuilder stack_cb;
if (!result.stack->serialize(stack_cb)) {
ERROR_RESPONSE(PSTRING() << "Couldn't serialize stack");