Skip to content

Commit

Permalink
Increase gas limit for a specific wallet (enabled by config) (#859)
Browse files Browse the repository at this point in the history
  • Loading branch information
SpyCheese authored Jan 15, 2024
1 parent ff40c1f commit 388c8a6
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 9 deletions.
94 changes: 86 additions & 8 deletions crypto/block/transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1081,19 +1081,33 @@ bool ComputePhaseConfig::is_address_suspended(ton::WorkchainId wc, td::Bits256 a
}
}

/**
* Computes the maximum gas fee based on the gas prices and limits.
*
* @param gas_price256 The gas price from config as RefInt256
* @param gas_limit The gas limit from config
* @param flat_gas_limit The flat gas limit from config
* @param flat_gas_price The flat gas price from config
*
* @returns The maximum gas fee.
*/
static td::RefInt256 compute_max_gas_threshold(const td::RefInt256& gas_price256, td::uint64 gas_limit,
td::uint64 flat_gas_limit, td::uint64 flat_gas_price) {
if (gas_limit > flat_gas_limit) {
return td::rshift(gas_price256 * (gas_limit - flat_gas_limit), 16, 1) + td::make_bigint(flat_gas_price);
} else {
return td::make_refint(flat_gas_price);
}
}

/**
* Computes the maximum for gas fee based on the gas prices and limits.
*
* Updates max_gas_threshold.
*/
void ComputePhaseConfig::compute_threshold() {
gas_price256 = td::make_refint(gas_price);
if (gas_limit > flat_gas_limit) {
max_gas_threshold =
td::rshift(gas_price256 * (gas_limit - flat_gas_limit), 16, 1) + td::make_bigint(flat_gas_price);
} else {
max_gas_threshold = td::make_refint(flat_gas_price);
}
max_gas_threshold = compute_max_gas_threshold(gas_price256, gas_limit, flat_gas_limit, flat_gas_price);
}

/**
Expand Down Expand Up @@ -1130,6 +1144,67 @@ td::RefInt256 ComputePhaseConfig::compute_gas_price(td::uint64 gas_used) const {
}

namespace transaction {

/**
* Checks if it is required to increase gas_limit (from GasLimitsPrices config) to special_gas_limit * 2
* from masterchain GasLimitsPrices config for the transaction.
*
* In January 2024 a highload wallet of @wallet Telegram bot in mainnet was stuck because current gas limit (1M) is
* not enough to clean up old queires, thus locking funds inside.
* See comment in crypto/smartcont/highload-wallet-v2-code.fc for details on why this happened.
* Account address: EQD_v9j1rlsuHHw2FIhcsCFFSD367ldfDdCKcsNmNpIRzUlu
* It was proposed to validators to increase gas limit for this account for a limited amount of time (until 2024-02-16).
* It is activated by setting gas_prices_v3 in ConfigParam 20 (config_mc_gas_prices).
* This config change also activates new behavior for special accounts in masterchain.
*
* @param cfg The compute phase configuration.
* @param now The Unix time of the transaction.
* @param account The account of the transaction.
*
* @returns True if gas_limit override is required, false otherwise
*/
static bool override_gas_limit(const ComputePhaseConfig& cfg, ton::UnixTime now, const Account& account) {
if (!cfg.mc_gas_prices.special_full_limit) {
return false;
}
ton::UnixTime until = 1708041600; // 2024-02-16 00:00:00 UTC
ton::WorkchainId wc = 0;
const char* addr_hex = "FFBFD8F5AE5B2E1C7C3614885CB02145483DFAEE575F0DD08A72C366369211CD";
return now < until && account.workchain == wc && account.addr.to_hex() == addr_hex;
}

/**
* Computes the amount of gas that can be bought for a given amount of nanograms.
* Usually equal to `cfg.gas_bought_for(nanograms)`
* However, it overrides gas_limit from config in special cases.
*
* @param cfg The compute phase configuration.
* @param nanograms The amount of nanograms to compute gas for.
*
* @returns The amount of gas.
*/
td::uint64 Transaction::gas_bought_for(const ComputePhaseConfig& cfg, td::RefInt256 nanograms) {
if (override_gas_limit(cfg, now, account)) {
gas_limit_overridden = true;
// Same as ComputePhaseConfig::gas_bought for, but with other gas_limit and max_gas_threshold
auto gas_limit = cfg.mc_gas_prices.special_gas_limit * 2;
auto max_gas_threshold =
compute_max_gas_threshold(cfg.gas_price256, gas_limit, cfg.flat_gas_limit, cfg.flat_gas_price);
if (nanograms.is_null() || sgn(nanograms) < 0) {
return 0;
}
if (nanograms >= max_gas_threshold) {
return gas_limit;
}
if (nanograms < cfg.flat_gas_price) {
return 0;
}
auto res = td::div((std::move(nanograms) - cfg.flat_gas_price) << 16, cfg.gas_price256);
return res->to_long() + cfg.flat_gas_limit;
}
return cfg.gas_bought_for(nanograms);
}

