1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-12 11:12:16 +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 {
// [ 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(

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

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

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) {
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();

View file

@ -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;

View file

@ -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) {

View file

@ -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();

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.
## 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).

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);
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

View file

@ -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();

View file

@ -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 ****