1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-14 12:12:21 +00:00

Merge pull request #1482 from ton-blockchain/tvm-v9

Tvm v9
This commit is contained in:
EmelyanenkoK 2025-01-22 11:13:49 +03:00 committed by GitHub
commit fe46f0e238
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 147 additions and 81 deletions

View file

@ -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 { td::Result<Ref<vm::Tuple>> ConfigInfo::get_prev_blocks_info() const {
// [ wc:Integer shard:Integer seqno:Integer root_hash:Integer file_hash:Integer] = BlockId; // [ wc:Integer shard:Integer seqno:Integer root_hash:Integer file_hash:Integer] = BlockId;
// [ last_mc_blocks:[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> { auto block_id_to_tuple = [](const ton::BlockIdExt& block_id) -> vm::Ref<vm::Tuple> {
td::RefInt256 shard = td::make_refint(block_id.id.shard); td::RefInt256 shard = td::make_refint(block_id.id.shard);
if (shard->sgn() < 0) { 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::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)); 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)); 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;) { for (ton::BlockSeqno seqno = block_id.id.seqno; seqno > 0 && last_mc_blocks.size() < 16;) {
--seqno; --seqno;
ton::BlockIdExt block_id; ton::BlockIdExt id;
if (!get_old_mc_block_id(seqno, block_id)) { if (!get_old_mc_block_id(seqno, id)) {
return td::Status::Error("cannot fetch old mc block"); 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::BlockIdExt last_key_block;
ton::LogicalTime last_key_block_lt; ton::LogicalTime last_key_block_lt;
if (!get_last_key_block(last_key_block, 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 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)), tuple.push_back(block_id_to_tuple(last_key_block));
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( td::optional<PrecompiledContractsConfig::Contract> PrecompiledContractsConfig::get_contract(

View file

@ -1145,31 +1145,64 @@ td::RefInt256 ComputePhaseConfig::compute_gas_price(td::uint64 gas_used) const {
namespace transaction { namespace transaction {
/** /**
* Checks if it is required to increase gas_limit (from GasLimitsPrices config) to special_gas_limit * 2 * Checks if it is required to increase gas_limit (from GasLimitsPrices config) for the transaction
* from masterchain 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 * 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. * 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. * See comment in crypto/smartcont/highload-wallet-v2-code.fc for details on why this happened.
* Account address: EQD_v9j1rlsuHHw2FIhcsCFFSD367ldfDdCKcsNmNpIRzUlu * 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. * It is activated by setting global version to 5 in ConfigParam 8.
* This config change also activates new behavior for special accounts in masterchain. * 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 cfg The compute phase configuration.
* @param now The Unix time of the transaction. * @param now The Unix time of the transaction.
* @param account The account 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) { static td::optional<td::uint64> override_gas_limit(const ComputePhaseConfig& cfg, ton::UnixTime now,
if (!cfg.special_gas_full) { const Account& account) {
return false; 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 return it->second.new_limit;
ton::WorkchainId wc = 0;
const char* addr_hex = "FFBFD8F5AE5B2E1C7C3614885CB02145483DFAEE575F0DD08A72C366369211CD";
return now < until && account.workchain == wc && account.addr.to_hex() == addr_hex;
} }
/** /**
@ -1183,10 +1216,12 @@ static bool override_gas_limit(const ComputePhaseConfig& cfg, ton::UnixTime now,
* @returns The amount of gas. * @returns The amount of gas.
*/ */
td::uint64 Transaction::gas_bought_for(const ComputePhaseConfig& cfg, td::RefInt256 nanograms) { 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; gas_limit_overridden = true;
// Same as ComputePhaseConfig::gas_bought for, but with other gas_limit and max_gas_threshold // 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 = auto max_gas_threshold =
compute_max_gas_threshold(cfg.gas_price256, gas_limit, cfg.flat_gas_limit, cfg.flat_gas_price); compute_max_gas_threshold(cfg.gas_price256, gas_limit, cfg.flat_gas_limit, cfg.flat_gas_price);
if (nanograms.is_null() || sgn(nanograms) < 0) { 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) // See crypto/block/mc-config.cpp#2223 (get_prev_blocks_info)
// [ wc:Integer shard:Integer seqno:Integer root_hash:Integer file_hash:Integer] = BlockId; // [ wc:Integer shard:Integer seqno:Integer root_hash:Integer file_hash:Integer] = BlockId;
// [ last_mc_blocks:[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 // 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) // 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 // 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_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_c7(prepare_vm_c7(cfg)); // tuple with SmartContractInfo
vm.set_chksig_always_succeed(cfg.ignore_chksig); vm.set_chksig_always_succeed(cfg.ignore_chksig);
vm.set_stop_on_accept_message(cfg.stop_on_accept_message); vm.set_stop_on_accept_message(cfg.stop_on_accept_message);

View file

@ -1312,6 +1312,7 @@ x{F832} @Defop CONFIGPARAM
x{F833} @Defop CONFIGOPTPARAM x{F833} @Defop CONFIGOPTPARAM
x{F83400} @Defop PREVMCBLOCKS x{F83400} @Defop PREVMCBLOCKS
x{F83401} @Defop PREVKEYBLOCK x{F83401} @Defop PREVKEYBLOCK
x{F83402} @Defop PREVMCBLOCKS_100
x{F835} @Defop GLOBALID x{F835} @Defop GLOBALID
x{F836} @Defop GETGASFEE x{F836} @Defop GETGASFEE
x{F837} @Defop GETSTORAGEFEE x{F837} @Defop GETSTORAGEFEE

View file

@ -223,14 +223,14 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stac
stack->dump(os, 2); stack->dump(os, 2);
LOG(DEBUG) << "VM stack:\n" << os.str(); 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_c7(std::move(c7));
vm.set_chksig_always_succeed(ignore_chksig); vm.set_chksig_always_succeed(ignore_chksig);
if (!libraries.is_null()) { if (!libraries.is_null()) {
vm.register_library_collection(libraries); vm.register_library_collection(libraries);
} }
if (config) { if (config) {
vm.set_global_version(config->get_global_version());
auto r_limits = config->get_size_limits_config(); auto r_limits = config->get_size_limits_config();
if (r_limits.is_ok()) { if (r_limits.is_ok()) {
vm.set_max_data_depth(r_limits.ok().max_vm_data_depth); vm.set_max_data_depth(r_limits.ok().max_vm_data_depth);

View file

@ -261,10 +261,10 @@ int exec_runvm_common(VmState* st, unsigned mode) {
vm::GasLimits gas{gas_limit, gas_max}; vm::GasLimits gas{gas_limit, gas_max};
VmStateInterface::Guard guard{nullptr}; // Don't consume gas for creating/loading cells during VM init 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), VmState new_state{
VmLog{}, std::vector<Ref<Cell>>{}, std::move(c7)}; 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_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); st->run_child_vm(std::move(new_state), with_data, mode & 32, mode & 8, mode & 128, ret_vals);
return 0; return 0;
} }

View file

@ -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) { int exec_pfx_dict_set(VmState* st, Dictionary::SetMode mode, const char* name) {
Stack& stack = st->get_stack(); Stack& stack = st->get_stack();
VM_LOG(st) << "execute PFXDICT" << name; 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); int n = stack.pop_smallint_range(PrefixDictionary::max_key_bits);
PrefixDictionary dict{stack.pop_maybe_cell(), n}; PrefixDictionary dict{stack.pop_maybe_cell(), n};
auto key_slice = stack.pop_cellslice(); 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) { int exec_pfx_dict_delete(VmState* st) {
Stack& stack = st->get_stack(); Stack& stack = st->get_stack();
VM_LOG(st) << "execute PFXDICTDEL\n"; 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); int n = stack.pop_smallint_range(PrefixDictionary::max_key_bits);
PrefixDictionary dict{stack.pop_maybe_cell(), n}; PrefixDictionary dict{stack.pop_maybe_cell(), n};
auto key_slice = stack.pop_cellslice(); auto key_slice = stack.pop_cellslice();

View file

@ -279,6 +279,7 @@ int exec_get_global_id(VmState* st) {
int exec_get_gas_fee(VmState* st) { int exec_get_gas_fee(VmState* st) {
VM_LOG(st) << "execute GETGASFEE"; VM_LOG(st) << "execute GETGASFEE";
Stack& stack = st->get_stack(); Stack& stack = st->get_stack();
stack.check_underflow(st->get_global_version() >= 9 ? 2 : 0);
bool is_masterchain = stack.pop_bool(); bool is_masterchain = stack.pop_bool();
td::uint64 gas = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0); 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); 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) { int exec_get_storage_fee(VmState* st) {
VM_LOG(st) << "execute GETSTORAGEFEE"; VM_LOG(st) << "execute GETSTORAGEFEE";
Stack& stack = st->get_stack(); Stack& stack = st->get_stack();
stack.check_underflow(st->get_global_version() >= 9 ? 4 : 0);
bool is_masterchain = stack.pop_bool(); bool is_masterchain = stack.pop_bool();
td::int64 delta = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0); 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); 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) { int exec_get_forward_fee(VmState* st) {
VM_LOG(st) << "execute GETFORWARDFEE"; VM_LOG(st) << "execute GETFORWARDFEE";
Stack& stack = st->get_stack(); Stack& stack = st->get_stack();
stack.check_underflow(st->get_global_version() >= 9 ? 3 : 0);
bool is_masterchain = stack.pop_bool(); bool is_masterchain = stack.pop_bool();
td::uint64 bits = 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);
td::uint64 cells = 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) { int exec_get_original_fwd_fee(VmState* st) {
VM_LOG(st) << "execute GETORIGINALFWDFEE"; VM_LOG(st) << "execute GETORIGINALFWDFEE";
Stack& stack = st->get_stack(); Stack& stack = st->get_stack();
stack.check_underflow(st->get_global_version() >= 9 ? 2 : 0);
bool is_masterchain = stack.pop_bool(); bool is_masterchain = stack.pop_bool();
td::RefInt256 fwd_fee = stack.pop_int_finite(); td::RefInt256 fwd_fee = stack.pop_int_finite();
if (fwd_fee->sgn() < 0) { 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) { int exec_get_gas_fee_simple(VmState* st) {
VM_LOG(st) << "execute GETGASFEESIMPLE"; VM_LOG(st) << "execute GETGASFEESIMPLE";
Stack& stack = st->get_stack(); Stack& stack = st->get_stack();
stack.check_underflow(st->get_global_version() >= 9 ? 2 : 0);
bool is_masterchain = stack.pop_bool(); bool is_masterchain = stack.pop_bool();
td::uint64 gas = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0); 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); 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) { int exec_get_forward_fee_simple(VmState* st) {
VM_LOG(st) << "execute GETFORWARDFEESIMPLE"; VM_LOG(st) << "execute GETFORWARDFEESIMPLE";
Stack& stack = st->get_stack(); Stack& stack = st->get_stack();
stack.check_underflow(st->get_global_version() >= 9 ? 3 : 0);
bool is_masterchain = stack.pop_bool(); bool is_masterchain = stack.pop_bool();
td::uint64 bits = 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);
td::uint64 cells = 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(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(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(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(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(0xf836, 16, "GETGASFEE", exec_get_gas_fee)->require_version(6))
.insert(OpcodeInstr::mksimple(0xf837, 16, "GETSTORAGEFEE", exec_get_storage_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); VM_LOG(st) << "execute HASHEXT" << (append ? "A" : "") << (rev ? "R" : "") << " " << (hash_id == 255 ? -1 : hash_id);
Stack& stack = st->get_stack(); Stack& stack = st->get_stack();
if (hash_id == 255) { if (hash_id == 255) {
stack.check_underflow(st->get_global_version() >= 9 ? 2 : 0);
hash_id = stack.pop_smallint_range(254); 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}; Hasher hasher{hash_id};
size_t total_bits = 0; size_t total_bits = 0;
long long gas_consumed = 0; long long gas_consumed = 0;

View file

@ -22,6 +22,8 @@
#include "vm/log.h" #include "vm/log.h"
#include "vm/vm.h" #include "vm/vm.h"
#include "cp0.h" #include "cp0.h"
#include "memo.h"
#include <sodium.h> #include <sodium.h>
namespace vm { namespace vm {
@ -31,33 +33,8 @@ VmState::VmState() : cp(-1), dispatch(&dummy_dispatch_table), quit0(true, 0), qu
init_cregs(); init_cregs();
} }
VmState::VmState(Ref<CellSlice> _code) VmState::VmState(Ref<CellSlice> _code, int global_version, Ref<Stack> _stack, const GasLimits& gas, int flags,
: code(std::move(_code)), cp(-1), dispatch(&dummy_dispatch_table), quit0(true, 0), quit1(true, 1) { Ref<Cell> _data, VmLog log, std::vector<Ref<Cell>> _libraries, Ref<Tuple> init_c7)
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)
: code(std::move(_code)) : code(std::move(_code))
, stack(std::move(_stack)) , stack(std::move(_stack))
, cp(-1) , cp(-1)
@ -67,7 +44,8 @@ VmState::VmState(Ref<CellSlice> _code, Ref<Stack> _stack, const GasLimits& gas,
, log(log) , log(log)
, gas(gas) , gas(gas)
, libraries(std::move(_libraries)) , libraries(std::move(_libraries))
, stack_trace((flags >> 2) & 1) { , stack_trace((flags >> 2) & 1)
, global_version(global_version) {
ensure_throw(init_cp(0)); ensure_throw(init_cp(0));
set_c4(std::move(_data)); set_c4(std::move(_data));
if (init_c7.not_null()) { 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()) { if (code_cell.is_null()) {
return {}; return {};
} }
Ref<CellSlice> csr{true, NoVmOrd(), code_cell}; Ref<CellSlice> csr;
if (csr->is_valid()) { 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 csr;
} }
return load_cell_slice_ref(CellBuilder{}.store_ref(std::move(code_cell)).finalize()); 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, GasLimits* gas_limits, std::vector<Ref<Cell>> libraries, Ref<Tuple> init_c7, Ref<Cell>* actions_ptr,
int global_version) { int global_version) {
VmState vm{code, VmState vm{code,
global_version,
std::move(stack), std::move(stack),
gas_limits ? *gas_limits : GasLimits{}, gas_limits ? *gas_limits : GasLimits{},
flags, flags,
@ -584,7 +575,6 @@ int run_vm_code(Ref<CellSlice> code, Ref<Stack>& stack, int flags, Ref<Cell>* da
log, log,
std::move(libraries), std::move(libraries),
std::move(init_c7)}; std::move(init_c7)};
vm.set_global_version(global_version);
int res = vm.run(); int res = vm.run();
stack = vm.get_stack_ref(); stack = vm.get_stack_ref();
if (vm.committed() && data_ptr) { if (vm.committed() && data_ptr) {

View file

@ -164,14 +164,12 @@ class VmState final : public VmStateInterface {
bls_pairing_element_gas_price = 11800 bls_pairing_element_gas_price = 11800
}; };
VmState(); VmState();
VmState(Ref<CellSlice> _code); VmState(Ref<CellSlice> _code, int global_version, Ref<Stack> _stack, const GasLimits& _gas, int flags = 0, Ref<Cell> _data = {},
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 = {},
VmLog log = {}, std::vector<Ref<Cell>> _libraries = {}, Ref<Tuple> init_c7 = {}); VmLog log = {}, std::vector<Ref<Cell>> _libraries = {}, Ref<Tuple> init_c7 = {});
template <typename... Args> VmState(Ref<Cell> _code, int global_version, Ref<Stack> _stack, const GasLimits& _gas, int flags = 0,
VmState(Ref<Cell> code_cell, Args&&... args) Ref<Cell> _data = {}, VmLog log = {}, std::vector<Ref<Cell>> _libraries = {}, Ref<Tuple> init_c7 = {})
: VmState(convert_code_cell(std::move(code_cell)), std::forward<Args>(args)...) { : 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(const VmState&) = delete;
VmState(VmState&&) = default; VmState(VmState&&) = default;
@ -345,9 +343,6 @@ class VmState final : public VmStateInterface {
int get_global_version() const override { int get_global_version() const override {
return global_version; return global_version;
} }
void set_global_version(int version) {
global_version = version;
}
int call(Ref<Continuation> cont); int call(Ref<Continuation> cont);
int call(Ref<Continuation> cont, int pass_args, int ret_args = -1); int call(Ref<Continuation> cont, int pass_args, int ret_args = -1);
int jump(Ref<Continuation> cont); int jump(Ref<Continuation> cont);
@ -382,7 +377,8 @@ class VmState final : public VmStateInterface {
} }
return res; 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(); bool try_commit();
void force_commit(); void force_commit();

View file

@ -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. Various features are enabled depending on the global version.
## Version 4 ## 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 ### New TVM instructions
* `PREVMCBLOCKS`, `PREVKEYBLOCK` * `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). 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 * 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. 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. See [this post](https://t.me/tonstatus/88) for details.
### Loading libraries ### Loading libraries
@ -113,14 +114,23 @@ Operations for working with Merkle proofs, where cells can have non-zero level a
## Version 9 ## 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 ### 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). - `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`. `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`). - `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. - `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 ### Other changes
- Fix `RAWRESERVE` action with flag `4` (use original balance of the account) by explicitly setting `original_balance` to `balance - msg_balance_remaining`. - 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. - 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). - 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).

View file

@ -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); // auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr);
vm::GasLimits gas{gas_limit}; vm::GasLimits gas{gas_limit};
LOG(DEBUG) << "creating VM"; 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()), 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 balance)); // tuple with SmartContractInfo
// vm.incr_stack_trace(1); // enable stack dump after each step // vm.incr_stack_trace(1); // enable stack dump after each step

View file

@ -135,8 +135,8 @@ runInfo time_run_vm(td::Slice command, td::Ref<vm::Stack> stack) {
CHECK(stack.is_unique()); CHECK(stack.is_unique());
try { try {
vm::GasLimits gas_limit; vm::GasLimits gas_limit;
vm::VmState vm{vm::load_cell_slice_ref(cell), std::move(stack), gas_limit, 0, {}, vm::VmLog{}, {}, c7}; vm::VmState vm{
vm.set_global_version(ton::SUPPORTED_VERSION); vm::load_cell_slice_ref(cell), ton::SUPPORTED_VERSION, std::move(stack), gas_limit, 0, {}, vm::VmLog{}, {}, c7};
std::clock_t cStart = std::clock(); std::clock_t cStart = std::clock();
int ret = ~vm.run(); int ret = ~vm.run();
std::clock_t cEnd = std::clock(); std::clock_t cEnd = std::clock();

View file

@ -1520,11 +1520,17 @@ void LiteQuery::finish_runSmcMethod(td::BufferSlice shard_proof, td::BufferSlice
libraries.push_back(acc_libs); libraries.push_back(acc_libs);
} }
vm::GasLimits gas{gas_limit, gas_limit}; 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(), 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); std::move(code), due_payment);
vm.set_c7(c7); // tuple with SmartContractInfo 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 // 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(); LOG(INFO) << "starting VM to run GET-method of smart contract " << acc_workchain_ << ":" << acc_addr_.to_hex();
// **** RUN VM **** // **** RUN VM ****