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:
commit
b78199370e
6 changed files with 94 additions and 70 deletions
|
@ -19,6 +19,6 @@
|
|||
namespace ton {
|
||||
|
||||
// See doc/GlobalVersions.md
|
||||
const int SUPPORTED_VERSION = 8;
|
||||
const int SUPPORTED_VERSION = 9;
|
||||
|
||||
}
|
||||
|
|
|
@ -1555,7 +1555,14 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
|
|||
// ...
|
||||
compute_phase = std::make_unique<ComputePhase>();
|
||||
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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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).
|
Loading…
Reference in a new issue