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

Merge pull request #1202 from ton-blockchain/tvm-patch

Fix transaction original_balance and VmState::jump_to
This commit is contained in:
EmelyanenkoK 2024-09-23 21:10:57 +03:00 committed by GitHub
commit b78199370e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 94 additions and 70 deletions

View file

@ -19,6 +19,6 @@
namespace ton { namespace ton {
// See doc/GlobalVersions.md // See doc/GlobalVersions.md
const int SUPPORTED_VERSION = 8; const int SUPPORTED_VERSION = 9;
} }

View file

@ -1555,7 +1555,14 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
// ... // ...
compute_phase = std::make_unique<ComputePhase>(); compute_phase = std::make_unique<ComputePhase>();
ComputePhase& cp = *(compute_phase.get()); 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) { if (td::sgn(balance.grams) <= 0) {
// no gas // no gas
cp.skip_reason = ComputePhase::sk_no_gas; cp.skip_reason = ComputePhase::sk_no_gas;

View file

@ -27,8 +27,8 @@
namespace vm { namespace vm {
int Continuation::jump_w(VmState* st) & { td::Ref<Continuation> Continuation::jump_w(VmState* st, int& exitcode) & {
return static_cast<const Continuation*>(this)->jump(st); return static_cast<const Continuation*>(this)->jump(st, exitcode);
} }
bool Continuation::has_c0() const { bool Continuation::has_c0() const {
@ -286,7 +286,7 @@ std::string QuitCont::type() const {
return "vmc_quit"; return "vmc_quit";
} }
int ExcQuitCont::jump(VmState* st) const & { td::Ref<Continuation> ExcQuitCont::jump(VmState* st, int& exitcode) const& {
int n = 0; int n = 0;
try { try {
n = st->get_stack().pop_smallint_range(0xffff); n = st->get_stack().pop_smallint_range(0xffff);
@ -294,7 +294,8 @@ int ExcQuitCont::jump(VmState* st) const & {
n = vme.get_errno(); n = vme.get_errno();
} }
VM_LOG(st) << "default exception handler, terminating vm with exit code " << n; VM_LOG(st) << "default exception handler, terminating vm with exit code " << n;
return ~n; exitcode = ~n;
return {};
} }
std::string ExcQuitCont::type() const { std::string ExcQuitCont::type() const {
@ -311,16 +312,16 @@ Ref<ExcQuitCont> ExcQuitCont::deserialize(CellSlice& cs, int mode) {
return cs.fetch_ulong(4) == 9 ? Ref<ExcQuitCont>{true} : Ref<ExcQuitCont>{}; return cs.fetch_ulong(4) == 9 ? Ref<ExcQuitCont>{true} : Ref<ExcQuitCont>{};
} }
int PushIntCont::jump(VmState* st) const & { td::Ref<Continuation> PushIntCont::jump(VmState* st, int& exitcode) const& {
VM_LOG(st) << "execute implicit PUSH " << push_val << " (slow)"; VM_LOG(st) << "execute implicit PUSH " << push_val << " (slow)";
st->get_stack().push_smallint(push_val); st->get_stack().push_smallint(push_val);
return st->jump(next); return next;
} }
int PushIntCont::jump_w(VmState* st) & { td::Ref<Continuation> PushIntCont::jump_w(VmState* st, int& exitcode) & {
VM_LOG(st) << "execute implicit PUSH " << push_val; VM_LOG(st) << "execute implicit PUSH " << push_val;
st->get_stack().push_smallint(push_val); st->get_stack().push_smallint(push_val);
return st->jump(std::move(next)); return std::move(next);
} }
std::string PushIntCont::type() const { std::string PushIntCont::type() const {
@ -345,20 +346,20 @@ Ref<PushIntCont> PushIntCont::deserialize(CellSlice& cs, int mode) {
} }
} }
int ArgContExt::jump(VmState* st) const & { td::Ref<Continuation> ArgContExt::jump(VmState* st, int& exitcode) const& {
st->adjust_cr(data.save); st->adjust_cr(data.save);
if (data.cp != -1) { if (data.cp != -1) {
st->force_cp(data.cp); st->force_cp(data.cp);
} }
return ext->jump(st); return ext;
} }
int ArgContExt::jump_w(VmState* st) & { td::Ref<Continuation> ArgContExt::jump_w(VmState* st, int& exitcode) & {
st->adjust_cr(std::move(data.save)); st->adjust_cr(std::move(data.save));
if (data.cp != -1) { if (data.cp != -1) {
st->force_cp(data.cp); st->force_cp(data.cp);
} }
return st->jump_to(std::move(ext)); return std::move(ext);
} }
bool ArgContExt::serialize(CellBuilder& cb) const { bool ArgContExt::serialize(CellBuilder& cb) const {
@ -382,32 +383,32 @@ std::string ArgContExt::type() const {
return "vmc_envelope"; return "vmc_envelope";
} }
int RepeatCont::jump(VmState* st) const & { td::Ref<Continuation> RepeatCont::jump(VmState* st, int& exitcode) const& {
VM_LOG(st) << "repeat " << count << " more times (slow)\n"; VM_LOG(st) << "repeat " << count << " more times (slow)\n";
if (count <= 0) { if (count <= 0) {
return st->jump(after); return after;
} }
if (body->has_c0()) { if (body->has_c0()) {
return st->jump(body); return body;
} }
st->set_c0(Ref<RepeatCont>{true, body, after, count - 1}); st->set_c0(Ref<RepeatCont>{true, body, after, count - 1});
return st->jump(body); return body;
} }
int RepeatCont::jump_w(VmState* st) & { td::Ref<Continuation> RepeatCont::jump_w(VmState* st, int& exitcode) & {
VM_LOG(st) << "repeat " << count << " more times\n"; VM_LOG(st) << "repeat " << count << " more times\n";
if (count <= 0) { if (count <= 0) {
body.clear(); body.clear();
return st->jump(std::move(after)); return std::move(after);
} }
if (body->has_c0()) { if (body->has_c0()) {
after.clear(); after.clear();
return st->jump(std::move(body)); return std::move(body);
} }
// optimization: since this is unique, reuse *this instead of creating new object // optimization: since this is unique, reuse *this instead of creating new object
--count; --count;
st->set_c0(Ref<RepeatCont>{this}); st->set_c0(Ref<RepeatCont>{this});
return st->jump(body); return body;
} }
bool RepeatCont::serialize(CellBuilder& cb) const { bool RepeatCont::serialize(CellBuilder& cb) const {
@ -443,21 +444,21 @@ int VmState::repeat(Ref<Continuation> body, Ref<Continuation> after, long long c
} }
} }
int AgainCont::jump(VmState* st) const & { td::Ref<Continuation> AgainCont::jump(VmState* st, int& exitcode) const& {
VM_LOG(st) << "again an infinite loop iteration (slow)\n"; VM_LOG(st) << "again an infinite loop iteration (slow)\n";
if (!body->has_c0()) { if (!body->has_c0()) {
st->set_c0(Ref<AgainCont>{this}); st->set_c0(Ref<AgainCont>{this});
} }
return st->jump(body); return body;
} }
int AgainCont::jump_w(VmState* st) & { td::Ref<Continuation> AgainCont::jump_w(VmState* st, int& exitcode) & {
VM_LOG(st) << "again an infinite loop iteration\n"; VM_LOG(st) << "again an infinite loop iteration\n";
if (!body->has_c0()) { if (!body->has_c0()) {
st->set_c0(Ref<AgainCont>{this}); st->set_c0(Ref<AgainCont>{this});
return st->jump(body); return body;
} else { } else {
return st->jump(std::move(body)); return std::move(body);
} }
} }
@ -485,31 +486,31 @@ int VmState::again(Ref<Continuation> body) {
return jump(Ref<AgainCont>{true, std::move(body)}); return jump(Ref<AgainCont>{true, std::move(body)});
} }
int UntilCont::jump(VmState* st) const & { td::Ref<Continuation> UntilCont::jump(VmState* st, int& exitcode) const& {
VM_LOG(st) << "until loop body end (slow)\n"; VM_LOG(st) << "until loop body end (slow)\n";
if (st->get_stack().pop_bool()) { if (st->get_stack().pop_bool()) {
VM_LOG(st) << "until loop terminated\n"; VM_LOG(st) << "until loop terminated\n";
return st->jump(after); return after;
} }
if (!body->has_c0()) { if (!body->has_c0()) {
st->set_c0(Ref<UntilCont>{this}); st->set_c0(Ref<UntilCont>{this});
} }
return st->jump(body); return body;
} }
int UntilCont::jump_w(VmState* st) & { td::Ref<Continuation> UntilCont::jump_w(VmState* st, int& exitcode) & {
VM_LOG(st) << "until loop body end\n"; VM_LOG(st) << "until loop body end\n";
if (st->get_stack().pop_bool()) { if (st->get_stack().pop_bool()) {
VM_LOG(st) << "until loop terminated\n"; VM_LOG(st) << "until loop terminated\n";
body.clear(); body.clear();
return st->jump(std::move(after)); return std::move(after);
} }
if (!body->has_c0()) { if (!body->has_c0()) {
st->set_c0(Ref<UntilCont>{this}); st->set_c0(Ref<UntilCont>{this});
return st->jump(body); return body;
} else { } else {
after.clear(); after.clear();
return st->jump(std::move(body)); return std::move(body);
} }
} }
@ -541,54 +542,54 @@ int VmState::until(Ref<Continuation> body, Ref<Continuation> after) {
return jump(std::move(body)); return jump(std::move(body));
} }
int WhileCont::jump(VmState* st) const & { td::Ref<Continuation> WhileCont::jump(VmState* st, int& exitcode) const& {
if (chkcond) { if (chkcond) {
VM_LOG(st) << "while loop condition end (slow)\n"; VM_LOG(st) << "while loop condition end (slow)\n";
if (!st->get_stack().pop_bool()) { if (!st->get_stack().pop_bool()) {
VM_LOG(st) << "while loop terminated\n"; VM_LOG(st) << "while loop terminated\n";
return st->jump(after); return after;
} }
if (!body->has_c0()) { if (!body->has_c0()) {
st->set_c0(Ref<WhileCont>{true, cond, body, after, false}); st->set_c0(Ref<WhileCont>{true, cond, body, after, false});
} }
return st->jump(body); return body;
} else { } else {
VM_LOG(st) << "while loop body end (slow)\n"; VM_LOG(st) << "while loop body end (slow)\n";
if (!cond->has_c0()) { if (!cond->has_c0()) {
st->set_c0(Ref<WhileCont>{true, cond, body, after, true}); st->set_c0(Ref<WhileCont>{true, cond, body, after, true});
} }
return st->jump(cond); return cond;
} }
} }
int WhileCont::jump_w(VmState* st) & { td::Ref<Continuation> WhileCont::jump_w(VmState* st, int& exitcode) & {
if (chkcond) { if (chkcond) {
VM_LOG(st) << "while loop condition end\n"; VM_LOG(st) << "while loop condition end\n";
if (!st->get_stack().pop_bool()) { if (!st->get_stack().pop_bool()) {
VM_LOG(st) << "while loop terminated\n"; VM_LOG(st) << "while loop terminated\n";
cond.clear(); cond.clear();
body.clear(); body.clear();
return st->jump(std::move(after)); return std::move(after);
} }
if (!body->has_c0()) { if (!body->has_c0()) {
chkcond = false; // re-use current object since we hold the unique pointer to it chkcond = false; // re-use current object since we hold the unique pointer to it
st->set_c0(Ref<WhileCont>{this}); st->set_c0(Ref<WhileCont>{this});
return st->jump(body); return body;
} else { } else {
cond.clear(); cond.clear();
after.clear(); after.clear();
return st->jump(std::move(body)); return std::move(body);
} }
} else { } else {
VM_LOG(st) << "while loop body end\n"; VM_LOG(st) << "while loop body end\n";
if (!cond->has_c0()) { if (!cond->has_c0()) {
chkcond = true; // re-use current object chkcond = true; // re-use current object
st->set_c0(Ref<WhileCont>{this}); st->set_c0(Ref<WhileCont>{this});
return st->jump(cond); return cond;
} else { } else {
body.clear(); body.clear();
after.clear(); after.clear();
return st->jump(std::move(cond)); return std::move(cond);
} }
} }
} }
@ -627,16 +628,16 @@ int VmState::loop_while(Ref<Continuation> cond, Ref<Continuation> body, Ref<Cont
return jump(std::move(cond)); return jump(std::move(cond));
} }
int OrdCont::jump(VmState* st) const & { td::Ref<Continuation> OrdCont::jump(VmState* st, int& exitcode) const& {
st->adjust_cr(data.save); st->adjust_cr(data.save);
st->set_code(code, data.cp); st->set_code(code, data.cp);
return 0; return {};
} }
int OrdCont::jump_w(VmState* st) & { td::Ref<Continuation> OrdCont::jump_w(VmState* st, int& exitcode) & {
st->adjust_cr(std::move(data.save)); st->adjust_cr(std::move(data.save));
st->set_code(std::move(code), data.cp); st->set_code(std::move(code), data.cp);
return 0; return {};
} }
bool OrdCont::serialize(CellBuilder& cb) const { bool OrdCont::serialize(CellBuilder& cb) const {

View file

@ -161,8 +161,8 @@ struct ControlData {
class Continuation : public td::CntObject { class Continuation : public td::CntObject {
public: public:
virtual int jump(VmState* st) const & = 0; virtual td::Ref<Continuation> jump(VmState* st, int& exitcode) const& = 0;
virtual int jump_w(VmState* st) &; virtual td::Ref<Continuation> jump_w(VmState* st, int& exitcode) &;
virtual ControlData* get_cdata() { virtual ControlData* get_cdata() {
return 0; return 0;
} }
@ -203,8 +203,9 @@ class QuitCont : public Continuation {
QuitCont(int _code = 0) : exit_code(_code) { QuitCont(int _code = 0) : exit_code(_code) {
} }
~QuitCont() override = default; ~QuitCont() override = default;
int jump(VmState* st) const & override { td::Ref<Continuation> jump(VmState* st, int& exitcode) const& override {
return ~exit_code; exitcode = ~exit_code;
return {};
} }
bool serialize(CellBuilder& cb) const override; bool serialize(CellBuilder& cb) const override;
static Ref<QuitCont> deserialize(CellSlice& cs, int mode = 0); static Ref<QuitCont> deserialize(CellSlice& cs, int mode = 0);
@ -215,7 +216,7 @@ class ExcQuitCont : public Continuation {
public: public:
ExcQuitCont() = default; ExcQuitCont() = default;
~ExcQuitCont() override = default; ~ExcQuitCont() override = default;
int jump(VmState* st) const & override; td::Ref<Continuation> jump(VmState* st, int& exitcode) const& override;
bool serialize(CellBuilder& cb) const override; bool serialize(CellBuilder& cb) const override;
static Ref<ExcQuitCont> deserialize(CellSlice& cs, int mode = 0); static Ref<ExcQuitCont> deserialize(CellSlice& cs, int mode = 0);
std::string type() const override; std::string type() const override;
@ -229,8 +230,8 @@ class PushIntCont : public Continuation {
PushIntCont(int val, Ref<Continuation> _next) : push_val(val), next(_next) { PushIntCont(int val, Ref<Continuation> _next) : push_val(val), next(_next) {
} }
~PushIntCont() override = default; ~PushIntCont() override = default;
int jump(VmState* st) const & override; td::Ref<Continuation> jump(VmState* st, int& exitcode) const& override;
int jump_w(VmState* st) & override; td::Ref<Continuation> jump_w(VmState* st, int& exitcode) & override;
bool serialize(CellBuilder& cb) const override; bool serialize(CellBuilder& cb) const override;
static Ref<PushIntCont> deserialize(CellSlice& cs, int mode = 0); static Ref<PushIntCont> deserialize(CellSlice& cs, int mode = 0);
std::string type() const override; std::string type() const override;
@ -245,8 +246,8 @@ class RepeatCont : public Continuation {
: body(std::move(_body)), after(std::move(_after)), count(_count) { : body(std::move(_body)), after(std::move(_after)), count(_count) {
} }
~RepeatCont() override = default; ~RepeatCont() override = default;
int jump(VmState* st) const & override; td::Ref<Continuation> jump(VmState* st, int& exitcode) const& override;
int jump_w(VmState* st) & override; td::Ref<Continuation> jump_w(VmState* st, int& exitcode) & override;
bool serialize(CellBuilder& cb) const override; bool serialize(CellBuilder& cb) const override;
static Ref<RepeatCont> deserialize(CellSlice& cs, int mode = 0); static Ref<RepeatCont> deserialize(CellSlice& cs, int mode = 0);
std::string type() const override; std::string type() const override;
@ -259,8 +260,8 @@ class AgainCont : public Continuation {
AgainCont(Ref<Continuation> _body) : body(std::move(_body)) { AgainCont(Ref<Continuation> _body) : body(std::move(_body)) {
} }
~AgainCont() override = default; ~AgainCont() override = default;
int jump(VmState* st) const & override; td::Ref<Continuation> jump(VmState* st, int& exitcode) const& override;
int jump_w(VmState* st) & override; td::Ref<Continuation> jump_w(VmState* st, int& exitcode) & override;
bool serialize(CellBuilder& cb) const override; bool serialize(CellBuilder& cb) const override;
static Ref<AgainCont> deserialize(CellSlice& cs, int mode = 0); static Ref<AgainCont> deserialize(CellSlice& cs, int mode = 0);
std::string type() const override; std::string type() const override;
@ -273,8 +274,8 @@ class UntilCont : public Continuation {
UntilCont(Ref<Continuation> _body, Ref<Continuation> _after) : body(std::move(_body)), after(std::move(_after)) { UntilCont(Ref<Continuation> _body, Ref<Continuation> _after) : body(std::move(_body)), after(std::move(_after)) {
} }
~UntilCont() override = default; ~UntilCont() override = default;
int jump(VmState* st) const & override; td::Ref<Continuation> jump(VmState* st, int& exitcode) const& override;
int jump_w(VmState* st) & override; td::Ref<Continuation> jump_w(VmState* st, int& exitcode) & override;
bool serialize(CellBuilder& cb) const override; bool serialize(CellBuilder& cb) const override;
static Ref<UntilCont> deserialize(CellSlice& cs, int mode = 0); static Ref<UntilCont> deserialize(CellSlice& cs, int mode = 0);
std::string type() const override; 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) { : cond(std::move(_cond)), body(std::move(_body)), after(std::move(_after)), chkcond(_chk) {
} }
~WhileCont() override = default; ~WhileCont() override = default;
int jump(VmState* st) const & override; td::Ref<Continuation> jump(VmState* st, int& exitcode) const& override;
int jump_w(VmState* st) & override; td::Ref<Continuation> jump_w(VmState* st, int& exitcode) & override;
bool serialize(CellBuilder& cb) const override; bool serialize(CellBuilder& cb) const override;
static Ref<WhileCont> deserialize(CellSlice& cs, int mode = 0); static Ref<WhileCont> deserialize(CellSlice& cs, int mode = 0);
std::string type() const override; std::string type() const override;
@ -312,8 +313,8 @@ class ArgContExt : public Continuation {
ArgContExt(const ArgContExt&) = default; ArgContExt(const ArgContExt&) = default;
ArgContExt(ArgContExt&&) = default; ArgContExt(ArgContExt&&) = default;
~ArgContExt() override = default; ~ArgContExt() override = default;
int jump(VmState* st) const & override; td::Ref<Continuation> jump(VmState* st, int& exitcode) const& override;
int jump_w(VmState* st) & override; td::Ref<Continuation> jump_w(VmState* st, int& exitcode) & override;
ControlData* get_cdata() override { ControlData* get_cdata() override {
return &data; return &data;
} }
@ -354,8 +355,8 @@ class OrdCont : public Continuation {
td::CntObject* make_copy() const override { td::CntObject* make_copy() const override {
return new OrdCont{*this}; return new OrdCont{*this};
} }
int jump(VmState* st) const & override; td::Ref<Continuation> jump(VmState* st, int& exitcode) const& override;
int jump_w(VmState* st) & override; td::Ref<Continuation> jump_w(VmState* st, int& exitcode) & override;
ControlData* get_cdata() override { ControlData* get_cdata() override {
return &data; return &data;

View file

@ -118,6 +118,7 @@ class VmState final : public VmStateInterface {
stack_entry_gas_price = 1, stack_entry_gas_price = 1,
runvm_gas_price = 40, runvm_gas_price = 40,
hash_ext_entry_gas_price = 1, hash_ext_entry_gas_price = 1,
free_nested_cont_jump = 8,
rist255_mul_gas_price = 2000, rist255_mul_gas_price = 2000,
rist255_mulbase_gas_price = 750, 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); return cond ? c1_envelope(std::move(cont), save) : std::move(cont);
} }
void c1_save_set(bool save = true); void c1_save_set(bool save = true);
void fatal(void) const { void fatal() const {
throw VmFatal{}; throw VmFatal{};
} }
int jump_to(Ref<Continuation> cont) { int jump_to(Ref<Continuation> 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<CellSlice> convert_code_cell(Ref<Cell> code_cell); static Ref<CellSlice> convert_code_cell(Ref<Cell> code_cell);
bool try_commit(); bool try_commit();

View file

@ -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`. - 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. - Fill in `skipped_actions` for both invalid and valid messages with `IGNORE_ERROR` mode that can't be sent.
- Allow unfreeze through external messages. - Allow unfreeze through external messages.
- Don't use user-provided `fwd_fee` and `ihr_fee` for internal messages. - 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).