/**
* Computes the gas limits for a transaction.
*
Expand All @@ -1143,7 +1218,7 @@ bool Transaction::compute_gas_limits(ComputePhase& cp, const ComputePhaseConfig&
if (account.is_special) {
cp.gas_max = cfg.special_gas_limit;
} else {
cp.gas_max = cfg.gas_bought_for(balance.grams);
cp.gas_max = gas_bought_for(cfg, balance.grams);
}
cp.gas_credit = 0;
if (trans_type != tr_ord || (account.is_special && cfg.special_gas_full)) {
Expand All @@ -1152,7 +1227,7 @@ bool Transaction::compute_gas_limits(ComputePhase& cp, const ComputePhaseConfig&
} else {
// originally use only gas bought using remaining message balance
// if the message is "accepted" by the smart contract, the gas limit will be set to gas_max
cp.gas_limit = std::min(cfg.gas_bought_for(msg_balance_remaining.grams), cp.gas_max);
cp.gas_limit = std::min(gas_bought_for(cfg, msg_balance_remaining.grams), cp.gas_max);
if (!block::tlb::t_Message.is_internal(in_msg)) {
// external messages carry no balance, give them some credit to check whether they are accepted
cp.gas_credit = std::min(cfg.gas_credit, cp.gas_max);
Expand Down Expand Up @@ -3454,6 +3529,9 @@ td::Status FetchConfigParams::fetch_config_params(
storage_phase_cfg->delete_due_limit)) {
return td::Status::Error(-668, "cannot unpack current gas prices and limits from masterchain configuration");
}
TRY_RESULT_PREFIX(mc_gas_prices, config.get_gas_limits_prices(true),
"cannot unpack masterchain gas prices and limits: ");
compute_phase_cfg->mc_gas_prices = std::move(mc_gas_prices);
storage_phase_cfg->enable_due_payment = config.get_global_version() >= 4;
compute_phase_cfg->block_rand_seed = *rand_seed;
compute_phase_cfg->max_vm_data_depth = size_limits.max_vm_data_depth;
Expand Down
3 changes: 3 additions & 0 deletions crypto/block/transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ struct ComputePhaseConfig {
td::uint64 flat_gas_limit = 0;
td::uint64 flat_gas_price = 0;
bool special_gas_full = false;
block::GasLimitsPrices mc_gas_prices;
static constexpr td::uint64 gas_infty = (1ULL << 63) - 1;
td::RefInt256 gas_price256;
td::RefInt256 max_gas_threshold;
Expand Down Expand Up @@ -358,12 +359,14 @@ struct Transaction {
std::unique_ptr<ActionPhase> action_phase;
std::unique_ptr<BouncePhase> bounce_phase;
vm::CellStorageStat new_storage_stat;
bool gas_limit_overridden{false};
Transaction(const Account& _account, int ttype, ton::LogicalTime req_start_lt, ton::UnixTime _now,
Ref<vm::Cell> _inmsg = {});
bool unpack_input_msg(bool ihr_delivered, const ActionPhaseConfig* cfg);
bool check_in_msg_state_hash();
bool prepare_storage_phase(const StoragePhaseConfig& cfg, bool force_collect = true, bool adjust_msg_value = false);
bool prepare_credit_phase();
td::uint64 gas_bought_for(const ComputePhaseConfig& cfg, td::RefInt256 nanograms);
bool compute_gas_limits(ComputePhase& cp, const ComputePhaseConfig& cfg);
Ref<vm::Stack> prepare_vm_stack(ComputePhase& cp);
std::vector<Ref<vm::Cell>> compute_vm_libraries(const ComputePhaseConfig& cfg);
Expand Down
7 changes: 6 additions & 1 deletion validator/impl/validate-query.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,11 @@ bool ValidateQuery::fetch_config_params() {
storage_phase_cfg_.delete_due_limit)) {
return fatal_error("cannot unpack current gas prices and limits from masterchain configuration");
}
auto mc_gas_prices = config_->get_gas_limits_prices(true);
if (mc_gas_prices.is_error()) {
return fatal_error(mc_gas_prices.move_as_error_prefix("cannot unpack masterchain gas prices and limits: "));
}
compute_phase_cfg_.mc_gas_prices = mc_gas_prices.move_as_ok();
storage_phase_cfg_.enable_due_payment = config_->get_global_version() >= 4;
compute_phase_cfg_.block_rand_seed = rand_seed_;
compute_phase_cfg_.libraries = std::make_unique<vm::Dictionary>(config_->get_libraries_root(), 256);
Expand Down Expand Up @@ -5052,7 +5057,7 @@ bool ValidateQuery::check_one_transaction(block::Account& account, ton::LogicalT
<< " for smart contract " << addr.to_hex());
}
if (!trs->update_limits(*block_limit_status_,
/* with_gas = */ !account.is_special,
/* with_gas = */ !account.is_special && !trs->gas_limit_overridden,
/* with_size = */ false)) {
return fatal_error(PSTRING() << "cannot update block limit status to include transaction " << lt << " of account "
<< addr.to_hex());
Expand Down

0 comments on commit 388c8a6

Please sign in to comment.