From 0116edd3261563a1439fc360bbae46405ba995fc Mon Sep 17 00:00:00 2001 From: Andrei Kashin Date: Fri, 5 Apr 2024 12:12:35 +0100 Subject: [PATCH] Use constant minimum_new_receipt_gas This addresses the issue from #10829. Because we plan to decrease the function call cost, we need to modify the refund estimation logic that relies on function call base cost. This PR sets minimum_new_receipt_gas to 108_059_500_000 + 2_319_861_500_000 + 2_319_861_500_000 ~= 4.7TGas which would limit the overcharging to 6x which is the current level. --- chain/indexer/src/streamer/mod.rs | 3 +++ chain/indexer/src/streamer/utils.rs | 3 +++ core/primitives/src/version.rs | 6 +++++- runtime/runtime/src/config.rs | 13 ++++++++++++- runtime/runtime/src/verifier.rs | 2 +- 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/chain/indexer/src/streamer/mod.rs b/chain/indexer/src/streamer/mod.rs index 388639c8ed5..2b10100de63 100644 --- a/chain/indexer/src/streamer/mod.rs +++ b/chain/indexer/src/streamer/mod.rs @@ -136,6 +136,7 @@ pub async fn build_streamer_message( .filter(|tx| tx.transaction.signer_id == tx.transaction.receiver_id) .collect::>(), &block, + protocol_config_view.protocol_version, ) .await?; @@ -322,6 +323,7 @@ async fn find_local_receipt_by_id_in_block( ) -> Result, FailedToFetchData> { let chunks = fetch_block_chunks(&client, &block).await?; + let protocol_config_view = fetch_protocol_config(&client, block.header.hash).await?; let mut shards_outcomes = fetch_outcomes(&client, block.header.hash).await?; for chunk in chunks { @@ -348,6 +350,7 @@ async fn find_local_receipt_by_id_in_block( &runtime_config, vec![&indexer_transaction], &block, + protocol_config_view.protocol_version, ) .await?; diff --git a/chain/indexer/src/streamer/utils.rs b/chain/indexer/src/streamer/utils.rs index 0ec742cb900..15d559b8ac8 100644 --- a/chain/indexer/src/streamer/utils.rs +++ b/chain/indexer/src/streamer/utils.rs @@ -2,6 +2,7 @@ use actix::Addr; use near_indexer_primitives::IndexerTransactionWithOutcome; use near_parameters::RuntimeConfig; +use near_primitives::version::ProtocolVersion; use near_primitives::views; use node_runtime::config::tx_cost; @@ -13,6 +14,7 @@ pub(crate) async fn convert_transactions_sir_into_local_receipts( runtime_config: &RuntimeConfig, txs: Vec<&IndexerTransactionWithOutcome>, block: &views::BlockView, + protocol_version: ProtocolVersion, ) -> Result, FailedToFetchData> { if txs.is_empty() { return Ok(vec![]); @@ -43,6 +45,7 @@ pub(crate) async fn convert_transactions_sir_into_local_receipts( }, prev_block_gas_price, true, + protocol_version, ); views::ReceiptView { predecessor_id: tx.transaction.signer_id.clone(), diff --git a/core/primitives/src/version.rs b/core/primitives/src/version.rs index 74d70f70751..58593f0feac 100644 --- a/core/primitives/src/version.rs +++ b/core/primitives/src/version.rs @@ -44,9 +44,13 @@ pub const DELETE_KEY_STORAGE_USAGE_PROTOCOL_VERSION: ProtocolVersion = 40; pub const SHARD_CHUNK_HEADER_UPGRADE_VERSION: ProtocolVersion = 41; -/// Updates the way receipt ID is constructed to use current block hash instead of last block hash +/// Updates the way receipt ID is constructed to use current block hash instead of last block hash. pub const CREATE_RECEIPT_ID_SWITCH_TO_CURRENT_BLOCK_VERSION: ProtocolVersion = 42; +/// Pessimistic gas price estimation uses a fixed value of `minimum_new_receipt_gas` to stop being +/// tied to the function call base cost. +pub const FIXED_MINIMUM_NEW_RECEIPT_GAS_VERSION: ProtocolVersion = 66; + /// The points in time after which the voting for the latest protocol version /// should start. /// diff --git a/runtime/runtime/src/config.rs b/runtime/runtime/src/config.rs index aa4e976e1bd..c5681c8481f 100644 --- a/runtime/runtime/src/config.rs +++ b/runtime/runtime/src/config.rs @@ -2,6 +2,8 @@ use near_primitives::account::AccessKeyPermission; use near_primitives::errors::IntegerOverflowError; +use near_primitives::version::FIXED_MINIMUM_NEW_RECEIPT_GAS_VERSION; +use near_primitives_core::types::ProtocolVersion; use num_bigint::BigUint; use num_traits::cast::ToPrimitive; use num_traits::pow::Pow; @@ -248,6 +250,7 @@ pub fn tx_cost( transaction: &Transaction, gas_price: Balance, sender_is_receiver: bool, + protocol_version: ProtocolVersion, ) -> Result { let fees = &config.fees; let mut gas_burnt: Gas = fees.fee(ActionCosts::new_action_receipt).send_fee(sender_is_receiver); @@ -267,7 +270,15 @@ pub fn tx_cost( // If signer is equals to receiver the receipt will be processed at the same block as this // transaction. Otherwise it will processed in the next block and the gas might be inflated. let initial_receipt_hop = if transaction.signer_id == transaction.receiver_id { 0 } else { 1 }; - let minimum_new_receipt_gas = fees.min_receipt_with_function_call_gas(); + let minimum_new_receipt_gas = if protocol_version < FIXED_MINIMUM_NEW_RECEIPT_GAS_VERSION { + fees.min_receipt_with_function_call_gas() + } else { + // The pessimistic gas pricing is a best-effort limit which can be breached in case of + // congestion when receipts are delayed before they execute. Hence there is not much + // value to tie this limit to the function call base cost. Making it constant limits + // overcharging to 6x, which was the value before the cost increase. + 4_855_842_000_000 // 4.855TGas. + }; // In case the config is free, we don't care about the maximum depth. let receipt_gas_price = if gas_price == 0 { 0 diff --git a/runtime/runtime/src/verifier.rs b/runtime/runtime/src/verifier.rs index 57c0bdd4b5a..2c4647e80e3 100644 --- a/runtime/runtime/src/verifier.rs +++ b/runtime/runtime/src/verifier.rs @@ -128,7 +128,7 @@ pub fn validate_transaction( let sender_is_receiver = &transaction.receiver_id == signer_id; - tx_cost(&config, transaction, gas_price, sender_is_receiver) + tx_cost(&config, transaction, gas_price, sender_is_receiver, current_protocol_version) .map_err(|_| InvalidTxError::CostOverflow.into()) }