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 {
// 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>();
ComputePhase& cp = *(compute_phase.get());
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;

View file

@ -27,8 +27,8 @@
namespace vm {
int Continuation::jump_w(VmState* st) & {
return static_cast<const Continuation*>(this)->jump(st);
td::Ref<Continuation> Continuation::jump_w(VmState* st, int& exitcode) & {
return static_cast<const Continuation*>(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<Continuation> 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> ExcQuitCont::deserialize(CellSlice& cs, int mode) {
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)";
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;
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> 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);
if (data.cp != -1) {
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));
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<Continuation> 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<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";
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<RepeatCont>{this});
return st->jump(body);
return body;
}
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";
if (!body->has_c0()) {
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";
if (!body->has_c0()) {
st->set_c0(Ref<AgainCont>{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<Continuation> 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";
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<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";
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<UntilCont>{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<Continuation> body, Ref<Continuation> after) {
return jump(std::move(body));
}
int WhileCont::jump(VmState* st) const & {
td::Ref<Continuation> 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<WhileCont>{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<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) {
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<WhileCont>{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<WhileCont>{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<Continuation> cond, Ref<Continuation> body, Ref<Cont
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->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->set_code(std::move(code), data.cp);
return 0;
return {};
}
bool OrdCont::serialize(CellBuilder& cb) const {

View file

@ -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<Continuation> jump(VmState* st, int& exitcode) const& = 0;
virtual td::Ref<Continuation> 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<Continuation> jump(VmState* st, int& exitcode) const& override {
exitcode = ~exit_code;
return {};
}
bool serialize(CellBuilder& cb) const override;
static Ref<QuitCont> 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<Continuation> jump(VmState* st, int& exitcode) const& override;
bool serialize(CellBuilder& cb) const override;
static Ref<ExcQuitCont> deserialize(CellSlice& cs, int mode = 0);
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() override = default;
int jump(VmState* st) const & override;
int jump_w(VmState* st) & override;
td::Ref<Continuation> jump(VmState* st, int& exitcode) const& override;
td::Ref<Continuation> jump_w(VmState* st, int& exitcode) & override;
bool serialize(CellBuilder& cb) const override;
static Ref<PushIntCont> 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<Continuation> jump(VmState* st, int& exitcode) const& override;
td::Ref<Continuation> jump_w(VmState* st, int& exitcode) & override;
bool serialize(CellBuilder& cb) const override;
static Ref<RepeatCont> deserialize(CellSlice& cs, int mode = 0);
std::string type() const override;
@ -259,8 +260,8 @@ class AgainCont : public Continuation {
AgainCont(Ref<Continuation> _body) : body(std::move(_body)) {
}
~AgainCont() override = default;
int jump(VmState* st) const & override;
int jump_w(VmState* st) & override;
td::Ref<Continuation> jump(VmState* st, int& exitcode) const& override;
td::Ref<Continuation> jump_w(VmState* st, int& exitcode) & override;
bool serialize(CellBuilder& cb) const override;
static Ref<AgainCont> deserialize(CellSlice& cs, int mode = 0);
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() override = default;
int jump(VmState* st) const & override;
int jump_w(VmState* st) & override;
td::Ref<Continuation> jump(VmState* st, int& exitcode) const& override;
td::Ref<Continuation> jump_w(VmState* st, int& exitcode) & override;
bool serialize(CellBuilder& cb) const override;
static Ref<UntilCont> 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<Continuation> jump(VmState* st, int& exitcode) const& override;
td::Ref<Continuation> jump_w(VmState* st, int& exitcode) & override;
bool serialize(CellBuilder& cb) const override;
static Ref<WhileCont> 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<Continuation> jump(VmState* st, int& exitcode) const& override;
td::Ref<Continuation> 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<Continuation> jump(VmState* st, int& exitcode) const& override;
td::Ref<Continuation> jump_w(VmState* st, int& exitcode) & override;
ControlData* get_cdata() override {
return &data;

View file

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

View file

@ -110,3 +110,9 @@ Operations for working with Merkle proofs, where cells can have non-zero level a
- 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.
## 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).