Ton

Форк
0
/
transaction-emulator.cpp 
269 строк · 11.4 Кб
1
#include <string>
2
#include "transaction-emulator.h"
3
#include "crypto/common/refcnt.hpp"
4
#include "vm/vm.h"
5
#include "tdutils/td/utils/Time.h"
6

7
using td::Ref;
8
using namespace std::string_literals;
9

10
namespace emulator {
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) {
13

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;
20
    
21
    if (!utime) {
22
      utime = unixtime_;
23
    }
24
    if (!utime) {
25
      utime = (unsigned)std::time(nullptr);
26
    }
27

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 ");
35
    }
36

37
    TRY_STATUS(vm::init_vm(debug_enabled_));
38

39
    if (!lt) {
40
      lt = lt_;
41
    }
42
    if (!lt) {
43
      lt = (account.last_trans_lt_ / block::ConfigInfo::get_lt_align() + 1) * block::ConfigInfo::get_lt_align(); // next block after account_.last_trans_lt_
44
    }
45
    account.block_lt = lt - lt % block::ConfigInfo::get_lt_align();
46

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_;
51

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,
55
                                                    &action_phase_cfg);
56
    double elapsed = td::Time::now() - start_time;
57

58
    if(res.is_error()) {
59
      return res.move_as_error_prefix("cannot run message on account ");
60
    }
61
    std::unique_ptr<block::transaction::Transaction> trans = res.move_as_ok();
62

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);
67
    }
68

69
    if (!trans->serialize()) {
70
      return td::Status::Error(-669,"cannot serialize new transaction for smart contract "s + trans->account.addr.to_hex());
71
    }
72

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");
76
    }
77

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);
80
}
81

82
td::Result<TransactionEmulator::EmulationSuccess> TransactionEmulator::emulate_transaction(block::Account&& account, td::Ref<vm::Cell> original_trans) {
83

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");
87
    }
88

89
    ton::LogicalTime lt = record_trans.lt;
90
    ton::UnixTime utime = record_trans.now;
91
    account.now_ = utime;
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));
95

96
    int trans_type = block::transaction::Transaction::tr_none;
97
    switch (tag) {
98
      case block::gen::TransactionDescr::trans_ord: {
99
        trans_type = block::transaction::Transaction::tr_ord;
100
        break;
101
      }
102
      case block::gen::TransactionDescr::trans_storage: {
103
        trans_type = block::transaction::Transaction::tr_storage;
104
        break;
105
      }
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");
110
        }
111
        trans_type = tick_tock.is_tock ? block::transaction::Transaction::tr_tock : block::transaction::Transaction::tr_tick;
112
        break;
113
      }
114
      case block::gen::TransactionDescr::trans_split_prepare: {
115
        trans_type = block::transaction::Transaction::tr_split_prepare;
116
        break;
117
      }
118
      case block::gen::TransactionDescr::trans_split_install: {
119
        trans_type = block::transaction::Transaction::tr_split_install;
120
        break;
121
      }
122
      case block::gen::TransactionDescr::trans_merge_prepare: {
123
        trans_type = block::transaction::Transaction::tr_merge_prepare;
124
        break;
125
      }
126
      case block::gen::TransactionDescr::trans_merge_install: {
127
        trans_type = block::transaction::Transaction::tr_merge_install;
128
        break;
129
      }
130
    }
131

132
    TRY_RESULT(emulation, emulate_transaction(std::move(account), msg_root, utime, lt, trans_type));
133

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");
137
    }
138

139
    if (!check_state_update(emulation_result.account, record_trans)) {
140
      return td::Status::Error("account hash mismatch");
141
    }
142

143
    return emulation_result;
144
}
145

146
td::Result<TransactionEmulator::EmulationChain> TransactionEmulator::emulate_transactions_chain(block::Account&& account, std::vector<td::Ref<vm::Cell>>&& original_transactions) {
147

148
  std::vector<td::Ref<vm::Cell>> emulated_transactions;
149
  for (const auto& original_trans : original_transactions) {
150
    if (original_trans.is_null()) {
151
      continue;
152
    }
153

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);
157
  }
158

159
  return TransactionEmulator::EmulationChain{ std::move(emulated_transactions), std::move(account) };
160
}
161

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();
166
}
167

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};
175

176
  if (msg_root.not_null()) {
177
    auto cs = vm::load_cell_slice(msg_root);
178
    external = block::gen::t_CommonMsgInfo.get_tag(cs);
179
  }
180

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;
185
  }
186

187
  std::unique_ptr<block::transaction::Transaction> trans =
188
      std::make_unique<block::transaction::Transaction>(*acc, trans_type, lt, utime, msg_root);
189

190
  if (msg_root.not_null() && !trans->unpack_input_msg(ihr_delivered, action_phase_cfg)) {
191
    if (external) {
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");
195
    }
196
    return td::Status::Error(-669,"cannot unpack input message for a new transaction");
197
  }
198

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());
202
    }
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());
205
    }
206
  } else {
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());
209
    }
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());
212
    }
213
  }
214

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());
217
  }
218

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 (?)");
223
    }
224
  }
225

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());
228
  }
229

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());
232
  }
233

234
  return trans;
235
}
236

237
void TransactionEmulator::set_unixtime(ton::UnixTime unixtime) {
238
  unixtime_ = unixtime;
239
}
240

241
void TransactionEmulator::set_lt(ton::LogicalTime lt) {
242
  lt_ = lt;
243
}
244

245
void TransactionEmulator::set_rand_seed(td::BitArray<256>& rand_seed) {
246
  rand_seed_ = rand_seed;
247
}
248

249
void TransactionEmulator::set_ignore_chksig(bool ignore_chksig) {
250
  ignore_chksig_ = ignore_chksig;
251
}
252

253
void TransactionEmulator::set_config(block::Config &&config) {
254
  config_ = std::forward<block::Config>(config);
255
}
256

257
void TransactionEmulator::set_libs(vm::Dictionary &&libs) {
258
  libraries_ = std::forward<vm::Dictionary>(libs);
259
}
260

261
void TransactionEmulator::set_debug_enabled(bool debug_enabled) {
262
  debug_enabled_ = debug_enabled;
263
}
264

265
void TransactionEmulator::set_prev_blocks_info(td::Ref<vm::Tuple> prev_blocks_info) {
266
  prev_blocks_info_ = std::move(prev_blocks_info);
267
}
268

269
} // namespace emulator
270

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.