diff --git a/common/global-version.h b/common/global-version.h index a3032ebf2..533e5e8d7 100644 --- a/common/global-version.h +++ b/common/global-version.h @@ -19,6 +19,6 @@ namespace ton { // See doc/GlobalVersions.md -const int SUPPORTED_VERSION = 8; +const int SUPPORTED_VERSION = 9; } diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index dbf0199e7..9830ac86d 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -1555,7 +1555,14 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) { // ... compute_phase = std::make_unique(); ComputePhase& cp = *(compute_phase.get()); - original_balance -= total_fees; + if (cfg.global_version >= 9) { + original_balance = balance; + if (msg_balance_remaining.is_valid()) { + original_balance -= msg_balance_remaining; + } + } else { + original_balance -= total_fees; + } if (td::sgn(balance.grams) <= 0) { // no gas cp.skip_reason = ComputePhase::sk_no_gas; diff --git a/crypto/vm/continuation.cpp b/crypto/vm/continuation.cpp index 913869808..bc67c6d22 100644 --- a/crypto/vm/continuation.cpp +++ b/crypto/vm/continuation.cpp @@ -27,8 +27,8 @@ namespace vm { -int Continuation::jump_w(VmState* st) & { - return static_cast(this)->jump(st); +td::Ref Continuation::jump_w(VmState* st, int& exitcode) & { + return static_cast(this)->jump(st, exitcode); } bool Continuation::has_c0() const { @@ -286,7 +286,7 @@ std::string QuitCont::type() const { return "vmc_quit"; } -int ExcQuitCont::jump(VmState* st) const & { +td::Ref ExcQuitCont::jump(VmState* st, int& exitcode) const& { int n = 0; try { n = st->get_stack().pop_smallint_range(0xffff); @@ -294,7 +294,8 @@ int ExcQuitCont::jump(VmState* st) const & { n = vme.get_errno(); } VM_LOG(st) << "default exception handler, terminating vm with exit code " << n; - return ~n; + exitcode = ~n; + return {}; } std::string ExcQuitCont::type() const { @@ -311,16 +312,16 @@ Ref ExcQuitCont::deserialize(CellSlice& cs, int mode) { return cs.fetch_ulong(4) == 9 ? Ref{true} : Ref{}; } -int PushIntCont::jump(VmState* st) const & { +td::Ref PushIntCont::jump(VmState* st, int& exitcode) const& { VM_LOG(st) << "execute implicit PUSH " << push_val << " (slow)"; st->get_stack().push_smallint(push_val); - return st->jump(next); + return next; } -int PushIntCont::jump_w(VmState* st) & { +td::Ref PushIntCont::jump_w(VmState* st, int& exitcode) & { VM_LOG(st) << "execute implicit PUSH " << push_val; st->get_stack().push_smallint(push_val); - return st->jump(std::move(next)); + return std::move(next); } std::string PushIntCont::type() const { @@ -345,20 +346,20 @@ Ref PushIntCont::deserialize(CellSlice& cs, int mode) { } } -int ArgContExt::jump(VmState* st) const & { +td::Ref ArgContExt::jump(VmState* st, int& exitcode) const& { st->adjust_cr(data.save); if (data.cp != -1) { st->force_cp(data.cp); } - return ext->jump(st); + return ext; } -int ArgContExt::jump_w(VmState* st) & { +td::Ref ArgContExt::jump_w(VmState* st, int& exitcode) & { st->adjust_cr(std::move(data.save)); if (data.cp != -1) { st->force_cp(data.cp); } - return st->jump_to(std::move(ext)); + return std::move(ext); } bool ArgContExt::serialize(CellBuilder& cb) const { @@ -382,32 +383,32 @@ std::string ArgContExt::type() const { return "vmc_envelope"; } -int RepeatCont::jump(VmState* st) const & { +td::Ref RepeatCont::jump(VmState* st, int& exitcode) const& { VM_LOG(st) << "repeat " << count << " more times (slow)\n"; if (count <= 0) { - return st->jump(after); + return after; } if (body->has_c0()) { - return st->jump(body); + return body; } st->set_c0(Ref{true, body, after, count - 1}); - return st->jump(body); + return body; } -int RepeatCont::jump_w(VmState* st) & { +td::Ref RepeatCont::jump_w(VmState* st, int& exitcode) & { VM_LOG(st) << "repeat " << count << " more times\n"; if (count <= 0) { body.clear(); - return st->jump(std::move(after)); + return std::move(after); } if (body->has_c0()) { after.clear(); - return st->jump(std::move(body)); + return std::move(body); } // optimization: since this is unique, reuse *this instead of creating new object --count; st->set_c0(Ref{this}); - return st->jump(body); + return body; } bool RepeatCont::serialize(CellBuilder& cb) const { @@ -443,21 +444,21 @@ int VmState::repeat(Ref body, Ref after, long long c } } -int AgainCont::jump(VmState* st) const & { +td::Ref AgainCont::jump(VmState* st, int& exitcode) const& { VM_LOG(st) << "again an infinite loop iteration (slow)\n"; if (!body->has_c0()) { st->set_c0(Ref{this}); } - return st->jump(body); + return body; } -int AgainCont::jump_w(VmState* st) & { +td::Ref AgainCont::jump_w(VmState* st, int& exitcode) & { VM_LOG(st) << "again an infinite loop iteration\n"; if (!body->has_c0()) { st->set_c0(Ref{this}); - return st->jump(body); + return body; } else { - return st->jump(std::move(body)); + return std::move(body); } } @@ -485,31 +486,31 @@ int VmState::again(Ref body) { return jump(Ref{true, std::move(body)}); } -int UntilCont::jump(VmState* st) const & { +td::Ref UntilCont::jump(VmState* st, int& exitcode) const& { VM_LOG(st) << "until loop body end (slow)\n"; if (st->get_stack().pop_bool()) { VM_LOG(st) << "until loop terminated\n"; - return st->jump(after); + return after; } if (!body->has_c0()) { st->set_c0(Ref{this}); } - return st->jump(body); + return body; } -int UntilCont::jump_w(VmState* st) & { +td::Ref UntilCont::jump_w(VmState* st, int& exitcode) & { VM_LOG(st) << "until loop body end\n"; if (st->get_stack().pop_bool()) { VM_LOG(st) << "until loop terminated\n"; body.clear(); - return st->jump(std::move(after)); + return std::move(after); } if (!body->has_c0()) { st->set_c0(Ref{this}); - return st->jump(body); + return body; } else { after.clear(); - return st->jump(std::move(body)); + return std::move(body); } } @@ -541,54 +542,54 @@ int VmState::until(Ref body, Ref after) { return jump(std::move(body)); } -int WhileCont::jump(VmState* st) const & { +td::Ref WhileCont::jump(VmState* st, int& exitcode) const& { if (chkcond) { VM_LOG(st) << "while loop condition end (slow)\n"; if (!st->get_stack().pop_bool()) { VM_LOG(st) << "while loop terminated\n"; - return st->jump(after); + return after; } if (!body->has_c0()) { st->set_c0(Ref{true, cond, body, after, false}); } - return st->jump(body); + return body; } else { VM_LOG(st) << "while loop body end (slow)\n"; if (!cond->has_c0()) { st->set_c0(Ref{true, cond, body, after, true}); } - return st->jump(cond); + return cond; } } -int WhileCont::jump_w(VmState* st) & { +td::Ref WhileCont::jump_w(VmState* st, int& exitcode) & { if (chkcond) { VM_LOG(st) << "while loop condition end\n"; if (!st->get_stack().pop_bool()) { VM_LOG(st) << "while loop terminated\n"; cond.clear(); body.clear(); - return st->jump(std::move(after)); + return std::move(after); } if (!body->has_c0()) { chkcond = false; // re-use current object since we hold the unique pointer to it st->set_c0(Ref{this}); - return st->jump(body); + return body; } else { cond.clear(); after.clear(); - return st->jump(std::move(body)); + return std::move(body); } } else { VM_LOG(st) << "while loop body end\n"; if (!cond->has_c0()) { chkcond = true; // re-use current object st->set_c0(Ref{this}); - return st->jump(cond); + return cond; } else { body.clear(); after.clear(); - return st->jump(std::move(cond)); + return std::move(cond); } } } @@ -627,16 +628,16 @@ int VmState::loop_while(Ref cond, Ref body, Ref OrdCont::jump(VmState* st, int& exitcode) const& { st->adjust_cr(data.save); st->set_code(code, data.cp); - return 0; + return {}; } -int OrdCont::jump_w(VmState* st) & { +td::Ref OrdCont::jump_w(VmState* st, int& exitcode) & { st->adjust_cr(std::move(data.save)); st->set_code(std::move(code), data.cp); - return 0; + return {}; } bool OrdCont::serialize(CellBuilder& cb) const { diff --git a/crypto/vm/continuation.h b/crypto/vm/continuation.h index 8208fc16a..0c758c922 100644 --- a/crypto/vm/continuation.h +++ b/crypto/vm/continuation.h @@ -161,8 +161,8 @@ struct ControlData { class Continuation : public td::CntObject { public: - virtual int jump(VmState* st) const & = 0; - virtual int jump_w(VmState* st) &; + virtual td::Ref jump(VmState* st, int& exitcode) const& = 0; + virtual td::Ref jump_w(VmState* st, int& exitcode) &; virtual ControlData* get_cdata() { return 0; } @@ -203,8 +203,9 @@ class QuitCont : public Continuation { QuitCont(int _code = 0) : exit_code(_code) { } ~QuitCont() override = default; - int jump(VmState* st) const & override { - return ~exit_code; + td::Ref jump(VmState* st, int& exitcode) const& override { + exitcode = ~exit_code; + return {}; } bool serialize(CellBuilder& cb) const override; static Ref deserialize(CellSlice& cs, int mode = 0); @@ -215,7 +216,7 @@ class ExcQuitCont : public Continuation { public: ExcQuitCont() = default; ~ExcQuitCont() override = default; - int jump(VmState* st) const & override; + td::Ref jump(VmState* st, int& exitcode) const& override; bool serialize(CellBuilder& cb) const override; static Ref deserialize(CellSlice& cs, int mode = 0); std::string type() const override; @@ -229,8 +230,8 @@ class PushIntCont : public Continuation { PushIntCont(int val, Ref _next) : push_val(val), next(_next) { } ~PushIntCont() override = default; - int jump(VmState* st) const & override; - int jump_w(VmState* st) & override; + td::Ref jump(VmState* st, int& exitcode) const& override; + td::Ref jump_w(VmState* st, int& exitcode) & override; bool serialize(CellBuilder& cb) const override; static Ref deserialize(CellSlice& cs, int mode = 0); std::string type() const override; @@ -245,8 +246,8 @@ class RepeatCont : public Continuation { : body(std::move(_body)), after(std::move(_after)), count(_count) { } ~RepeatCont() override = default; - int jump(VmState* st) const & override; - int jump_w(VmState* st) & override; + td::Ref jump(VmState* st, int& exitcode) const& override; + td::Ref jump_w(VmState* st, int& exitcode) & override; bool serialize(CellBuilder& cb) const override; static Ref deserialize(CellSlice& cs, int mode = 0); std::string type() const override; @@ -259,8 +260,8 @@ class AgainCont : public Continuation { AgainCont(Ref _body) : body(std::move(_body)) { } ~AgainCont() override = default; - int jump(VmState* st) const & override; - int jump_w(VmState* st) & override; + td::Ref jump(VmState* st, int& exitcode) const& override; + td::Ref jump_w(VmState* st, int& exitcode) & override; bool serialize(CellBuilder& cb) const override; static Ref deserialize(CellSlice& cs, int mode = 0); std::string type() const override; @@ -273,8 +274,8 @@ class UntilCont : public Continuation { UntilCont(Ref _body, Ref _after) : body(std::move(_body)), after(std::move(_after)) { } ~UntilCont() override = default; - int jump(VmState* st) const & override; - int jump_w(VmState* st) & override; + td::Ref jump(VmState* st, int& exitcode) const& override; + td::Ref jump_w(VmState* st, int& exitcode) & override; bool serialize(CellBuilder& cb) const override; static Ref deserialize(CellSlice& cs, int mode = 0); std::string type() const override; @@ -289,8 +290,8 @@ class WhileCont : public Continuation { : cond(std::move(_cond)), body(std::move(_body)), after(std::move(_after)), chkcond(_chk) { } ~WhileCont() override = default; - int jump(VmState* st) const & override; - int jump_w(VmState* st) & override; + td::Ref jump(VmState* st, int& exitcode) const& override; + td::Ref jump_w(VmState* st, int& exitcode) & override; bool serialize(CellBuilder& cb) const override; static Ref deserialize(CellSlice& cs, int mode = 0); std::string type() const override; @@ -312,8 +313,8 @@ class ArgContExt : public Continuation { ArgContExt(const ArgContExt&) = default; ArgContExt(ArgContExt&&) = default; ~ArgContExt() override = default; - int jump(VmState* st) const & override; - int jump_w(VmState* st) & override; + td::Ref jump(VmState* st, int& exitcode) const& override; + td::Ref jump_w(VmState* st, int& exitcode) & override; ControlData* get_cdata() override { return &data; } @@ -354,8 +355,8 @@ class OrdCont : public Continuation { td::CntObject* make_copy() const override { return new OrdCont{*this}; } - int jump(VmState* st) const & override; - int jump_w(VmState* st) & override; + td::Ref jump(VmState* st, int& exitcode) const& override; + td::Ref jump_w(VmState* st, int& exitcode) & override; ControlData* get_cdata() override { return &data; diff --git a/crypto/vm/vm.h b/crypto/vm/vm.h index e5cca026c..f202e55fa 100644 --- a/crypto/vm/vm.h +++ b/crypto/vm/vm.h @@ -118,6 +118,7 @@ class VmState final : public VmStateInterface { stack_entry_gas_price = 1, runvm_gas_price = 40, hash_ext_entry_gas_price = 1, + free_nested_cont_jump = 8, rist255_mul_gas_price = 2000, rist255_mulbase_gas_price = 750, @@ -366,11 +367,19 @@ class VmState final : public VmStateInterface { return cond ? c1_envelope(std::move(cont), save) : std::move(cont); } void c1_save_set(bool save = true); - void fatal(void) const { + void fatal() const { throw VmFatal{}; } int jump_to(Ref cont) { - return cont->is_unique() ? cont.unique_write().jump_w(this) : cont->jump(this); + int res = 0, cnt = 0; + while (cont.not_null()) { + cnt++; + cont = cont->is_unique() ? cont.unique_write().jump_w(this, res) : cont->jump(this, res); + } + if (global_version >= 9 && cnt > free_nested_cont_jump) { + consume_gas(cnt - free_nested_cont_jump); + } + return res; } static Ref convert_code_cell(Ref code_cell); bool try_commit(); diff --git a/doc/GlobalVersions.md b/doc/GlobalVersions.md index e649c009a..36d1ab360 100644 --- a/doc/GlobalVersions.md +++ b/doc/GlobalVersions.md @@ -109,4 +109,10 @@ Operations for working with Merkle proofs, where cells can have non-zero level a - Slightly change random seed generation to fix mix of `addr_rewrite` and `addr`. - Fill in `skipped_actions` for both invalid and valid messages with `IGNORE_ERROR` mode that can't be sent. - Allow unfreeze through external messages. -- Don't use user-provided `fwd_fee` and `ihr_fee` for internal messages. \ No newline at end of file +- Don't use user-provided `fwd_fee` and `ihr_fee` for internal messages. + +## Version 9 + +- 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). \ No newline at end of file