From 62838571ebbb874985ec78146eee9c37868a2c03 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Wed, 15 Jan 2025 07:43:33 +0000 Subject: [PATCH] Support extra currencies in reserve action with +2 flag (#1429) * Support extra currencies in reserve action with +2 flag * Enable new reserve behavior in version 9 --- crypto/block/block.cpp | 30 ++++++++++++++++++++++++++++++ crypto/block/block.h | 1 + crypto/block/transaction.cpp | 22 +++++++++++++--------- crypto/block/transaction.h | 1 + doc/GlobalVersions.md | 3 ++- validator/impl/validate-query.cpp | 1 + 6 files changed, 48 insertions(+), 10 deletions(-) diff --git a/crypto/block/block.cpp b/crypto/block/block.cpp index 98546a2d4..302a2aa46 100644 --- a/crypto/block/block.cpp +++ b/crypto/block/block.cpp @@ -1319,6 +1319,36 @@ CurrencyCollection CurrencyCollection::operator-(td::RefInt256 other_grams) cons } } +bool CurrencyCollection::clamp(const CurrencyCollection& other) { + if (!is_valid() || !other.is_valid()) { + return invalidate(); + } + grams = std::min(grams, other.grams); + vm::Dictionary dict1{extra, 32}, dict2(other.extra, 32); + bool ok = dict1.check_for_each([&](td::Ref cs1, td::ConstBitPtr key, int n) { + CHECK(n == 32); + td::Ref cs2 = dict2.lookup(key, 32); + td::RefInt256 val1 = tlb::t_VarUIntegerPos_32.as_integer(cs1); + if (val1.is_null()) { + return false; + } + td::RefInt256 val2 = cs2.is_null() ? td::zero_refint() : tlb::t_VarUIntegerPos_32.as_integer(cs2); + if (val2.is_null()) { + return false; + } + if (val1 > val2) { + if (val2->sgn() == 0) { + dict1.lookup_delete(key, 32); + } else { + dict1.set(key, 32, cs2); + } + } + return true; + }); + extra = dict1.get_root_cell(); + return ok || invalidate(); +} + bool CurrencyCollection::operator==(const CurrencyCollection& other) const { return is_valid() && other.is_valid() && !td::cmp(grams, other.grams) && (extra.not_null() == other.extra.not_null()) && diff --git a/crypto/block/block.h b/crypto/block/block.h index 56e6dd38a..f64f00a87 100644 --- a/crypto/block/block.h +++ b/crypto/block/block.h @@ -390,6 +390,7 @@ struct CurrencyCollection { CurrencyCollection operator-(const CurrencyCollection& other) const; CurrencyCollection operator-(CurrencyCollection&& other) const; CurrencyCollection operator-(td::RefInt256 other_grams) const; + bool clamp(const CurrencyCollection& other); bool store(vm::CellBuilder& cb) const; bool store_or_zero(vm::CellBuilder& cb) const; bool fetch(vm::CellSlice& cs); diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index a32bad52f..49325957d 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -2760,22 +2760,25 @@ int Transaction::try_action_reserve_currency(vm::CellSlice& cs, ActionPhase& ap, LOG(DEBUG) << "cannot reserve a negative amount: " << reserve.to_str(); return -1; } - if (reserve.grams > ap.remaining_balance.grams) { - if (mode & 2) { - reserve.grams = ap.remaining_balance.grams; + if (mode & 2) { + if (cfg.reserve_extra_enabled) { + if (!reserve.clamp(ap.remaining_balance)) { + LOG(DEBUG) << "failed to clamp reserve amount" << mode; + return -1; + } } else { - LOG(DEBUG) << "cannot reserve " << reserve.grams << " nanograms : only " << ap.remaining_balance.grams - << " available"; - return 37; // not enough grams + reserve.grams = std::min(reserve.grams, ap.remaining_balance.grams); } } + if (reserve.grams > ap.remaining_balance.grams) { + LOG(DEBUG) << "cannot reserve " << reserve.grams << " nanograms : only " << ap.remaining_balance.grams + << " available"; + return 37; // not enough grams + } if (!block::sub_extra_currency(ap.remaining_balance.extra, reserve.extra, newc.extra)) { LOG(DEBUG) << "not enough extra currency to reserve: " << block::CurrencyCollection{0, reserve.extra}.to_str() << " required, only " << block::CurrencyCollection{0, ap.remaining_balance.extra}.to_str() << " available"; - if (mode & 2) { - // TODO: process (mode & 2) correctly by setting res_extra := inf (reserve.extra, ap.remaining_balance.extra) - } return 38; // not enough (extra) funds } newc.grams = ap.remaining_balance.grams - reserve.grams; @@ -3778,6 +3781,7 @@ td::Status FetchConfigParams::fetch_config_params( action_phase_cfg->bounce_on_fail_enabled = config.get_global_version() >= 4; action_phase_cfg->message_skip_enabled = config.get_global_version() >= 8; action_phase_cfg->disable_custom_fess = config.get_global_version() >= 8; + action_phase_cfg->reserve_extra_enabled = config.get_global_version() >= 9; action_phase_cfg->mc_blackhole_addr = config.get_burning_config().blackhole_addr; } { diff --git a/crypto/block/transaction.h b/crypto/block/transaction.h index 20d7cb291..0f6952dc7 100644 --- a/crypto/block/transaction.h +++ b/crypto/block/transaction.h @@ -169,6 +169,7 @@ struct ActionPhaseConfig { bool bounce_on_fail_enabled{false}; bool message_skip_enabled{false}; bool disable_custom_fess{false}; + bool reserve_extra_enabled{false}; td::optional mc_blackhole_addr; const MsgPrices& fetch_msg_prices(bool is_masterchain) const { return is_masterchain ? fwd_mc : fwd_std; diff --git a/doc/GlobalVersions.md b/doc/GlobalVersions.md index 5db1ab768..1739b73ad 100644 --- a/doc/GlobalVersions.md +++ b/doc/GlobalVersions.md @@ -122,4 +122,5 @@ Operations for working with Merkle proofs, where cells can have non-zero level a ### Other changes - 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 +- Jumps to nested continuations of depth more than 8 consume 1 gas for eact subsequent continuation (this does not affect most of TVM code). +- Support extra currencies in reserve action with `+2` mode. \ No newline at end of file diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index 9a66ea81f..9e4d406e6 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -1002,6 +1002,7 @@ bool ValidateQuery::fetch_config_params() { action_phase_cfg_.bounce_on_fail_enabled = config_->get_global_version() >= 4; action_phase_cfg_.message_skip_enabled = config_->get_global_version() >= 8; action_phase_cfg_.disable_custom_fess = config_->get_global_version() >= 8; + action_phase_cfg_.reserve_extra_enabled = config_->get_global_version() >= 9; action_phase_cfg_.mc_blackhole_addr = config_->get_burning_config().blackhole_addr; } {