From 7646ec6e34e85ece44c9ab7cac42586081493a32 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Tue, 17 Dec 2024 11:18:34 +0300 Subject: [PATCH] Fix loading library cell in contract code --- crypto/block/transaction.cpp | 3 +-- crypto/fift/lib/Asm.fif | 1 + crypto/smc-envelope/SmartContract.cpp | 4 ++-- crypto/vm/contops.cpp | 6 ++--- crypto/vm/vm.cpp | 34 ++++++++++++++++----------- crypto/vm/vm.h | 15 +++++------- doc/GlobalVersions.md | 3 ++- lite-client/lite-client.cpp | 2 +- utils/opcode-timing.cpp | 4 ++-- validator/impl/liteserver.cpp | 10 ++++++-- 10 files changed, 46 insertions(+), 36 deletions(-) diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index 7acd18348..920433761 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -1692,9 +1692,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); diff --git a/crypto/fift/lib/Asm.fif b/crypto/fift/lib/Asm.fif index 39cb759d1..976093f80 100644 --- a/crypto/fift/lib/Asm.fif +++ b/crypto/fift/lib/Asm.fif @@ -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 diff --git a/crypto/smc-envelope/SmartContract.cpp b/crypto/smc-envelope/SmartContract.cpp index 2578a9514..34ab18a24 100644 --- a/crypto/smc-envelope/SmartContract.cpp +++ b/crypto/smc-envelope/SmartContract.cpp @@ -222,14 +222,14 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Refdump(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() : SUPPORTED_VERSION; + 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); diff --git a/crypto/vm/contops.cpp b/crypto/vm/contops.cpp index 3b8926586..1ccf53daf 100644 --- a/crypto/vm/contops.cpp +++ b/crypto/vm/contops.cpp @@ -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>{}, 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>{}, 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; } diff --git a/crypto/vm/vm.cpp b/crypto/vm/vm.cpp index fb774f80a..7c5ccf3a4 100644 --- a/crypto/vm/vm.cpp +++ b/crypto/vm/vm.cpp @@ -31,13 +31,7 @@ VmState::VmState() : cp(-1), dispatch(&dummy_dispatch_table), quit0(true, 0), qu init_cregs(); } -VmState::VmState(Ref _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 _code, Ref _stack, int flags, Ref _data, VmLog log, +VmState::VmState(Ref _code, int global_version, Ref _stack, int flags, Ref _data, VmLog log, std::vector> _libraries, Ref init_c7) : code(std::move(_code)) , stack(std::move(_stack)) @@ -47,7 +41,8 @@ VmState::VmState(Ref _code, Ref _stack, int flags, Ref _ , quit1(true, 1) , log(log) , 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()) { @@ -56,8 +51,8 @@ VmState::VmState(Ref _code, Ref _stack, int flags, Ref _ init_cregs(flags & 1, flags & 2); } -VmState::VmState(Ref _code, Ref _stack, const GasLimits& gas, int flags, Ref _data, VmLog log, - std::vector> _libraries, Ref init_c7) +VmState::VmState(Ref _code, int global_version, Ref _stack, const GasLimits& gas, int flags, + Ref _data, VmLog log, std::vector> _libraries, Ref init_c7) : code(std::move(_code)) , stack(std::move(_stack)) , cp(-1) @@ -67,7 +62,8 @@ VmState::VmState(Ref _code, Ref _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()) { @@ -106,8 +102,18 @@ Ref VmState::convert_code_cell(Ref code_cell) { if (code_cell.is_null()) { return {}; } - Ref csr{true, NoVmOrd(), code_cell}; - if (csr->is_valid()) { + Ref csr; + if (global_version >= 9) { + Guard guard(this); + try { + csr = load_cell_slice_ref(code_cell); + return csr; + } catch (VmError&) { // NOLINT(*-empty-catch) + } + } else { + csr = td::Ref{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 +583,7 @@ int run_vm_code(Ref code, Ref& stack, int flags, Ref* da GasLimits* gas_limits, std::vector> libraries, Ref init_c7, Ref* actions_ptr, int global_version) { VmState vm{code, + global_version, std::move(stack), gas_limits ? *gas_limits : GasLimits{}, flags, @@ -584,7 +591,6 @@ int run_vm_code(Ref code, Ref& stack, int flags, Ref* 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) { diff --git a/crypto/vm/vm.h b/crypto/vm/vm.h index cf5322938..c13869f27 100644 --- a/crypto/vm/vm.h +++ b/crypto/vm/vm.h @@ -164,14 +164,14 @@ class VmState final : public VmStateInterface { bls_pairing_element_gas_price = 11800 }; VmState(); - VmState(Ref _code); - VmState(Ref _code, Ref _stack, int flags = 0, Ref _data = {}, VmLog log = {}, + VmState(Ref _code, int global_version, Ref _stack, int flags = 0, Ref _data = {}, VmLog log = {}, std::vector> _libraries = {}, Ref init_c7 = {}); - VmState(Ref _code, Ref _stack, const GasLimits& _gas, int flags = 0, Ref _data = {}, + VmState(Ref _code, int global_version, Ref _stack, const GasLimits& _gas, int flags = 0, Ref _data = {}, VmLog log = {}, std::vector> _libraries = {}, Ref init_c7 = {}); template - VmState(Ref code_cell, Args&&... args) - : VmState(convert_code_cell(std::move(code_cell)), std::forward(args)...) { + explicit VmState(Ref code_cell, Args&&... args) + : VmState(td::Ref{}, std::forward(args)...) { + code = convert_code_cell(std::move(code_cell)); } VmState(const VmState&) = delete; VmState(VmState&&) = default; @@ -345,9 +345,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 cont); int call(Ref cont, int pass_args, int ret_args = -1); int jump(Ref cont); @@ -382,7 +379,7 @@ class VmState final : public VmStateInterface { } return res; } - static Ref convert_code_cell(Ref code_cell); + Ref convert_code_cell(Ref code_cell); bool try_commit(); void force_commit(); diff --git a/doc/GlobalVersions.md b/doc/GlobalVersions.md index 6b31e3eeb..64b2342a4 100644 --- a/doc/GlobalVersions.md +++ b/doc/GlobalVersions.md @@ -130,4 +130,5 @@ Example: if the last masterchain block seqno is `19071` then the list contains b - 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). - 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` \ No newline at end of file + - `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. \ No newline at end of file diff --git a/lite-client/lite-client.cpp b/lite-client/lite-client.cpp index dc09ae52b..1050e6d27 100644 --- a/lite-client/lite-client.cpp +++ b/lite-client/lite-client.cpp @@ -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(acc.addr->clone()), balance)); // tuple with SmartContractInfo // vm.incr_stack_trace(1); // enable stack dump after each step diff --git a/utils/opcode-timing.cpp b/utils/opcode-timing.cpp index 876ba109e..47171eec0 100644 --- a/utils/opcode-timing.cpp +++ b/utils/opcode-timing.cpp @@ -135,8 +135,8 @@ runInfo time_run_vm(td::Slice command, td::Ref 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(); diff --git a/validator/impl/liteserver.cpp b/validator/impl/liteserver.cpp index 6bd4e4219..723dbfe97 100644 --- a/validator/impl/liteserver.cpp +++ b/validator/impl/liteserver.cpp @@ -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(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 ****