2
#include "transaction-emulator.h"
3
#include "crypto/common/refcnt.hpp"
5
#include "tdutils/td/utils/Time.h"
8
using namespace std::string_literals;
11
td::Result<std::unique_ptr<TransactionEmulator::EmulationResult>> TransactionEmulator::emulate_transaction(
12
block::Account&& account, td::Ref<vm::Cell> msg_root, ton::UnixTime utime, ton::LogicalTime lt, int trans_type) {
14
td::Ref<vm::Cell> old_mparams;
15
std::vector<block::StoragePrices> storage_prices;
16
block::StoragePhaseConfig storage_phase_cfg{&storage_prices};
17
block::ComputePhaseConfig compute_phase_cfg;
18
block::ActionPhaseConfig action_phase_cfg;
19
td::RefInt256 masterchain_create_fee, basechain_create_fee;
25
utime = (unsigned)std::time(nullptr);
28
auto fetch_res = block::FetchConfigParams::fetch_config_params(config_, prev_blocks_info_, &old_mparams,
29
&storage_prices, &storage_phase_cfg,
30
&rand_seed_, &compute_phase_cfg,
31
&action_phase_cfg, &masterchain_create_fee,
32
&basechain_create_fee, account.workchain, utime);
33
if(fetch_res.is_error()) {
34
return fetch_res.move_as_error_prefix("cannot fetch config params ");
37
TRY_STATUS(vm::init_vm(debug_enabled_));
43
lt = (account.last_trans_lt_ / block::ConfigInfo::get_lt_align() + 1) * block::ConfigInfo::get_lt_align(); // next block after account_.last_trans_lt_
45
account.block_lt = lt - lt % block::ConfigInfo::get_lt_align();
47
compute_phase_cfg.libraries = std::make_unique<vm::Dictionary>(libraries_);
48
compute_phase_cfg.ignore_chksig = ignore_chksig_;
49
compute_phase_cfg.with_vm_log = true;
50
compute_phase_cfg.vm_log_verbosity = vm_log_verbosity_;
52
double start_time = td::Time::now();
53
auto res = create_transaction(msg_root, &account, utime, lt, trans_type,
54
&storage_phase_cfg, &compute_phase_cfg,
56
double elapsed = td::Time::now() - start_time;
59
return res.move_as_error_prefix("cannot run message on account ");
61
std::unique_ptr<block::transaction::Transaction> trans = res.move_as_ok();
63
if (!trans->compute_phase->accepted && trans->in_msg_extern) {
64
auto vm_log = trans->compute_phase->vm_log;
65
auto vm_exit_code = trans->compute_phase->exit_code;
66
return std::make_unique<TransactionEmulator::EmulationExternalNotAccepted>(std::move(vm_log), vm_exit_code, elapsed);
69
if (!trans->serialize()) {
70
return td::Status::Error(-669,"cannot serialize new transaction for smart contract "s + trans->account.addr.to_hex());
73
auto trans_root = trans->commit(account);
74
if (trans_root.is_null()) {
75
return td::Status::Error(PSLICE() << "cannot commit new transaction for smart contract");
78
return std::make_unique<TransactionEmulator::EmulationSuccess>(std::move(trans_root), std::move(account),
79
std::move(trans->compute_phase->vm_log), std::move(trans->compute_phase->actions), elapsed);
82
td::Result<TransactionEmulator::EmulationSuccess> TransactionEmulator::emulate_transaction(block::Account&& account, td::Ref<vm::Cell> original_trans) {
84
block::gen::Transaction::Record record_trans;
85
if (!tlb::unpack_cell(original_trans, record_trans)) {
86
return td::Status::Error("Failed to unpack Transaction");
89
ton::LogicalTime lt = record_trans.lt;
90
ton::UnixTime utime = record_trans.now;
92
account.block_lt = record_trans.lt - record_trans.lt % block::ConfigInfo::get_lt_align();
93
td::Ref<vm::Cell> msg_root = record_trans.r1.in_msg->prefetch_ref();
94
int tag = block::gen::t_TransactionDescr.get_tag(vm::load_cell_slice(record_trans.description));
96
int trans_type = block::transaction::Transaction::tr_none;
98
case block::gen::TransactionDescr::trans_ord: {
99
trans_type = block::transaction::Transaction::tr_ord;
102
case block::gen::TransactionDescr::trans_storage: {
103
trans_type = block::transaction::Transaction::tr_storage;
106
case block::gen::TransactionDescr::trans_tick_tock: {
107
block::gen::TransactionDescr::Record_trans_tick_tock tick_tock;
108
if (!tlb::unpack_cell(record_trans.description, tick_tock)) {
109
return td::Status::Error("Failed to unpack tick tock transaction description");
111
trans_type = tick_tock.is_tock ? block::transaction::Transaction::tr_tock : block::transaction::Transaction::tr_tick;
114
case block::gen::TransactionDescr::trans_split_prepare: {
115
trans_type = block::transaction::Transaction::tr_split_prepare;
118
case block::gen::TransactionDescr::trans_split_install: {
119
trans_type = block::transaction::Transaction::tr_split_install;
122
case block::gen::TransactionDescr::trans_merge_prepare: {
123
trans_type = block::transaction::Transaction::tr_merge_prepare;
126
case block::gen::TransactionDescr::trans_merge_install: {
127
trans_type = block::transaction::Transaction::tr_merge_install;
132
TRY_RESULT(emulation, emulate_transaction(std::move(account), msg_root, utime, lt, trans_type));
134
auto emulation_result = dynamic_cast<EmulationSuccess&>(*emulation);
135
if (td::Bits256(emulation_result.transaction->get_hash().bits()) != td::Bits256(original_trans->get_hash().bits())) {
136
return td::Status::Error("transaction hash mismatch");
139
if (!check_state_update(emulation_result.account, record_trans)) {
140
return td::Status::Error("account hash mismatch");
143
return emulation_result;
146
td::Result<TransactionEmulator::EmulationChain> TransactionEmulator::emulate_transactions_chain(block::Account&& account, std::vector<td::Ref<vm::Cell>>&& original_transactions) {
148
std::vector<td::Ref<vm::Cell>> emulated_transactions;
149
for (const auto& original_trans : original_transactions) {
150
if (original_trans.is_null()) {
154
TRY_RESULT(emulation_result, emulate_transaction(std::move(account), original_trans));
155
emulated_transactions.push_back(std::move(emulation_result.transaction));
156
account = std::move(emulation_result.account);
159
return TransactionEmulator::EmulationChain{ std::move(emulated_transactions), std::move(account) };
162
bool TransactionEmulator::check_state_update(const block::Account& account, const block::gen::Transaction::Record& trans) {
163
block::gen::HASH_UPDATE::Record hash_update;
164
return tlb::type_unpack_cell(trans.state_update, block::gen::t_HASH_UPDATE_Account, hash_update) &&
165
hash_update.new_hash == account.total_state->get_hash().bits();
168
td::Result<std::unique_ptr<block::transaction::Transaction>> TransactionEmulator::create_transaction(
169
td::Ref<vm::Cell> msg_root, block::Account* acc,
170
ton::UnixTime utime, ton::LogicalTime lt, int trans_type,
171
block::StoragePhaseConfig* storage_phase_cfg,
172
block::ComputePhaseConfig* compute_phase_cfg,
173
block::ActionPhaseConfig* action_phase_cfg) {
174
bool external{false}, ihr_delivered{false}, need_credit_phase{false};
176
if (msg_root.not_null()) {
177
auto cs = vm::load_cell_slice(msg_root);
178
external = block::gen::t_CommonMsgInfo.get_tag(cs);
181
if (trans_type == block::transaction::Transaction::tr_ord) {
182
need_credit_phase = !external;
183
} else if (trans_type == block::transaction::Transaction::tr_merge_install) {
184
need_credit_phase = true;
187
std::unique_ptr<block::transaction::Transaction> trans =
188
std::make_unique<block::transaction::Transaction>(*acc, trans_type, lt, utime, msg_root);
190
if (msg_root.not_null() && !trans->unpack_input_msg(ihr_delivered, action_phase_cfg)) {
192
// inbound external message was not accepted
193
return td::Status::Error(-701,"inbound external message rejected by account "s + acc->addr.to_hex() +
194
" before smart-contract execution");
196
return td::Status::Error(-669,"cannot unpack input message for a new transaction");
199
if (trans->bounce_enabled) {
200
if (!trans->prepare_storage_phase(*storage_phase_cfg, true)) {
201
return td::Status::Error(-669,"cannot create storage phase of a new transaction for smart contract "s + acc->addr.to_hex());
203
if (need_credit_phase && !trans->prepare_credit_phase()) {
204
return td::Status::Error(-669,"cannot create credit phase of a new transaction for smart contract "s + acc->addr.to_hex());
207
if (need_credit_phase && !trans->prepare_credit_phase()) {
208
return td::Status::Error(-669,"cannot create credit phase of a new transaction for smart contract "s + acc->addr.to_hex());
210
if (!trans->prepare_storage_phase(*storage_phase_cfg, true, need_credit_phase)) {
211
return td::Status::Error(-669,"cannot create storage phase of a new transaction for smart contract "s + acc->addr.to_hex());
215
if (!trans->prepare_compute_phase(*compute_phase_cfg)) {
216
return td::Status::Error(-669,"cannot create compute phase of a new transaction for smart contract "s + acc->addr.to_hex());
219
if (!trans->compute_phase->accepted) {
220
if (!external && trans->compute_phase->skip_reason == block::ComputePhase::sk_none) {
221
return td::Status::Error(-669,"new ordinary transaction for smart contract "s + acc->addr.to_hex() +
222
" has not been accepted by the smart contract (?)");
226
if (trans->compute_phase->success && !trans->prepare_action_phase(*action_phase_cfg)) {
227
return td::Status::Error(-669,"cannot create action phase of a new transaction for smart contract "s + acc->addr.to_hex());
230
if (trans->bounce_enabled && !trans->compute_phase->success && !trans->prepare_bounce_phase(*action_phase_cfg)) {
231
return td::Status::Error(-669,"cannot create bounce phase of a new transaction for smart contract "s + acc->addr.to_hex());
237
void TransactionEmulator::set_unixtime(ton::UnixTime unixtime) {
238
unixtime_ = unixtime;
241
void TransactionEmulator::set_lt(ton::LogicalTime lt) {
245
void TransactionEmulator::set_rand_seed(td::BitArray<256>& rand_seed) {
246
rand_seed_ = rand_seed;
249
void TransactionEmulator::set_ignore_chksig(bool ignore_chksig) {
250
ignore_chksig_ = ignore_chksig;
253
void TransactionEmulator::set_config(block::Config &&config) {
254
config_ = std::forward<block::Config>(config);
257
void TransactionEmulator::set_libs(vm::Dictionary &&libs) {
258
libraries_ = std::forward<vm::Dictionary>(libs);
261
void TransactionEmulator::set_debug_enabled(bool debug_enabled) {
262
debug_enabled_ = debug_enabled;
265
void TransactionEmulator::set_prev_blocks_info(td::Ref<vm::Tuple> prev_blocks_info) {
266
prev_blocks_info_ = std::move(prev_blocks_info);
269
} // namespace emulator