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:
parent
86623b4cea
commit
c527bfeceb
6 changed files with 154 additions and 20 deletions
|
@ -12,6 +12,8 @@ struct TransactionEmulationParams {
|
||||||
uint64_t lt;
|
uint64_t lt;
|
||||||
td::optional<std::string> rand_seed_hex;
|
td::optional<std::string> rand_seed_hex;
|
||||||
bool ignore_chksig;
|
bool ignore_chksig;
|
||||||
|
bool is_tick_tock;
|
||||||
|
bool is_tock;
|
||||||
bool debug_enabled;
|
bool debug_enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,6 +43,16 @@ td::Result<TransactionEmulationParams> decode_transaction_emulation_params(const
|
||||||
TRY_RESULT(debug_enabled, td::get_json_object_bool_field(obj, "debug_enabled", false));
|
TRY_RESULT(debug_enabled, td::get_json_object_bool_field(obj, "debug_enabled", false));
|
||||||
params.debug_enabled = debug_enabled;
|
params.debug_enabled = debug_enabled;
|
||||||
|
|
||||||
|
TRY_RESULT(is_tick_tock, td::get_json_object_bool_field(obj, "is_tick_tock", true, false));
|
||||||
|
params.is_tick_tock = is_tick_tock;
|
||||||
|
|
||||||
|
TRY_RESULT(is_tock, td::get_json_object_bool_field(obj, "is_tock", true, false));
|
||||||
|
params.is_tock = is_tock;
|
||||||
|
|
||||||
|
if (is_tock && !is_tick_tock) {
|
||||||
|
return td::Status::Error("Inconsistent parameters is_tick_tock=false, is_tock=true");
|
||||||
|
}
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +149,12 @@ const char *emulate(const char *config, const char* libs, int verbosity, const c
|
||||||
return strdup(R"({"fail":true,"message":"Can't set params"})");
|
return strdup(R"({"fail":true,"message":"Can't set params"})");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto tx = transaction_emulator_emulate_transaction(em, account, message);
|
const char *result;
|
||||||
|
if (decoded_params.is_tick_tock) {
|
||||||
|
result = transaction_emulator_emulate_tick_tock_transaction(em, account, decoded_params.is_tock);
|
||||||
|
} else {
|
||||||
|
result = transaction_emulator_emulate_transaction(em, account, message);
|
||||||
|
}
|
||||||
|
|
||||||
transaction_emulator_destroy(em);
|
transaction_emulator_destroy(em);
|
||||||
|
|
||||||
|
@ -145,12 +162,12 @@ const char *emulate(const char *config, const char* libs, int verbosity, const c
|
||||||
{
|
{
|
||||||
td::JsonBuilder jb;
|
td::JsonBuilder jb;
|
||||||
auto json_obj = jb.enter_object();
|
auto json_obj = jb.enter_object();
|
||||||
json_obj("output", td::JsonRaw(td::Slice(tx)));
|
json_obj("output", td::JsonRaw(td::Slice(result)));
|
||||||
json_obj("logs", logger.get_string());
|
json_obj("logs", logger.get_string());
|
||||||
json_obj.leave();
|
json_obj.leave();
|
||||||
output = strdup(jb.string_builder().as_cslice().c_str());
|
output = strdup(jb.string_builder().as_cslice().c_str());
|
||||||
}
|
}
|
||||||
free((void*) tx);
|
free((void*) result);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "transaction-emulator.h"
|
#include "transaction-emulator.h"
|
||||||
#include "tvm-emulator.hpp"
|
#include "tvm-emulator.hpp"
|
||||||
#include "crypto/vm/stack.hpp"
|
#include "crypto/vm/stack.hpp"
|
||||||
|
#include "crypto/vm/memo.h"
|
||||||
|
|
||||||
td::Result<td::Ref<vm::Cell>> boc_b64_to_cell(const char *boc) {
|
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: ");
|
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();
|
auto json_obj = jb.enter_object();
|
||||||
json_obj("success", td::JsonFalse());
|
json_obj("success", td::JsonFalse());
|
||||||
json_obj("error", std::move(error));
|
json_obj("error", std::move(error));
|
||||||
|
json_obj("external_not_accepted", td::JsonFalse());
|
||||||
json_obj.leave();
|
json_obj.leave();
|
||||||
return strdup(jb.string_builder().as_cslice().c_str());
|
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();
|
auto json_obj = jb.enter_object();
|
||||||
json_obj("success", td::JsonFalse());
|
json_obj("success", td::JsonFalse());
|
||||||
json_obj("error", "External message not accepted by smart contract");
|
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_log", std::move(vm_log));
|
||||||
json_obj("vm_exit_code", vm_exit_code);
|
json_obj("vm_exit_code", vm_exit_code);
|
||||||
json_obj("elapsed_time", elapsed_time);
|
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());
|
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);
|
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)) {
|
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");
|
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()) {
|
if (result.is_error()) {
|
||||||
ERROR_RESPONSE(PSTRING() << "Emulate transaction failed: " << result.move_as_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);
|
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) {
|
bool transaction_emulator_set_unixtime(void *transaction_emulator, uint32_t unixtime) {
|
||||||
auto emulator = static_cast<emulator::TransactionEmulator *>(transaction_emulator);
|
auto emulator = static_cast<emulator::TransactionEmulator *>(transaction_emulator);
|
||||||
|
|
||||||
|
@ -309,17 +388,20 @@ bool tvm_emulator_set_c7(void *tvm_emulator, const char *address, uint32_t unixt
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<block::Config> global_config;
|
||||||
|
if (config_boc != nullptr) {
|
||||||
auto config_params_cell = boc_b64_to_cell(config_boc);
|
auto config_params_cell = boc_b64_to_cell(config_boc);
|
||||||
if (config_params_cell.is_error()) {
|
if (config_params_cell.is_error()) {
|
||||||
LOG(ERROR) << "Can't deserialize config params boc: " << config_params_cell.move_as_error();
|
LOG(ERROR) << "Can't deserialize config params boc: " << config_params_cell.move_as_error();
|
||||||
return false;
|
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);
|
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();
|
auto unpack_res = global_config->unpack();
|
||||||
if (unpack_res.is_error()) {
|
if (unpack_res.is_error()) {
|
||||||
LOG(ERROR) << "Can't unpack config params";
|
LOG(ERROR) << "Can't unpack config params";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto rand_seed_hex_slice = td::Slice(rand_seed_hex);
|
auto rand_seed_hex_slice = td::Slice(rand_seed_hex);
|
||||||
if (rand_seed_hex_slice.size() != 64) {
|
if (rand_seed_hex_slice.size() != 64) {
|
||||||
|
@ -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 emulator = static_cast<emulator::TvmEmulator *>(tvm_emulator);
|
||||||
auto result = emulator->run_get_method(method_id, stack);
|
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;
|
vm::CellBuilder stack_cb;
|
||||||
if (!result.stack->serialize(stack_cb)) {
|
if (!result.stack->serialize(stack_cb)) {
|
||||||
ERROR_RESPONSE(PSTRING() << "Couldn't serialize stack");
|
ERROR_RESPONSE(PSTRING() << "Couldn't serialize stack");
|
||||||
|
|
|
@ -80,8 +80,9 @@ EMULATOR_EXPORT bool transaction_emulator_set_debug_enabled(void *transaction_em
|
||||||
* @return Json object with error:
|
* @return Json object with error:
|
||||||
* {
|
* {
|
||||||
* "success": false,
|
* "success": false,
|
||||||
* "error": "Error description"
|
* "error": "Error description",
|
||||||
* // and optional fields "vm_exit_code" and "vm_log" in case external message was not accepted.
|
* "external_not_accepted": false,
|
||||||
|
* // and optional fields "vm_exit_code", "vm_log", "elapsed_time" in case external message was not accepted.
|
||||||
* }
|
* }
|
||||||
* Or success:
|
* Or success:
|
||||||
* {
|
* {
|
||||||
|
@ -89,11 +90,35 @@ EMULATOR_EXPORT bool transaction_emulator_set_debug_enabled(void *transaction_em
|
||||||
* "transaction": "Base64 encoded Transaction boc",
|
* "transaction": "Base64 encoded Transaction boc",
|
||||||
* "shard_account": "Base64 encoded new ShardAccount boc",
|
* "shard_account": "Base64 encoded new ShardAccount boc",
|
||||||
* "vm_log": "execute DUP...",
|
* "vm_log": "execute DUP...",
|
||||||
* "actions": "Base64 encoded compute phase actions boc (OutList n)"
|
* "actions": "Base64 encoded compute phase actions boc (OutList n)",
|
||||||
|
* "elapsed_time": 0.02
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
EMULATOR_EXPORT const char *transaction_emulator_emulate_transaction(void *transaction_emulator, const char *shard_account_boc, const char *message_boc);
|
EMULATOR_EXPORT const char *transaction_emulator_emulate_transaction(void *transaction_emulator, const char *shard_account_boc, const char *message_boc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Emulate tick tock transaction
|
||||||
|
* @param transaction_emulator Pointer to TransactionEmulator object
|
||||||
|
* @param shard_account_boc Base64 encoded BoC serialized ShardAccount of special account
|
||||||
|
* @param is_tock True for tock transactions, false for tick
|
||||||
|
* @return Json object with error:
|
||||||
|
* {
|
||||||
|
* "success": false,
|
||||||
|
* "error": "Error description",
|
||||||
|
* "external_not_accepted": false
|
||||||
|
* }
|
||||||
|
* Or success:
|
||||||
|
* {
|
||||||
|
* "success": true,
|
||||||
|
* "transaction": "Base64 encoded Transaction boc",
|
||||||
|
* "shard_account": "Base64 encoded new ShardAccount boc",
|
||||||
|
* "vm_log": "execute DUP...",
|
||||||
|
* "actions": "Base64 encoded compute phase actions boc (OutList n)",
|
||||||
|
* "elapsed_time": 0.02
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
EMULATOR_EXPORT const char *transaction_emulator_emulate_tick_tock_transaction(void *transaction_emulator, const char *shard_account_boc, bool is_tock);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Destroy TransactionEmulator object
|
* @brief Destroy TransactionEmulator object
|
||||||
* @param transaction_emulator Pointer to TransactionEmulator object
|
* @param transaction_emulator Pointer to TransactionEmulator object
|
||||||
|
@ -129,7 +154,7 @@ EMULATOR_EXPORT bool tvm_emulator_set_libraries(void *tvm_emulator, const char *
|
||||||
* @param unixtime Unix timestamp
|
* @param unixtime Unix timestamp
|
||||||
* @param balance Smart contract balance
|
* @param balance Smart contract balance
|
||||||
* @param rand_seed_hex Random seed as hex string of length 64
|
* @param rand_seed_hex Random seed as hex string of length 64
|
||||||
* @param config Base64 encoded BoC serialized Config dictionary (Hashmap 32 ^Cell)
|
* @param config Base64 encoded BoC serialized Config dictionary (Hashmap 32 ^Cell). Optional.
|
||||||
* @return true in case of success, false in case of error
|
* @return true in case of success, false in case of error
|
||||||
*/
|
*/
|
||||||
EMULATOR_EXPORT bool tvm_emulator_set_c7(void *tvm_emulator, const char *address, uint32_t unixtime, uint64_t balance, const char *rand_seed_hex, const char *config);
|
EMULATOR_EXPORT bool tvm_emulator_set_c7(void *tvm_emulator, const char *address, uint32_t unixtime, uint64_t balance, const char *rand_seed_hex, const char *config);
|
||||||
|
|
|
@ -7,6 +7,7 @@ _transaction_emulator_set_config
|
||||||
_transaction_emulator_set_libs
|
_transaction_emulator_set_libs
|
||||||
_transaction_emulator_set_debug_enabled
|
_transaction_emulator_set_debug_enabled
|
||||||
_transaction_emulator_emulate_transaction
|
_transaction_emulator_emulate_transaction
|
||||||
|
_transaction_emulator_emulate_tick_tock_transaction
|
||||||
_transaction_emulator_destroy
|
_transaction_emulator_destroy
|
||||||
_emulator_set_verbosity_level
|
_emulator_set_verbosity_level
|
||||||
_tvm_emulator_create
|
_tvm_emulator_create
|
||||||
|
|
|
@ -59,6 +59,10 @@ public:
|
||||||
return config_;
|
return config_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ton::UnixTime get_unixtime() {
|
||||||
|
return unixtime_;
|
||||||
|
}
|
||||||
|
|
||||||
td::Result<std::unique_ptr<EmulationResult>> emulate_transaction(
|
td::Result<std::unique_ptr<EmulationResult>> emulate_transaction(
|
||||||
block::Account&& account, td::Ref<vm::Cell> msg_root, ton::UnixTime utime, ton::LogicalTime lt, int trans_type);
|
block::Account&& account, td::Ref<vm::Cell> msg_root, ton::UnixTime utime, ton::LogicalTime lt, int trans_type);
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,10 @@ public:
|
||||||
args_.set_now(unixtime);
|
args_.set_now(unixtime);
|
||||||
args_.set_balance(balance);
|
args_.set_balance(balance);
|
||||||
args_.set_rand_seed(rand_seed);
|
args_.set_rand_seed(rand_seed);
|
||||||
|
if (config) {
|
||||||
args_.set_config(config);
|
args_.set_config(config);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void set_debug_enabled(bool debug_enabled) {
|
void set_debug_enabled(bool debug_enabled) {
|
||||||
args_.set_debug_enabled(debug_enabled);
|
args_.set_debug_enabled(debug_enabled);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue