mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	Add account state by transaction and emulator (extended) (#592)
* account_state_by_transaction * Correct time calculation * Bug fixes * Refactor * namespace block::transaction * smc.forget * RunEmulator: remove wallet_id * Refactor & fixes * AccountStateByTransaction: use shardchain block instead of masterchain block * transaction emulator core * refactor * tx emulator major functionality * small format changes * readme * clean * return json, add support for init messages * tx emulator readme * refactor getConfigParam and getConfigAll * add shardchain_libs_boc parameter * option to change verbosity level of transaction emulator * fix deserializing ShardAccount with account_none * add mode needSpecialSmc when unpack config * emulator: block::Transaction -> block::transaction::Transaction * Refactor * emulator: Fix bug * emulator: Support for emulator-extern * emulator: Refactor * Return vm log and vm exit code. * fix build on macos, emulator_static added * adjust documentation * ignore_chksig for external messages * tvm emulator, run get method * Added more params for transaction emulator * Added setters for optional transaction emulator params, moved libs to a new setter * Added actions cell output to transaction emulator * fix tonlib build * refactoring, rand seed as hex size 64, tvm emulator send message * tvm send message, small refactoring * fix config decoding, rename * improve documentation * macos export symbols * Added run_get_method to transaction emulator emscipten wrapper * Fixed empty action list serialization * Changed actions list cell to serialize as json null instead of empty string in transaction emulator * stack as boc * log gas remaining * Fix prev_block_id * fix build errors * Refactor fetch_config_params * fix failing unwrap of optional rand_seed * lookup correct shard, choose prev_block based on account shard * fix tonlib android jni build --------- Co-authored-by: legaii <jgates.ardux@gmail.com> Co-authored-by: ms <dungeon666master@protonmail.com> Co-authored-by: krigga <krigga7@gmail.com>
This commit is contained in:
		
							parent
							
								
									adf67aa869
								
							
						
					
					
						commit
						3b3c25b654
					
				
					 32 changed files with 2095 additions and 158 deletions
				
			
		| 
						 | 
				
			
			@ -383,6 +383,7 @@ add_subdirectory(tl-utils)
 | 
			
		|||
add_subdirectory(adnl)
 | 
			
		||||
add_subdirectory(crypto)
 | 
			
		||||
add_subdirectory(lite-client)
 | 
			
		||||
add_subdirectory(emulator)
 | 
			
		||||
 | 
			
		||||
#BEGIN tonlib
 | 
			
		||||
add_subdirectory(tonlib)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -274,6 +274,10 @@ add_library(ton_crypto STATIC ${TON_CRYPTO_SOURCE})
 | 
			
		|||
target_include_directories(ton_crypto PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
 | 
			
		||||
	$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
 | 
			
		||||
target_link_libraries(ton_crypto PUBLIC ${OPENSSL_CRYPTO_LIBRARY} tdutils tddb_utils)
 | 
			
		||||
if (USE_EMSCRIPTEN)
 | 
			
		||||
  target_link_options(ton_crypto PRIVATE -fexceptions)
 | 
			
		||||
  target_compile_options(ton_crypto PRIVATE -fexceptions)
 | 
			
		||||
endif()
 | 
			
		||||
if (NOT WIN32)
 | 
			
		||||
  find_library(DL dl) 
 | 
			
		||||
  if (DL)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1000,7 +1000,7 @@ bool Account::skip_copy_depth_balance(vm::CellBuilder& cb, vm::CellSlice& cs) co
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
const Account t_Account, t_AccountE{true};
 | 
			
		||||
const RefTo<Account> t_Ref_Account;
 | 
			
		||||
const RefTo<Account> t_Ref_AccountE{true};
 | 
			
		||||
 | 
			
		||||
bool ShardAccount::extract_account_state(Ref<vm::CellSlice> cs_ref, Ref<vm::Cell>& acc_state) {
 | 
			
		||||
  if (cs_ref.is_null()) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -536,7 +536,7 @@ struct Account final : TLB_Complex {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
extern const Account t_Account, t_AccountE;
 | 
			
		||||
extern const RefTo<Account> t_Ref_Account;
 | 
			
		||||
extern const RefTo<Account> t_Ref_AccountE;
 | 
			
		||||
 | 
			
		||||
struct AccountStatus final : TLB {
 | 
			
		||||
  enum { acc_state_uninit, acc_state_frozen, acc_state_active, acc_state_nonexist };
 | 
			
		||||
| 
						 | 
				
			
			@ -572,7 +572,7 @@ struct ShardAccount final : TLB_Complex {
 | 
			
		|||
    return cs.advance_ext(0x140, 1);
 | 
			
		||||
  }
 | 
			
		||||
  bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
 | 
			
		||||
    return cs.advance(0x140) && t_Ref_Account.validate_skip(ops, cs, weak);
 | 
			
		||||
    return cs.advance(0x140) && t_Ref_AccountE.validate_skip(ops, cs, weak);
 | 
			
		||||
  }
 | 
			
		||||
  static bool unpack(vm::CellSlice& cs, Record& info) {
 | 
			
		||||
    return info.unpack(cs);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -366,7 +366,7 @@ trans_merge_install$0111 split_info:SplitMergeInfo
 | 
			
		|||
smc_info#076ef1ea actions:uint16 msgs_sent:uint16
 | 
			
		||||
  unixtime:uint32 block_lt:uint64 trans_lt:uint64 
 | 
			
		||||
  rand_seed:bits256 balance_remaining:CurrencyCollection
 | 
			
		||||
  myself:MsgAddressInt = SmartContractInfo;
 | 
			
		||||
  myself:MsgAddressInt global_config:(Maybe Cell) = SmartContractInfo;
 | 
			
		||||
//
 | 
			
		||||
//
 | 
			
		||||
out_list_empty$_ = OutList 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -632,7 +632,6 @@ class Config {
 | 
			
		|||
  static td::Result<std::vector<int>> unpack_param_dict(vm::Dictionary& dict);
 | 
			
		||||
  static td::Result<std::vector<int>> unpack_param_dict(Ref<vm::Cell> dict_root);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  Config(int _mode) : mode(_mode) {
 | 
			
		||||
    config_addr.set_zero();
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,7 @@
 | 
			
		|||
#include "block/block.h"
 | 
			
		||||
#include "block/block-parse.h"
 | 
			
		||||
#include "block/block-auto.h"
 | 
			
		||||
#include "crypto/openssl/rand.hpp"
 | 
			
		||||
#include "td/utils/bits.h"
 | 
			
		||||
#include "td/utils/uint128.h"
 | 
			
		||||
#include "ton/ton-shard.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -513,6 +514,7 @@ td::RefInt256 Account::compute_storage_fees(ton::UnixTime now, const std::vector
 | 
			
		|||
  return StoragePrices::compute_storage_fees(now, pricing, storage_stat, last_paid, is_special, is_masterchain());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace transaction {
 | 
			
		||||
Transaction::Transaction(const Account& _account, int ttype, ton::LogicalTime req_start_lt, ton::UnixTime _now,
 | 
			
		||||
                         Ref<vm::Cell> _inmsg)
 | 
			
		||||
    : trans_type(ttype)
 | 
			
		||||
| 
						 | 
				
			
			@ -745,6 +747,7 @@ bool Transaction::prepare_credit_phase() {
 | 
			
		|||
  total_fees += std::move(collected);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
}  // namespace transaction
 | 
			
		||||
 | 
			
		||||
bool ComputePhaseConfig::parse_GasLimitsPrices(Ref<vm::Cell> cell, td::RefInt256& freeze_due_limit,
 | 
			
		||||
                                               td::RefInt256& delete_due_limit) {
 | 
			
		||||
| 
						 | 
				
			
			@ -837,6 +840,7 @@ td::RefInt256 ComputePhaseConfig::compute_gas_price(td::uint64 gas_used) const {
 | 
			
		|||
                                    : td::rshift(gas_price256 * (gas_used - flat_gas_limit), 16, 1) + flat_gas_price;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace transaction {
 | 
			
		||||
bool Transaction::compute_gas_limits(ComputePhase& cp, const ComputePhaseConfig& cfg) {
 | 
			
		||||
  // Compute gas limits
 | 
			
		||||
  if (account.is_special) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1057,13 +1061,21 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
 | 
			
		|||
  std::unique_ptr<StringLoggerTail> logger;
 | 
			
		||||
  auto vm_log = vm::VmLog();
 | 
			
		||||
  if (cfg.with_vm_log) {
 | 
			
		||||
    logger = std::make_unique<StringLoggerTail>();
 | 
			
		||||
    size_t log_max_size = cfg.vm_log_verbosity > 0 ? 1024 * 1024 : 256;
 | 
			
		||||
    logger = std::make_unique<StringLoggerTail>(log_max_size);
 | 
			
		||||
    vm_log.log_interface = logger.get();
 | 
			
		||||
    vm_log.log_options = td::LogOptions(VERBOSITY_NAME(DEBUG), true, false);
 | 
			
		||||
    if (cfg.vm_log_verbosity > 1) {
 | 
			
		||||
      vm_log.log_mask |= vm::VmLog::ExecLocation;
 | 
			
		||||
      if (cfg.vm_log_verbosity > 2) {
 | 
			
		||||
        vm_log.log_mask |= vm::VmLog::DumpStack | vm::VmLog::GasRemaining;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  vm::VmState vm{new_code, std::move(stack), gas, 1, new_data, vm_log, compute_vm_libraries(cfg)};
 | 
			
		||||
  vm.set_max_data_depth(cfg.max_vm_data_depth);
 | 
			
		||||
  vm.set_c7(prepare_vm_c7(cfg));  // tuple with SmartContractInfo
 | 
			
		||||
  vm.set_chksig_always_succeed(cfg.ignore_chksig);
 | 
			
		||||
  // vm.incr_stack_trace(1);    // enable stack dump after each step
 | 
			
		||||
 | 
			
		||||
  LOG(DEBUG) << "starting VM";
 | 
			
		||||
| 
						 | 
				
			
			@ -1338,6 +1350,7 @@ int Transaction::try_action_change_library(vm::CellSlice& cs, ActionPhase& ap, c
 | 
			
		|||
  ap.spec_actions++;
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
}  // namespace transaction
 | 
			
		||||
 | 
			
		||||
// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms
 | 
			
		||||
// ihr_fwd_fees = ceil((msg_fwd_fees * ihr_price_factor)/2^16) nanograms
 | 
			
		||||
| 
						 | 
				
			
			@ -1372,6 +1385,7 @@ td::RefInt256 MsgPrices::get_next_part(td::RefInt256 total) const {
 | 
			
		|||
  return (std::move(total) * next_frac) >> 16;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace transaction {
 | 
			
		||||
bool Transaction::check_replace_src_addr(Ref<vm::CellSlice>& src_addr) const {
 | 
			
		||||
  int t = (int)src_addr->prefetch_ulong(2);
 | 
			
		||||
  if (!t && src_addr->size_ext() == 2) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1978,6 +1992,7 @@ bool Transaction::prepare_bounce_phase(const ActionPhaseConfig& cfg) {
 | 
			
		|||
  bp.ok = true;
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
}  // namespace transaction
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 
 | 
			
		||||
| 
						 | 
				
			
			@ -2033,6 +2048,7 @@ static td::optional<vm::CellStorageStat> try_update_storage_stat(const vm::CellS
 | 
			
		|||
  return new_stat;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace transaction {
 | 
			
		||||
bool Transaction::compute_state() {
 | 
			
		||||
  if (new_total_state.not_null()) {
 | 
			
		||||
    return true;
 | 
			
		||||
| 
						 | 
				
			
			@ -2460,6 +2476,7 @@ void Transaction::extract_out_msgs(std::vector<LtCellRef>& list) {
 | 
			
		|||
    list.emplace_back(start_lt + i + 1, std::move(out_msgs[i]));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}  // namespace transaction
 | 
			
		||||
 | 
			
		||||
void Account::push_transaction(Ref<vm::Cell> trans_root, ton::LogicalTime trans_lt) {
 | 
			
		||||
  transactions.emplace_back(trans_lt, std::move(trans_root));
 | 
			
		||||
| 
						 | 
				
			
			@ -2503,4 +2520,82 @@ bool Account::libraries_changed() const {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Status FetchConfigParams::fetch_config_params(const block::Config& config,
 | 
			
		||||
                                              Ref<vm::Cell>* old_mparams,
 | 
			
		||||
                                              std::vector<block::StoragePrices>* storage_prices,
 | 
			
		||||
                                              block::StoragePhaseConfig* storage_phase_cfg,
 | 
			
		||||
                                              td::BitArray<256>* rand_seed,
 | 
			
		||||
                                              block::ComputePhaseConfig* compute_phase_cfg,
 | 
			
		||||
                                              block::ActionPhaseConfig* action_phase_cfg,
 | 
			
		||||
                                              td::RefInt256* masterchain_create_fee,
 | 
			
		||||
                                              td::RefInt256* basechain_create_fee,
 | 
			
		||||
                                              ton::WorkchainId wc, 
 | 
			
		||||
                                              ton::UnixTime now) {
 | 
			
		||||
  *old_mparams = config.get_config_param(9);
 | 
			
		||||
  {
 | 
			
		||||
    auto res = config.get_storage_prices();
 | 
			
		||||
    if (res.is_error()) {
 | 
			
		||||
      return res.move_as_error();
 | 
			
		||||
    }
 | 
			
		||||
    *storage_prices = res.move_as_ok();
 | 
			
		||||
  }
 | 
			
		||||
  if (rand_seed->is_zero()) {
 | 
			
		||||
    // generate rand seed
 | 
			
		||||
    prng::rand_gen().strong_rand_bytes(rand_seed->data(), 32);
 | 
			
		||||
    LOG(DEBUG) << "block random seed set to " << rand_seed->to_hex();
 | 
			
		||||
  }
 | 
			
		||||
  TRY_RESULT(size_limits, config.get_size_limits_config());
 | 
			
		||||
  {
 | 
			
		||||
    // compute compute_phase_cfg / storage_phase_cfg
 | 
			
		||||
    auto cell = config.get_config_param(wc == ton::masterchainId ? 20 : 21);
 | 
			
		||||
    if (cell.is_null()) {
 | 
			
		||||
      return td::Status::Error(-668, "cannot fetch current gas prices and limits from masterchain configuration");
 | 
			
		||||
    }
 | 
			
		||||
    if (!compute_phase_cfg->parse_GasLimitsPrices(std::move(cell), storage_phase_cfg->freeze_due_limit,
 | 
			
		||||
                                                  storage_phase_cfg->delete_due_limit)) {
 | 
			
		||||
      return td::Status::Error(-668, "cannot unpack current gas prices and limits from masterchain configuration");
 | 
			
		||||
    }
 | 
			
		||||
    compute_phase_cfg->block_rand_seed = *rand_seed;
 | 
			
		||||
    compute_phase_cfg->max_vm_data_depth = size_limits.max_vm_data_depth;
 | 
			
		||||
    compute_phase_cfg->global_config = config.get_root_cell();
 | 
			
		||||
    compute_phase_cfg->suspended_addresses = config.get_suspended_addresses(now);
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    // compute action_phase_cfg
 | 
			
		||||
    block::gen::MsgForwardPrices::Record rec;
 | 
			
		||||
    auto cell = config.get_config_param(24);
 | 
			
		||||
    if (cell.is_null() || !tlb::unpack_cell(std::move(cell), rec)) {
 | 
			
		||||
      return td::Status::Error(-668, "cannot fetch masterchain message transfer prices from masterchain configuration");
 | 
			
		||||
    }
 | 
			
		||||
    action_phase_cfg->fwd_mc =
 | 
			
		||||
        block::MsgPrices{rec.lump_price,           rec.bit_price,          rec.cell_price, rec.ihr_price_factor,
 | 
			
		||||
                         (unsigned)rec.first_frac, (unsigned)rec.next_frac};
 | 
			
		||||
    cell = config.get_config_param(25);
 | 
			
		||||
    if (cell.is_null() || !tlb::unpack_cell(std::move(cell), rec)) {
 | 
			
		||||
      return td::Status::Error(-668, "cannot fetch standard message transfer prices from masterchain configuration");
 | 
			
		||||
    }
 | 
			
		||||
    action_phase_cfg->fwd_std =
 | 
			
		||||
        block::MsgPrices{rec.lump_price,           rec.bit_price,          rec.cell_price, rec.ihr_price_factor,
 | 
			
		||||
                         (unsigned)rec.first_frac, (unsigned)rec.next_frac};
 | 
			
		||||
    action_phase_cfg->workchains = &config.get_workchain_list();
 | 
			
		||||
    action_phase_cfg->bounce_msg_body = (config.has_capability(ton::capBounceMsgBody) ? 256 : 0);
 | 
			
		||||
    action_phase_cfg->size_limits = size_limits;
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    // fetch block_grams_created
 | 
			
		||||
    auto cell = config.get_config_param(14);
 | 
			
		||||
    if (cell.is_null()) {
 | 
			
		||||
      *basechain_create_fee = *masterchain_create_fee = td::zero_refint();
 | 
			
		||||
    } else {
 | 
			
		||||
      block::gen::BlockCreateFees::Record create_fees;
 | 
			
		||||
      if (!(tlb::unpack_cell(cell, create_fees) &&
 | 
			
		||||
            block::tlb::t_Grams.as_integer_to(create_fees.masterchain_block_fee, *masterchain_create_fee) &&
 | 
			
		||||
            block::tlb::t_Grams.as_integer_to(create_fees.basechain_block_fee, *basechain_create_fee))) {
 | 
			
		||||
        return td::Status::Error(-668, "cannot unpack BlockCreateFees from configuration parameter #14");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return td::Status::OK();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace block
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,7 +35,10 @@ using td::Ref;
 | 
			
		|||
using LtCellRef = std::pair<ton::LogicalTime, Ref<vm::Cell>>;
 | 
			
		||||
 | 
			
		||||
struct Account;
 | 
			
		||||
 | 
			
		||||
namespace transaction {
 | 
			
		||||
struct Transaction;
 | 
			
		||||
}  // namespace transaction
 | 
			
		||||
 | 
			
		||||
struct CollatorError {
 | 
			
		||||
  std::string msg;
 | 
			
		||||
| 
						 | 
				
			
			@ -106,9 +109,11 @@ struct ComputePhaseConfig {
 | 
			
		|||
  std::unique_ptr<vm::Dictionary> libraries;
 | 
			
		||||
  Ref<vm::Cell> global_config;
 | 
			
		||||
  td::BitArray<256> block_rand_seed;
 | 
			
		||||
  bool ignore_chksig{false};
 | 
			
		||||
  bool with_vm_log{false};
 | 
			
		||||
  td::uint16 max_vm_data_depth = 512;
 | 
			
		||||
  std::unique_ptr<vm::Dictionary> suspended_addresses;
 | 
			
		||||
  int vm_log_verbosity = 0;
 | 
			
		||||
  ComputePhaseConfig(td::uint64 _gas_price = 0, td::uint64 _gas_limit = 0, td::uint64 _gas_credit = 0)
 | 
			
		||||
      : gas_price(_gas_price), gas_limit(_gas_limit), special_gas_limit(_gas_limit), gas_credit(_gas_credit) {
 | 
			
		||||
    compute_threshold();
 | 
			
		||||
| 
						 | 
				
			
			@ -273,7 +278,7 @@ struct Account {
 | 
			
		|||
  bool create_account_block(vm::CellBuilder& cb);  // stores an AccountBlock with all transactions
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  friend struct Transaction;
 | 
			
		||||
  friend struct transaction::Transaction;
 | 
			
		||||
  bool set_split_depth(int split_depth);
 | 
			
		||||
  bool check_split_depth(int split_depth) const;
 | 
			
		||||
  bool forget_split_depth();
 | 
			
		||||
| 
						 | 
				
			
			@ -288,6 +293,7 @@ struct Account {
 | 
			
		|||
  bool compute_my_addr(bool force = false);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace transaction {
 | 
			
		||||
struct Transaction {
 | 
			
		||||
  enum {
 | 
			
		||||
    tr_none,
 | 
			
		||||
| 
						 | 
				
			
			@ -390,5 +396,20 @@ struct Transaction {
 | 
			
		|||
  bool serialize_bounce_phase(vm::CellBuilder& cb);
 | 
			
		||||
  bool unpack_msg_state(bool lib_only = false);
 | 
			
		||||
};
 | 
			
		||||
}  // namespace transaction
 | 
			
		||||
 | 
			
		||||
struct FetchConfigParams {
 | 
			
		||||
static td::Status fetch_config_params(const block::Config& config,
 | 
			
		||||
                                      Ref<vm::Cell>* old_mparams,
 | 
			
		||||
                                      std::vector<block::StoragePrices>* storage_prices,
 | 
			
		||||
                                      StoragePhaseConfig* storage_phase_cfg,
 | 
			
		||||
                                      td::BitArray<256>* rand_seed,
 | 
			
		||||
                                      ComputePhaseConfig* compute_phase_cfg,
 | 
			
		||||
                                      ActionPhaseConfig* action_phase_cfg,
 | 
			
		||||
                                      td::RefInt256* masterchain_create_fee,
 | 
			
		||||
                                      td::RefInt256* basechain_create_fee,
 | 
			
		||||
                                      ton::WorkchainId wc,
 | 
			
		||||
                                      ton::UnixTime now);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace block
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,7 +54,11 @@ td::Ref<vm::Stack> prepare_vm_stack(td::RefInt256 amount, td::Ref<vm::CellSlice>
 | 
			
		|||
 | 
			
		||||
td::Ref<vm::Tuple> prepare_vm_c7(SmartContract::Args args) {
 | 
			
		||||
  td::BitArray<256> rand_seed;
 | 
			
		||||
  rand_seed.as_slice().fill(0);
 | 
			
		||||
  if (args.rand_seed) {
 | 
			
		||||
    rand_seed = args.rand_seed.unwrap();
 | 
			
		||||
  } else {
 | 
			
		||||
    rand_seed.as_slice().fill(0);
 | 
			
		||||
  }
 | 
			
		||||
  td::RefInt256 rand_seed_int{true};
 | 
			
		||||
  rand_seed_int.unique_write().import_bits(rand_seed.cbits(), 256, false);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +100,7 @@ td::Ref<vm::Tuple> prepare_vm_c7(SmartContract::Args args) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stack> stack, td::Ref<vm::Tuple> c7,
 | 
			
		||||
                                    vm::GasLimits gas, bool ignore_chksig, td::Ref<vm::Cell> libraries) {
 | 
			
		||||
                                    vm::GasLimits gas, bool ignore_chksig, td::Ref<vm::Cell> libraries, int vm_log_verbosity) {
 | 
			
		||||
  auto gas_credit = gas.gas_credit;
 | 
			
		||||
  vm::init_op_cp0();
 | 
			
		||||
  vm::DictionaryBase::get_empty_dictionary();
 | 
			
		||||
| 
						 | 
				
			
			@ -109,15 +113,12 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stac
 | 
			
		|||
    std::string res;
 | 
			
		||||
  };
 | 
			
		||||
  Logger logger;
 | 
			
		||||
  vm::VmLog log{&logger, td::LogOptions::plain()};
 | 
			
		||||
 | 
			
		||||
  if (GET_VERBOSITY_LEVEL() >= VERBOSITY_NAME(DEBUG)) {
 | 
			
		||||
    log.log_options.level = 4;
 | 
			
		||||
    log.log_options.fix_newlines = true;
 | 
			
		||||
    log.log_mask |= vm::VmLog::DumpStack;
 | 
			
		||||
  } else {
 | 
			
		||||
    log.log_options.level = 0;
 | 
			
		||||
    log.log_mask = 0;
 | 
			
		||||
  vm::VmLog log{&logger, td::LogOptions(VERBOSITY_NAME(DEBUG), true, false)};
 | 
			
		||||
  if (vm_log_verbosity > 1) {
 | 
			
		||||
    log.log_mask |= vm::VmLog::ExecLocation;
 | 
			
		||||
    if (vm_log_verbosity > 2) {
 | 
			
		||||
      log.log_mask |= vm::VmLog::DumpStack | vm::VmLog::GasRemaining;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  SmartContract::Answer res;
 | 
			
		||||
| 
						 | 
				
			
			@ -137,13 +138,13 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stac
 | 
			
		|||
  } catch (...) {
 | 
			
		||||
    LOG(FATAL) << "catch unhandled exception";
 | 
			
		||||
  }
 | 
			
		||||
  td::ConstBitPtr mlib = vm.get_missing_library();
 | 
			
		||||
  res.new_state = std::move(state);
 | 
			
		||||
  res.stack = vm.get_stack_ref();
 | 
			
		||||
  gas = vm.get_gas_limits();
 | 
			
		||||
  res.gas_used = gas.gas_consumed();
 | 
			
		||||
  res.accepted = gas.gas_credit == 0;
 | 
			
		||||
  res.success = (res.accepted && (unsigned)res.code <= 1);
 | 
			
		||||
  res.vm_log = logger.res;
 | 
			
		||||
  if (GET_VERBOSITY_LEVEL() >= VERBOSITY_NAME(DEBUG)) {
 | 
			
		||||
    LOG(DEBUG) << "VM log\n" << logger.res;
 | 
			
		||||
    std::ostringstream os;
 | 
			
		||||
| 
						 | 
				
			
			@ -153,6 +154,7 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stac
 | 
			
		|||
    LOG(DEBUG) << "VM accepted: " << res.accepted;
 | 
			
		||||
    LOG(DEBUG) << "VM success: " << res.success;
 | 
			
		||||
  }
 | 
			
		||||
  td::ConstBitPtr mlib = vm.get_missing_library();
 | 
			
		||||
  if (!mlib.is_null()) {
 | 
			
		||||
    LOG(DEBUG) << "Missing library: " << mlib.to_hex(256);
 | 
			
		||||
    res.missing_library = mlib;
 | 
			
		||||
| 
						 | 
				
			
			@ -219,7 +221,7 @@ SmartContract::Answer SmartContract::run_method(Args args) {
 | 
			
		|||
  args.stack.value().write().push_smallint(args.method_id.unwrap());
 | 
			
		||||
  auto res =
 | 
			
		||||
      run_smartcont(get_state(), args.stack.unwrap(), args.c7.unwrap(), args.limits.unwrap(), args.ignore_chksig,
 | 
			
		||||
                    args.libraries ? args.libraries.unwrap().get_root_cell() : td::Ref<vm::Cell>{});
 | 
			
		||||
                    args.libraries ? args.libraries.unwrap().get_root_cell() : td::Ref<vm::Cell>{}, args.vm_log_verbosity_level);
 | 
			
		||||
  state_ = res.new_state;
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -237,7 +239,7 @@ SmartContract::Answer SmartContract::run_get_method(Args args) const {
 | 
			
		|||
  CHECK(args.method_id);
 | 
			
		||||
  args.stack.value().write().push_smallint(args.method_id.unwrap());
 | 
			
		||||
  return run_smartcont(get_state(), args.stack.unwrap(), args.c7.unwrap(), args.limits.unwrap(), args.ignore_chksig,
 | 
			
		||||
                       args.libraries ? args.libraries.unwrap().get_root_cell() : td::Ref<vm::Cell>{});
 | 
			
		||||
                       args.libraries ? args.libraries.unwrap().get_root_cell() : td::Ref<vm::Cell>{}, args.vm_log_verbosity_level);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SmartContract::Answer SmartContract::run_get_method(td::Slice method, Args args) const {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,6 +50,7 @@ class SmartContract : public td::CntObject {
 | 
			
		|||
    td::int32 code;
 | 
			
		||||
    td::int64 gas_used;
 | 
			
		||||
    td::ConstBitPtr missing_library{0};
 | 
			
		||||
    std::string vm_log;
 | 
			
		||||
    static int output_actions_count(td::Ref<vm::Cell> list);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -59,9 +60,11 @@ class SmartContract : public td::CntObject {
 | 
			
		|||
    td::optional<td::Ref<vm::Tuple>> c7;
 | 
			
		||||
    td::optional<td::Ref<vm::Stack>> stack;
 | 
			
		||||
    td::optional<td::int32> now;
 | 
			
		||||
    td::optional<td::BitArray<256>> rand_seed;
 | 
			
		||||
    bool ignore_chksig{false};
 | 
			
		||||
    td::uint64 amount{0};
 | 
			
		||||
    td::uint64 balance{0};
 | 
			
		||||
    int vm_log_verbosity_level{0};
 | 
			
		||||
 | 
			
		||||
    td::optional<block::StdAddress> address;
 | 
			
		||||
    td::optional<std::shared_ptr<const block::Config>> config;
 | 
			
		||||
| 
						 | 
				
			
			@ -100,6 +103,10 @@ class SmartContract : public td::CntObject {
 | 
			
		|||
      this->stack = std::move(stack);
 | 
			
		||||
      return std::move(*this);
 | 
			
		||||
    }
 | 
			
		||||
    Args&& set_rand_seed(td::BitArray<256> rand_seed) {
 | 
			
		||||
      this->rand_seed = std::move(rand_seed);
 | 
			
		||||
      return std::move(*this);
 | 
			
		||||
    }
 | 
			
		||||
    Args&& set_ignore_chksig(bool ignore_chksig) {
 | 
			
		||||
      this->ignore_chksig = ignore_chksig;
 | 
			
		||||
      return std::move(*this);
 | 
			
		||||
| 
						 | 
				
			
			@ -124,6 +131,10 @@ class SmartContract : public td::CntObject {
 | 
			
		|||
      this->libraries = libraries;
 | 
			
		||||
      return std::move(*this);
 | 
			
		||||
    }
 | 
			
		||||
    Args&& set_vm_verbosity_level(int vm_log_verbosity_level) {
 | 
			
		||||
      this->vm_log_verbosity_level = vm_log_verbosity_level;
 | 
			
		||||
      return std::move(*this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    td::Result<td::int32> get_method_id() const {
 | 
			
		||||
      if (!method_id) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,7 +31,7 @@ namespace vm {
 | 
			
		|||
struct VmLog {
 | 
			
		||||
  td::LogInterface *log_interface{td::log_interface};
 | 
			
		||||
  td::LogOptions log_options{td::log_options};
 | 
			
		||||
  enum { DumpStack = 2 };
 | 
			
		||||
  enum { DumpStack = 2, ExecLocation = 4, GasRemaining = 8 };
 | 
			
		||||
  int log_mask{1};
 | 
			
		||||
  static VmLog Null() {
 | 
			
		||||
    VmLog res;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -433,18 +433,24 @@ void VmState::change_gas_limit(long long new_limit) {
 | 
			
		|||
 | 
			
		||||
int VmState::step() {
 | 
			
		||||
  CHECK(code.not_null() && stack.not_null());
 | 
			
		||||
  //VM_LOG(st) << "stack:";  stack->dump(VM_LOG(st));
 | 
			
		||||
  //VM_LOG(st) << "; cr0.refcnt = " << get_c0()->get_refcnt() - 1 << std::endl;
 | 
			
		||||
  if (log.log_mask & vm::VmLog::DumpStack) {
 | 
			
		||||
    std::stringstream ss;
 | 
			
		||||
    stack->dump(ss, 3);
 | 
			
		||||
    VM_LOG(this) << "stack:" << ss.str();
 | 
			
		||||
  }
 | 
			
		||||
  if (stack_trace) {
 | 
			
		||||
    stack->dump(std::cerr, 3);
 | 
			
		||||
  }
 | 
			
		||||
  ++steps;
 | 
			
		||||
  if (code->size()) {
 | 
			
		||||
    VM_LOG_MASK(this, vm::VmLog::ExecLocation) << "code cell hash: " << code->get_base_cell()->get_hash().to_hex() << " offset: " << code->cur_pos();
 | 
			
		||||
    return dispatch->dispatch(this, code.write());
 | 
			
		||||
  } else if (code->size_refs()) {
 | 
			
		||||
    VM_LOG(this) << "execute implicit JMPREF";
 | 
			
		||||
    auto ref_cell = code->prefetch_ref();
 | 
			
		||||
    VM_LOG_MASK(this, vm::VmLog::ExecLocation) << "code cell hash: " << ref_cell->get_hash().to_hex() << " offset: 0";
 | 
			
		||||
    gas.consume_chk(implicit_jmpref_gas_price);
 | 
			
		||||
    Ref<Continuation> cont = Ref<OrdCont>{true, load_cell_slice_ref(code->prefetch_ref()), get_cp()};
 | 
			
		||||
    Ref<Continuation> cont = Ref<OrdCont>{true, load_cell_slice_ref(std::move(ref_cell)), get_cp()};
 | 
			
		||||
    return jump(std::move(cont));
 | 
			
		||||
  } else {
 | 
			
		||||
    VM_LOG(this) << "execute implicit RET";
 | 
			
		||||
| 
						 | 
				
			
			@ -465,6 +471,7 @@ int VmState::run() {
 | 
			
		|||
      try {
 | 
			
		||||
        try {
 | 
			
		||||
          res = step();
 | 
			
		||||
          VM_LOG_MASK(this, vm::VmLog::GasRemaining) << "gas remaining: " << gas.gas_remaining;
 | 
			
		||||
          gas.check();
 | 
			
		||||
        } catch (vm::CellBuilder::CellWriteError) {
 | 
			
		||||
          throw VmError{Excno::cell_ov};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										55
									
								
								emulator/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								emulator/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,55 @@
 | 
			
		|||
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
 | 
			
		||||
 | 
			
		||||
if (NOT OPENSSL_FOUND)
 | 
			
		||||
  find_package(OpenSSL REQUIRED)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
set(EMULATOR_STATIC_SOURCE
 | 
			
		||||
  transaction-emulator.cpp
 | 
			
		||||
  tvm-emulator.hpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
set(EMULATOR_HEADERS 
 | 
			
		||||
  transaction-emulator.h
 | 
			
		||||
  emulator-extern.h
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
set(EMULATOR_SOURCE
 | 
			
		||||
  emulator-extern.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
set(EMULATOR_EMSCRIPTEN_SOURCE
 | 
			
		||||
  transaction-emscripten.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
include(GenerateExportHeader)
 | 
			
		||||
 | 
			
		||||
add_library(emulator_static STATIC ${EMULATOR_STATIC_SOURCE})
 | 
			
		||||
target_link_libraries(emulator_static PUBLIC ton_crypto ton_block smc-envelope)
 | 
			
		||||
 | 
			
		||||
add_library(emulator SHARED ${EMULATOR_SOURCE} ${EMULATOR_HEADERS})
 | 
			
		||||
target_link_libraries(emulator PUBLIC emulator_static)
 | 
			
		||||
generate_export_header(emulator EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/emulator_export.h)
 | 
			
		||||
target_include_directories(emulator PUBLIC
 | 
			
		||||
  $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
 | 
			
		||||
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
 | 
			
		||||
if (APPLE)
 | 
			
		||||
  set_target_properties(emulator PROPERTIES LINK_FLAGS "-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/emulator_export_list")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (USE_EMSCRIPTEN)
 | 
			
		||||
  add_executable(emulator-emscripten ${EMULATOR_EMSCRIPTEN_SOURCE})
 | 
			
		||||
  target_link_libraries(emulator-emscripten PUBLIC emulator)
 | 
			
		||||
  target_link_options(emulator-emscripten PRIVATE -sEXPORTED_RUNTIME_METHODS=_malloc,free,UTF8ToString,stringToUTF8,allocate,ALLOC_NORMAL,lengthBytesUTF8)
 | 
			
		||||
  target_link_options(emulator-emscripten PRIVATE -sEXPORTED_FUNCTIONS=_emulate,_free,_run_get_method)
 | 
			
		||||
  target_link_options(emulator-emscripten PRIVATE -sEXPORT_NAME=EmulatorModule)
 | 
			
		||||
  target_link_options(emulator-emscripten PRIVATE -sERROR_ON_UNDEFINED_SYMBOLS=0)
 | 
			
		||||
  target_link_options(emulator-emscripten PRIVATE -Oz)
 | 
			
		||||
  target_link_options(emulator-emscripten PRIVATE -sIGNORE_MISSING_MAIN=1)
 | 
			
		||||
  target_link_options(emulator-emscripten PRIVATE -sAUTO_NATIVE_LIBRARIES=0)
 | 
			
		||||
  target_link_options(emulator-emscripten PRIVATE -sMODULARIZE=1)
 | 
			
		||||
  target_link_options(emulator-emscripten PRIVATE -sENVIRONMENT=web)
 | 
			
		||||
  target_link_options(emulator-emscripten PRIVATE -sFILESYSTEM=0)
 | 
			
		||||
  target_link_options(emulator-emscripten PRIVATE -fexceptions)
 | 
			
		||||
  target_compile_options(emulator-emscripten PRIVATE -fexceptions)
 | 
			
		||||
endif()
 | 
			
		||||
							
								
								
									
										32
									
								
								emulator/README.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								emulator/README.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
# Emulator
 | 
			
		||||
 | 
			
		||||
Emulator is a shared library containing the following functionality:
 | 
			
		||||
- Emulating blockchain transactions
 | 
			
		||||
- Emulating TVM - get methods and sending external and internal messages.
 | 
			
		||||
 | 
			
		||||
## Transaction Emulator
 | 
			
		||||
 | 
			
		||||
To emulate transaction you need the following data:
 | 
			
		||||
 | 
			
		||||
- Account state of type *ShardAccount*.
 | 
			
		||||
- Global config of type *(Hashmap 32 ^Cell)*.
 | 
			
		||||
- Inbound message of type *MessageAny*.
 | 
			
		||||
 | 
			
		||||
Optionally you can set emulation parameters:
 | 
			
		||||
- *ignore_chksig* - whether CHKSIG instructions are set to always succeed. Default: *false*
 | 
			
		||||
- *lt* - logical time of emulation. Default: next block's lt after the account's last transaction block.
 | 
			
		||||
- *unixtime* - unix time of emulation. Default: current system time
 | 
			
		||||
- *rand_seed* - random seed. Default: generated randomly
 | 
			
		||||
- *libs* - shared libraries. If your smart contract uses shared libraries (located in masterchain), you should set this parameter.
 | 
			
		||||
 | 
			
		||||
Emulator output contains:
 | 
			
		||||
- Transaction object (*Transaction*)
 | 
			
		||||
- New account state (*ShardAccount*)
 | 
			
		||||
- Actions cell (*OutList n*)
 | 
			
		||||
- TVM log
 | 
			
		||||
 | 
			
		||||
## TVM Emulator
 | 
			
		||||
 | 
			
		||||
TVM emulator is intended to run get methods or emulate sending message on TVM level. It is initialized with smart contract code and data cells. 
 | 
			
		||||
- To run get method you pass *initial stack* and *method id* (as integer).
 | 
			
		||||
- To emulate sending message you pass *message body* and in case of internal message *amount* in nanograms.
 | 
			
		||||
							
								
								
									
										27
									
								
								emulator/StringLog.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								emulator/StringLog.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
#ifndef TON_STRINGLOG_H
 | 
			
		||||
#define TON_STRINGLOG_H
 | 
			
		||||
 | 
			
		||||
#include "td/utils/logging.h"
 | 
			
		||||
#include <thread>
 | 
			
		||||
 | 
			
		||||
class StringLog : public td::LogInterface {
 | 
			
		||||
 public:
 | 
			
		||||
  StringLog() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void append(td::CSlice new_slice, int log_level) override {
 | 
			
		||||
    str.append(new_slice.str());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void rotate() override {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::string get_string() const {
 | 
			
		||||
    return str;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  std::string str;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif  //TON_STRINGLOG_H
 | 
			
		||||
							
								
								
									
										189
									
								
								emulator/emulator-emscripten.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								emulator/emulator-emscripten.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,189 @@
 | 
			
		|||
#include "emulator-extern.h"
 | 
			
		||||
#include "td/utils/logging.h"
 | 
			
		||||
#include "td/utils/JsonBuilder.h"
 | 
			
		||||
#include "td/utils/misc.h"
 | 
			
		||||
#include "td/utils/optional.h"
 | 
			
		||||
#include "StringLog.h"
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include "crypto/common/bitstring.h"
 | 
			
		||||
 | 
			
		||||
struct TransactionEmulationParams {
 | 
			
		||||
  uint32_t utime;
 | 
			
		||||
  uint64_t lt;
 | 
			
		||||
  td::optional<std::string> rand_seed_hex;
 | 
			
		||||
  bool ignore_chksig;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
td::Result<TransactionEmulationParams> decode_transaction_emulation_params(const char* json) {
 | 
			
		||||
  TransactionEmulationParams params;
 | 
			
		||||
 | 
			
		||||
  std::string json_str(json);
 | 
			
		||||
  TRY_RESULT(input_json, td::json_decode(td::MutableSlice(json_str)));
 | 
			
		||||
  auto &obj = input_json.get_object();
 | 
			
		||||
 | 
			
		||||
  TRY_RESULT(utime_field, td::get_json_object_field(obj, "utime", td::JsonValue::Type::Number, false));
 | 
			
		||||
  TRY_RESULT(utime, td::to_integer_safe<td::uint32>(utime_field.get_number()));
 | 
			
		||||
  params.utime = utime;
 | 
			
		||||
 | 
			
		||||
  TRY_RESULT(lt_field, td::get_json_object_field(obj, "lt", td::JsonValue::Type::String, false));
 | 
			
		||||
  TRY_RESULT(lt, td::to_integer_safe<td::uint64>(lt_field.get_string()));
 | 
			
		||||
  params.lt = lt;
 | 
			
		||||
 | 
			
		||||
  TRY_RESULT(rand_seed_str, td::get_json_object_string_field(obj, "rand_seed", true));
 | 
			
		||||
  if (rand_seed_str.size() > 0) {
 | 
			
		||||
    params.rand_seed_hex = rand_seed_str;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  TRY_RESULT(ignore_chksig, td::get_json_object_bool_field(obj, "ignore_chksig", false));
 | 
			
		||||
  params.ignore_chksig = ignore_chksig;
 | 
			
		||||
 | 
			
		||||
  return params;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct GetMethodParams {
 | 
			
		||||
  std::string code;
 | 
			
		||||
  std::string data;
 | 
			
		||||
  int verbosity;
 | 
			
		||||
  td::optional<std::string> libs;
 | 
			
		||||
  std::string address;
 | 
			
		||||
  uint32_t unixtime;
 | 
			
		||||
  uint64_t balance;
 | 
			
		||||
  std::string rand_seed_hex;
 | 
			
		||||
  int64_t gas_limit;
 | 
			
		||||
  int method_id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
td::Result<GetMethodParams> decode_get_method_params(const char* json) {
 | 
			
		||||
  GetMethodParams params;
 | 
			
		||||
 | 
			
		||||
  std::string json_str(json);
 | 
			
		||||
  TRY_RESULT(input_json, td::json_decode(td::MutableSlice(json_str)));
 | 
			
		||||
  auto &obj = input_json.get_object();
 | 
			
		||||
 | 
			
		||||
  TRY_RESULT(code, td::get_json_object_string_field(obj, "code", false));
 | 
			
		||||
  params.code = code;
 | 
			
		||||
 | 
			
		||||
  TRY_RESULT(data, td::get_json_object_string_field(obj, "data", false));
 | 
			
		||||
  params.data = data;
 | 
			
		||||
 | 
			
		||||
  TRY_RESULT(verbosity, td::get_json_object_int_field(obj, "verbosity", false));
 | 
			
		||||
  params.verbosity = verbosity;
 | 
			
		||||
 | 
			
		||||
  TRY_RESULT(libs, td::get_json_object_string_field(obj, "libs", true));
 | 
			
		||||
  if (libs.size() > 0) {
 | 
			
		||||
    params.libs = libs;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  TRY_RESULT(address, td::get_json_object_string_field(obj, "address", false));
 | 
			
		||||
  params.address = address;
 | 
			
		||||
 | 
			
		||||
  TRY_RESULT(unixtime_field, td::get_json_object_field(obj, "unixtime", td::JsonValue::Type::Number, false));
 | 
			
		||||
  TRY_RESULT(unixtime, td::to_integer_safe<td::uint32>(unixtime_field.get_number()));
 | 
			
		||||
  params.unixtime = unixtime;
 | 
			
		||||
 | 
			
		||||
  TRY_RESULT(balance_field, td::get_json_object_field(obj, "balance", td::JsonValue::Type::String, false));
 | 
			
		||||
  TRY_RESULT(balance, td::to_integer_safe<td::uint64>(balance_field.get_string()));
 | 
			
		||||
  params.balance = balance;
 | 
			
		||||
 | 
			
		||||
  TRY_RESULT(rand_seed_str, td::get_json_object_string_field(obj, "rand_seed", false));
 | 
			
		||||
  params.rand_seed_hex = rand_seed_str;
 | 
			
		||||
 | 
			
		||||
  TRY_RESULT(gas_limit_field, td::get_json_object_field(obj, "gas_limit", td::JsonValue::Type::String, false));
 | 
			
		||||
  TRY_RESULT(gas_limit, td::to_integer_safe<td::uint64>(gas_limit_field.get_string()));
 | 
			
		||||
  params.gas_limit = gas_limit;
 | 
			
		||||
 | 
			
		||||
  TRY_RESULT(method_id, td::get_json_object_int_field(obj, "method_id", false));
 | 
			
		||||
  params.method_id = method_id;
 | 
			
		||||
 | 
			
		||||
  return params;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
 | 
			
		||||
const char *emulate(const char *config, const char* libs, int verbosity, const char* account, const char* message, const char* params) {
 | 
			
		||||
    StringLog logger;
 | 
			
		||||
 | 
			
		||||
    td::log_interface = &logger;
 | 
			
		||||
    SET_VERBOSITY_LEVEL(verbosity_DEBUG);
 | 
			
		||||
 | 
			
		||||
    auto decoded_params_res = decode_transaction_emulation_params(params);
 | 
			
		||||
    if (decoded_params_res.is_error()) {
 | 
			
		||||
        return strdup(R"({"fail":true,"message":"Can't decode other params"})");
 | 
			
		||||
    }
 | 
			
		||||
    auto decoded_params = decoded_params_res.move_as_ok();
 | 
			
		||||
 | 
			
		||||
    auto em = transaction_emulator_create(config, verbosity);
 | 
			
		||||
 | 
			
		||||
    bool rand_seed_set = true;
 | 
			
		||||
    if (decoded_params.rand_seed_hex) {
 | 
			
		||||
      rand_seed_set = transaction_emulator_set_rand_seed(em, decoded_params.rand_seed_hex.unwrap().c_str());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!transaction_emulator_set_libs(em, libs) ||
 | 
			
		||||
        !transaction_emulator_set_lt(em, decoded_params.lt) ||
 | 
			
		||||
        !transaction_emulator_set_unixtime(em, decoded_params.utime) ||
 | 
			
		||||
        !transaction_emulator_set_ignore_chksig(em, decoded_params.ignore_chksig) ||
 | 
			
		||||
        !rand_seed_set) {
 | 
			
		||||
        transaction_emulator_destroy(em);
 | 
			
		||||
        return strdup(R"({"fail":true,"message":"Can't set params"})");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto tx = transaction_emulator_emulate_transaction(em, account, message);
 | 
			
		||||
 | 
			
		||||
    transaction_emulator_destroy(em);
 | 
			
		||||
 | 
			
		||||
    const char* output = nullptr;
 | 
			
		||||
    {
 | 
			
		||||
        td::JsonBuilder jb;
 | 
			
		||||
        auto json_obj = jb.enter_object();
 | 
			
		||||
        json_obj("output", td::JsonRaw(td::Slice(tx)));
 | 
			
		||||
        json_obj("logs", logger.get_string());
 | 
			
		||||
        json_obj.leave();
 | 
			
		||||
        output = strdup(jb.string_builder().as_cslice().c_str());
 | 
			
		||||
    }
 | 
			
		||||
    free((void*) tx);
 | 
			
		||||
 | 
			
		||||
    return output;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *run_get_method(const char *params, const char* stack, const char* config) {
 | 
			
		||||
    StringLog logger;
 | 
			
		||||
 | 
			
		||||
    td::log_interface = &logger;
 | 
			
		||||
    SET_VERBOSITY_LEVEL(verbosity_DEBUG);
 | 
			
		||||
 | 
			
		||||
    auto decoded_params_res = decode_get_method_params(params);
 | 
			
		||||
    if (decoded_params_res.is_error()) {
 | 
			
		||||
        return strdup(R"({"fail":true,"message":"Can't decode params"})");
 | 
			
		||||
    }
 | 
			
		||||
    auto decoded_params = decoded_params_res.move_as_ok();
 | 
			
		||||
 | 
			
		||||
    auto tvm = tvm_emulator_create(decoded_params.code.c_str(), decoded_params.data.c_str(), decoded_params.verbosity);
 | 
			
		||||
 | 
			
		||||
    if ((decoded_params.libs && !tvm_emulator_set_libraries(tvm, decoded_params.libs.value().c_str())) ||
 | 
			
		||||
        !tvm_emulator_set_c7(tvm, decoded_params.address.c_str(), decoded_params.unixtime,
 | 
			
		||||
          decoded_params.balance, decoded_params.rand_seed_hex.c_str(), config) ||
 | 
			
		||||
        (decoded_params.gas_limit > 0 && !tvm_emulator_set_gas_limit(tvm, decoded_params.gas_limit))) {
 | 
			
		||||
      tvm_emulator_destroy(tvm);
 | 
			
		||||
      return strdup(R"({"fail":true,"message":"Can't set params"})");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto res = tvm_emulator_run_get_method(tvm, decoded_params.method_id, stack);
 | 
			
		||||
 | 
			
		||||
    tvm_emulator_destroy(tvm);
 | 
			
		||||
 | 
			
		||||
    const char* output = nullptr;
 | 
			
		||||
    {
 | 
			
		||||
        td::JsonBuilder jb;
 | 
			
		||||
        auto json_obj = jb.enter_object();
 | 
			
		||||
        json_obj("output", td::JsonRaw(td::Slice(res)));
 | 
			
		||||
        json_obj("logs", logger.get_string());
 | 
			
		||||
        json_obj.leave();
 | 
			
		||||
        output = strdup(jb.string_builder().as_cslice().c_str());
 | 
			
		||||
    }
 | 
			
		||||
    free((void*) res);
 | 
			
		||||
 | 
			
		||||
    return output;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										435
									
								
								emulator/emulator-extern.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										435
									
								
								emulator/emulator-extern.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,435 @@
 | 
			
		|||
#include "emulator-extern.h"
 | 
			
		||||
#include "td/utils/base64.h"
 | 
			
		||||
#include "td/utils/Status.h"
 | 
			
		||||
#include "td/utils/JsonBuilder.h"
 | 
			
		||||
#include "td/utils/logging.h"
 | 
			
		||||
#include "td/utils/Variant.h"
 | 
			
		||||
#include "td/utils/overloaded.h"
 | 
			
		||||
#include "transaction-emulator.h"
 | 
			
		||||
#include "tvm-emulator.hpp"
 | 
			
		||||
#include "crypto/vm/stack.hpp"
 | 
			
		||||
 | 
			
		||||
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: ");
 | 
			
		||||
  return vm::std_boc_deserialize(boc_decoded);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Result<std::string> cell_to_boc_b64(td::Ref<vm::Cell> cell) {
 | 
			
		||||
  TRY_RESULT_PREFIX(boc, vm::std_boc_serialize(std::move(cell), vm::BagOfCells::Mode::WithCRC32C), "Can't serialize cell: ");
 | 
			
		||||
  return td::base64_encode(boc.as_slice());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *success_response(std::string&& transaction, std::string&& new_shard_account, std::string&& vm_log, td::optional<std::string>&& actions) {
 | 
			
		||||
  td::JsonBuilder jb;
 | 
			
		||||
  auto json_obj = jb.enter_object();
 | 
			
		||||
  json_obj("success", td::JsonTrue());
 | 
			
		||||
  json_obj("transaction", std::move(transaction));
 | 
			
		||||
  json_obj("shard_account", std::move(new_shard_account));
 | 
			
		||||
  json_obj("vm_log", std::move(vm_log));
 | 
			
		||||
  if (actions) {
 | 
			
		||||
    json_obj("actions", actions.unwrap());
 | 
			
		||||
  } else {
 | 
			
		||||
    json_obj("actions", td::JsonNull());
 | 
			
		||||
  }
 | 
			
		||||
  json_obj.leave();
 | 
			
		||||
  return strdup(jb.string_builder().as_cslice().c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *error_response(std::string&& error) {
 | 
			
		||||
  td::JsonBuilder jb;
 | 
			
		||||
  auto json_obj = jb.enter_object();
 | 
			
		||||
  json_obj("success", td::JsonFalse());
 | 
			
		||||
  json_obj("error", std::move(error));
 | 
			
		||||
  json_obj.leave();
 | 
			
		||||
  return strdup(jb.string_builder().as_cslice().c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *external_not_accepted_response(std::string&& vm_log, int vm_exit_code) {
 | 
			
		||||
  td::JsonBuilder jb;
 | 
			
		||||
  auto json_obj = jb.enter_object();
 | 
			
		||||
  json_obj("success", td::JsonFalse());
 | 
			
		||||
  json_obj("error", "External message not accepted by smart contract");
 | 
			
		||||
  json_obj("vm_log", std::move(vm_log));
 | 
			
		||||
  json_obj("vm_exit_code", vm_exit_code);
 | 
			
		||||
  json_obj.leave();
 | 
			
		||||
  return strdup(jb.string_builder().as_cslice().c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define ERROR_RESPONSE(error) return error_response(error)
 | 
			
		||||
 | 
			
		||||
td::Result<block::Config> decode_config(const char* config_boc) {
 | 
			
		||||
  TRY_RESULT_PREFIX(config_params_cell, boc_b64_to_cell(config_boc), "Can't deserialize config params boc: ");
 | 
			
		||||
  auto global_config = block::Config(config_params_cell, td::Bits256::zero(), block::Config::needWorkchainInfo | block::Config::needSpecialSmc);
 | 
			
		||||
  TRY_STATUS_PREFIX(global_config.unpack(), "Can't unpack config params: ");
 | 
			
		||||
  return global_config;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *transaction_emulator_create(const char *config_params_boc, int vm_log_verbosity) {
 | 
			
		||||
  auto global_config_res = decode_config(config_params_boc);
 | 
			
		||||
  if (global_config_res.is_error()) {
 | 
			
		||||
    LOG(ERROR) << global_config_res.move_as_error().message();
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return new emulator::TransactionEmulator(global_config_res.move_as_ok(), vm_log_verbosity);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *transaction_emulator_emulate_transaction(void *transaction_emulator, const char *shard_account_boc, const char *message_boc) {
 | 
			
		||||
  auto emulator = static_cast<emulator::TransactionEmulator *>(transaction_emulator);
 | 
			
		||||
  
 | 
			
		||||
  auto message_cell_r = boc_b64_to_cell(message_boc);
 | 
			
		||||
  if (message_cell_r.is_error()) {
 | 
			
		||||
    ERROR_RESPONSE(PSTRING() << "Can't deserialize message boc: " << message_cell_r.move_as_error());
 | 
			
		||||
  }
 | 
			
		||||
  auto message_cell = message_cell_r.move_as_ok();
 | 
			
		||||
  auto message_cs = vm::load_cell_slice(message_cell);
 | 
			
		||||
  int msg_tag = block::gen::t_CommonMsgInfo.get_tag(message_cs);
 | 
			
		||||
 | 
			
		||||
  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) {
 | 
			
		||||
    if (msg_tag == block::gen::CommonMsgInfo::ext_in_msg_info) {
 | 
			
		||||
      block::gen::CommonMsgInfo::Record_ext_in_msg_info info;
 | 
			
		||||
      if (!tlb::unpack(message_cs, info)) {
 | 
			
		||||
        ERROR_RESPONSE(PSTRING() <<  "Can't unpack inbound external message");
 | 
			
		||||
      }
 | 
			
		||||
      addr_slice = std::move(info.dest);
 | 
			
		||||
    }
 | 
			
		||||
    else if (msg_tag == block::gen::CommonMsgInfo::int_msg_info) {
 | 
			
		||||
      block::gen::CommonMsgInfo::Record_int_msg_info info;
 | 
			
		||||
      if (!tlb::unpack(message_cs, info)) {
 | 
			
		||||
          ERROR_RESPONSE(PSTRING() << "Can't unpack inbound internal message");
 | 
			
		||||
      }
 | 
			
		||||
      addr_slice = std::move(info.dest);
 | 
			
		||||
    } else {
 | 
			
		||||
      ERROR_RESPONSE(PSTRING() << "Only ext in and int message are supported");
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    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 = (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);
 | 
			
		||||
  if (result.is_error()) {
 | 
			
		||||
    ERROR_RESPONSE(PSTRING() << "Emulate transaction failed: " << result.move_as_error());
 | 
			
		||||
  }
 | 
			
		||||
  auto emulation_result = result.move_as_ok();
 | 
			
		||||
 | 
			
		||||
  auto external_not_accepted = dynamic_cast<emulator::TransactionEmulator::EmulationExternalNotAccepted *>(emulation_result.get());
 | 
			
		||||
  if (external_not_accepted) {
 | 
			
		||||
    return external_not_accepted_response(std::move(external_not_accepted->vm_log), external_not_accepted->vm_exit_code);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool transaction_emulator_set_unixtime(void *transaction_emulator, uint32_t unixtime) {
 | 
			
		||||
  auto emulator = static_cast<emulator::TransactionEmulator *>(transaction_emulator);
 | 
			
		||||
 | 
			
		||||
  emulator->set_unixtime(unixtime);
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool transaction_emulator_set_lt(void *transaction_emulator, uint64_t lt) {
 | 
			
		||||
  auto emulator = static_cast<emulator::TransactionEmulator *>(transaction_emulator);
 | 
			
		||||
 | 
			
		||||
  emulator->set_lt(lt);
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool transaction_emulator_set_rand_seed(void *transaction_emulator, const char* rand_seed_hex) {
 | 
			
		||||
  auto emulator = static_cast<emulator::TransactionEmulator *>(transaction_emulator);
 | 
			
		||||
 | 
			
		||||
  auto rand_seed_hex_slice = td::Slice(rand_seed_hex);
 | 
			
		||||
  if (rand_seed_hex_slice.size() != 64) {
 | 
			
		||||
    LOG(ERROR) << "Rand seed expected as 64 characters hex string";
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  auto rand_seed_bytes = td::hex_decode(rand_seed_hex_slice);
 | 
			
		||||
  if (rand_seed_bytes.is_error()) {
 | 
			
		||||
    LOG(ERROR) << "Can't decode hex rand seed";
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  td::BitArray<256> rand_seed;
 | 
			
		||||
  rand_seed.as_slice().copy_from(rand_seed_bytes.move_as_ok());
 | 
			
		||||
 | 
			
		||||
  emulator->set_rand_seed(rand_seed);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool transaction_emulator_set_ignore_chksig(void *transaction_emulator, bool ignore_chksig) {
 | 
			
		||||
  auto emulator = static_cast<emulator::TransactionEmulator *>(transaction_emulator);
 | 
			
		||||
 | 
			
		||||
  emulator->set_ignore_chksig(ignore_chksig);
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool transaction_emulator_set_config(void *transaction_emulator, const char* config_boc) {
 | 
			
		||||
  auto emulator = static_cast<emulator::TransactionEmulator *>(transaction_emulator);
 | 
			
		||||
 | 
			
		||||
  auto global_config_res = decode_config(config_boc);
 | 
			
		||||
  if (global_config_res.is_error()) {
 | 
			
		||||
    LOG(ERROR) << global_config_res.move_as_error().message();
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  emulator->set_config(global_config_res.move_as_ok());
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool transaction_emulator_set_libs(void *transaction_emulator, const char* shardchain_libs_boc) {
 | 
			
		||||
  auto emulator = static_cast<emulator::TransactionEmulator *>(transaction_emulator);
 | 
			
		||||
 | 
			
		||||
  if (shardchain_libs_boc != nullptr) {
 | 
			
		||||
    auto shardchain_libs_cell = boc_b64_to_cell(shardchain_libs_boc);
 | 
			
		||||
    if (shardchain_libs_cell.is_error()) {
 | 
			
		||||
      LOG(ERROR) << "Can't deserialize shardchain libraries boc: " << shardchain_libs_cell.move_as_error();
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    emulator->set_libs(vm::Dictionary(shardchain_libs_cell.move_as_ok(), 256));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void transaction_emulator_destroy(void *transaction_emulator) {
 | 
			
		||||
  delete static_cast<emulator::TransactionEmulator *>(transaction_emulator);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool emulator_set_verbosity_level(int verbosity_level) {
 | 
			
		||||
  if (0 <= verbosity_level && verbosity_level <= VERBOSITY_NAME(NEVER)) {
 | 
			
		||||
    SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL) + verbosity_level);
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *tvm_emulator_create(const char *code, const char *data, int vm_log_verbosity) {
 | 
			
		||||
  auto code_cell = boc_b64_to_cell(code);
 | 
			
		||||
  if (code_cell.is_error()) {
 | 
			
		||||
    LOG(ERROR) << "Can't deserialize code boc: " << code_cell.move_as_error();
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  }
 | 
			
		||||
  auto data_cell = boc_b64_to_cell(data);
 | 
			
		||||
  if (data_cell.is_error()) {
 | 
			
		||||
    LOG(ERROR) << "Can't deserialize code boc: " << data_cell.move_as_error();
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto emulator = new emulator::TvmEmulator(code_cell.move_as_ok(), data_cell.move_as_ok());
 | 
			
		||||
  emulator->set_vm_verbosity_level(vm_log_verbosity);
 | 
			
		||||
  return emulator;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool tvm_emulator_set_libraries(void *tvm_emulator, const char *libs_boc) {
 | 
			
		||||
  vm::Dictionary libs{256};
 | 
			
		||||
  auto libs_cell = boc_b64_to_cell(libs_boc);
 | 
			
		||||
  if (libs_cell.is_error()) {
 | 
			
		||||
    LOG(ERROR) << "Can't deserialize libraries boc: " << libs_cell.move_as_error();
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  libs = vm::Dictionary(libs_cell.move_as_ok(), 256);
 | 
			
		||||
 | 
			
		||||
  auto emulator = static_cast<emulator::TvmEmulator *>(tvm_emulator);
 | 
			
		||||
  emulator->set_libraries(std::move(libs));
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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_boc) {
 | 
			
		||||
  auto emulator = static_cast<emulator::TvmEmulator *>(tvm_emulator);
 | 
			
		||||
  auto std_address = block::StdAddress::parse(td::Slice(address));
 | 
			
		||||
  if (std_address.is_error()) {
 | 
			
		||||
    LOG(ERROR) << "Can't parse address: " << std_address.move_as_error();
 | 
			
		||||
    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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto rand_seed_hex_slice = td::Slice(rand_seed_hex);
 | 
			
		||||
  if (rand_seed_hex_slice.size() != 64) {
 | 
			
		||||
    LOG(ERROR) << "Rand seed expected as 64 characters hex string";
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  auto rand_seed_bytes = td::hex_decode(rand_seed_hex_slice);
 | 
			
		||||
  if (rand_seed_bytes.is_error()) {
 | 
			
		||||
    LOG(ERROR) << "Can't decode hex rand seed";
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  td::BitArray<256> rand_seed;
 | 
			
		||||
  rand_seed.as_slice().copy_from(rand_seed_bytes.move_as_ok());
 | 
			
		||||
 | 
			
		||||
  emulator->set_c7(std_address.move_as_ok(), unixtime, balance, rand_seed, std::const_pointer_cast<const block::Config>(global_config));
 | 
			
		||||
  
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool tvm_emulator_set_gas_limit(void *tvm_emulator, int64_t gas_limit) {
 | 
			
		||||
  auto emulator = static_cast<emulator::TvmEmulator *>(tvm_emulator);
 | 
			
		||||
  emulator->set_gas_limit(gas_limit);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *tvm_emulator_run_get_method(void *tvm_emulator, int method_id, const char *stack_boc) {
 | 
			
		||||
  auto stack_cell = boc_b64_to_cell(stack_boc);
 | 
			
		||||
  if (stack_cell.is_error()) {
 | 
			
		||||
    ERROR_RESPONSE(PSTRING() << "Couldn't deserialize stack cell: " << stack_cell.move_as_error().to_string());
 | 
			
		||||
  }
 | 
			
		||||
  auto stack_cs = vm::load_cell_slice(stack_cell.move_as_ok());
 | 
			
		||||
  td::Ref<vm::Stack> stack;
 | 
			
		||||
  if (!vm::Stack::deserialize_to(stack_cs, stack)) {
 | 
			
		||||
     ERROR_RESPONSE(PSTRING() << "Couldn't deserialize stack");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto emulator = static_cast<emulator::TvmEmulator *>(tvm_emulator);
 | 
			
		||||
  auto result = emulator->run_get_method(method_id, stack);
 | 
			
		||||
  
 | 
			
		||||
  vm::CellBuilder stack_cb;
 | 
			
		||||
  if (!result.stack->serialize(stack_cb)) {
 | 
			
		||||
    ERROR_RESPONSE(PSTRING() << "Couldn't serialize stack");
 | 
			
		||||
  }
 | 
			
		||||
  auto result_stack_boc = cell_to_boc_b64(stack_cb.finalize());
 | 
			
		||||
  if (result_stack_boc.is_error()) {
 | 
			
		||||
    ERROR_RESPONSE(PSTRING() << "Couldn't serialize stack cell: " << result_stack_boc.move_as_error().to_string());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  td::JsonBuilder jb;
 | 
			
		||||
  auto json_obj = jb.enter_object();
 | 
			
		||||
  json_obj("success", td::JsonTrue());
 | 
			
		||||
  json_obj("stack", result_stack_boc.move_as_ok());
 | 
			
		||||
  json_obj("gas_used", std::to_string(result.gas_used));
 | 
			
		||||
  json_obj("vm_exit_code", result.code);
 | 
			
		||||
  json_obj("vm_log", result.vm_log);
 | 
			
		||||
  if (result.missing_library.is_null()) {
 | 
			
		||||
    json_obj("missing_library", td::JsonNull());
 | 
			
		||||
  } else {
 | 
			
		||||
    json_obj("missing_library", td::Bits256(result.missing_library).to_hex());
 | 
			
		||||
  }
 | 
			
		||||
  json_obj.leave();
 | 
			
		||||
 | 
			
		||||
  return strdup(jb.string_builder().as_cslice().c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *tvm_emulator_send_external_message(void *tvm_emulator, const char *message_body_boc) {
 | 
			
		||||
  auto message_body_cell = boc_b64_to_cell(message_body_boc);
 | 
			
		||||
  if (message_body_cell.is_error()) {
 | 
			
		||||
    ERROR_RESPONSE(PSTRING() << "Can't deserialize message body boc: " << message_body_cell.move_as_error());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto emulator = static_cast<emulator::TvmEmulator *>(tvm_emulator);
 | 
			
		||||
  auto result = emulator->send_external_message(message_body_cell.move_as_ok());
 | 
			
		||||
 | 
			
		||||
  td::JsonBuilder jb;
 | 
			
		||||
  auto json_obj = jb.enter_object();
 | 
			
		||||
  json_obj("success", td::JsonTrue());
 | 
			
		||||
  json_obj("gas_used", std::to_string(result.gas_used));
 | 
			
		||||
  json_obj("vm_exit_code", result.code);
 | 
			
		||||
  json_obj("accepted", td::JsonBool(result.accepted));
 | 
			
		||||
  json_obj("vm_log", result.vm_log);
 | 
			
		||||
  if (result.missing_library.is_null()) {
 | 
			
		||||
    json_obj("missing_library", td::JsonNull());
 | 
			
		||||
  } else {
 | 
			
		||||
    json_obj("missing_library", td::Bits256(result.missing_library).to_hex());
 | 
			
		||||
  }
 | 
			
		||||
  json_obj("actions", cell_to_boc_b64(result.actions).move_as_ok());
 | 
			
		||||
  json_obj("new_code", cell_to_boc_b64(result.new_state.code).move_as_ok());
 | 
			
		||||
  json_obj("new_data", cell_to_boc_b64(result.new_state.data).move_as_ok());
 | 
			
		||||
  json_obj.leave();
 | 
			
		||||
 | 
			
		||||
  return strdup(jb.string_builder().as_cslice().c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *tvm_emulator_send_internal_message(void *tvm_emulator, const char *message_body_boc, uint64_t amount) {
 | 
			
		||||
  auto message_body_cell = boc_b64_to_cell(message_body_boc);
 | 
			
		||||
  if (message_body_cell.is_error()) {
 | 
			
		||||
    ERROR_RESPONSE(PSTRING() << "Can't deserialize message body boc: " << message_body_cell.move_as_error());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto emulator = static_cast<emulator::TvmEmulator *>(tvm_emulator);
 | 
			
		||||
  auto result = emulator->send_internal_message(message_body_cell.move_as_ok(), amount);
 | 
			
		||||
 | 
			
		||||
  td::JsonBuilder jb;
 | 
			
		||||
  auto json_obj = jb.enter_object();
 | 
			
		||||
  json_obj("success", td::JsonTrue());
 | 
			
		||||
  json_obj("gas_used", std::to_string(result.gas_used));
 | 
			
		||||
  json_obj("vm_exit_code", result.code);
 | 
			
		||||
  json_obj("accepted", td::JsonBool(result.accepted));
 | 
			
		||||
  json_obj("vm_log", result.vm_log);
 | 
			
		||||
  if (result.missing_library.is_null()) {
 | 
			
		||||
    json_obj("missing_library", td::JsonNull());
 | 
			
		||||
  } else {
 | 
			
		||||
    json_obj("missing_library", td::Bits256(result.missing_library).to_hex());
 | 
			
		||||
  }
 | 
			
		||||
  json_obj("actions", cell_to_boc_b64(result.actions).move_as_ok());
 | 
			
		||||
  json_obj("new_code", cell_to_boc_b64(result.new_state.code).move_as_ok());
 | 
			
		||||
  json_obj("new_data", cell_to_boc_b64(result.new_state.data).move_as_ok());
 | 
			
		||||
  json_obj.leave();
 | 
			
		||||
 | 
			
		||||
  return strdup(jb.string_builder().as_cslice().c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void tvm_emulator_destroy(void *tvm_emulator) {
 | 
			
		||||
  delete static_cast<emulator::TvmEmulator *>(tvm_emulator);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										216
									
								
								emulator/emulator-extern.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								emulator/emulator-extern.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,216 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include "emulator_export.h"
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Creates TransactionEmulator object
 | 
			
		||||
 * @param config_params_boc Base64 encoded BoC serialized Config dictionary (Hashmap 32 ^Cell)
 | 
			
		||||
 * @param vm_log_verbosity Verbosity level of VM log. 0 - log truncated to last 256 characters. 1 - unlimited length log.
 | 
			
		||||
 * 2 - for each command prints its cell hash and offset. 3 - for each command log prints all stack values.
 | 
			
		||||
 * @return Pointer to TransactionEmulator or nullptr in case of error
 | 
			
		||||
 */
 | 
			
		||||
EMULATOR_EXPORT void *transaction_emulator_create(const char *config_params_boc, int vm_log_verbosity);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Set unixtime for emulation
 | 
			
		||||
 * @param transaction_emulator Pointer to TransactionEmulator object
 | 
			
		||||
 * @param unixtime Unix timestamp
 | 
			
		||||
 * @return true in case of success, false in case of error
 | 
			
		||||
 */
 | 
			
		||||
EMULATOR_EXPORT bool transaction_emulator_set_unixtime(void *transaction_emulator, uint32_t unixtime);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Set lt for emulation
 | 
			
		||||
 * @param transaction_emulator Pointer to TransactionEmulator object
 | 
			
		||||
 * @param lt Logical time
 | 
			
		||||
 * @return true in case of success, false in case of error
 | 
			
		||||
 */
 | 
			
		||||
EMULATOR_EXPORT bool transaction_emulator_set_lt(void *transaction_emulator, uint64_t lt);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Set rand seed for emulation
 | 
			
		||||
 * @param transaction_emulator Pointer to TransactionEmulator object
 | 
			
		||||
 * @param rand_seed_hex Hex string of length 64
 | 
			
		||||
 * @return true in case of success, false in case of error
 | 
			
		||||
 */
 | 
			
		||||
EMULATOR_EXPORT bool transaction_emulator_set_rand_seed(void *transaction_emulator, const char* rand_seed_hex);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Set ignore_chksig flag for emulation
 | 
			
		||||
 * @param transaction_emulator Pointer to TransactionEmulator object
 | 
			
		||||
 * @param ignore_chksig Whether emulation should always succeed on CHKSIG operation
 | 
			
		||||
 * @return true in case of success, false in case of error
 | 
			
		||||
 */
 | 
			
		||||
EMULATOR_EXPORT bool transaction_emulator_set_ignore_chksig(void *transaction_emulator, bool ignore_chksig);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Set unixtime for emulation
 | 
			
		||||
 * @param transaction_emulator Pointer to TransactionEmulator object
 | 
			
		||||
 * @param config_boc Base64 encoded BoC serialized Config dictionary (Hashmap 32 ^Cell) 
 | 
			
		||||
 * @return true in case of success, false in case of error
 | 
			
		||||
 */
 | 
			
		||||
EMULATOR_EXPORT bool transaction_emulator_set_config(void *transaction_emulator, const char* config_boc);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Set unixtime for emulation
 | 
			
		||||
 * @param transaction_emulator Pointer to TransactionEmulator object
 | 
			
		||||
 * @param libs_boc Base64 encoded BoC serialized shared libraries dictionary (HashmapE 256 ^Cell).
 | 
			
		||||
 * @return true in case of success, false in case of error
 | 
			
		||||
 */
 | 
			
		||||
EMULATOR_EXPORT bool transaction_emulator_set_libs(void *transaction_emulator, const char* libs_boc);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Emulate transaction
 | 
			
		||||
 * @param transaction_emulator Pointer to TransactionEmulator object
 | 
			
		||||
 * @param shard_account_boc Base64 encoded BoC serialized ShardAccount
 | 
			
		||||
 * @param message_boc Base64 encoded BoC serialized inbound Message (internal or external)
 | 
			
		||||
 * @return Json object with error:
 | 
			
		||||
 * { 
 | 
			
		||||
 *   "success": false, 
 | 
			
		||||
 *   "error": "Error description" 
 | 
			
		||||
 *   // and optional fields "vm_exit_code" and "vm_log" in case external message was not accepted.
 | 
			
		||||
 * } 
 | 
			
		||||
 * 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)"
 | 
			
		||||
 * }
 | 
			
		||||
 */
 | 
			
		||||
EMULATOR_EXPORT const char *transaction_emulator_emulate_transaction(void *transaction_emulator, const char *shard_account_boc, const char *message_boc);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Destroy TransactionEmulator object
 | 
			
		||||
 * @param transaction_emulator Pointer to TransactionEmulator object
 | 
			
		||||
 */
 | 
			
		||||
EMULATOR_EXPORT void transaction_emulator_destroy(void *transaction_emulator);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Set global verbosity level of the library
 | 
			
		||||
 * @param verbosity_level New verbosity level (0 - never, 1 - error, 2 - warning, 3 - info, 4 - debug)
 | 
			
		||||
 */
 | 
			
		||||
EMULATOR_EXPORT bool emulator_set_verbosity_level(int verbosity_level);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Create TVM emulator
 | 
			
		||||
 * @param code_boc Base64 encoded BoC serialized smart contract code cell
 | 
			
		||||
 * @param data_boc Base64 encoded BoC serialized smart contract data cell
 | 
			
		||||
 * @param vm_log_verbosity Verbosity level of VM log
 | 
			
		||||
 * @return Pointer to TVM emulator object
 | 
			
		||||
 */
 | 
			
		||||
EMULATOR_EXPORT void *tvm_emulator_create(const char *code_boc, const char *data_boc, int vm_log_verbosity);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Set libraries for TVM emulator
 | 
			
		||||
 * @param libs_boc Base64 encoded BoC serialized libraries dictionary (HashmapE 256 ^Cell).
 | 
			
		||||
 * @return true in case of success, false in case of error
 | 
			
		||||
 */
 | 
			
		||||
EMULATOR_EXPORT bool tvm_emulator_set_libraries(void *tvm_emulator, const char *libs_boc);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Set c7 parameters
 | 
			
		||||
 * @param tvm_emulator Pointer to TVM emulator
 | 
			
		||||
 * @param address Adress of smart contract
 | 
			
		||||
 * @param unixtime Unix timestamp
 | 
			
		||||
 * @param balance Smart contract balance
 | 
			
		||||
 * @param rand_seed_hex Random seed as hex string of length 64
 | 
			
		||||
 * @param config Base64 encoded BoC serialized Config dictionary (Hashmap 32 ^Cell)
 | 
			
		||||
 * @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);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Set TVM gas limit
 | 
			
		||||
 * @param tvm_emulator Pointer to TVM emulator
 | 
			
		||||
 * @param gas_limit Gas limit
 | 
			
		||||
 * @return true in case of success, false in case of error
 | 
			
		||||
 */
 | 
			
		||||
EMULATOR_EXPORT bool tvm_emulator_set_gas_limit(void *tvm_emulator, int64_t gas_limit);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Run get method
 | 
			
		||||
 * @param tvm_emulator Pointer to TVM emulator
 | 
			
		||||
 * @param method_id Integer method id
 | 
			
		||||
 * @param stack_boc Base64 encoded BoC serialized stack (VmStack)
 | 
			
		||||
 * @return Json object with error:
 | 
			
		||||
 * { 
 | 
			
		||||
 *   "success": false, 
 | 
			
		||||
 *   "error": "Error description"
 | 
			
		||||
 * } 
 | 
			
		||||
 * Or success:
 | 
			
		||||
 * {
 | 
			
		||||
 *   "success": true
 | 
			
		||||
 *   "vm_log": "...", 
 | 
			
		||||
 *   "vm_exit_code": 0, 
 | 
			
		||||
 *   "stack": "Base64 encoded BoC serialized stack (VmStack)", 
 | 
			
		||||
 *   "missing_library": null, 
 | 
			
		||||
 *   "gas_used": 1212
 | 
			
		||||
 * }
 | 
			
		||||
 */
 | 
			
		||||
EMULATOR_EXPORT const char *tvm_emulator_run_get_method(void *tvm_emulator, int method_id, const char *stack_boc);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Send external message
 | 
			
		||||
 * @param tvm_emulator Pointer to TVM emulator
 | 
			
		||||
 * @param message_body_boc Base64 encoded BoC serialized message body cell.
 | 
			
		||||
 * @return Json object with error:
 | 
			
		||||
 * { 
 | 
			
		||||
 *   "success": false, 
 | 
			
		||||
 *   "error": "Error description"
 | 
			
		||||
 * } 
 | 
			
		||||
 * Or success:
 | 
			
		||||
 * {
 | 
			
		||||
 *   "success": true,
 | 
			
		||||
 *   "new_code": "Base64 boc decoded new code cell",
 | 
			
		||||
 *   "new_data": "Base64 boc decoded new data cell",
 | 
			
		||||
 *   "accepted": true,
 | 
			
		||||
 *   "vm_exit_code": 0, 
 | 
			
		||||
 *   "vm_log": "...", 
 | 
			
		||||
 *   "missing_library": null, 
 | 
			
		||||
 *   "gas_used": 1212,
 | 
			
		||||
 *   "actions": "Base64 boc decoded actions cell of type (OutList n)"
 | 
			
		||||
 * }
 | 
			
		||||
 */
 | 
			
		||||
EMULATOR_EXPORT const char *tvm_emulator_send_external_message(void *tvm_emulator, const char *message_body_boc);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Send internal message
 | 
			
		||||
 * @param tvm_emulator Pointer to TVM emulator
 | 
			
		||||
 * @param message_body_boc Base64 encoded BoC serialized message body cell.
 | 
			
		||||
 * @param amount Amount of nanograms attached with internal message.
 | 
			
		||||
 * @return Json object with error:
 | 
			
		||||
 * { 
 | 
			
		||||
 *   "success": false, 
 | 
			
		||||
 *   "error": "Error description"
 | 
			
		||||
 * } 
 | 
			
		||||
 * Or success:
 | 
			
		||||
 * {
 | 
			
		||||
 *   "success": true,
 | 
			
		||||
 *   "new_code": "Base64 boc decoded new code cell",
 | 
			
		||||
 *   "new_data": "Base64 boc decoded new data cell",
 | 
			
		||||
 *   "accepted": true,
 | 
			
		||||
 *   "vm_exit_code": 0, 
 | 
			
		||||
 *   "vm_log": "...", 
 | 
			
		||||
 *   "missing_library": null, 
 | 
			
		||||
 *   "gas_used": 1212,
 | 
			
		||||
 *   "actions": "Base64 boc decoded actions cell of type (OutList n)"
 | 
			
		||||
 * }
 | 
			
		||||
 */
 | 
			
		||||
EMULATOR_EXPORT const char *tvm_emulator_send_internal_message(void *tvm_emulator, const char *message_body_boc, uint64_t amount);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Destroy TVM emulator object
 | 
			
		||||
 * @param tvm_emulator Pointer to TVM emulator object
 | 
			
		||||
 */
 | 
			
		||||
EMULATOR_EXPORT void tvm_emulator_destroy(void *tvm_emulator);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}  // extern "C"
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										17
									
								
								emulator/emulator_export_list
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								emulator/emulator_export_list
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
_transaction_emulator_create
 | 
			
		||||
_transaction_emulator_set_lt
 | 
			
		||||
_transaction_emulator_set_rand_seed
 | 
			
		||||
_transaction_emulator_set_ignore_chksig
 | 
			
		||||
_transaction_emulator_set_config
 | 
			
		||||
_transaction_emulator_set_libs
 | 
			
		||||
_transaction_emulator_emulate_transaction
 | 
			
		||||
_transaction_emulator_destroy
 | 
			
		||||
_emulator_set_verbosity_level
 | 
			
		||||
_tvm_emulator_create
 | 
			
		||||
_tvm_emulator_set_libraries
 | 
			
		||||
_tvm_emulator_set_c7
 | 
			
		||||
_tvm_emulator_set_gas_limit
 | 
			
		||||
_tvm_emulator_run_get_method
 | 
			
		||||
_tvm_emulator_send_external_message
 | 
			
		||||
_tvm_emulator_send_internal_message
 | 
			
		||||
_tvm_emulator_destroy
 | 
			
		||||
							
								
								
									
										254
									
								
								emulator/transaction-emulator.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								emulator/transaction-emulator.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,254 @@
 | 
			
		|||
#include <string>
 | 
			
		||||
#include "transaction-emulator.h"
 | 
			
		||||
#include "crypto/common/refcnt.hpp"
 | 
			
		||||
#include "vm/cp0.h"
 | 
			
		||||
 | 
			
		||||
using td::Ref;
 | 
			
		||||
using namespace std::string_literals;
 | 
			
		||||
 | 
			
		||||
namespace emulator {
 | 
			
		||||
td::Result<std::unique_ptr<TransactionEmulator::EmulationResult>> TransactionEmulator::emulate_transaction(
 | 
			
		||||
    block::Account&& account, td::Ref<vm::Cell> msg_root, ton::UnixTime utime, ton::LogicalTime lt, int trans_type) {
 | 
			
		||||
 | 
			
		||||
    td::Ref<vm::Cell> old_mparams;
 | 
			
		||||
    std::vector<block::StoragePrices> storage_prices;
 | 
			
		||||
    block::StoragePhaseConfig storage_phase_cfg{&storage_prices};
 | 
			
		||||
    block::ComputePhaseConfig compute_phase_cfg;
 | 
			
		||||
    block::ActionPhaseConfig action_phase_cfg;
 | 
			
		||||
    td::RefInt256 masterchain_create_fee, basechain_create_fee;
 | 
			
		||||
    
 | 
			
		||||
    if (!utime) {
 | 
			
		||||
      utime = unixtime_;
 | 
			
		||||
    }
 | 
			
		||||
    if (!utime) {
 | 
			
		||||
      utime = (unsigned)std::time(nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto fetch_res = block::FetchConfigParams::fetch_config_params(config_, &old_mparams,
 | 
			
		||||
                                                                   &storage_prices, &storage_phase_cfg,
 | 
			
		||||
                                                                   &rand_seed_, &compute_phase_cfg,
 | 
			
		||||
                                                                   &action_phase_cfg, &masterchain_create_fee,
 | 
			
		||||
                                                                   &basechain_create_fee, account.workchain, utime);
 | 
			
		||||
    if(fetch_res.is_error()) {
 | 
			
		||||
        return fetch_res.move_as_error_prefix("cannot fetch config params ");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vm::init_op_cp0();    
 | 
			
		||||
 | 
			
		||||
    if (!lt) {
 | 
			
		||||
      lt = lt_;
 | 
			
		||||
    }
 | 
			
		||||
    if (!lt) {
 | 
			
		||||
      lt = (account.last_trans_lt_ / block::ConfigInfo::get_lt_align() + 1) * block::ConfigInfo::get_lt_align(); // next block after account_.last_trans_lt_
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    compute_phase_cfg.libraries = std::make_unique<vm::Dictionary>(libraries_);
 | 
			
		||||
    compute_phase_cfg.ignore_chksig = ignore_chksig_;
 | 
			
		||||
    compute_phase_cfg.with_vm_log = true;
 | 
			
		||||
    compute_phase_cfg.vm_log_verbosity = vm_log_verbosity_;
 | 
			
		||||
 | 
			
		||||
    auto res = create_transaction(msg_root, &account, utime, lt, trans_type,
 | 
			
		||||
                                                    &storage_phase_cfg, &compute_phase_cfg,
 | 
			
		||||
                                                    &action_phase_cfg);
 | 
			
		||||
    if(res.is_error()) {
 | 
			
		||||
      return res.move_as_error_prefix("cannot run message on account ");
 | 
			
		||||
    }
 | 
			
		||||
    std::unique_ptr<block::transaction::Transaction> trans = res.move_as_ok();
 | 
			
		||||
 | 
			
		||||
    if (!trans->compute_phase->accepted && trans->in_msg_extern) {
 | 
			
		||||
      auto vm_log = trans->compute_phase->vm_log;
 | 
			
		||||
      auto vm_exit_code = trans->compute_phase->exit_code;
 | 
			
		||||
      return std::make_unique<TransactionEmulator::EmulationExternalNotAccepted>(std::move(vm_log), vm_exit_code);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!trans->serialize()) {
 | 
			
		||||
      return td::Status::Error(-669,"cannot serialize new transaction for smart contract "s + trans->account.addr.to_hex());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto trans_root = trans->commit(account);
 | 
			
		||||
    if (trans_root.is_null()) {
 | 
			
		||||
      return td::Status::Error(PSLICE() << "cannot commit new transaction for smart contract");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return std::make_unique<TransactionEmulator::EmulationSuccess>(std::move(trans_root), std::move(account), std::move(trans->compute_phase->vm_log), std::move(trans->compute_phase->actions));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Result<TransactionEmulator::EmulationSuccess> TransactionEmulator::emulate_transaction(block::Account&& account, td::Ref<vm::Cell> original_trans) {
 | 
			
		||||
 | 
			
		||||
    block::gen::Transaction::Record record_trans;
 | 
			
		||||
    if (!tlb::unpack_cell(original_trans, record_trans)) {
 | 
			
		||||
      return td::Status::Error("Failed to unpack Transaction");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ton::LogicalTime lt = record_trans.lt;
 | 
			
		||||
    ton::UnixTime utime = record_trans.now;
 | 
			
		||||
    account.now_ = utime;
 | 
			
		||||
    td::Ref<vm::Cell> msg_root = record_trans.r1.in_msg->prefetch_ref();
 | 
			
		||||
    int tag = block::gen::t_TransactionDescr.get_tag(vm::load_cell_slice(record_trans.description));
 | 
			
		||||
 | 
			
		||||
    int trans_type = block::transaction::Transaction::tr_none;
 | 
			
		||||
    switch (tag) {
 | 
			
		||||
      case block::gen::TransactionDescr::trans_ord: {
 | 
			
		||||
        trans_type = block::transaction::Transaction::tr_ord;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case block::gen::TransactionDescr::trans_storage: {
 | 
			
		||||
        trans_type = block::transaction::Transaction::tr_storage;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case block::gen::TransactionDescr::trans_tick_tock: {
 | 
			
		||||
        block::gen::TransactionDescr::Record_trans_tick_tock tick_tock;
 | 
			
		||||
        if (!tlb::unpack_cell(record_trans.description, tick_tock)) {
 | 
			
		||||
          return td::Status::Error("Failed to unpack tick tock transaction description");
 | 
			
		||||
        }
 | 
			
		||||
        trans_type = tick_tock.is_tock ? block::transaction::Transaction::tr_tock : block::transaction::Transaction::tr_tick;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case block::gen::TransactionDescr::trans_split_prepare: {
 | 
			
		||||
        trans_type = block::transaction::Transaction::tr_split_prepare;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case block::gen::TransactionDescr::trans_split_install: {
 | 
			
		||||
        trans_type = block::transaction::Transaction::tr_split_install;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case block::gen::TransactionDescr::trans_merge_prepare: {
 | 
			
		||||
        trans_type = block::transaction::Transaction::tr_merge_prepare;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case block::gen::TransactionDescr::trans_merge_install: {
 | 
			
		||||
        trans_type = block::transaction::Transaction::tr_merge_install;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TRY_RESULT(emulation, emulate_transaction(std::move(account), msg_root, utime, lt, trans_type));
 | 
			
		||||
 | 
			
		||||
    auto emulation_result = dynamic_cast<EmulationSuccess&>(*emulation);
 | 
			
		||||
    if (td::Bits256(emulation_result.transaction->get_hash().bits()) != td::Bits256(original_trans->get_hash().bits())) {
 | 
			
		||||
      return td::Status::Error("transaction hash mismatch");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!check_state_update(emulation_result.account, record_trans)) {
 | 
			
		||||
      return td::Status::Error("account hash mismatch");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return emulation_result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Result<TransactionEmulator::EmulationChain> TransactionEmulator::emulate_transactions_chain(block::Account&& account, std::vector<td::Ref<vm::Cell>>&& original_transactions) {
 | 
			
		||||
 | 
			
		||||
  std::vector<td::Ref<vm::Cell>> emulated_transactions;
 | 
			
		||||
  for (const auto& original_trans : original_transactions) {
 | 
			
		||||
    if (original_trans.is_null()) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TRY_RESULT(emulation_result, emulate_transaction(std::move(account), original_trans));
 | 
			
		||||
    emulated_transactions.push_back(std::move(emulation_result.transaction));
 | 
			
		||||
    account = std::move(emulation_result.account);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return TransactionEmulator::EmulationChain{ std::move(emulated_transactions), std::move(account) };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool TransactionEmulator::check_state_update(const block::Account& account, const block::gen::Transaction::Record& trans) {
 | 
			
		||||
  block::gen::HASH_UPDATE::Record hash_update;
 | 
			
		||||
  return tlb::type_unpack_cell(trans.state_update, block::gen::t_HASH_UPDATE_Account, hash_update) &&
 | 
			
		||||
    hash_update.new_hash == account.total_state->get_hash().bits();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Result<std::unique_ptr<block::transaction::Transaction>> TransactionEmulator::create_transaction(
 | 
			
		||||
                                                         td::Ref<vm::Cell> msg_root, block::Account* acc,
 | 
			
		||||
                                                         ton::UnixTime utime, ton::LogicalTime lt, int trans_type,
 | 
			
		||||
                                                         block::StoragePhaseConfig* storage_phase_cfg,
 | 
			
		||||
                                                         block::ComputePhaseConfig* compute_phase_cfg,
 | 
			
		||||
                                                         block::ActionPhaseConfig* action_phase_cfg) {
 | 
			
		||||
  bool external{false}, ihr_delivered{false}, need_credit_phase{false};
 | 
			
		||||
 | 
			
		||||
  if (msg_root.not_null()) {
 | 
			
		||||
    auto cs = vm::load_cell_slice(msg_root);
 | 
			
		||||
    external = block::gen::t_CommonMsgInfo.get_tag(cs);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (trans_type == block::transaction::Transaction::tr_ord) {
 | 
			
		||||
    need_credit_phase = !external;
 | 
			
		||||
  } else if (trans_type == block::transaction::Transaction::tr_merge_install) {
 | 
			
		||||
    need_credit_phase = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::unique_ptr<block::transaction::Transaction> trans =
 | 
			
		||||
      std::make_unique<block::transaction::Transaction>(*acc, trans_type, lt, utime, msg_root);
 | 
			
		||||
 | 
			
		||||
  if (msg_root.not_null() && !trans->unpack_input_msg(ihr_delivered, action_phase_cfg)) {
 | 
			
		||||
    if (external) {
 | 
			
		||||
      // inbound external message was not accepted
 | 
			
		||||
      return td::Status::Error(-701,"inbound external message rejected by account "s + acc->addr.to_hex() +
 | 
			
		||||
                                                           " before smart-contract execution");
 | 
			
		||||
    }
 | 
			
		||||
    return td::Status::Error(-669,"cannot unpack input message for a new transaction");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (trans->bounce_enabled) {
 | 
			
		||||
    if (!trans->prepare_storage_phase(*storage_phase_cfg, true)) {
 | 
			
		||||
      return td::Status::Error(-669,"cannot create storage phase of a new transaction for smart contract "s + acc->addr.to_hex());
 | 
			
		||||
    }
 | 
			
		||||
    if (need_credit_phase && !trans->prepare_credit_phase()) {
 | 
			
		||||
      return td::Status::Error(-669,"cannot create credit phase of a new transaction for smart contract "s + acc->addr.to_hex());
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    if (need_credit_phase && !trans->prepare_credit_phase()) {
 | 
			
		||||
      return td::Status::Error(-669,"cannot create credit phase of a new transaction for smart contract "s + acc->addr.to_hex());
 | 
			
		||||
    }
 | 
			
		||||
    if (!trans->prepare_storage_phase(*storage_phase_cfg, true, need_credit_phase)) {
 | 
			
		||||
      return td::Status::Error(-669,"cannot create storage phase of a new transaction for smart contract "s + acc->addr.to_hex());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!trans->prepare_compute_phase(*compute_phase_cfg)) {
 | 
			
		||||
    return td::Status::Error(-669,"cannot create compute phase of a new transaction for smart contract "s + acc->addr.to_hex());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!trans->compute_phase->accepted) {
 | 
			
		||||
    if (!external && trans->compute_phase->skip_reason == block::ComputePhase::sk_none) {
 | 
			
		||||
      return td::Status::Error(-669,"new ordinary transaction for smart contract "s + acc->addr.to_hex() +
 | 
			
		||||
                " has not been accepted by the smart contract (?)");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (trans->compute_phase->success && !trans->prepare_action_phase(*action_phase_cfg)) {
 | 
			
		||||
    return td::Status::Error(-669,"cannot create action phase of a new transaction for smart contract "s + acc->addr.to_hex());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (trans->bounce_enabled && !trans->compute_phase->success && !trans->prepare_bounce_phase(*action_phase_cfg)) {
 | 
			
		||||
    return td::Status::Error(-669,"cannot create bounce phase of a new transaction for smart contract "s + acc->addr.to_hex());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return trans;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TransactionEmulator::set_unixtime(ton::UnixTime unixtime) {
 | 
			
		||||
  unixtime_ = unixtime;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TransactionEmulator::set_lt(ton::LogicalTime lt) {
 | 
			
		||||
  lt_ = lt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TransactionEmulator::set_rand_seed(td::BitArray<256>& rand_seed) {
 | 
			
		||||
  rand_seed_ = rand_seed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TransactionEmulator::set_ignore_chksig(bool ignore_chksig) {
 | 
			
		||||
  ignore_chksig_ = ignore_chksig;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TransactionEmulator::set_config(block::Config &&config) {
 | 
			
		||||
  config_ = std::forward<block::Config>(config);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TransactionEmulator::set_libs(vm::Dictionary &&libs) {
 | 
			
		||||
  libraries_ = std::forward<vm::Dictionary>(libs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace emulator
 | 
			
		||||
							
								
								
									
										83
									
								
								emulator/transaction-emulator.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								emulator/transaction-emulator.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,83 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
#include "crypto/common/refcnt.hpp"
 | 
			
		||||
#include "ton/ton-types.h"
 | 
			
		||||
#include "crypto/vm/cells.h"
 | 
			
		||||
#include "block/transaction.h"
 | 
			
		||||
#include "block/block-auto.h"
 | 
			
		||||
#include "block/block-parse.h"
 | 
			
		||||
#include "block/mc-config.h"
 | 
			
		||||
 | 
			
		||||
namespace emulator {
 | 
			
		||||
class TransactionEmulator {
 | 
			
		||||
  block::Config config_;
 | 
			
		||||
  vm::Dictionary libraries_;
 | 
			
		||||
  int vm_log_verbosity_;
 | 
			
		||||
  ton::UnixTime unixtime_;
 | 
			
		||||
  ton::LogicalTime lt_;
 | 
			
		||||
  td::BitArray<256> rand_seed_;
 | 
			
		||||
  bool ignore_chksig_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  TransactionEmulator(block::Config&& config, int vm_log_verbosity = 0) : 
 | 
			
		||||
    config_(std::move(config)), libraries_(256), vm_log_verbosity_(vm_log_verbosity),
 | 
			
		||||
    unixtime_(0), lt_(0), rand_seed_(0), ignore_chksig_(false) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  struct EmulationResult {
 | 
			
		||||
    std::string vm_log;
 | 
			
		||||
 | 
			
		||||
    EmulationResult(std::string vm_log_) : vm_log(vm_log_) {}
 | 
			
		||||
    virtual ~EmulationResult() = default;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  struct EmulationSuccess: EmulationResult {
 | 
			
		||||
    td::Ref<vm::Cell> transaction;
 | 
			
		||||
    block::Account account;
 | 
			
		||||
    td::Ref<vm::Cell> actions;
 | 
			
		||||
 | 
			
		||||
    EmulationSuccess(td::Ref<vm::Cell> transaction_, block::Account account_, std::string vm_log_, td::Ref<vm::Cell> actions_) :
 | 
			
		||||
      EmulationResult(vm_log_), transaction(transaction_), account(account_) , actions(actions_)
 | 
			
		||||
    {}
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  struct EmulationExternalNotAccepted: EmulationResult {
 | 
			
		||||
    int vm_exit_code;
 | 
			
		||||
 | 
			
		||||
    EmulationExternalNotAccepted(std::string vm_log_, int vm_exit_code_) : 
 | 
			
		||||
      EmulationResult(vm_log_), vm_exit_code(vm_exit_code_) 
 | 
			
		||||
    {}
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  struct EmulationChain {
 | 
			
		||||
    std::vector<td::Ref<vm::Cell>> transactions;
 | 
			
		||||
    block::Account account;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const block::Config& get_config() {
 | 
			
		||||
    return config_;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
  td::Result<EmulationSuccess> emulate_transaction(block::Account&& account, td::Ref<vm::Cell> original_trans);
 | 
			
		||||
  td::Result<EmulationChain> emulate_transactions_chain(block::Account&& account, std::vector<td::Ref<vm::Cell>>&& original_transactions);
 | 
			
		||||
 | 
			
		||||
  void set_unixtime(ton::UnixTime unixtime);
 | 
			
		||||
  void set_lt(ton::LogicalTime lt);
 | 
			
		||||
  void set_rand_seed(td::BitArray<256>& rand_seed);
 | 
			
		||||
  void set_ignore_chksig(bool ignore_chksig);
 | 
			
		||||
  void set_config(block::Config &&config);
 | 
			
		||||
  void set_libs(vm::Dictionary &&libs);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  bool check_state_update(const block::Account& account, const block::gen::Transaction::Record& trans);
 | 
			
		||||
 | 
			
		||||
  td::Result<std::unique_ptr<block::transaction::Transaction>> create_transaction(
 | 
			
		||||
                                                         td::Ref<vm::Cell> msg_root, block::Account* acc,
 | 
			
		||||
                                                         ton::UnixTime utime, ton::LogicalTime lt, int trans_type,
 | 
			
		||||
                                                         block::StoragePhaseConfig* storage_phase_cfg,
 | 
			
		||||
                                                         block::ComputePhaseConfig* compute_phase_cfg,
 | 
			
		||||
                                                         block::ActionPhaseConfig* action_phase_cfg);
 | 
			
		||||
};
 | 
			
		||||
} // namespace emulator
 | 
			
		||||
							
								
								
									
										46
									
								
								emulator/tvm-emulator.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								emulator/tvm-emulator.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,46 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
#include "smc-envelope/SmartContract.h"
 | 
			
		||||
 | 
			
		||||
namespace emulator {
 | 
			
		||||
class TvmEmulator {
 | 
			
		||||
  ton::SmartContract smc_;
 | 
			
		||||
  ton::SmartContract::Args args_;
 | 
			
		||||
public:
 | 
			
		||||
  using Answer = ton::SmartContract::Answer;
 | 
			
		||||
 | 
			
		||||
  TvmEmulator(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data): smc_({code, data}) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_vm_verbosity_level(int vm_log_verbosity) {
 | 
			
		||||
    args_.set_vm_verbosity_level(vm_log_verbosity);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_libraries(vm::Dictionary&& libraries) {
 | 
			
		||||
    args_.set_libraries(libraries);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_gas_limit(int64_t limit) {
 | 
			
		||||
    args_.set_limits(vm::GasLimits(limit));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_c7(block::StdAddress address, uint32_t unixtime, uint64_t balance, td::BitArray<256> rand_seed, std::shared_ptr<const block::Config> config) {
 | 
			
		||||
    args_.set_address(address);
 | 
			
		||||
    args_.set_now(unixtime);
 | 
			
		||||
    args_.set_balance(balance);
 | 
			
		||||
    args_.set_rand_seed(rand_seed);
 | 
			
		||||
    args_.set_config(config);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Answer run_get_method(int method_id, td::Ref<vm::Stack> stack) {
 | 
			
		||||
    return smc_.run_get_method(args_.set_stack(stack).set_method_id(method_id));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Answer send_external_message(td::Ref<vm::Cell> message_body) {
 | 
			
		||||
    return smc_.send_external_message(message_body, args_);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Answer send_internal_message(td::Ref<vm::Cell> message_body, uint64_t amount) {
 | 
			
		||||
    return smc_.send_internal_message(message_body, args_.set_amount(amount));
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -257,6 +257,7 @@ getBip39Hints prefix:string = Bip39Hints;
 | 
			
		|||
 | 
			
		||||
//raw.init initial_account_state:raw.initialAccountState = Ok;
 | 
			
		||||
raw.getAccountState account_address:accountAddress = raw.FullAccountState;
 | 
			
		||||
raw.getAccountStateByTransaction account_address:accountAddress transaction_id:internal.transactionId = raw.FullAccountState;
 | 
			
		||||
raw.getTransactions private_key:InputKey account_address:accountAddress from_transaction_id:internal.transactionId = raw.Transactions;
 | 
			
		||||
raw.getTransactionsV2 private_key:InputKey account_address:accountAddress from_transaction_id:internal.transactionId count:# try_decode_messages:Bool = raw.Transactions;
 | 
			
		||||
raw.sendMessage body:bytes = Ok;
 | 
			
		||||
| 
						 | 
				
			
			@ -278,9 +279,13 @@ guessAccountRevision initial_account_state:InitialAccountState workchain_id:int3
 | 
			
		|||
guessAccount public_key:string rwallet_init_public_key:string = AccountRevisionList;
 | 
			
		||||
 | 
			
		||||
getAccountState account_address:accountAddress = FullAccountState;
 | 
			
		||||
getAccountStateByTransaction account_address:accountAddress transaction_id:internal.transactionId = FullAccountState;
 | 
			
		||||
getShardAccountCell account_address:accountAddress = tvm.Cell;
 | 
			
		||||
getShardAccountCellByTransaction account_address:accountAddress transaction_id:internal.transactionId = tvm.Cell;
 | 
			
		||||
createQuery private_key:InputKey address:accountAddress timeout:int32 action:Action initial_account_state:InitialAccountState = query.Info;
 | 
			
		||||
 | 
			
		||||
getConfigParam mode:# id:ton.blockIdExt param:# = ConfigInfo;
 | 
			
		||||
getConfigParam mode:# param:# = ConfigInfo;
 | 
			
		||||
getConfigAll mode:# = ConfigInfo;
 | 
			
		||||
 | 
			
		||||
msg.decrypt input_key:InputKey data:msg.dataEncryptedArray = msg.DataDecryptedArray;
 | 
			
		||||
msg.decryptWithProof proof:bytes data:msg.dataEncrypted = msg.Data;
 | 
			
		||||
| 
						 | 
				
			
			@ -292,6 +297,7 @@ query.estimateFees id:int53 ignore_chksig:Bool = query.Fees;
 | 
			
		|||
query.getInfo id:int53 = query.Info;
 | 
			
		||||
 | 
			
		||||
smc.load account_address:accountAddress = smc.Info;
 | 
			
		||||
smc.loadByTransaction account_address:accountAddress transaction_id:internal.transactionId = smc.Info;
 | 
			
		||||
smc.forget id:int53 = Ok;
 | 
			
		||||
smc.getCode id:int53 = tvm.Cell;
 | 
			
		||||
smc.getData id:int53 = tvm.Cell;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											Binary file not shown.
										
									
								
							| 
						 | 
				
			
			@ -60,7 +60,7 @@ target_include_directories(tonlib PUBLIC
 | 
			
		|||
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/..
 | 
			
		||||
  $<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>
 | 
			
		||||
)
 | 
			
		||||
target_link_libraries(tonlib PRIVATE tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ton_block lite-client-common smc-envelope)
 | 
			
		||||
target_link_libraries(tonlib PRIVATE tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ton_block lite-client-common smc-envelope emulator_static)
 | 
			
		||||
target_link_libraries(tonlib PUBLIC tdutils tl_tonlib_api)
 | 
			
		||||
 | 
			
		||||
if (TONLIB_ENABLE_JNI AND NOT ANDROID) # jni is available by default on Android
 | 
			
		||||
| 
						 | 
				
			
			@ -136,7 +136,7 @@ if (WIN32)
 | 
			
		|||
  set(WINGETOPT_TARGET wingetopt)
 | 
			
		||||
endif()
 | 
			
		||||
install(TARGETS tdnet keys crc32c tdactor adnllite tl_api tl-utils tl_lite_api tl-lite-utils ton_crypto ton_block smc-envelope ${WINGETOPT_TARGET}
 | 
			
		||||
  tdutils tl_tonlib_api tonlib lite-client-common tddb_utils Tonlib EXPORT Tonlib
 | 
			
		||||
  tdutils tl_tonlib_api tonlib lite-client-common tddb_utils emulator_static Tonlib EXPORT Tonlib
 | 
			
		||||
  LIBRARY DESTINATION lib
 | 
			
		||||
  ARCHIVE DESTINATION lib
 | 
			
		||||
  RUNTIME DESTINATION bin
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,6 +36,8 @@
 | 
			
		|||
#include "smc-envelope/PaymentChannel.h"
 | 
			
		||||
#include "smc-envelope/SmartContractCode.h"
 | 
			
		||||
 | 
			
		||||
#include "emulator/transaction-emulator.h"
 | 
			
		||||
 | 
			
		||||
#include "auto/tl/tonlib_api.hpp"
 | 
			
		||||
#include "block/block-auto.h"
 | 
			
		||||
#include "block/check-proof.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -74,6 +76,14 @@ struct GetAccountState {
 | 
			
		|||
  using ReturnType = td::unique_ptr<AccountState>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GetAccountStateByTransaction {
 | 
			
		||||
  block::StdAddress address;
 | 
			
		||||
  std::int64_t lt;
 | 
			
		||||
  td::Bits256 hash;
 | 
			
		||||
  //td::optional<td::Ed25519::PublicKey> public_key;
 | 
			
		||||
  using ReturnType = td::unique_ptr<AccountState>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct RemoteRunSmcMethod {
 | 
			
		||||
  block::StdAddress address;
 | 
			
		||||
  td::optional<ton::BlockIdExt> block_id;
 | 
			
		||||
| 
						 | 
				
			
			@ -416,6 +426,19 @@ class AccountState {
 | 
			
		|||
        get_wallet_revision());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  td::Result<tonlib_api::object_ptr<tonlib_api::tvm_cell>> to_shardAccountCell() const {
 | 
			
		||||
    auto account_root = raw_.info.root;
 | 
			
		||||
    if (account_root.is_null()) {
 | 
			
		||||
      block::gen::Account().cell_pack_account_none(account_root);
 | 
			
		||||
    }
 | 
			
		||||
    auto cell = vm::CellBuilder().store_ref(account_root).store_bits(raw_.info.last_trans_hash.as_bitslice()).store_long(raw_.info.last_trans_lt).finalize();
 | 
			
		||||
    return tonlib_api::make_object<tonlib_api::tvm_cell>(to_bytes(cell));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  td::Result<td::Ref<vm::CellSlice>> to_shardAccountCellSlice() const {
 | 
			
		||||
    return vm::CellBuilder().store_ref(raw_.info.root).store_bits(raw_.info.last_trans_hash.as_bitslice()).store_long(raw_.info.last_trans_lt).as_cellslice_ref();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //NB: Order is important! Used during guessAccountRevision
 | 
			
		||||
  enum WalletType {
 | 
			
		||||
    Empty,
 | 
			
		||||
| 
						 | 
				
			
			@ -940,7 +963,7 @@ class Query {
 | 
			
		|||
    } while (list.not_null());
 | 
			
		||||
    return i;
 | 
			
		||||
  }
 | 
			
		||||
};  // namespace tonlib
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
td::Result<td::int64> to_balance_or_throw(td::Ref<vm::CellSlice> balance_ref) {
 | 
			
		||||
  vm::CellSlice balance_slice = *balance_ref;
 | 
			
		||||
| 
						 | 
				
			
			@ -1633,6 +1656,305 @@ class GetShardBlockProof : public td::actor::Actor {
 | 
			
		|||
  std::vector<std::pair<ton::BlockIdExt, td::BufferSlice>> links_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
auto to_lite_api(const tonlib_api::ton_blockIdExt& blk) -> td::Result<lite_api_ptr<ton::lite_api::tonNode_blockIdExt>>;
 | 
			
		||||
auto to_tonlib_api(const ton::lite_api::liteServer_transactionId& txid) -> tonlib_api_ptr<tonlib_api::blocks_shortTxId>;
 | 
			
		||||
 | 
			
		||||
class RunEmulator : public td::actor::Actor {
 | 
			
		||||
 public:
 | 
			
		||||
  RunEmulator(ExtClientRef ext_client_ref, int_api::GetAccountStateByTransaction request,
 | 
			
		||||
      td::actor::ActorShared<> parent, td::Promise<td::unique_ptr<AccountState>>&& promise)
 | 
			
		||||
    : request_(std::move(request)), parent_(std::move(parent)), promise_(std::move(promise)) {
 | 
			
		||||
    client_.set_client(ext_client_ref);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  struct FullBlockId {
 | 
			
		||||
    ton::BlockIdExt id;
 | 
			
		||||
    ton::BlockIdExt mc;
 | 
			
		||||
    ton::BlockIdExt prev;
 | 
			
		||||
    ton::Bits256 rand_seed;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  ExtClient client_;
 | 
			
		||||
  int_api::GetAccountStateByTransaction request_;
 | 
			
		||||
  td::actor::ActorShared<> parent_;
 | 
			
		||||
  td::Promise<td::unique_ptr<AccountState>> promise_;
 | 
			
		||||
 | 
			
		||||
  std::map<td::int64, td::actor::ActorOwn<>> actors_;
 | 
			
		||||
  td::int64 actor_id_{1};
 | 
			
		||||
 | 
			
		||||
  FullBlockId block_id_;
 | 
			
		||||
  td::Ref<vm::Cell> mc_state_root_; // ^ShardStateUnsplit
 | 
			
		||||
  td::unique_ptr<AccountState> account_state_;
 | 
			
		||||
  std::vector<td::Ref<vm::Cell>> transactions_; // std::vector<^Transaction>
 | 
			
		||||
 | 
			
		||||
  size_t count_{0};
 | 
			
		||||
  size_t count_transactions_{0};
 | 
			
		||||
  bool incomplete_{true};
 | 
			
		||||
  bool stopped_{false};
 | 
			
		||||
 | 
			
		||||
  void get_block_id(td::Promise<FullBlockId>&& promise) {
 | 
			
		||||
    auto shard_id = ton::shard_prefix(request_.address.addr, 60);
 | 
			
		||||
    auto query = ton::lite_api::liteServer_lookupBlock(0b111111010, ton::create_tl_lite_block_id_simple({request_.address.workchain, shard_id, 0}), request_.lt, 0);
 | 
			
		||||
    client_.send_query(std::move(query), promise.wrap([self = this, shard_id](td::Result<tonlib_api::object_ptr<ton::lite_api::liteServer_blockHeader>> header_r) -> td::Result<FullBlockId> {
 | 
			
		||||
 | 
			
		||||
      TRY_RESULT(header, std::move(header_r));
 | 
			
		||||
      ton::BlockIdExt block_id = ton::create_block_id(header->id_);
 | 
			
		||||
      TRY_RESULT(root, vm::std_boc_deserialize(std::move(header->header_proof_)));
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        auto virt_root = vm::MerkleProof::virtualize(root, 1);
 | 
			
		||||
        if (virt_root.is_null()) {
 | 
			
		||||
          return td::Status::Error("block header proof is not a valid Merkle proof");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ton::RootHash vhash{virt_root->get_hash().bits()};
 | 
			
		||||
        if (ton::RootHash{virt_root->get_hash().bits()} != block_id.root_hash) {
 | 
			
		||||
          return td::Status::Error("block header has incorrect root hash");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::vector<ton::BlockIdExt> prev_blocks;
 | 
			
		||||
        ton::BlockIdExt mc_block_id;
 | 
			
		||||
        bool after_split;
 | 
			
		||||
        td::Status status = block::unpack_block_prev_blk_ext(virt_root, block_id, prev_blocks, mc_block_id, after_split);
 | 
			
		||||
        if (status.is_error()) {
 | 
			
		||||
          return status.move_as_error();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ton::BlockIdExt prev_block;
 | 
			
		||||
        if (prev_blocks.size() == 1 || ton::shard_is_ancestor(prev_blocks[0].id.shard, shard_id)) {
 | 
			
		||||
          prev_block = std::move(prev_blocks[0]);
 | 
			
		||||
        } else {
 | 
			
		||||
          prev_block = std::move(prev_blocks[1]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        block::gen::Block::Record block;
 | 
			
		||||
        block::gen::BlockExtra::Record extra;
 | 
			
		||||
        if (!tlb::unpack_cell(virt_root, block) || !tlb::unpack_cell(block.extra, extra)) {
 | 
			
		||||
          return td::Status::Error("cannot unpack block header");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return FullBlockId{std::move(block_id), std::move(mc_block_id), std::move(prev_block), std::move(extra.rand_seed)};
 | 
			
		||||
      } catch (vm::VmError& err) {
 | 
			
		||||
        return err.as_status("error processing header");
 | 
			
		||||
      } catch (vm::VmVirtError& err) {
 | 
			
		||||
        return err.as_status("error processing header");
 | 
			
		||||
      }
 | 
			
		||||
    }));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void get_mc_state_root(td::Promise<td::Ref<vm::Cell>>&& promise) {
 | 
			
		||||
    TRY_RESULT_PROMISE(promise, lite_block, to_lite_api(*to_tonlib_api(block_id_.mc)));
 | 
			
		||||
    auto block = ton::create_block_id(lite_block);
 | 
			
		||||
    client_.send_query(ton::lite_api::liteServer_getConfigAll(0b11'11111111, std::move(lite_block)), promise.wrap([self = this, block](auto r_config) -> td::Result<td::Ref<vm::Cell>> {
 | 
			
		||||
 | 
			
		||||
      TRY_RESULT(state, block::check_extract_state_proof(block, r_config->state_proof_.as_slice(), r_config->config_proof_.as_slice()));
 | 
			
		||||
 | 
			
		||||
      return std::move(state);
 | 
			
		||||
    }));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void get_account_state(td::Promise<td::unique_ptr<AccountState>>&& promise) {
 | 
			
		||||
    auto actor_id = actor_id_++;
 | 
			
		||||
    actors_[actor_id] = td::actor::create_actor<GetRawAccountState>(
 | 
			
		||||
      "GetAccountState", client_.get_client(), request_.address, block_id_.prev,
 | 
			
		||||
      actor_shared(this, actor_id),
 | 
			
		||||
      promise.wrap([address = request_.address](auto&& state) {
 | 
			
		||||
        return td::make_unique<AccountState>(std::move(address), std::move(state), 0);
 | 
			
		||||
    }));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  td::Status get_transactions(std::int64_t lt) {
 | 
			
		||||
    TRY_RESULT(lite_block, to_lite_api(*to_tonlib_api(block_id_.id)));
 | 
			
		||||
    auto after = ton::lite_api::make_object<ton::lite_api::liteServer_transactionId3>(request_.address.addr, lt);
 | 
			
		||||
    auto query = ton::lite_api::liteServer_listBlockTransactions(std::move(lite_block), 0b10100111, 256, std::move(after), false, false);
 | 
			
		||||
 | 
			
		||||
    client_.send_query(std::move(query), [self = this](lite_api_ptr<ton::lite_api::liteServer_blockTransactions>&& bTxes) {
 | 
			
		||||
      if (!bTxes) {
 | 
			
		||||
        self->check(td::Status::Error("liteServer.blockTransactions is null"));
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      std::int64_t last_lt = 0;
 | 
			
		||||
      for (auto& id : bTxes->ids_) {
 | 
			
		||||
        last_lt = id->lt_;
 | 
			
		||||
        if (id->account_ != self->request_.address.addr) {
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (id->lt_ == self->request_.lt && id->hash_ == self->request_.hash) {
 | 
			
		||||
          self->incomplete_ = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self->transactions_.push_back({});
 | 
			
		||||
        self->get_transaction(id->lt_, id->hash_, [self, i = self->transactions_.size() - 1](auto transaction) { self->set_transaction(i, std::move(transaction)); });
 | 
			
		||||
 | 
			
		||||
        if (!self->incomplete_) {
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (bTxes->incomplete_) {
 | 
			
		||||
        self->check(self->get_transactions(last_lt));
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    return td::Status::OK();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void get_transaction(std::int64_t lt, td::Bits256 hash, td::Promise<td::Ref<vm::Cell>>&& promise) {
 | 
			
		||||
    auto actor_id = actor_id_++;
 | 
			
		||||
    actors_[actor_id] = td::actor::create_actor<GetTransactionHistory>(
 | 
			
		||||
        "GetTransactionHistory", client_.get_client(), request_.address, lt, hash, 1, actor_shared(this, actor_id),
 | 
			
		||||
        promise.wrap([](auto&& transactions) mutable {
 | 
			
		||||
          return std::move(transactions.transactions.front().transaction);
 | 
			
		||||
        }));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void start_up() override {
 | 
			
		||||
    if (stopped_) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    get_block_id([self = this](td::Result<FullBlockId>&& block_id) { self->set_block_id(std::move(block_id)); });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_block_id(td::Result<FullBlockId>&& block_id) {
 | 
			
		||||
    if (block_id.is_error()) {
 | 
			
		||||
      check(block_id.move_as_error());
 | 
			
		||||
    } else {
 | 
			
		||||
      block_id_ = block_id.move_as_ok();
 | 
			
		||||
 | 
			
		||||
      get_mc_state_root([self = this](td::Result<td::Ref<vm::Cell>>&& mc_state_root) { self->set_mc_state_root(std::move(mc_state_root)); });
 | 
			
		||||
      get_account_state([self = this](td::Result<td::unique_ptr<AccountState>>&& state) { self->set_account_state(std::move(state)); });
 | 
			
		||||
      check(get_transactions(0));
 | 
			
		||||
 | 
			
		||||
      inc();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_mc_state_root(td::Result<td::Ref<vm::Cell>>&& mc_state_root) {
 | 
			
		||||
    if (mc_state_root.is_error()) {
 | 
			
		||||
      check(mc_state_root.move_as_error());
 | 
			
		||||
    } else {
 | 
			
		||||
      mc_state_root_ = mc_state_root.move_as_ok();
 | 
			
		||||
      inc();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_account_state(td::Result<td::unique_ptr<AccountState>>&& account_state) {
 | 
			
		||||
    if (account_state.is_error()) {
 | 
			
		||||
      check(account_state.move_as_error());
 | 
			
		||||
    } else {
 | 
			
		||||
      account_state_ = account_state.move_as_ok();
 | 
			
		||||
      inc();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_transaction(size_t i, td::Result<td::Ref<vm::Cell>>&& transaction) {
 | 
			
		||||
    if (transaction.is_error()) {
 | 
			
		||||
      check(transaction.move_as_error());
 | 
			
		||||
    } else {
 | 
			
		||||
      transactions_[i] = transaction.move_as_ok();
 | 
			
		||||
      inc_transactions();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void inc_transactions() {
 | 
			
		||||
    if (stopped_ || ++count_transactions_ != transactions_.size() || incomplete_) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    inc();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void inc() {
 | 
			
		||||
    if (stopped_ || ++count_ != 4) { // 4 -- block_id + mc_state_root + account_state + transactions
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto r_config = block::Config::extract_from_state(mc_state_root_, 0b11'11111111);
 | 
			
		||||
    if (r_config.is_error()) {
 | 
			
		||||
      check(r_config.move_as_error());
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    std::unique_ptr<block::Config> config = r_config.move_as_ok();
 | 
			
		||||
 | 
			
		||||
    block::gen::ShardStateUnsplit::Record shard_state;
 | 
			
		||||
    if (!tlb::unpack_cell(mc_state_root_, shard_state)) {
 | 
			
		||||
      check(td::Status::Error("Failed to unpack masterchain state"));
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    vm::Dictionary libraries(shard_state.r1.libraries->prefetch_ref(), 256);
 | 
			
		||||
 | 
			
		||||
    auto r_shard_account = account_state_->to_shardAccountCellSlice();
 | 
			
		||||
    if (r_shard_account.is_error()) {
 | 
			
		||||
      check(r_shard_account.move_as_error());
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    td::Ref<vm::CellSlice> shard_account = r_shard_account.move_as_ok();
 | 
			
		||||
 | 
			
		||||
    const block::StdAddress& address = account_state_->get_address();
 | 
			
		||||
    ton::UnixTime now = account_state_->get_sync_time();
 | 
			
		||||
    bool is_special = address.workchain == ton::masterchainId && config->is_special_smartcontract(address.addr);
 | 
			
		||||
    block::Account account(address.workchain, address.addr.bits());
 | 
			
		||||
    if (!account.unpack(std::move(shard_account), td::Ref<vm::CellSlice>(), now, is_special)) {
 | 
			
		||||
      check(td::Status::Error("Can't unpack shard account"));
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    emulator::TransactionEmulator trans_emulator(std::move(*config));
 | 
			
		||||
    trans_emulator.set_libs(std::move(libraries));
 | 
			
		||||
    trans_emulator.set_rand_seed(block_id_.rand_seed);
 | 
			
		||||
    td::Result<emulator::TransactionEmulator::EmulationChain> emulation_result = trans_emulator.emulate_transactions_chain(std::move(account), std::move(transactions_));
 | 
			
		||||
 | 
			
		||||
    if (emulation_result.is_error()) {
 | 
			
		||||
      promise_.set_error(emulation_result.move_as_error());
 | 
			
		||||
    } else {
 | 
			
		||||
      account = std::move(emulation_result.move_as_ok().account);
 | 
			
		||||
      RawAccountState raw = std::move(account_state_->raw());
 | 
			
		||||
      raw.block_id = block_id_.id;
 | 
			
		||||
      raw.balance = account.get_balance().grams->to_long();
 | 
			
		||||
      raw.storage_last_paid = std::move(account.last_paid);
 | 
			
		||||
      raw.storage_stat = std::move(account.storage_stat);
 | 
			
		||||
      raw.code = std::move(account.code);
 | 
			
		||||
      raw.data = std::move(account.data);
 | 
			
		||||
      raw.state = std::move(account.total_state);
 | 
			
		||||
      raw.info.last_trans_lt = account.last_trans_lt_;
 | 
			
		||||
      raw.info.last_trans_hash = account.last_trans_hash_;
 | 
			
		||||
      raw.info.gen_utime = account.now_;
 | 
			
		||||
 | 
			
		||||
      if (account.status == block::Account::acc_frozen) {
 | 
			
		||||
        raw.frozen_hash = (char*)account.state_hash.data();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      promise_.set_value(td::make_unique<AccountState>(address, std::move(raw), 0));
 | 
			
		||||
    }
 | 
			
		||||
    stopped_ = true;
 | 
			
		||||
    try_stop();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void check(td::Status status) {
 | 
			
		||||
    if (status.is_error()) {
 | 
			
		||||
      promise_.set_error(std::move(status));
 | 
			
		||||
      stopped_ = true;
 | 
			
		||||
      try_stop();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void try_stop() {
 | 
			
		||||
    if (stopped_ && actors_.empty()) {
 | 
			
		||||
      stop();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void hangup_shared() override {
 | 
			
		||||
    actors_.erase(get_link_token());
 | 
			
		||||
    try_stop();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void hangup() override {
 | 
			
		||||
    check(TonlibError::Cancelled());
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TonlibClient::TonlibClient(td::unique_ptr<TonlibCallback> callback) : callback_(std::move(callback)) {
 | 
			
		||||
}
 | 
			
		||||
TonlibClient::~TonlibClient() = default;
 | 
			
		||||
| 
						 | 
				
			
			@ -2765,6 +3087,27 @@ td::Status TonlibClient::do_request(tonlib_api::raw_getAccountState& request,
 | 
			
		|||
  return td::Status::OK();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Status TonlibClient::do_request(tonlib_api::raw_getAccountStateByTransaction& request,
 | 
			
		||||
                                    td::Promise<object_ptr<tonlib_api::raw_fullAccountState>>&& promise) {
 | 
			
		||||
  if (!request.account_address_) {
 | 
			
		||||
    return TonlibError::EmptyField("account_address");
 | 
			
		||||
  }
 | 
			
		||||
  if (!request.transaction_id_) {
 | 
			
		||||
    return TonlibError::EmptyField("transaction_id");
 | 
			
		||||
  }
 | 
			
		||||
  TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_));
 | 
			
		||||
  auto lt = request.transaction_id_->lt_;
 | 
			
		||||
  auto hash_str = request.transaction_id_->hash_;
 | 
			
		||||
  if (hash_str.size() != 32) {
 | 
			
		||||
    return td::Status::Error(400, "Invalid transaction id hash size");
 | 
			
		||||
  }
 | 
			
		||||
  td::Bits256 hash;
 | 
			
		||||
  hash.as_slice().copy_from(hash_str);
 | 
			
		||||
  make_request(int_api::GetAccountStateByTransaction{account_address, lt, hash},
 | 
			
		||||
               promise.wrap([](auto&& res) { return res->to_raw_fullAccountState(); }));
 | 
			
		||||
  return td::Status::OK();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Result<KeyStorage::InputKey> from_tonlib(tonlib_api::inputKeyRegular& input_key) {
 | 
			
		||||
  if (!input_key.key_) {
 | 
			
		||||
    return TonlibError::EmptyField("key");
 | 
			
		||||
| 
						 | 
				
			
			@ -2877,6 +3220,59 @@ td::Status TonlibClient::do_request(const tonlib_api::getAccountState& request,
 | 
			
		|||
  return td::Status::OK();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Status TonlibClient::do_request(const tonlib_api::getAccountStateByTransaction& request,
 | 
			
		||||
                                    td::Promise<object_ptr<tonlib_api::fullAccountState>>&& promise) {
 | 
			
		||||
  if (!request.account_address_) {
 | 
			
		||||
    return TonlibError::EmptyField("account_address");
 | 
			
		||||
  }
 | 
			
		||||
  if (!request.transaction_id_) {
 | 
			
		||||
    return TonlibError::EmptyField("transaction_id");
 | 
			
		||||
  }
 | 
			
		||||
  TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_));
 | 
			
		||||
  auto lt = request.transaction_id_->lt_;
 | 
			
		||||
  auto hash_str = request.transaction_id_->hash_;
 | 
			
		||||
  if (hash_str.size() != 32) {
 | 
			
		||||
    return td::Status::Error(400, "Invalid transaction id hash size");
 | 
			
		||||
  }
 | 
			
		||||
  td::Bits256 hash;
 | 
			
		||||
  hash.as_slice().copy_from(hash_str);
 | 
			
		||||
  make_request(int_api::GetAccountStateByTransaction{account_address, lt, hash},
 | 
			
		||||
               promise.wrap([](auto&& res) { return res->to_fullAccountState(); }));
 | 
			
		||||
  return td::Status::OK();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Status TonlibClient::do_request(const tonlib_api::getShardAccountCell& request,
 | 
			
		||||
                                    td::Promise<object_ptr<tonlib_api::tvm_cell>>&& promise) {
 | 
			
		||||
  if (!request.account_address_) {
 | 
			
		||||
    return TonlibError::EmptyField("account_address");
 | 
			
		||||
  }
 | 
			
		||||
  TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_));
 | 
			
		||||
  make_request(int_api::GetAccountState{std::move(account_address), query_context_.block_id.copy(), {}},
 | 
			
		||||
               promise.wrap([](auto&& res) { return res->to_shardAccountCell(); }));
 | 
			
		||||
  return td::Status::OK();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Status TonlibClient::do_request(const tonlib_api::getShardAccountCellByTransaction& request,
 | 
			
		||||
                                    td::Promise<object_ptr<tonlib_api::tvm_cell>>&& promise) {
 | 
			
		||||
  if (!request.account_address_) {
 | 
			
		||||
    return TonlibError::EmptyField("account_address");
 | 
			
		||||
  }
 | 
			
		||||
  if (!request.transaction_id_) {
 | 
			
		||||
    return TonlibError::EmptyField("transaction_id");
 | 
			
		||||
  }
 | 
			
		||||
  TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_));
 | 
			
		||||
  auto lt = request.transaction_id_->lt_;
 | 
			
		||||
  auto hash_str = request.transaction_id_->hash_;
 | 
			
		||||
  if (hash_str.size() != 32) {
 | 
			
		||||
    return td::Status::Error(400, "Invalid transaction id hash size");
 | 
			
		||||
  }
 | 
			
		||||
  td::Bits256 hash;
 | 
			
		||||
  hash.as_slice().copy_from(hash_str);
 | 
			
		||||
  make_request(int_api::GetAccountStateByTransaction{account_address, lt, hash},
 | 
			
		||||
               promise.wrap([](auto&& res) { return res->to_shardAccountCell(); }));
 | 
			
		||||
  return td::Status::OK();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Result<ton::ManualDns::EntryData> to_dns_entry_data(tonlib_api::dns_EntryData& entry_data) {
 | 
			
		||||
  using R = td::Result<ton::ManualDns::EntryData>;
 | 
			
		||||
  return downcast_call2<R>(
 | 
			
		||||
| 
						 | 
				
			
			@ -3502,7 +3898,7 @@ class GenericCreateSendGrams : public TonlibQueryActor {
 | 
			
		|||
 | 
			
		||||
    return with_wallet(*source_->get_wallet());
 | 
			
		||||
  }
 | 
			
		||||
};  // namespace tonlib
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
td::int64 TonlibClient::register_query(td::unique_ptr<Query> query) {
 | 
			
		||||
  auto query_id = ++next_query_id_;
 | 
			
		||||
| 
						 | 
				
			
			@ -3713,6 +4109,27 @@ td::Status TonlibClient::do_request(const tonlib_api::smc_load& request,
 | 
			
		|||
  return td::Status::OK();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Status TonlibClient::do_request(const tonlib_api::smc_loadByTransaction& request,
 | 
			
		||||
                                    td::Promise<object_ptr<tonlib_api::smc_info>>&& promise) {
 | 
			
		||||
  if (!request.account_address_) {
 | 
			
		||||
    return TonlibError::EmptyField("account_address");
 | 
			
		||||
  }
 | 
			
		||||
  if (!request.transaction_id_) {
 | 
			
		||||
    return TonlibError::EmptyField("transaction_id");
 | 
			
		||||
  }
 | 
			
		||||
  TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_));
 | 
			
		||||
  auto lt = request.transaction_id_->lt_;
 | 
			
		||||
  auto hash_str = request.transaction_id_->hash_;
 | 
			
		||||
  if (hash_str.size() != 32) {
 | 
			
		||||
    return td::Status::Error(400, "Invalid transaction id hash size");
 | 
			
		||||
  }
 | 
			
		||||
  td::Bits256 hash;
 | 
			
		||||
  hash.as_slice().copy_from(hash_str);
 | 
			
		||||
  make_request(int_api::GetAccountStateByTransaction{account_address, lt, hash},
 | 
			
		||||
               promise.send_closure(actor_id(this), &TonlibClient::finish_load_smc));
 | 
			
		||||
  return td::Status::OK();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Status TonlibClient::do_request(const tonlib_api::smc_forget& request,
 | 
			
		||||
                                    td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
 | 
			
		||||
  auto it = smcs_.find(request.id_);
 | 
			
		||||
| 
						 | 
				
			
			@ -4555,6 +4972,17 @@ td::Status TonlibClient::do_request(int_api::GetAccountState request,
 | 
			
		|||
  return td::Status::OK();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Status TonlibClient::do_request(int_api::GetAccountStateByTransaction request,
 | 
			
		||||
                                    td::Promise<td::unique_ptr<AccountState>>&& promise) {
 | 
			
		||||
  auto actor_id = actor_id_++;
 | 
			
		||||
  actors_[actor_id] = td::actor::create_actor<RunEmulator>(
 | 
			
		||||
      "RunEmulator", client_.get_client(), request, actor_shared(this, actor_id),
 | 
			
		||||
      promise.wrap([](auto&& state) {
 | 
			
		||||
        return std::move(state);
 | 
			
		||||
      }));
 | 
			
		||||
  return td::Status::OK();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Status TonlibClient::do_request(int_api::RemoteRunSmcMethod request,
 | 
			
		||||
                                    td::Promise<int_api::RemoteRunSmcMethod::ReturnType>&& promise) {
 | 
			
		||||
  auto actor_id = actor_id_++;
 | 
			
		||||
| 
						 | 
				
			
			@ -4641,29 +5069,72 @@ td::Result<ton::BlockIdExt> to_block_id(const tonlib_api::ton_blockIdExt& blk) {
 | 
			
		|||
  return ton::BlockIdExt(blk.workchain_, blk.shard_, blk.seqno_, root_hash, file_hash);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Status TonlibClient::do_request(const tonlib_api::getConfigParam& request,
 | 
			
		||||
                        td::Promise<object_ptr<tonlib_api::configInfo>>&& promise) {
 | 
			
		||||
  TRY_RESULT(lite_block, to_lite_api(*request.id_))
 | 
			
		||||
  auto block = create_block_id(std::move(lite_block));
 | 
			
		||||
  auto param = request.param_;
 | 
			
		||||
void TonlibClient::get_config_param(int32_t param, int32_t mode, ton::BlockIdExt block, td::Promise<object_ptr<tonlib_api::configInfo>>&& promise) {
 | 
			
		||||
  std::vector<int32_t> params = { param };
 | 
			
		||||
 | 
			
		||||
  client_.send_query(ton::lite_api::liteServer_getConfigParams(0, std::move(lite_block), std::move(params)),
 | 
			
		||||
                     promise.wrap([block, param](auto r_config) {
 | 
			
		||||
  client_.send_query(ton::lite_api::liteServer_getConfigParams(mode, ton::create_tl_lite_block_id(block), std::move(params)),
 | 
			
		||||
                  promise.wrap([param, block](auto r_config) -> td::Result<object_ptr<tonlib_api::configInfo>> { 
 | 
			
		||||
    auto state = block::check_extract_state_proof(block, r_config->state_proof_.as_slice(),
 | 
			
		||||
                                                  r_config->config_proof_.as_slice());
 | 
			
		||||
    if (state.is_error()) {
 | 
			
		||||
      LOG(ERROR) << "block::check_extract_state_proof failed: " << state.error();
 | 
			
		||||
      return state.move_as_error_prefix(TonlibError::ValidateConfig());
 | 
			
		||||
    }
 | 
			
		||||
    auto config = block::Config::extract_from_state(std::move(state.move_as_ok()), 0);
 | 
			
		||||
    if (config.is_error()) {
 | 
			
		||||
      LOG(ERROR) << "block::Config::extract_from_state failed: " << config.error();
 | 
			
		||||
      return config.move_as_error_prefix(TonlibError::ValidateConfig());
 | 
			
		||||
    }
 | 
			
		||||
    tonlib_api::configInfo config_result;
 | 
			
		||||
    config_result.config_ = tonlib_api::make_object<tonlib_api::tvm_cell>(to_bytes(config.move_as_ok()->get_config_param(param)));
 | 
			
		||||
    return tonlib_api::make_object<tonlib_api::configInfo>(std::move(config_result));
 | 
			
		||||
  }));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Status TonlibClient::do_request(const tonlib_api::getConfigParam& request,
 | 
			
		||||
                        td::Promise<object_ptr<tonlib_api::configInfo>>&& promise) {
 | 
			
		||||
  if (query_context_.block_id) {
 | 
			
		||||
    get_config_param(request.param_, request.mode_, query_context_.block_id.value(), std::move(promise));
 | 
			
		||||
  } else {
 | 
			
		||||
    client_.with_last_block([this, promise = std::move(promise), param = request.param_, mode = request.mode_](td::Result<LastBlockState> r_last_block) mutable {       
 | 
			
		||||
      if (r_last_block.is_error()) {
 | 
			
		||||
        promise.set_error(r_last_block.move_as_error_prefix(TonlibError::Internal("get last block failed ")));
 | 
			
		||||
      } else {
 | 
			
		||||
        this->get_config_param(param, mode, r_last_block.move_as_ok().last_block_id, std::move(promise));
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  return td::Status::OK();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TonlibClient::get_config_all(int32_t mode, ton::BlockIdExt block, td::Promise<object_ptr<tonlib_api::configInfo>>&& promise) {
 | 
			
		||||
  client_.send_query(ton::lite_api::liteServer_getConfigAll(mode, ton::create_tl_lite_block_id(block)),
 | 
			
		||||
                     promise.wrap([block](auto r_config) -> td::Result<object_ptr<tonlib_api::configInfo>> { 
 | 
			
		||||
    auto state = block::check_extract_state_proof(block, r_config->state_proof_.as_slice(),
 | 
			
		||||
                                                  r_config->config_proof_.as_slice());
 | 
			
		||||
    if (state.is_error()) {
 | 
			
		||||
      return state.move_as_error_prefix(TonlibError::ValidateConfig());
 | 
			
		||||
    }
 | 
			
		||||
    auto config = block::Config::extract_from_state(std::move(state.move_as_ok()), 0);
 | 
			
		||||
    if (config.is_error()) {
 | 
			
		||||
      return config.move_as_error_prefix(TonlibError::ValidateConfig());
 | 
			
		||||
    }
 | 
			
		||||
    tonlib_api::configInfo config_result;
 | 
			
		||||
    config_result.config_ = tonlib_api::make_object<tonlib_api::tvm_cell>(to_bytes(config.move_as_ok()->get_root_cell()));
 | 
			
		||||
    return tonlib_api::make_object<tonlib_api::configInfo>(std::move(config_result));
 | 
			
		||||
  }));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Status TonlibClient::do_request(const tonlib_api::getConfigAll& request,
 | 
			
		||||
                        td::Promise<object_ptr<tonlib_api::configInfo>>&& promise) {
 | 
			
		||||
  if (query_context_.block_id) {
 | 
			
		||||
    get_config_all(request.mode_, query_context_.block_id.value(), std::move(promise));
 | 
			
		||||
  } else {
 | 
			
		||||
    client_.with_last_block([this, promise = std::move(promise), mode = request.mode_](td::Result<LastBlockState> r_last_block) mutable {       
 | 
			
		||||
      if (r_last_block.is_error()) {
 | 
			
		||||
        promise.set_error(r_last_block.move_as_error_prefix(TonlibError::Internal("get last block failed ")));
 | 
			
		||||
      } else {
 | 
			
		||||
        this->get_config_all(mode, r_last_block.move_as_ok().last_block_id, std::move(promise));
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  return td::Status::OK();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,6 +39,7 @@
 | 
			
		|||
namespace tonlib {
 | 
			
		||||
namespace int_api {
 | 
			
		||||
struct GetAccountState;
 | 
			
		||||
struct GetAccountStateByTransaction;
 | 
			
		||||
struct GetPrivateKey;
 | 
			
		||||
struct GetDnsResolver;
 | 
			
		||||
struct SendMessage;
 | 
			
		||||
| 
						 | 
				
			
			@ -51,6 +52,7 @@ inline std::string to_string(const int_api::SendMessage&) {
 | 
			
		|||
}  // namespace int_api
 | 
			
		||||
class AccountState;
 | 
			
		||||
class Query;
 | 
			
		||||
class RunEmulator;
 | 
			
		||||
 | 
			
		||||
td::Result<tonlib_api::object_ptr<tonlib_api::dns_EntryData>> to_tonlib_api(
 | 
			
		||||
    const ton::ManualDns::EntryData& entry_data);
 | 
			
		||||
| 
						 | 
				
			
			@ -234,6 +236,8 @@ class TonlibClient : public td::actor::Actor {
 | 
			
		|||
 | 
			
		||||
  td::Status do_request(tonlib_api::raw_getAccountState& request,
 | 
			
		||||
                        td::Promise<object_ptr<tonlib_api::raw_fullAccountState>>&& promise);
 | 
			
		||||
  td::Status do_request(tonlib_api::raw_getAccountStateByTransaction& request,
 | 
			
		||||
                        td::Promise<object_ptr<tonlib_api::raw_fullAccountState>>&& promise);
 | 
			
		||||
  td::Status do_request(tonlib_api::raw_getTransactions& request,
 | 
			
		||||
                        td::Promise<object_ptr<tonlib_api::raw_transactions>>&& promise);
 | 
			
		||||
  td::Status do_request(tonlib_api::raw_getTransactionsV2& request,
 | 
			
		||||
| 
						 | 
				
			
			@ -241,6 +245,12 @@ class TonlibClient : public td::actor::Actor {
 | 
			
		|||
 | 
			
		||||
  td::Status do_request(const tonlib_api::getAccountState& request,
 | 
			
		||||
                        td::Promise<object_ptr<tonlib_api::fullAccountState>>&& promise);
 | 
			
		||||
  td::Status do_request(const tonlib_api::getAccountStateByTransaction& request,
 | 
			
		||||
                        td::Promise<object_ptr<tonlib_api::fullAccountState>>&& promise);
 | 
			
		||||
  td::Status do_request(const tonlib_api::getShardAccountCell& request,
 | 
			
		||||
                        td::Promise<object_ptr<tonlib_api::tvm_cell>>&& promise);
 | 
			
		||||
  td::Status do_request(const tonlib_api::getShardAccountCellByTransaction& request,
 | 
			
		||||
                        td::Promise<object_ptr<tonlib_api::tvm_cell>>&& promise); 
 | 
			
		||||
  td::Status do_request(tonlib_api::guessAccountRevision& request,
 | 
			
		||||
                        td::Promise<object_ptr<tonlib_api::accountRevisionList>>&& promise);
 | 
			
		||||
  td::Status do_request(tonlib_api::guessAccount& request,
 | 
			
		||||
| 
						 | 
				
			
			@ -305,6 +315,7 @@ class TonlibClient : public td::actor::Actor {
 | 
			
		|||
  td::Result<tonlib_api::object_ptr<tonlib_api::smc_info>> get_smc_info(td::int64 id);
 | 
			
		||||
  void finish_load_smc(td::unique_ptr<AccountState> query, td::Promise<object_ptr<tonlib_api::smc_info>>&& promise);
 | 
			
		||||
  td::Status do_request(const tonlib_api::smc_load& request, td::Promise<object_ptr<tonlib_api::smc_info>>&& promise);
 | 
			
		||||
  td::Status do_request(const tonlib_api::smc_loadByTransaction& request, td::Promise<object_ptr<tonlib_api::smc_info>>&& promise);
 | 
			
		||||
  td::Status do_request(const tonlib_api::smc_forget& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
 | 
			
		||||
  td::Status do_request(const tonlib_api::smc_getCode& request,
 | 
			
		||||
                        td::Promise<object_ptr<tonlib_api::tvm_cell>>&& promise);
 | 
			
		||||
| 
						 | 
				
			
			@ -344,6 +355,7 @@ class TonlibClient : public td::actor::Actor {
 | 
			
		|||
                          td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise);
 | 
			
		||||
 | 
			
		||||
  td::Status do_request(int_api::GetAccountState request, td::Promise<td::unique_ptr<AccountState>>&&);
 | 
			
		||||
  td::Status do_request(int_api::GetAccountStateByTransaction request, td::Promise<td::unique_ptr<AccountState>>&&);
 | 
			
		||||
  td::Status do_request(int_api::GetPrivateKey request, td::Promise<KeyStorage::PrivateKey>&&);
 | 
			
		||||
  td::Status do_request(int_api::GetDnsResolver request, td::Promise<block::StdAddress>&&);
 | 
			
		||||
  td::Status do_request(int_api::RemoteRunSmcMethod request,
 | 
			
		||||
| 
						 | 
				
			
			@ -370,8 +382,14 @@ class TonlibClient : public td::actor::Actor {
 | 
			
		|||
  td::Status do_request(const tonlib_api::blocks_getShardBlockProof& request,
 | 
			
		||||
                        td::Promise<object_ptr<tonlib_api::blocks_shardBlockProof>>&& promise);
 | 
			
		||||
 | 
			
		||||
  void get_config_param(int32_t param, int32_t mode, ton::BlockIdExt block, 
 | 
			
		||||
                        td::Promise<object_ptr<tonlib_api::configInfo>>&& promise);
 | 
			
		||||
  td::Status do_request(const tonlib_api::getConfigParam& request,
 | 
			
		||||
                        td::Promise<object_ptr<tonlib_api::configInfo>>&& promise);
 | 
			
		||||
  void get_config_all(int32_t mode, ton::BlockIdExt block, 
 | 
			
		||||
                      td::Promise<object_ptr<tonlib_api::configInfo>>&& promise);
 | 
			
		||||
  td::Status do_request(const tonlib_api::getConfigAll& request,
 | 
			
		||||
                        td::Promise<object_ptr<tonlib_api::configInfo>>&& promise);
 | 
			
		||||
 | 
			
		||||
  void proxy_request(td::int64 query_id, std::string data);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -389,6 +389,7 @@ class TonlibCli : public td::actor::Actor {
 | 
			
		|||
      td::TerminalIO::out() << "runmethod <addr> <method-id> <params>...\tRuns GET method <method-id> of account "
 | 
			
		||||
                               "<addr> with specified parameters\n";
 | 
			
		||||
      td::TerminalIO::out() << "getstate <key_id>\tget state of wallet with requested key\n";
 | 
			
		||||
      td::TerminalIO::out() << "getstatebytransaction <key_id> <lt> <hash>\tget state of wallet with requested key after transaction with local time <lt> and hash <hash> (base64url)\n";
 | 
			
		||||
      td::TerminalIO::out() << "guessrevision <key_id>\tsearch of existing accounts corresponding to the given key\n";
 | 
			
		||||
      td::TerminalIO::out() << "guessaccount <key_id>\tsearch of existing accounts corresponding to the given key\n";
 | 
			
		||||
      td::TerminalIO::out() << "getaddress <key_id>\tget address of wallet with requested key\n";
 | 
			
		||||
| 
						 | 
				
			
			@ -489,6 +490,8 @@ class TonlibCli : public td::actor::Actor {
 | 
			
		|||
      transfer(parser, cmd, std::move(cmd_promise));
 | 
			
		||||
    } else if (cmd == "getstate") {
 | 
			
		||||
      get_state(parser.read_word(), std::move(cmd_promise));
 | 
			
		||||
    } else if (cmd == "getstatebytransaction") {
 | 
			
		||||
      get_state_by_transaction(parser, std::move(cmd_promise));
 | 
			
		||||
    } else if (cmd == "getaddress") {
 | 
			
		||||
      get_address(parser.read_word(), std::move(cmd_promise));
 | 
			
		||||
    } else if (cmd == "importkeypem") {
 | 
			
		||||
| 
						 | 
				
			
			@ -2067,6 +2070,30 @@ class TonlibCli : public td::actor::Actor {
 | 
			
		|||
               }));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void get_state_by_transaction(td::ConstParser& parser, td::Promise<td::Unit> promise) {
 | 
			
		||||
    TRY_RESULT_PROMISE(promise, address, to_account_address(parser.read_word(), false));
 | 
			
		||||
    TRY_RESULT_PROMISE(promise, lt, td::to_integer_safe<std::int64_t>(parser.read_word()));
 | 
			
		||||
    TRY_RESULT_PROMISE(promise, hash, td::base64url_decode(parser.read_word()));
 | 
			
		||||
 | 
			
		||||
    auto address_str = address.address->account_address_;
 | 
			
		||||
    auto transaction_id = std::make_unique<tonlib_api::internal_transactionId>(lt, std::move(hash));
 | 
			
		||||
    send_query(make_object<tonlib_api::getAccountStateByTransaction>(
 | 
			
		||||
                   ton::move_tl_object_as<tonlib_api::accountAddress>(std::move(address.address)),
 | 
			
		||||
                   ton::move_tl_object_as<tonlib_api::internal_transactionId>(std::move(transaction_id))),
 | 
			
		||||
               promise.wrap([address_str](auto&& state) {
 | 
			
		||||
                 td::TerminalIO::out() << "Address: " << address_str << "\n";
 | 
			
		||||
                 td::TerminalIO::out() << "Balance: "
 | 
			
		||||
                                       << Grams{td::narrow_cast<td::uint64>(state->balance_ * (state->balance_ > 0))}
 | 
			
		||||
                                       << "\n";
 | 
			
		||||
                 td::TerminalIO::out() << "Sync utime: " << state->sync_utime_ << "\n";
 | 
			
		||||
                 td::TerminalIO::out() << "transaction.LT: " << state->last_transaction_id_->lt_ << "\n";
 | 
			
		||||
                 td::TerminalIO::out() << "transaction.Hash: " << td::base64_encode(state->last_transaction_id_->hash_)
 | 
			
		||||
                                       << "\n";
 | 
			
		||||
                 td::TerminalIO::out() << to_string(state->account_state_);
 | 
			
		||||
                 return td::Unit();
 | 
			
		||||
               }));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void get_address(td::Slice key, td::Promise<td::Unit> promise) {
 | 
			
		||||
    TRY_RESULT_PROMISE(promise, address, to_account_address(key, false));
 | 
			
		||||
    promise.set_value(td::Unit());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -104,19 +104,7 @@ class Collator final : public td::actor::Actor {
 | 
			
		|||
    return 2;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static td::Result<std::unique_ptr<block::ConfigInfo>>
 | 
			
		||||
                     impl_fetch_config_params(std::unique_ptr<block::ConfigInfo> config,
 | 
			
		||||
                                              Ref<vm::Cell>* old_mparams,
 | 
			
		||||
                                              std::vector<block::StoragePrices>* storage_prices,
 | 
			
		||||
                                              block::StoragePhaseConfig* storage_phase_cfg,
 | 
			
		||||
                                              td::BitArray<256>* rand_seed,
 | 
			
		||||
                                              block::ComputePhaseConfig* compute_phase_cfg,
 | 
			
		||||
                                              block::ActionPhaseConfig* action_phase_cfg,
 | 
			
		||||
                                              td::RefInt256* masterchain_create_fee,
 | 
			
		||||
                                              td::RefInt256* basechain_create_fee,
 | 
			
		||||
                                              WorkchainId wc, UnixTime now);
 | 
			
		||||
 | 
			
		||||
  static td::Result<std::unique_ptr<block::Transaction>>
 | 
			
		||||
  static td::Result<std::unique_ptr<block::transaction::Transaction>>
 | 
			
		||||
                        impl_create_ordinary_transaction(Ref<vm::Cell> msg_root,
 | 
			
		||||
                                                         block::Account* acc,
 | 
			
		||||
                                                         UnixTime utime, LogicalTime lt,
 | 
			
		||||
| 
						 | 
				
			
			@ -285,7 +273,7 @@ class Collator final : public td::actor::Actor {
 | 
			
		|||
  td::Result<bool> register_shard_signatures_cell(Ref<vm::Cell> shard_blk_signatures);
 | 
			
		||||
  td::Result<bool> register_shard_signatures(td::Slice shard_blk_signatures_boc);
 | 
			
		||||
  void register_new_msg(block::NewOutMsg msg);
 | 
			
		||||
  void register_new_msgs(block::Transaction& trans);
 | 
			
		||||
  void register_new_msgs(block::transaction::Transaction& trans);
 | 
			
		||||
  bool process_new_messages(bool enqueue_only = false);
 | 
			
		||||
  int process_one_new_message(block::NewOutMsg msg, bool enqueue_only = false, Ref<vm::Cell>* is_special = nullptr);
 | 
			
		||||
  bool process_inbound_internal_messages();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1561,90 +1561,19 @@ bool Collator::init_lt() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
bool Collator::fetch_config_params() {
 | 
			
		||||
  auto res = impl_fetch_config_params(std::move(config_), &old_mparams_, &storage_prices_, &storage_phase_cfg_,
 | 
			
		||||
                                      &rand_seed_, &compute_phase_cfg_, &action_phase_cfg_, &masterchain_create_fee_,
 | 
			
		||||
                                      &basechain_create_fee_, workchain(), now_);
 | 
			
		||||
  auto res = block::FetchConfigParams::fetch_config_params(*config_,
 | 
			
		||||
                                      &old_mparams_, &storage_prices_, &storage_phase_cfg_,
 | 
			
		||||
                                      &rand_seed_, &compute_phase_cfg_, &action_phase_cfg_,
 | 
			
		||||
                                      &masterchain_create_fee_, &basechain_create_fee_,
 | 
			
		||||
                                      workchain(), now_
 | 
			
		||||
                                     );
 | 
			
		||||
  if (res.is_error()) {
 | 
			
		||||
    return fatal_error(res.move_as_error());
 | 
			
		||||
  }
 | 
			
		||||
  config_ = res.move_as_ok();
 | 
			
		||||
  compute_phase_cfg_.libraries = std::make_unique<vm::Dictionary>(config_->get_libraries_root(), 256);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Result<std::unique_ptr<block::ConfigInfo>> Collator::impl_fetch_config_params(
 | 
			
		||||
    std::unique_ptr<block::ConfigInfo> config, Ref<vm::Cell>* old_mparams,
 | 
			
		||||
    std::vector<block::StoragePrices>* storage_prices, block::StoragePhaseConfig* storage_phase_cfg,
 | 
			
		||||
    td::BitArray<256>* rand_seed, block::ComputePhaseConfig* compute_phase_cfg,
 | 
			
		||||
    block::ActionPhaseConfig* action_phase_cfg, td::RefInt256* masterchain_create_fee,
 | 
			
		||||
    td::RefInt256* basechain_create_fee, WorkchainId wc, UnixTime now) {
 | 
			
		||||
  *old_mparams = config->get_config_param(9);
 | 
			
		||||
  {
 | 
			
		||||
    auto res = config->get_storage_prices();
 | 
			
		||||
    if (res.is_error()) {
 | 
			
		||||
      return res.move_as_error();
 | 
			
		||||
    }
 | 
			
		||||
    *storage_prices = res.move_as_ok();
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    // generate rand seed
 | 
			
		||||
    prng::rand_gen().strong_rand_bytes(rand_seed->data(), 32);
 | 
			
		||||
    LOG(DEBUG) << "block random seed set to " << rand_seed->to_hex();
 | 
			
		||||
  }
 | 
			
		||||
  TRY_RESULT(size_limits, config->get_size_limits_config());
 | 
			
		||||
  {
 | 
			
		||||
    // compute compute_phase_cfg / storage_phase_cfg
 | 
			
		||||
    auto cell = config->get_config_param(wc == ton::masterchainId ? 20 : 21);
 | 
			
		||||
    if (cell.is_null()) {
 | 
			
		||||
      return td::Status::Error(-668, "cannot fetch current gas prices and limits from masterchain configuration");
 | 
			
		||||
    }
 | 
			
		||||
    if (!compute_phase_cfg->parse_GasLimitsPrices(std::move(cell), storage_phase_cfg->freeze_due_limit,
 | 
			
		||||
                                                  storage_phase_cfg->delete_due_limit)) {
 | 
			
		||||
      return td::Status::Error(-668, "cannot unpack current gas prices and limits from masterchain configuration");
 | 
			
		||||
    }
 | 
			
		||||
    compute_phase_cfg->block_rand_seed = *rand_seed;
 | 
			
		||||
    compute_phase_cfg->libraries = std::make_unique<vm::Dictionary>(config->get_libraries_root(), 256);
 | 
			
		||||
    compute_phase_cfg->max_vm_data_depth = size_limits.max_vm_data_depth;
 | 
			
		||||
    compute_phase_cfg->global_config = config->get_root_cell();
 | 
			
		||||
    compute_phase_cfg->suspended_addresses = config->get_suspended_addresses(now);
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    // compute action_phase_cfg
 | 
			
		||||
    block::gen::MsgForwardPrices::Record rec;
 | 
			
		||||
    auto cell = config->get_config_param(24);
 | 
			
		||||
    if (cell.is_null() || !tlb::unpack_cell(std::move(cell), rec)) {
 | 
			
		||||
      return td::Status::Error(-668, "cannot fetch masterchain message transfer prices from masterchain configuration");
 | 
			
		||||
    }
 | 
			
		||||
    action_phase_cfg->fwd_mc =
 | 
			
		||||
        block::MsgPrices{rec.lump_price,           rec.bit_price,          rec.cell_price, rec.ihr_price_factor,
 | 
			
		||||
                         (unsigned)rec.first_frac, (unsigned)rec.next_frac};
 | 
			
		||||
    cell = config->get_config_param(25);
 | 
			
		||||
    if (cell.is_null() || !tlb::unpack_cell(std::move(cell), rec)) {
 | 
			
		||||
      return td::Status::Error(-668, "cannot fetch standard message transfer prices from masterchain configuration");
 | 
			
		||||
    }
 | 
			
		||||
    action_phase_cfg->fwd_std =
 | 
			
		||||
        block::MsgPrices{rec.lump_price,           rec.bit_price,          rec.cell_price, rec.ihr_price_factor,
 | 
			
		||||
                         (unsigned)rec.first_frac, (unsigned)rec.next_frac};
 | 
			
		||||
    action_phase_cfg->workchains = &config->get_workchain_list();
 | 
			
		||||
    action_phase_cfg->bounce_msg_body = (config->has_capability(ton::capBounceMsgBody) ? 256 : 0);
 | 
			
		||||
    action_phase_cfg->size_limits = size_limits;
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    // fetch block_grams_created
 | 
			
		||||
    auto cell = config->get_config_param(14);
 | 
			
		||||
    if (cell.is_null()) {
 | 
			
		||||
      *basechain_create_fee = *masterchain_create_fee = td::zero_refint();
 | 
			
		||||
    } else {
 | 
			
		||||
      block::gen::BlockCreateFees::Record create_fees;
 | 
			
		||||
      if (!(tlb::unpack_cell(cell, create_fees) &&
 | 
			
		||||
            block::tlb::t_Grams.as_integer_to(create_fees.masterchain_block_fee, *masterchain_create_fee) &&
 | 
			
		||||
            block::tlb::t_Grams.as_integer_to(create_fees.basechain_block_fee, *basechain_create_fee))) {
 | 
			
		||||
        return td::Status::Error(-668, "cannot unpack BlockCreateFees from configuration parameter #14");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return std::move(config);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Collator::compute_minted_amount(block::CurrencyCollection& to_mint) {
 | 
			
		||||
  if (!is_masterchain()) {
 | 
			
		||||
    return to_mint.set_zero();
 | 
			
		||||
| 
						 | 
				
			
			@ -2167,8 +2096,8 @@ bool Collator::create_ticktock_transaction(const ton::StdSmcAddress& smc_addr, t
 | 
			
		|||
                                                   << "last transaction time in the state of account " << workchain()
 | 
			
		||||
                                                   << ":" << smc_addr.to_hex() << " is too large"));
 | 
			
		||||
  }
 | 
			
		||||
  std::unique_ptr<block::Transaction> trans = std::make_unique<block::Transaction>(
 | 
			
		||||
      *acc, mask == 2 ? block::Transaction::tr_tick : block::Transaction::tr_tock, req_start_lt, now_);
 | 
			
		||||
  std::unique_ptr<block::transaction::Transaction> trans = std::make_unique<block::transaction::Transaction>(
 | 
			
		||||
      *acc, mask == 2 ? block::transaction::Transaction::tr_tick : block::transaction::Transaction::tr_tock, req_start_lt, now_);
 | 
			
		||||
  if (!trans->prepare_storage_phase(storage_phase_cfg_, true)) {
 | 
			
		||||
    return fatal_error(td::Status::Error(
 | 
			
		||||
        -666, std::string{"cannot create storage phase of a new transaction for smart contract "} + smc_addr.to_hex()));
 | 
			
		||||
| 
						 | 
				
			
			@ -2258,7 +2187,7 @@ Ref<vm::Cell> Collator::create_ordinary_transaction(Ref<vm::Cell> msg_root) {
 | 
			
		|||
    fatal_error(std::move(error));
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  std::unique_ptr<block::Transaction> trans = res.move_as_ok();
 | 
			
		||||
  std::unique_ptr<block::transaction::Transaction> trans = res.move_as_ok();
 | 
			
		||||
 | 
			
		||||
  if (!trans->update_limits(*block_limit_status_)) {
 | 
			
		||||
    fatal_error("cannot update block limit status to include the new transaction");
 | 
			
		||||
| 
						 | 
				
			
			@ -2277,10 +2206,13 @@ Ref<vm::Cell> Collator::create_ordinary_transaction(Ref<vm::Cell> msg_root) {
 | 
			
		|||
 | 
			
		||||
// If td::status::error_code == 669 - Fatal Error block can not be produced
 | 
			
		||||
// if td::status::error_code == 701 - Transaction can not be included into block, but it's ok (external or too early internal)
 | 
			
		||||
td::Result<std::unique_ptr<block::Transaction>> Collator::impl_create_ordinary_transaction(
 | 
			
		||||
    Ref<vm::Cell> msg_root, block::Account* acc, UnixTime utime, LogicalTime lt,
 | 
			
		||||
    block::StoragePhaseConfig* storage_phase_cfg, block::ComputePhaseConfig* compute_phase_cfg,
 | 
			
		||||
    block::ActionPhaseConfig* action_phase_cfg, bool external, LogicalTime after_lt) {
 | 
			
		||||
td::Result<std::unique_ptr<block::transaction::Transaction>> Collator::impl_create_ordinary_transaction(Ref<vm::Cell> msg_root,
 | 
			
		||||
                                                         block::Account* acc,
 | 
			
		||||
                                                         UnixTime utime, LogicalTime lt,
 | 
			
		||||
                                                         block::StoragePhaseConfig* storage_phase_cfg,
 | 
			
		||||
                                                         block::ComputePhaseConfig* compute_phase_cfg,
 | 
			
		||||
                                                         block::ActionPhaseConfig* action_phase_cfg,
 | 
			
		||||
                                                         bool external, LogicalTime after_lt) {
 | 
			
		||||
  if (acc->last_trans_end_lt_ >= lt && acc->transactions.empty()) {
 | 
			
		||||
    return td::Status::Error(-669, PSTRING() << "last transaction time in the state of account " << acc->workchain
 | 
			
		||||
                                             << ":" << acc->addr.to_hex() << " is too large");
 | 
			
		||||
| 
						 | 
				
			
			@ -2291,8 +2223,8 @@ td::Result<std::unique_ptr<block::Transaction>> Collator::impl_create_ordinary_t
 | 
			
		|||
    trans_min_lt = std::max(trans_min_lt, after_lt);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::unique_ptr<block::Transaction> trans =
 | 
			
		||||
      std::make_unique<block::Transaction>(*acc, block::Transaction::tr_ord, trans_min_lt + 1, utime, msg_root);
 | 
			
		||||
  std::unique_ptr<block::transaction::Transaction> trans =
 | 
			
		||||
      std::make_unique<block::transaction::Transaction>(*acc, block::transaction::Transaction::tr_ord, trans_min_lt + 1, utime, msg_root);
 | 
			
		||||
  bool ihr_delivered = false;  // FIXME
 | 
			
		||||
  if (!trans->unpack_input_msg(ihr_delivered, action_phase_cfg)) {
 | 
			
		||||
    if (external) {
 | 
			
		||||
| 
						 | 
				
			
			@ -3041,7 +2973,7 @@ void Collator::register_new_msg(block::NewOutMsg new_msg) {
 | 
			
		|||
  new_msgs.push(std::move(new_msg));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Collator::register_new_msgs(block::Transaction& trans) {
 | 
			
		||||
void Collator::register_new_msgs(block::transaction::Transaction& trans) {
 | 
			
		||||
  CHECK(trans.root.not_null());
 | 
			
		||||
  for (unsigned i = 0; i < trans.out_msgs.size(); i++) {
 | 
			
		||||
    register_new_msg(trans.extract_out_msg_ext(i));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -143,16 +143,17 @@ td::Status ExtMessageQ::run_message_on_account(ton::WorkchainId wc,
 | 
			
		|||
   block::ActionPhaseConfig action_phase_cfg_;
 | 
			
		||||
   td::RefInt256 masterchain_create_fee, basechain_create_fee;
 | 
			
		||||
 | 
			
		||||
   auto fetch_res = Collator::impl_fetch_config_params(std::move(config), &old_mparams,
 | 
			
		||||
                                                 &storage_prices_, &storage_phase_cfg_,
 | 
			
		||||
                                                 &rand_seed_, &compute_phase_cfg_,
 | 
			
		||||
                                                 &action_phase_cfg_, &masterchain_create_fee,
 | 
			
		||||
                                                 &basechain_create_fee, wc, utime);
 | 
			
		||||
   auto fetch_res = block::FetchConfigParams::fetch_config_params(*config, &old_mparams,
 | 
			
		||||
                                                                  &storage_prices_, &storage_phase_cfg_,
 | 
			
		||||
                                                                  &rand_seed_, &compute_phase_cfg_,
 | 
			
		||||
                                                                  &action_phase_cfg_, &masterchain_create_fee,
 | 
			
		||||
                                                                  &basechain_create_fee, wc, utime);
 | 
			
		||||
   if(fetch_res.is_error()) {
 | 
			
		||||
     auto error = fetch_res.move_as_error();
 | 
			
		||||
     LOG(DEBUG) << "Cannot fetch config params: " << error.message();
 | 
			
		||||
     return error.move_as_error_prefix("Cannot fetch config params: ");
 | 
			
		||||
   }
 | 
			
		||||
   compute_phase_cfg_.libraries = std::make_unique<vm::Dictionary>(config->get_libraries_root(), 256);
 | 
			
		||||
   compute_phase_cfg_.with_vm_log = true;
 | 
			
		||||
 | 
			
		||||
   auto res = Collator::impl_create_ordinary_transaction(msg_root, acc, utime, lt,
 | 
			
		||||
| 
						 | 
				
			
			@ -164,7 +165,7 @@ td::Status ExtMessageQ::run_message_on_account(ton::WorkchainId wc,
 | 
			
		|||
     LOG(DEBUG) << "Cannot run message on account: " << error.message();
 | 
			
		||||
     return error.move_as_error_prefix("Cannot run message on account: ");
 | 
			
		||||
   }
 | 
			
		||||
   std::unique_ptr<block::Transaction> trans = res.move_as_ok();
 | 
			
		||||
   std::unique_ptr<block::transaction::Transaction> trans = res.move_as_ok();
 | 
			
		||||
 | 
			
		||||
   auto trans_root = trans->commit(*acc);
 | 
			
		||||
   if (trans_root.is_null()) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4359,7 +4359,7 @@ bool ValidateQuery::check_one_transaction(block::Account& account, ton::LogicalT
 | 
			
		|||
                                  << account.total_state->get_hash().to_hex());
 | 
			
		||||
  }
 | 
			
		||||
  // some type-specific checks
 | 
			
		||||
  int trans_type = block::Transaction::tr_none;
 | 
			
		||||
  int trans_type = block::transaction::Transaction::tr_none;
 | 
			
		||||
  switch (tag) {
 | 
			
		||||
    case block::gen::TransactionDescr::trans_ord: {
 | 
			
		||||
      if (!block_limit_status_->fits(block::ParamLimits::cl_medium)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -4369,7 +4369,7 @@ bool ValidateQuery::check_one_transaction(block::Account& account, ton::LogicalT
 | 
			
		|||
                                      << "lt_delta=" << block_limit_status_->cur_lt - block_limits_->start_lt
 | 
			
		||||
                                      << "(limit=" << block_limits_->lt_delta.hard() << ")");
 | 
			
		||||
      }
 | 
			
		||||
      trans_type = block::Transaction::tr_ord;
 | 
			
		||||
      trans_type = block::transaction::Transaction::tr_ord;
 | 
			
		||||
      if (in_msg_root.is_null()) {
 | 
			
		||||
        return reject_query(PSTRING() << "ordinary transaction " << lt << " of account " << addr.to_hex()
 | 
			
		||||
                                      << " has no inbound message");
 | 
			
		||||
| 
						 | 
				
			
			@ -4378,7 +4378,7 @@ bool ValidateQuery::check_one_transaction(block::Account& account, ton::LogicalT
 | 
			
		|||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case block::gen::TransactionDescr::trans_storage: {
 | 
			
		||||
      trans_type = block::Transaction::tr_storage;
 | 
			
		||||
      trans_type = block::transaction::Transaction::tr_storage;
 | 
			
		||||
      if (in_msg_root.not_null()) {
 | 
			
		||||
        return reject_query(PSTRING() << "storage transaction " << lt << " of account " << addr.to_hex()
 | 
			
		||||
                                      << " has an inbound message");
 | 
			
		||||
| 
						 | 
				
			
			@ -4394,7 +4394,7 @@ bool ValidateQuery::check_one_transaction(block::Account& account, ton::LogicalT
 | 
			
		|||
    }
 | 
			
		||||
    case block::gen::TransactionDescr::trans_tick_tock: {
 | 
			
		||||
      bool is_tock = (td_cs.prefetch_ulong(4) & 1);
 | 
			
		||||
      trans_type = is_tock ? block::Transaction::tr_tock : block::Transaction::tr_tick;
 | 
			
		||||
      trans_type = is_tock ? block::transaction::Transaction::tr_tock : block::transaction::Transaction::tr_tick;
 | 
			
		||||
      if (in_msg_root.not_null()) {
 | 
			
		||||
        return reject_query(PSTRING() << (is_tock ? "tock" : "tick") << " transaction " << lt << " of account "
 | 
			
		||||
                                      << addr.to_hex() << " has an inbound message");
 | 
			
		||||
| 
						 | 
				
			
			@ -4402,7 +4402,7 @@ bool ValidateQuery::check_one_transaction(block::Account& account, ton::LogicalT
 | 
			
		|||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case block::gen::TransactionDescr::trans_merge_prepare: {
 | 
			
		||||
      trans_type = block::Transaction::tr_merge_prepare;
 | 
			
		||||
      trans_type = block::transaction::Transaction::tr_merge_prepare;
 | 
			
		||||
      if (in_msg_root.not_null()) {
 | 
			
		||||
        return reject_query(PSTRING() << "merge prepare transaction " << lt << " of account " << addr.to_hex()
 | 
			
		||||
                                      << " has an inbound message");
 | 
			
		||||
| 
						 | 
				
			
			@ -4417,7 +4417,7 @@ bool ValidateQuery::check_one_transaction(block::Account& account, ton::LogicalT
 | 
			
		|||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case block::gen::TransactionDescr::trans_merge_install: {
 | 
			
		||||
      trans_type = block::Transaction::tr_merge_install;
 | 
			
		||||
      trans_type = block::transaction::Transaction::tr_merge_install;
 | 
			
		||||
      if (in_msg_root.is_null()) {
 | 
			
		||||
        return reject_query(PSTRING() << "merge install transaction " << lt << " of account " << addr.to_hex()
 | 
			
		||||
                                      << " has no inbound message");
 | 
			
		||||
| 
						 | 
				
			
			@ -4429,7 +4429,7 @@ bool ValidateQuery::check_one_transaction(block::Account& account, ton::LogicalT
 | 
			
		|||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case block::gen::TransactionDescr::trans_split_prepare: {
 | 
			
		||||
      trans_type = block::Transaction::tr_split_prepare;
 | 
			
		||||
      trans_type = block::transaction::Transaction::tr_split_prepare;
 | 
			
		||||
      if (in_msg_root.not_null()) {
 | 
			
		||||
        return reject_query(PSTRING() << "split prepare transaction " << lt << " of account " << addr.to_hex()
 | 
			
		||||
                                      << " has an inbound message");
 | 
			
		||||
| 
						 | 
				
			
			@ -4444,7 +4444,7 @@ bool ValidateQuery::check_one_transaction(block::Account& account, ton::LogicalT
 | 
			
		|||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case block::gen::TransactionDescr::trans_split_install: {
 | 
			
		||||
      trans_type = block::Transaction::tr_split_install;
 | 
			
		||||
      trans_type = block::transaction::Transaction::tr_split_install;
 | 
			
		||||
      if (in_msg_root.is_null()) {
 | 
			
		||||
        return reject_query(PSTRING() << "split install transaction " << lt << " of account " << addr.to_hex()
 | 
			
		||||
                                      << " has no inbound message");
 | 
			
		||||
| 
						 | 
				
			
			@ -4459,8 +4459,8 @@ bool ValidateQuery::check_one_transaction(block::Account& account, ton::LogicalT
 | 
			
		|||
  // check transaction computation by re-doing it
 | 
			
		||||
  // similar to Collator::create_ordinary_transaction() and Collator::create_ticktock_transaction()
 | 
			
		||||
  // ....
 | 
			
		||||
  std::unique_ptr<block::Transaction> trs =
 | 
			
		||||
      std::make_unique<block::Transaction>(account, trans_type, lt, now_, in_msg_root);
 | 
			
		||||
  std::unique_ptr<block::transaction::Transaction> trs =
 | 
			
		||||
      std::make_unique<block::transaction::Transaction>(account, trans_type, lt, now_, in_msg_root);
 | 
			
		||||
  if (in_msg_root.not_null()) {
 | 
			
		||||
    if (!trs->unpack_input_msg(ihr_delivered, &action_phase_cfg_)) {
 | 
			
		||||
      // inbound external message was not accepted
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue