mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	
						commit
						fe46f0e238
					
				
					 13 changed files with 147 additions and 81 deletions
				
			
		| 
						 | 
				
			
			@ -2292,7 +2292,8 @@ Ref<vm::Cell> ConfigInfo::lookup_library(td::ConstBitPtr root_hash) const {
 | 
			
		|||
td::Result<Ref<vm::Tuple>> ConfigInfo::get_prev_blocks_info() const {
 | 
			
		||||
  // [ wc:Integer shard:Integer seqno:Integer root_hash:Integer file_hash:Integer] = BlockId;
 | 
			
		||||
  // [ last_mc_blocks:[BlockId...]
 | 
			
		||||
  //   prev_key_block:BlockId ] : PrevBlocksInfo
 | 
			
		||||
  //   prev_key_block:BlockId
 | 
			
		||||
  //   last_mc_blocks_100[BlockId...] ] : PrevBlocksInfo
 | 
			
		||||
  auto block_id_to_tuple = [](const ton::BlockIdExt& block_id) -> vm::Ref<vm::Tuple> {
 | 
			
		||||
    td::RefInt256 shard = td::make_refint(block_id.id.shard);
 | 
			
		||||
    if (shard->sgn() < 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -2302,25 +2303,44 @@ td::Result<Ref<vm::Tuple>> ConfigInfo::get_prev_blocks_info() const {
 | 
			
		|||
                              td::make_refint(block_id.id.seqno), td::bits_to_refint(block_id.root_hash.bits(), 256),
 | 
			
		||||
                              td::bits_to_refint(block_id.file_hash.bits(), 256));
 | 
			
		||||
  };
 | 
			
		||||
  std::vector<vm::StackEntry> last_mc_blocks;
 | 
			
		||||
  std::vector<vm::StackEntry> tuple;
 | 
			
		||||
 | 
			
		||||
  std::vector<vm::StackEntry> last_mc_blocks;
 | 
			
		||||
  last_mc_blocks.push_back(block_id_to_tuple(block_id));
 | 
			
		||||
  for (ton::BlockSeqno seqno = block_id.id.seqno; seqno > 0 && last_mc_blocks.size() < 16;) {
 | 
			
		||||
    --seqno;
 | 
			
		||||
    ton::BlockIdExt block_id;
 | 
			
		||||
    if (!get_old_mc_block_id(seqno, block_id)) {
 | 
			
		||||
    ton::BlockIdExt id;
 | 
			
		||||
    if (!get_old_mc_block_id(seqno, id)) {
 | 
			
		||||
      return td::Status::Error("cannot fetch old mc block");
 | 
			
		||||
    }
 | 
			
		||||
    last_mc_blocks.push_back(block_id_to_tuple(block_id));
 | 
			
		||||
    last_mc_blocks.push_back(block_id_to_tuple(id));
 | 
			
		||||
  }
 | 
			
		||||
  tuple.push_back(td::make_cnt_ref<std::vector<vm::StackEntry>>(std::move(last_mc_blocks)));
 | 
			
		||||
 | 
			
		||||
  ton::BlockIdExt last_key_block;
 | 
			
		||||
  ton::LogicalTime last_key_block_lt;
 | 
			
		||||
  if (!get_last_key_block(last_key_block, last_key_block_lt)) {
 | 
			
		||||
    return td::Status::Error("cannot fetch last key block");
 | 
			
		||||
  }
 | 
			
		||||
  return vm::make_tuple_ref(td::make_cnt_ref<std::vector<vm::StackEntry>>(std::move(last_mc_blocks)),
 | 
			
		||||
                            block_id_to_tuple(last_key_block));
 | 
			
		||||
  tuple.push_back(block_id_to_tuple(last_key_block));
 | 
			
		||||
 | 
			
		||||
  if (get_global_version() >= 9) {
 | 
			
		||||
    std::vector<vm::StackEntry> last_mc_blocks_100;
 | 
			
		||||
    for (ton::BlockSeqno seqno = block_id.id.seqno / 100 * 100; last_mc_blocks_100.size() < 16;) {
 | 
			
		||||
      ton::BlockIdExt id;
 | 
			
		||||
      if (!get_old_mc_block_id(seqno, id)) {
 | 
			
		||||
        return td::Status::Error("cannot fetch old mc block");
 | 
			
		||||
      }
 | 
			
		||||
      last_mc_blocks_100.push_back(block_id_to_tuple(id));
 | 
			
		||||
      if (seqno < 100) {
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      seqno -= 100;
 | 
			
		||||
    }
 | 
			
		||||
    tuple.push_back(td::make_cnt_ref<std::vector<vm::StackEntry>>(std::move(last_mc_blocks_100)));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return td::make_cnt_ref<std::vector<vm::StackEntry>>(std::move(tuple));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::optional<PrecompiledContractsConfig::Contract> PrecompiledContractsConfig::get_contract(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1145,31 +1145,64 @@ td::RefInt256 ComputePhaseConfig::compute_gas_price(td::uint64 gas_used) const {
 | 
			
		|||
namespace transaction {
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Checks if it is required to increase gas_limit (from GasLimitsPrices config) to special_gas_limit * 2
 | 
			
		||||
 * from masterchain GasLimitsPrices config for the transaction.
 | 
			
		||||
 * Checks if it is required to increase gas_limit (from GasLimitsPrices config) for the transaction
 | 
			
		||||
 *
 | 
			
		||||
 * In January 2024 a highload wallet of @wallet Telegram bot in mainnet was stuck because current gas limit (1M) is
 | 
			
		||||
 * not enough to clean up old queires, thus locking funds inside.
 | 
			
		||||
 * See comment in crypto/smartcont/highload-wallet-v2-code.fc for details on why this happened.
 | 
			
		||||
 * Account address: EQD_v9j1rlsuHHw2FIhcsCFFSD367ldfDdCKcsNmNpIRzUlu
 | 
			
		||||
 * It was proposed to validators to increase gas limit for this account for a limited amount of time (until 2024-02-29).
 | 
			
		||||
 * It was proposed to validators to increase gas limit for this account to 70M for a limited amount
 | 
			
		||||
 * of time (until 2024-02-29).
 | 
			
		||||
 * It is activated by setting global version to 5 in ConfigParam 8.
 | 
			
		||||
 * This config change also activates new behavior for special accounts in masterchain.
 | 
			
		||||
 *
 | 
			
		||||
 * In Augost 2024 it was decided to unlock other old highload wallets that got into the same situation.
 | 
			
		||||
 * See https://t.me/tondev_news/129
 | 
			
		||||
 * It is activated by setting global version to 9.
 | 
			
		||||
 *
 | 
			
		||||
 * @param cfg The compute phase configuration.
 | 
			
		||||
 * @param now The Unix time of the transaction.
 | 
			
		||||
 * @param account The account of the transaction.
 | 
			
		||||
 *
 | 
			
		||||
 * @returns True if gas_limit override is required, false otherwise
 | 
			
		||||
 * @returns Overridden gas limit or empty td::optional
 | 
			
		||||
 */
 | 
			
		||||
static bool override_gas_limit(const ComputePhaseConfig& cfg, ton::UnixTime now, const Account& account) {
 | 
			
		||||
  if (!cfg.special_gas_full) {
 | 
			
		||||
    return false;
 | 
			
		||||
static td::optional<td::uint64> override_gas_limit(const ComputePhaseConfig& cfg, ton::UnixTime now,
 | 
			
		||||
                                                   const Account& account) {
 | 
			
		||||
  struct OverridenGasLimit {
 | 
			
		||||
    td::uint64 new_limit;
 | 
			
		||||
    int from_version;
 | 
			
		||||
    ton::UnixTime until;
 | 
			
		||||
  };
 | 
			
		||||
  static std::map<std::pair<ton::WorkchainId, ton::StdSmcAddress>, OverridenGasLimit> accounts = []() {
 | 
			
		||||
    auto parse_addr = [](const char* s) -> std::pair<ton::WorkchainId, ton::StdSmcAddress> {
 | 
			
		||||
      auto r_addr = StdAddress::parse(td::Slice(s));
 | 
			
		||||
      r_addr.ensure();
 | 
			
		||||
      return {r_addr.ok().workchain, r_addr.ok().addr};
 | 
			
		||||
    };
 | 
			
		||||
    std::map<std::pair<ton::WorkchainId, ton::StdSmcAddress>, OverridenGasLimit> accounts;
 | 
			
		||||
 | 
			
		||||
    // Increase limit for EQD_v9j1rlsuHHw2FIhcsCFFSD367ldfDdCKcsNmNpIRzUlu until 2024-02-29 00:00:00 UTC
 | 
			
		||||
    accounts[parse_addr("0:FFBFD8F5AE5B2E1C7C3614885CB02145483DFAEE575F0DD08A72C366369211CD")] = {
 | 
			
		||||
        .new_limit = 70'000'000, .from_version = 5, .until = 1709164800};
 | 
			
		||||
 | 
			
		||||
    // Increase limit for multiple accounts (https://t.me/tondev_news/129) until 2025-03-01 00:00:00 UTC
 | 
			
		||||
    accounts[parse_addr("UQBeSl-dumOHieZ3DJkNKVkjeso7wZ0VpzR4LCbLGTQ8xr57")] = {
 | 
			
		||||
        .new_limit = 70'000'000, .from_version = 9, .until = 1740787200};
 | 
			
		||||
    accounts[parse_addr("EQC3VcQ-43klww9UfimR58TBjBzk7GPupXQ3CNuthoNp-uTR")] = {
 | 
			
		||||
        .new_limit = 70'000'000, .from_version = 9, .until = 1740787200};
 | 
			
		||||
    accounts[parse_addr("EQBhwBb8jvokGvfreHRRoeVxI237PrOJgyrsAhLA-4rBC_H5")] = {
 | 
			
		||||
        .new_limit = 70'000'000, .from_version = 9, .until = 1740787200};
 | 
			
		||||
    accounts[parse_addr("EQCkoRp4OE-SFUoMEnYfL3vF43T3AzNfW8jyTC4yzk8cJqMS")] = {
 | 
			
		||||
        .new_limit = 70'000'000, .from_version = 9, .until = 1740787200};
 | 
			
		||||
    accounts[parse_addr("EQBDanbCeUqI4_v-xrnAN0_I2wRvEIaLg1Qg2ZN5c6Zl1KOh")] = {
 | 
			
		||||
        .new_limit = 225'000'000, .from_version = 9, .until = 1740787200};
 | 
			
		||||
    return accounts;
 | 
			
		||||
  }();
 | 
			
		||||
  auto it = accounts.find({account.workchain, account.addr});
 | 
			
		||||
  if (it == accounts.end() || cfg.global_version < it->second.from_version || now >= it->second.until) {
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  ton::UnixTime until = 1709164800;  // 2024-02-29 00:00:00 UTC
 | 
			
		||||
  ton::WorkchainId wc = 0;
 | 
			
		||||
  const char* addr_hex = "FFBFD8F5AE5B2E1C7C3614885CB02145483DFAEE575F0DD08A72C366369211CD";
 | 
			
		||||
  return now < until && account.workchain == wc && account.addr.to_hex() == addr_hex;
 | 
			
		||||
  return it->second.new_limit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -1183,10 +1216,12 @@ static bool override_gas_limit(const ComputePhaseConfig& cfg, ton::UnixTime now,
 | 
			
		|||
 * @returns The amount of gas.
 | 
			
		||||
 */
 | 
			
		||||
td::uint64 Transaction::gas_bought_for(const ComputePhaseConfig& cfg, td::RefInt256 nanograms) {
 | 
			
		||||
  if (override_gas_limit(cfg, now, account)) {
 | 
			
		||||
  if (auto new_limit = override_gas_limit(cfg, now, account)) {
 | 
			
		||||
    gas_limit_overridden = true;
 | 
			
		||||
    // Same as ComputePhaseConfig::gas_bought for, but with other gas_limit and max_gas_threshold
 | 
			
		||||
    auto gas_limit = cfg.mc_gas_prices.special_gas_limit * 2;
 | 
			
		||||
    auto gas_limit = new_limit.value();
 | 
			
		||||
    LOG(INFO) << "overridding gas limit for account " << account.workchain << ":" << account.addr.to_hex() << " to "
 | 
			
		||||
              << gas_limit;
 | 
			
		||||
    auto max_gas_threshold =
 | 
			
		||||
        compute_max_gas_threshold(cfg.gas_price256, gas_limit, cfg.flat_gas_limit, cfg.flat_gas_price);
 | 
			
		||||
    if (nanograms.is_null() || sgn(nanograms) < 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1336,7 +1371,8 @@ Ref<vm::Tuple> Transaction::prepare_vm_c7(const ComputePhaseConfig& cfg) const {
 | 
			
		|||
    // See crypto/block/mc-config.cpp#2223 (get_prev_blocks_info)
 | 
			
		||||
    // [ wc:Integer shard:Integer seqno:Integer root_hash:Integer file_hash:Integer] = BlockId;
 | 
			
		||||
    // [ last_mc_blocks:[BlockId...]
 | 
			
		||||
    //   prev_key_block:BlockId ] : PrevBlocksInfo
 | 
			
		||||
    //   prev_key_block:BlockId
 | 
			
		||||
    //   last_mc_blocks_100:[BlockId...] ] : PrevBlocksInfo
 | 
			
		||||
    // The only context where PrevBlocksInfo (13 parameter of c7) is null is inside emulator
 | 
			
		||||
    // where it need to be set via transaction_emulator_set_prev_blocks_info (see emulator/emulator-extern.cpp)
 | 
			
		||||
    // Inside validator, collator and liteserver checking external message  contexts
 | 
			
		||||
| 
						 | 
				
			
			@ -1691,9 +1727,8 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
 | 
			
		|||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  vm::VmState vm{new_code, std::move(stack), gas, 1, new_data, vm_log, compute_vm_libraries(cfg)};
 | 
			
		||||
  vm::VmState vm{new_code, cfg.global_version, 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_global_version(cfg.global_version);
 | 
			
		||||
  vm.set_c7(prepare_vm_c7(cfg));  // tuple with SmartContractInfo
 | 
			
		||||
  vm.set_chksig_always_succeed(cfg.ignore_chksig);
 | 
			
		||||
  vm.set_stop_on_accept_message(cfg.stop_on_accept_message);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1312,6 +1312,7 @@ x{F832} @Defop CONFIGPARAM
 | 
			
		|||
x{F833} @Defop CONFIGOPTPARAM
 | 
			
		||||
x{F83400} @Defop PREVMCBLOCKS
 | 
			
		||||
x{F83401} @Defop PREVKEYBLOCK
 | 
			
		||||
x{F83402} @Defop PREVMCBLOCKS_100
 | 
			
		||||
x{F835} @Defop GLOBALID
 | 
			
		||||
x{F836} @Defop GETGASFEE
 | 
			
		||||
x{F837} @Defop GETSTORAGEFEE
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -223,14 +223,14 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stac
 | 
			
		|||
    stack->dump(os, 2);
 | 
			
		||||
    LOG(DEBUG) << "VM stack:\n" << os.str();
 | 
			
		||||
  }
 | 
			
		||||
  vm::VmState vm{state.code, std::move(stack), gas, 1, state.data, log};
 | 
			
		||||
  int global_version = config ? config->get_global_version() : 0;
 | 
			
		||||
  vm::VmState vm{state.code, global_version, std::move(stack), gas, 1, state.data, log};
 | 
			
		||||
  vm.set_c7(std::move(c7));
 | 
			
		||||
  vm.set_chksig_always_succeed(ignore_chksig);
 | 
			
		||||
  if (!libraries.is_null()) {
 | 
			
		||||
    vm.register_library_collection(libraries);
 | 
			
		||||
  }
 | 
			
		||||
  if (config) {
 | 
			
		||||
    vm.set_global_version(config->get_global_version());
 | 
			
		||||
    auto r_limits = config->get_size_limits_config();
 | 
			
		||||
    if (r_limits.is_ok()) {
 | 
			
		||||
      vm.set_max_data_depth(r_limits.ok().max_vm_data_depth);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -261,10 +261,10 @@ int exec_runvm_common(VmState* st, unsigned mode) {
 | 
			
		|||
  vm::GasLimits gas{gas_limit, gas_max};
 | 
			
		||||
 | 
			
		||||
  VmStateInterface::Guard guard{nullptr}; // Don't consume gas for creating/loading cells during VM init
 | 
			
		||||
  VmState new_state{std::move(code), std::move(new_stack),     gas,          (int)mode & 3, std::move(data),
 | 
			
		||||
                    VmLog{},         std::vector<Ref<Cell>>{}, std::move(c7)};
 | 
			
		||||
  VmState new_state{
 | 
			
		||||
      std::move(code), st->get_global_version(), std::move(new_stack), gas, (int)mode & 3, std::move(data),
 | 
			
		||||
      VmLog{},         std::vector<Ref<Cell>>{}, std::move(c7)};
 | 
			
		||||
  new_state.set_chksig_always_succeed(st->get_chksig_always_succeed());
 | 
			
		||||
  new_state.set_global_version(st->get_global_version());
 | 
			
		||||
  st->run_child_vm(std::move(new_state), with_data, mode & 32, mode & 8, mode & 128, ret_vals);
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -566,7 +566,7 @@ int exec_dict_getnear(VmState* st, unsigned args) {
 | 
			
		|||
int exec_pfx_dict_set(VmState* st, Dictionary::SetMode mode, const char* name) {
 | 
			
		||||
  Stack& stack = st->get_stack();
 | 
			
		||||
  VM_LOG(st) << "execute PFXDICT" << name;
 | 
			
		||||
  stack.check_underflow(3);
 | 
			
		||||
  stack.check_underflow(st->get_global_version() >= 9 ? 4 : 3);
 | 
			
		||||
  int n = stack.pop_smallint_range(PrefixDictionary::max_key_bits);
 | 
			
		||||
  PrefixDictionary dict{stack.pop_maybe_cell(), n};
 | 
			
		||||
  auto key_slice = stack.pop_cellslice();
 | 
			
		||||
| 
						 | 
				
			
			@ -580,7 +580,7 @@ int exec_pfx_dict_set(VmState* st, Dictionary::SetMode mode, const char* name) {
 | 
			
		|||
int exec_pfx_dict_delete(VmState* st) {
 | 
			
		||||
  Stack& stack = st->get_stack();
 | 
			
		||||
  VM_LOG(st) << "execute PFXDICTDEL\n";
 | 
			
		||||
  stack.check_underflow(2);
 | 
			
		||||
  stack.check_underflow(st->get_global_version() >= 9 ? 3 : 2);
 | 
			
		||||
  int n = stack.pop_smallint_range(PrefixDictionary::max_key_bits);
 | 
			
		||||
  PrefixDictionary dict{stack.pop_maybe_cell(), n};
 | 
			
		||||
  auto key_slice = stack.pop_cellslice();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -279,6 +279,7 @@ int exec_get_global_id(VmState* st) {
 | 
			
		|||
int exec_get_gas_fee(VmState* st) {
 | 
			
		||||
  VM_LOG(st) << "execute GETGASFEE";
 | 
			
		||||
  Stack& stack = st->get_stack();
 | 
			
		||||
  stack.check_underflow(st->get_global_version() >= 9 ? 2 : 0);
 | 
			
		||||
  bool is_masterchain = stack.pop_bool();
 | 
			
		||||
  td::uint64 gas = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
 | 
			
		||||
  block::GasLimitsPrices prices = util::get_gas_prices(get_unpacked_config_tuple(st), is_masterchain);
 | 
			
		||||
| 
						 | 
				
			
			@ -289,6 +290,7 @@ int exec_get_gas_fee(VmState* st) {
 | 
			
		|||
int exec_get_storage_fee(VmState* st) {
 | 
			
		||||
  VM_LOG(st) << "execute GETSTORAGEFEE";
 | 
			
		||||
  Stack& stack = st->get_stack();
 | 
			
		||||
  stack.check_underflow(st->get_global_version() >= 9 ? 4 : 0);
 | 
			
		||||
  bool is_masterchain = stack.pop_bool();
 | 
			
		||||
  td::int64 delta = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
 | 
			
		||||
  td::uint64 bits = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
 | 
			
		||||
| 
						 | 
				
			
			@ -302,6 +304,7 @@ int exec_get_storage_fee(VmState* st) {
 | 
			
		|||
int exec_get_forward_fee(VmState* st) {
 | 
			
		||||
  VM_LOG(st) << "execute GETFORWARDFEE";
 | 
			
		||||
  Stack& stack = st->get_stack();
 | 
			
		||||
  stack.check_underflow(st->get_global_version() >= 9 ? 3 : 0);
 | 
			
		||||
  bool is_masterchain = stack.pop_bool();
 | 
			
		||||
  td::uint64 bits = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
 | 
			
		||||
  td::uint64 cells = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
 | 
			
		||||
| 
						 | 
				
			
			@ -320,6 +323,7 @@ int exec_get_precompiled_gas(VmState* st) {
 | 
			
		|||
int exec_get_original_fwd_fee(VmState* st) {
 | 
			
		||||
  VM_LOG(st) << "execute GETORIGINALFWDFEE";
 | 
			
		||||
  Stack& stack = st->get_stack();
 | 
			
		||||
  stack.check_underflow(st->get_global_version() >= 9 ? 2 : 0);
 | 
			
		||||
  bool is_masterchain = stack.pop_bool();
 | 
			
		||||
  td::RefInt256 fwd_fee = stack.pop_int_finite();
 | 
			
		||||
  if (fwd_fee->sgn() < 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -333,6 +337,7 @@ int exec_get_original_fwd_fee(VmState* st) {
 | 
			
		|||
int exec_get_gas_fee_simple(VmState* st) {
 | 
			
		||||
  VM_LOG(st) << "execute GETGASFEESIMPLE";
 | 
			
		||||
  Stack& stack = st->get_stack();
 | 
			
		||||
  stack.check_underflow(st->get_global_version() >= 9 ? 2 : 0);
 | 
			
		||||
  bool is_masterchain = stack.pop_bool();
 | 
			
		||||
  td::uint64 gas = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
 | 
			
		||||
  block::GasLimitsPrices prices = util::get_gas_prices(get_unpacked_config_tuple(st), is_masterchain);
 | 
			
		||||
| 
						 | 
				
			
			@ -343,6 +348,7 @@ int exec_get_gas_fee_simple(VmState* st) {
 | 
			
		|||
int exec_get_forward_fee_simple(VmState* st) {
 | 
			
		||||
  VM_LOG(st) << "execute GETFORWARDFEESIMPLE";
 | 
			
		||||
  Stack& stack = st->get_stack();
 | 
			
		||||
  stack.check_underflow(st->get_global_version() >= 9 ? 3 : 0);
 | 
			
		||||
  bool is_masterchain = stack.pop_bool();
 | 
			
		||||
  td::uint64 bits = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
 | 
			
		||||
  td::uint64 cells = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
 | 
			
		||||
| 
						 | 
				
			
			@ -373,6 +379,7 @@ void register_ton_config_ops(OpcodeTable& cp0) {
 | 
			
		|||
      .insert(OpcodeInstr::mksimple(0xf833, 16, "CONFIGOPTPARAM", std::bind(exec_get_config_param, _1, true)))
 | 
			
		||||
      .insert(OpcodeInstr::mksimple(0xf83400, 24, "PREVMCBLOCKS", std::bind(exec_get_prev_blocks_info, _1, 0, "PREVMCBLOCKS"))->require_version(4))
 | 
			
		||||
      .insert(OpcodeInstr::mksimple(0xf83401, 24, "PREVKEYBLOCK", std::bind(exec_get_prev_blocks_info, _1, 1, "PREVKEYBLOCK"))->require_version(4))
 | 
			
		||||
      .insert(OpcodeInstr::mksimple(0xf83402, 24, "PREVMCBLOCKS_100", std::bind(exec_get_prev_blocks_info, _1, 2, "PREVMCBLOCKS_100"))->require_version(9))
 | 
			
		||||
      .insert(OpcodeInstr::mksimple(0xf835, 16, "GLOBALID", exec_get_global_id)->require_version(4))
 | 
			
		||||
      .insert(OpcodeInstr::mksimple(0xf836, 16, "GETGASFEE", exec_get_gas_fee)->require_version(6))
 | 
			
		||||
      .insert(OpcodeInstr::mksimple(0xf837, 16, "GETSTORAGEFEE", exec_get_storage_fee)->require_version(6))
 | 
			
		||||
| 
						 | 
				
			
			@ -538,9 +545,10 @@ int exec_hash_ext(VmState* st, unsigned args) {
 | 
			
		|||
  VM_LOG(st) << "execute HASHEXT" << (append ? "A" : "") << (rev ? "R" : "") << " " << (hash_id == 255 ? -1 : hash_id);
 | 
			
		||||
  Stack& stack = st->get_stack();
 | 
			
		||||
  if (hash_id == 255) {
 | 
			
		||||
    stack.check_underflow(st->get_global_version() >= 9 ? 2 : 0);
 | 
			
		||||
    hash_id = stack.pop_smallint_range(254);
 | 
			
		||||
  }
 | 
			
		||||
  int cnt = stack.pop_smallint_range(stack.depth() - 1);
 | 
			
		||||
  int cnt = stack.pop_smallint_range(stack.depth() - 1 - (st->get_global_version() >= 9 ? (int)append : 0));
 | 
			
		||||
  Hasher hasher{hash_id};
 | 
			
		||||
  size_t total_bits = 0;
 | 
			
		||||
  long long gas_consumed = 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,6 +22,8 @@
 | 
			
		|||
#include "vm/log.h"
 | 
			
		||||
#include "vm/vm.h"
 | 
			
		||||
#include "cp0.h"
 | 
			
		||||
#include "memo.h"
 | 
			
		||||
 | 
			
		||||
#include <sodium.h>
 | 
			
		||||
 | 
			
		||||
namespace vm {
 | 
			
		||||
| 
						 | 
				
			
			@ -31,33 +33,8 @@ VmState::VmState() : cp(-1), dispatch(&dummy_dispatch_table), quit0(true, 0), qu
 | 
			
		|||
  init_cregs();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VmState::VmState(Ref<CellSlice> _code)
 | 
			
		||||
    : code(std::move(_code)), cp(-1), dispatch(&dummy_dispatch_table), quit0(true, 0), quit1(true, 1) {
 | 
			
		||||
  ensure_throw(init_cp(0));
 | 
			
		||||
  init_cregs();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VmState::VmState(Ref<CellSlice> _code, Ref<Stack> _stack, int flags, Ref<Cell> _data, VmLog log,
 | 
			
		||||
                 std::vector<Ref<Cell>> _libraries, Ref<Tuple> init_c7)
 | 
			
		||||
    : code(std::move(_code))
 | 
			
		||||
    , stack(std::move(_stack))
 | 
			
		||||
    , cp(-1)
 | 
			
		||||
    , dispatch(&dummy_dispatch_table)
 | 
			
		||||
    , quit0(true, 0)
 | 
			
		||||
    , quit1(true, 1)
 | 
			
		||||
    , log(log)
 | 
			
		||||
    , libraries(std::move(_libraries))
 | 
			
		||||
    , stack_trace((flags >> 2) & 1) {
 | 
			
		||||
  ensure_throw(init_cp(0));
 | 
			
		||||
  set_c4(std::move(_data));
 | 
			
		||||
  if (init_c7.not_null()) {
 | 
			
		||||
    set_c7(std::move(init_c7));
 | 
			
		||||
  }
 | 
			
		||||
  init_cregs(flags & 1, flags & 2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VmState::VmState(Ref<CellSlice> _code, Ref<Stack> _stack, const GasLimits& gas, int flags, Ref<Cell> _data, VmLog log,
 | 
			
		||||
                 std::vector<Ref<Cell>> _libraries, Ref<Tuple> init_c7)
 | 
			
		||||
VmState::VmState(Ref<CellSlice> _code, int global_version, Ref<Stack> _stack, const GasLimits& gas, int flags,
 | 
			
		||||
                 Ref<Cell> _data, VmLog log, std::vector<Ref<Cell>> _libraries, Ref<Tuple> init_c7)
 | 
			
		||||
    : code(std::move(_code))
 | 
			
		||||
    , stack(std::move(_stack))
 | 
			
		||||
    , cp(-1)
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +44,8 @@ VmState::VmState(Ref<CellSlice> _code, Ref<Stack> _stack, const GasLimits& gas,
 | 
			
		|||
    , log(log)
 | 
			
		||||
    , gas(gas)
 | 
			
		||||
    , libraries(std::move(_libraries))
 | 
			
		||||
    , stack_trace((flags >> 2) & 1) {
 | 
			
		||||
    , stack_trace((flags >> 2) & 1)
 | 
			
		||||
    , global_version(global_version) {
 | 
			
		||||
  ensure_throw(init_cp(0));
 | 
			
		||||
  set_c4(std::move(_data));
 | 
			
		||||
  if (init_c7.not_null()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -102,12 +80,24 @@ void VmState::init_cregs(bool same_c3, bool push_0) {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<CellSlice> VmState::convert_code_cell(Ref<Cell> code_cell) {
 | 
			
		||||
Ref<CellSlice> VmState::convert_code_cell(Ref<Cell> code_cell, int global_version,
 | 
			
		||||
                                          const std::vector<Ref<Cell>>& libraries) {
 | 
			
		||||
  if (code_cell.is_null()) {
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  Ref<CellSlice> csr{true, NoVmOrd(), code_cell};
 | 
			
		||||
  if (csr->is_valid()) {
 | 
			
		||||
  Ref<CellSlice> csr;
 | 
			
		||||
  if (global_version >= 9) {
 | 
			
		||||
    // Use DummyVmState instead of this to avoid consuming gas for cell loading
 | 
			
		||||
    DummyVmState dummy{libraries, global_version};
 | 
			
		||||
    Guard guard(&dummy);
 | 
			
		||||
    try {
 | 
			
		||||
      csr = load_cell_slice_ref(code_cell);
 | 
			
		||||
    } catch (VmError&) {  // NOLINT(*-empty-catch)
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    csr = td::Ref<CellSlice>{true, NoVmOrd(), code_cell};
 | 
			
		||||
  }
 | 
			
		||||
  if (csr.not_null() && csr->is_valid()) {
 | 
			
		||||
    return csr;
 | 
			
		||||
  }
 | 
			
		||||
  return load_cell_slice_ref(CellBuilder{}.store_ref(std::move(code_cell)).finalize());
 | 
			
		||||
| 
						 | 
				
			
			@ -577,6 +567,7 @@ int run_vm_code(Ref<CellSlice> code, Ref<Stack>& stack, int flags, Ref<Cell>* da
 | 
			
		|||
                GasLimits* gas_limits, std::vector<Ref<Cell>> libraries, Ref<Tuple> init_c7, Ref<Cell>* actions_ptr,
 | 
			
		||||
                int global_version) {
 | 
			
		||||
  VmState vm{code,
 | 
			
		||||
             global_version,
 | 
			
		||||
             std::move(stack),
 | 
			
		||||
             gas_limits ? *gas_limits : GasLimits{},
 | 
			
		||||
             flags,
 | 
			
		||||
| 
						 | 
				
			
			@ -584,7 +575,6 @@ int run_vm_code(Ref<CellSlice> code, Ref<Stack>& stack, int flags, Ref<Cell>* da
 | 
			
		|||
             log,
 | 
			
		||||
             std::move(libraries),
 | 
			
		||||
             std::move(init_c7)};
 | 
			
		||||
  vm.set_global_version(global_version);
 | 
			
		||||
  int res = vm.run();
 | 
			
		||||
  stack = vm.get_stack_ref();
 | 
			
		||||
  if (vm.committed() && data_ptr) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -164,14 +164,12 @@ class VmState final : public VmStateInterface {
 | 
			
		|||
    bls_pairing_element_gas_price = 11800
 | 
			
		||||
  };
 | 
			
		||||
  VmState();
 | 
			
		||||
  VmState(Ref<CellSlice> _code);
 | 
			
		||||
  VmState(Ref<CellSlice> _code, Ref<Stack> _stack, int flags = 0, Ref<Cell> _data = {}, VmLog log = {},
 | 
			
		||||
          std::vector<Ref<Cell>> _libraries = {}, Ref<Tuple> init_c7 = {});
 | 
			
		||||
  VmState(Ref<CellSlice> _code, Ref<Stack> _stack, const GasLimits& _gas, int flags = 0, Ref<Cell> _data = {},
 | 
			
		||||
  VmState(Ref<CellSlice> _code, int global_version, Ref<Stack> _stack, const GasLimits& _gas, int flags = 0, Ref<Cell> _data = {},
 | 
			
		||||
          VmLog log = {}, std::vector<Ref<Cell>> _libraries = {}, Ref<Tuple> init_c7 = {});
 | 
			
		||||
  template <typename... Args>
 | 
			
		||||
  VmState(Ref<Cell> code_cell, Args&&... args)
 | 
			
		||||
      : VmState(convert_code_cell(std::move(code_cell)), std::forward<Args>(args)...) {
 | 
			
		||||
  VmState(Ref<Cell> _code, int global_version, Ref<Stack> _stack, const GasLimits& _gas, int flags = 0,
 | 
			
		||||
          Ref<Cell> _data = {}, VmLog log = {}, std::vector<Ref<Cell>> _libraries = {}, Ref<Tuple> init_c7 = {})
 | 
			
		||||
      : VmState(convert_code_cell(std::move(_code), global_version, _libraries), global_version, std::move(_stack),
 | 
			
		||||
                _gas, flags, std::move(_data), std::move(log), _libraries, std::move(init_c7)) {
 | 
			
		||||
  }
 | 
			
		||||
  VmState(const VmState&) = delete;
 | 
			
		||||
  VmState(VmState&&) = default;
 | 
			
		||||
| 
						 | 
				
			
			@ -345,9 +343,6 @@ class VmState final : public VmStateInterface {
 | 
			
		|||
  int get_global_version() const override {
 | 
			
		||||
    return global_version;
 | 
			
		||||
  }
 | 
			
		||||
  void set_global_version(int version) {
 | 
			
		||||
    global_version = version;
 | 
			
		||||
  }
 | 
			
		||||
  int call(Ref<Continuation> cont);
 | 
			
		||||
  int call(Ref<Continuation> cont, int pass_args, int ret_args = -1);
 | 
			
		||||
  int jump(Ref<Continuation> cont);
 | 
			
		||||
| 
						 | 
				
			
			@ -382,7 +377,8 @@ class VmState final : public VmStateInterface {
 | 
			
		|||
    }
 | 
			
		||||
    return res;
 | 
			
		||||
  }
 | 
			
		||||
  static Ref<CellSlice> convert_code_cell(Ref<Cell> code_cell);
 | 
			
		||||
  static Ref<CellSlice> convert_code_cell(Ref<Cell> code_cell, int global_version,
 | 
			
		||||
                                          const std::vector<Ref<Cell>>& libraries);
 | 
			
		||||
  bool try_commit();
 | 
			
		||||
  void force_commit();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ Global version is a parameter specified in `ConfigParam 8` ([block.tlb](https://
 | 
			
		|||
Various features are enabled depending on the global version.
 | 
			
		||||
 | 
			
		||||
## Version 4
 | 
			
		||||
New features of version 4 are desctibed in detail in [the documentation](https://docs.ton.org/v3/documentation/tvm/changelog/tvm-upgrade-2023-07).
 | 
			
		||||
 | 
			
		||||
### New TVM instructions
 | 
			
		||||
* `PREVMCBLOCKS`, `PREVKEYBLOCK`
 | 
			
		||||
| 
						 | 
				
			
			@ -47,7 +48,7 @@ Version 5 enables higher gas limits for special contracts.
 | 
			
		|||
Previously only ticktock transactions had this limit, while ordinary transactions had a default limit of `gas_limit` gas (1M).
 | 
			
		||||
* Gas usage of special contracts is not taken into account when checking block limits. This allows keeping masterchain block limits low
 | 
			
		||||
while having high gas limits for elector.
 | 
			
		||||
* Gas limit on `EQD_v9j1rlsuHHw2FIhcsCFFSD367ldfDdCKcsNmNpIRzUlu` is increased to `special_gas_limit * 2` until 2024-02-29.
 | 
			
		||||
* Gas limit on `EQD_v9j1rlsuHHw2FIhcsCFFSD367ldfDdCKcsNmNpIRzUlu` is increased to 70M (`special_gas_limit * 2`) until 2024-02-29.
 | 
			
		||||
See [this post](https://t.me/tonstatus/88) for details.
 | 
			
		||||
 | 
			
		||||
### Loading libraries
 | 
			
		||||
| 
						 | 
				
			
			@ -113,14 +114,23 @@ Operations for working with Merkle proofs, where cells can have non-zero level a
 | 
			
		|||
 | 
			
		||||
## Version 9
 | 
			
		||||
 | 
			
		||||
### c7 tuple
 | 
			
		||||
c7 tuple parameter number **13** (previous blocks info tuple) now has the third element. It contains ids of the 16 last masterchain blocks with seqno divisible by 100.
 | 
			
		||||
Example: if the last masterchain block seqno is `19071` then the list contains block ids with seqnos `19000`, `18900`, ..., `17500`.
 | 
			
		||||
 | 
			
		||||
### New TVM instructions
 | 
			
		||||
- `SECP256K1_XONLY_PUBKEY_TWEAK_ADD` (`key tweak - 0 or f x y -1`) - performs [`secp256k1_xonly_pubkey_tweak_add`](https://github.com/bitcoin-core/secp256k1/blob/master/include/secp256k1_extrakeys.h#L120).
 | 
			
		||||
`key` and `tweak` are 256-bit unsigned integers. 65-byte public key is returned as `uint8 f`, `uint256 x, y` (as in `ECRECOVER`). Gas cost: `1276`.
 | 
			
		||||
- `mask SETCONTCTRMANY` (`cont - cont'`) - takes continuation, performs the equivalent of `c[i] PUSHCTR SWAP c[i] SETCONTCNR` for each `i` that is set in `mask` (mask is in `0..255`).
 | 
			
		||||
- `SETCONTCTRMANYX` (`cont mask - cont'`) - same as `SETCONTCTRMANY`, but takes `mask` from stack.
 | 
			
		||||
- `PREVMCBLOCKS_100` returns the third element of the previous block info tuple (see above).
 | 
			
		||||
 | 
			
		||||
### Other changes
 | 
			
		||||
- Fix `RAWRESERVE` action with flag `4` (use original balance of the account) by explicitly setting `original_balance` to `balance - msg_balance_remaining`.
 | 
			
		||||
  - Previously it did not work if storage fee was greater than the original balance.
 | 
			
		||||
- Jumps to nested continuations of depth more than 8 consume 1 gas for eact subsequent continuation (this does not affect most of TVM code).
 | 
			
		||||
- Support extra currencies in reserve action with `+2` mode.
 | 
			
		||||
- Support extra currencies in reserve action with `+2` mode.
 | 
			
		||||
- Fix exception code in some TVM instructions: now `stk_und` has priority over other error codes.
 | 
			
		||||
  - `PFXDICTADD`, `PFXDICTSET`, `PFXDICTREPLACE`, `PFXDICTDEL`, `GETGASFEE`, `GETSTORAGEFEE`, `GETFORWARDFEE`, `GETORIGINALFWDFEE`, `GETGASFEESIMPLE`, `GETFORWARDFEESIMPLE`, `HASHEXT`
 | 
			
		||||
- Now setting the contract code to a library cell does not consume additional gas on execution of the code.
 | 
			
		||||
- Temporary increase gas limit for some accounts (see [this post](https://t.me/tondev_news/129) for details, `override_gas_limit` in `transaction.cpp` for the list of accounts).
 | 
			
		||||
| 
						 | 
				
			
			@ -2227,7 +2227,7 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
 | 
			
		|||
    // auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr);
 | 
			
		||||
    vm::GasLimits gas{gas_limit};
 | 
			
		||||
    LOG(DEBUG) << "creating VM";
 | 
			
		||||
    vm::VmState vm{code, std::move(stack), gas, 1, data, vm::VmLog()};
 | 
			
		||||
    vm::VmState vm{code, ton::SUPPORTED_VERSION, std::move(stack), gas, 1, data, vm::VmLog()};
 | 
			
		||||
    vm.set_c7(liteclient::prepare_vm_c7(info.gen_utime, info.gen_lt, td::make_ref<vm::CellSlice>(acc.addr->clone()),
 | 
			
		||||
                                        balance));  // tuple with SmartContractInfo
 | 
			
		||||
    // vm.incr_stack_trace(1);    // enable stack dump after each step
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -135,8 +135,8 @@ runInfo time_run_vm(td::Slice command, td::Ref<vm::Stack> stack) {
 | 
			
		|||
  CHECK(stack.is_unique());
 | 
			
		||||
  try {
 | 
			
		||||
    vm::GasLimits gas_limit;
 | 
			
		||||
    vm::VmState vm{vm::load_cell_slice_ref(cell), std::move(stack), gas_limit, 0, {}, vm::VmLog{}, {}, c7};
 | 
			
		||||
    vm.set_global_version(ton::SUPPORTED_VERSION);
 | 
			
		||||
    vm::VmState vm{
 | 
			
		||||
        vm::load_cell_slice_ref(cell), ton::SUPPORTED_VERSION, std::move(stack), gas_limit, 0, {}, vm::VmLog{}, {}, c7};
 | 
			
		||||
    std::clock_t cStart = std::clock();
 | 
			
		||||
    int ret = ~vm.run();
 | 
			
		||||
    std::clock_t cEnd = std::clock();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1520,11 +1520,17 @@ void LiteQuery::finish_runSmcMethod(td::BufferSlice shard_proof, td::BufferSlice
 | 
			
		|||
    libraries.push_back(acc_libs);
 | 
			
		||||
  }
 | 
			
		||||
  vm::GasLimits gas{gas_limit, gas_limit};
 | 
			
		||||
  vm::VmState vm{code, std::move(stack_), gas, 1, std::move(data), vm::VmLog::Null(), std::move(libraries)};
 | 
			
		||||
  vm::VmState vm{code,
 | 
			
		||||
                 config->get_global_version(),
 | 
			
		||||
                 std::move(stack_),
 | 
			
		||||
                 gas,
 | 
			
		||||
                 1,
 | 
			
		||||
                 std::move(data),
 | 
			
		||||
                 vm::VmLog::Null(),
 | 
			
		||||
                 std::move(libraries)};
 | 
			
		||||
  auto c7 = prepare_vm_c7(gen_utime, gen_lt, td::make_ref<vm::CellSlice>(acc.addr->clone()), balance, config.get(),
 | 
			
		||||
                          std::move(code), due_payment);
 | 
			
		||||
  vm.set_c7(c7);  // tuple with SmartContractInfo
 | 
			
		||||
  vm.set_global_version(config->get_global_version());
 | 
			
		||||
  // vm.incr_stack_trace(1);    // enable stack dump after each step
 | 
			
		||||
  LOG(INFO) << "starting VM to run GET-method of smart contract " << acc_workchain_ << ":" << acc_addr_.to_hex();
 | 
			
		||||
  // **** RUN VM ****
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue