Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Drop gas price difference refunds #11192

Draft
wants to merge 19 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions chain/chain/src/tests/simple_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ fn build_chain() {
// cargo insta test --accept -p near-chain --features nightly -- tests::simple_chain::build_chain
let hash = chain.head().unwrap().last_block_hash;
if cfg!(feature = "nightly") {
insta::assert_snapshot!(hash, @"C3zeKRZubVungxfrSdq379TSCYnuz2YzjEkcJTdm3pU4");
insta::assert_snapshot!(hash, @"H9qfaTFJ7jFMEmJX9eYQ3kJQ6vwATAhidfbjwa9PvCPK");
} else {
insta::assert_snapshot!(hash, @"2WHohfYksQnwKwSEoTKpkseu2RWthbGf9kmGetgHgfQQ");
}
Expand All @@ -50,7 +50,7 @@ fn build_chain() {

let hash = chain.head().unwrap().last_block_hash;
if cfg!(feature = "nightly") {
insta::assert_snapshot!(hash, @"EjLaoHRiAdRp2NcDqwbMcAYYxGfcv5R7GuYUNfRpaJvB");
insta::assert_snapshot!(hash, @"HPRdNbp8dJL2JA5bRSmLg6GJpGXx6xmw472vVAWLpXhi");
} else {
insta::assert_snapshot!(hash, @"HJuuENeSwwikoR9BZA7cSonxAPZgY5mKQWL2pSXwjAwZ");
}
Expand Down
52 changes: 52 additions & 0 deletions core/parameters/src/cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,31 @@ impl RuntimeFeesConfig {
self.fee(ActionCosts::new_action_receipt).min_send_and_exec_fee()
+ self.fee(ActionCosts::function_call_base).min_send_and_exec_fee()
}

pub fn gas_refund_send_fee(&self) -> Gas {
self.fee(ActionCosts::new_action_receipt).send_fee(false)
+ self.fee(ActionCosts::transfer).send_fee(false)
+ self.fee(ActionCosts::add_function_call_key_base).send_fee(false)
}

pub fn gas_refund_exec_fee(&self) -> Gas {
self.fee(ActionCosts::new_action_receipt).exec_fee()
+ self.fee(ActionCosts::transfer).exec_fee()
+ self.fee(ActionCosts::add_function_call_key_base).exec_fee()
}

/// Penalty for the large gas refunds. Use 5% based on https://github.com/near/NEPs/pull/536
pub fn gas_penalty_for_gas_refund(&self, gas_refund: Gas) -> Gas {
if self.gas_refund_send_fee() > 0 {
std::cmp::max(
(u128::from(gas_refund) * 5 / 100) as Gas,
self.gas_refund_send_fee() + self.gas_refund_exec_fee(),
)
} else {
// To account for free config
0
}
}
}

impl StorageUsageConfig {
Expand Down Expand Up @@ -564,3 +589,30 @@ pub fn transfer_send_fee(
}
}
}

pub fn transfer_send_and_exec_fee(
cfg: &RuntimeFeesConfig,
sender_is_receiver: bool,
implicit_account_creation_allowed: bool,
eth_implicit_accounts_enabled: bool,
receiver_account_type: AccountType,
) -> Gas {
// TODO: Add `Copy` to `AccountType`.
let copy_of_receiver_account_type = match &receiver_account_type {
AccountType::NamedAccount => AccountType::NamedAccount,
AccountType::NearImplicitAccount => AccountType::NearImplicitAccount,
AccountType::EthImplicitAccount => AccountType::EthImplicitAccount,
};
transfer_send_fee(
cfg,
sender_is_receiver,
implicit_account_creation_allowed,
eth_implicit_accounts_enabled,
receiver_account_type,
) + transfer_exec_fee(
cfg,
implicit_account_creation_allowed,
eth_implicit_accounts_enabled,
copy_of_receiver_account_type,
)
}
4 changes: 3 additions & 1 deletion core/primitives-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@ expect-test.workspace = true
default = []
protocol_feature_fix_staking_threshold = []
protocol_feature_fix_contract_loading_cost = []
protocol_feature_reject_blocks_with_outdated_protocol_version = []
protocol_feature_gas_price_refund_adjustment_nep536 = []
protocol_feature_nonrefundable_transfer_nep491 = []
protocol_feature_reject_blocks_with_outdated_protocol_version = []

nightly = [
"nightly_protocol",
"protocol_feature_fix_contract_loading_cost",
"protocol_feature_fix_staking_threshold",
"protocol_feature_gas_price_refund_adjustment_nep536",
"protocol_feature_nonrefundable_transfer_nep491",
"protocol_feature_reject_blocks_with_outdated_protocol_version",
]
Expand Down
7 changes: 6 additions & 1 deletion core/primitives-core/src/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ pub enum ProtocolFeature {
CongestionControl,
// Stateless validation: Distribute state witness as reed solomon encoded parts
PartialEncodedStateWitness,
// Disable gas price refunds and require some amount of gas for refunds.
#[cfg(feature = "protocol_feature_gas_price_refund_adjustment_nep536")]
GasPriceRefundAdjustment,
}

impl ProtocolFeature {
Expand Down Expand Up @@ -236,6 +239,8 @@ impl ProtocolFeature {
// TODO(#11201): When stabilizing this feature in mainnet, also remove the temporary code
// that always enables this for mocknet (see config_mocknet function).
ProtocolFeature::ShuffleShardAssignments => 143,
#[cfg(feature = "protocol_feature_gas_price_refund_adjustment_nep536")]
ProtocolFeature::GasPriceRefundAdjustment => 144,
}
}

Expand All @@ -255,7 +260,7 @@ pub const PROTOCOL_VERSION: ProtocolVersion = if cfg!(feature = "statelessnet_pr
86
} else if cfg!(feature = "nightly_protocol") {
// On nightly, pick big enough version to support all features.
143
144
} else {
// Enable all stable features.
STABLE_PROTOCOL_VERSION
Expand Down
90 changes: 63 additions & 27 deletions integration-tests/src/tests/standard_cases/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ use near_parameters::RuntimeConfig;
use near_primitives::receipt::{ActionReceipt, Receipt, ReceiptEnum};
use near_primitives::test_utils;
use near_primitives::transaction::{Action, DeployContractAction, FunctionCallAction};
use near_primitives_core::checked_feature;
use near_primitives_core::version::PROTOCOL_VERSION;
use testlib::fees_utils::FeeHelper;
use testlib::runtime_utils::{
alice_account, bob_account, eve_dot_alice_account, x_dot_y_dot_alice_account,
Expand All @@ -42,6 +44,23 @@ pub(crate) fn fee_helper(node: &impl Node) -> FeeHelper {
FeeHelper::new(RuntimeConfig::test(), node.genesis().config.min_gas_price)
}

fn assert_receipts_outcome_len(
transaction_result: &FinalExecutionOutcomeView,
expected_len_without_refund: usize,
) {
if checked_feature!("nightly_protocol", GasPriceRefundAdjustment, PROTOCOL_VERSION) {
assert_eq!(
transaction_result.receipts_outcome.len(),
expected_len_without_refund,
"receipts_outcome: {:?}",
transaction_result.receipts_outcome
);
} else {
assert!([expected_len_without_refund, expected_len_without_refund + 1]
.contains(&transaction_result.receipts_outcome.len()));
}
}

/// Adds given access key to the given account_id using signer2.
fn add_access_key(
node: &impl Node,
Expand Down Expand Up @@ -359,7 +378,7 @@ pub fn transfer_tokens_to_implicit_account(node: impl Node, public_key: PublicKe
let transaction_result =
node_user.send_money(account_id.clone(), receiver_id.clone(), tokens_used).unwrap();
assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new()));
assert_eq!(transaction_result.receipts_outcome.len(), 2);
assert_receipts_outcome_len(&transaction_result, 1);
let new_root = node_user.get_state_root();
assert_ne!(root, new_root);
assert_eq!(node_user.get_access_key_nonce_for_signer(account_id).unwrap(), 1);
Expand Down Expand Up @@ -392,7 +411,7 @@ pub fn transfer_tokens_to_implicit_account(node: impl Node, public_key: PublicKe
node_user.send_money(account_id.clone(), receiver_id.clone(), tokens_used).unwrap();

assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new()));
assert_eq!(transaction_result.receipts_outcome.len(), 2);
assert_receipts_outcome_len(&transaction_result, 1);
let new_root = node_user.get_state_root();
assert_ne!(root, new_root);
assert_eq!(node_user.get_access_key_nonce_for_signer(account_id).unwrap(), 2);
Expand Down Expand Up @@ -426,19 +445,24 @@ pub fn trying_to_create_implicit_account(node: impl Node, public_key: PublicKey)
.create_account(account_id.clone(), receiver_id.clone(), public_key, tokens_used)
.unwrap();

let create_account_fee = fee_helper.cfg().fee(ActionCosts::create_account).send_fee(false);
let add_access_key_fee = fee_helper.cfg().fee(ActionCosts::add_full_access_key).send_fee(false);
let create_account_send_fee = fee_helper.cfg().fee(ActionCosts::create_account).send_fee(false);
let add_access_key_send_fee =
fee_helper.cfg().fee(ActionCosts::add_full_access_key).send_fee(false);

let create_account_exec_fee = fee_helper.cfg().fee(ActionCosts::create_account).exec_fee();
let add_access_key_exec_fee = fee_helper.cfg().fee(ActionCosts::add_full_access_key).exec_fee();

let cost = match receiver_id.get_account_type() {
AccountType::NearImplicitAccount => {
fee_helper.create_account_transfer_full_key_cost_fail_on_create_account()
+ fee_helper.gas_to_balance(create_account_fee + add_access_key_fee)
fee_helper.create_account_transfer_full_key_cost_fail_on_create_account(
create_account_exec_fee + add_access_key_exec_fee,
) + fee_helper.gas_to_balance(create_account_send_fee + add_access_key_send_fee)
}
AccountType::EthImplicitAccount => {
// This test uses `node_user.create_account` method that is normally used for NamedAccounts and should fail here.
fee_helper.create_account_transfer_full_key_cost_fail_on_create_account()
fee_helper.create_account_transfer_full_key_cost_fail_on_create_account(create_account_exec_fee)
// We add this fee analogously to the NEAR-implicit match arm above (without `add_access_key_fee`).
+ fee_helper.gas_to_balance(create_account_fee)
+ fee_helper.gas_to_balance(create_account_send_fee)
}
AccountType::NamedAccount => std::panic!("must be implicit"),
};
Expand All @@ -455,6 +479,7 @@ pub fn trying_to_create_implicit_account(node: impl Node, public_key: PublicKey)
.into()
)
);

assert_eq!(transaction_result.receipts_outcome.len(), 3);
let new_root = node_user.get_state_root();
assert_ne!(root, new_root);
Expand Down Expand Up @@ -488,8 +513,14 @@ pub fn test_smart_contract_reward(node: impl Node) {

let fee_helper = fee_helper(&node);
let bob = node_user.view_account(&bob_account()).unwrap();
let gas_burnt_for_function_call = transaction_result.receipts_outcome[0].outcome.gas_burnt
let mut gas_burnt_for_function_call = transaction_result.receipts_outcome[0].outcome.gas_burnt
- fee_helper.function_call_exec_gas(b"run_test".len() as u64);

if checked_feature!("nightly_protocol", GasPriceRefundAdjustment, PROTOCOL_VERSION) {
// There is a cost for a gas refund fee.
gas_burnt_for_function_call -= fee_helper.cfg().gas_refund_send_fee();
}

let reward = fee_helper.gas_burnt_to_reward(gas_burnt_for_function_call);
assert_eq!(bob.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE + reward);
}
Expand Down Expand Up @@ -536,7 +567,7 @@ pub fn test_refund_on_send_money_to_non_existent_account(node: impl Node) {
.into()
)
);
assert_eq!(transaction_result.receipts_outcome.len(), 3);
assert_receipts_outcome_len(&transaction_result, 2);
let new_root = node_user.get_state_root();
assert_ne!(root, new_root);
let result1 = node_user.view_account(account_id).unwrap();
Expand Down Expand Up @@ -567,8 +598,7 @@ pub fn test_create_account(node: impl Node) {
let create_account_cost = fee_helper.create_account_transfer_full_key_cost();

assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new()));
// Refund receipt may not be ready yet
assert!([1, 2].contains(&transaction_result.receipts_outcome.len()));
assert_receipts_outcome_len(&transaction_result, 1);
let new_root = node_user.get_state_root();
assert_ne!(root, new_root);
assert_eq!(node_user.get_access_key_nonce_for_signer(account_id).unwrap(), 1);
Expand Down Expand Up @@ -601,8 +631,7 @@ pub fn test_create_account_again(node: impl Node) {
.unwrap();

assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new()));
// Refund receipt may not be ready yet
assert!([1, 2].contains(&transaction_result.receipts_outcome.len()));
assert_receipts_outcome_len(&transaction_result, 1);
let fee_helper = fee_helper(&node);
let create_account_cost = fee_helper.create_account_transfer_full_key_cost();

Expand Down Expand Up @@ -634,14 +663,15 @@ pub fn test_create_account_again(node: impl Node) {
.into()
)
);
assert_eq!(transaction_result.receipts_outcome.len(), 3);
assert_receipts_outcome_len(&transaction_result, 2);
let new_root = node_user.get_state_root();
assert_ne!(root, new_root);
assert_eq!(node_user.get_access_key_nonce_for_signer(account_id).unwrap(), 2);

// Additional cost for trying to create an account with repeated name. Will fail after
// the first action.
let additional_cost = fee_helper.create_account_transfer_full_key_cost_fail_on_create_account();
let additional_cost =
fee_helper.create_account_transfer_full_key_cost_fail_on_create_account(0);

let result1 = node_user.view_account(account_id).unwrap();
assert_eq!(
Expand Down Expand Up @@ -670,7 +700,7 @@ pub fn test_create_account_failure_already_exists(node: impl Node) {
.unwrap();
let fee_helper = fee_helper(&node);
let create_account_cost =
fee_helper.create_account_transfer_full_key_cost_fail_on_create_account();
fee_helper.create_account_transfer_full_key_cost_fail_on_create_account(0);
assert_eq!(
transaction_result.status,
FinalExecutionStatus::Failure(
Expand All @@ -681,7 +711,7 @@ pub fn test_create_account_failure_already_exists(node: impl Node) {
.into()
)
);
assert_eq!(transaction_result.receipts_outcome.len(), 3);
assert_receipts_outcome_len(&transaction_result, 2);
let new_root = node_user.get_state_root();
assert_ne!(root, new_root);
assert_eq!(node_user.get_access_key_nonce_for_signer(account_id).unwrap(), 1);
Expand Down Expand Up @@ -1020,12 +1050,18 @@ pub fn test_access_key_smart_contract(node: impl Node) {
transaction_result.status,
FinalExecutionStatus::SuccessValue(10i32.to_le_bytes().to_vec())
);
let gas_refund = fee_helper.gas_to_balance(
prepaid_gas + exec_gas - transaction_result.receipts_outcome[0].outcome.gas_burnt,
);
let burnt_gas = transaction_result.receipts_outcome[0].outcome.gas_burnt;
let mut gas_refund = prepaid_gas + exec_gas - burnt_gas;

// Refund receipt may not be ready yet
assert!([1, 2].contains(&transaction_result.receipts_outcome.len()));
if checked_feature!("nightly_protocol", GasPriceRefundAdjustment, PROTOCOL_VERSION) {
gas_refund += fee_helper.cfg().gas_refund_send_fee();
gas_refund -= fee_helper.cfg().gas_penalty_for_gas_refund(gas_refund);
}

let gas_refund_amount = fee_helper.gas_to_balance(gas_refund);

// Need to wait for the refund to be received before checking the balance.
assert_eq!(transaction_result.receipts_outcome.len(), 2);
let new_root = node_user.get_state_root();
assert_ne!(root, new_root);

Expand All @@ -1035,7 +1071,7 @@ pub fn test_access_key_smart_contract(node: impl Node) {
AccessKey {
nonce: view_access_key.nonce,
permission: AccessKeyPermission::FunctionCall(FunctionCallPermission {
allowance: Some(FUNCTION_CALL_AMOUNT - function_call_cost + gas_refund),
allowance: Some(FUNCTION_CALL_AMOUNT - function_call_cost + gas_refund_amount),
receiver_id: bob_account().into(),
method_names: vec![],
}),
Expand Down Expand Up @@ -1185,7 +1221,7 @@ pub fn test_unstake_while_not_staked(node: impl Node) {
)
.unwrap();
assert_eq!(transaction_result.status, FinalExecutionStatus::SuccessValue(Vec::new()));
assert_eq!(transaction_result.receipts_outcome.len(), 2);
assert_receipts_outcome_len(&transaction_result, 1);
let transaction_result =
node_user.stake(eve_dot_alice_account(), node.block_signer().public_key(), 0).unwrap();
assert_eq!(
Expand Down Expand Up @@ -1283,7 +1319,7 @@ pub fn test_delete_account_fail(node: impl Node) {
.into()
)
);
assert_eq!(transaction_result.receipts_outcome.len(), 2);
assert_receipts_outcome_len(&transaction_result, 1);
assert!(node.user().view_account(&bob_account()).is_ok());
assert_eq!(
node.user().view_account(&node.account_id().unwrap()).unwrap().amount,
Expand All @@ -1305,7 +1341,7 @@ pub fn test_delete_account_no_account(node: impl Node) {
.into()
)
);
assert_eq!(transaction_result.receipts_outcome.len(), 2);
assert_receipts_outcome_len(&transaction_result, 1);
}

pub fn test_delete_account_while_staking(node: impl Node) {
Expand Down
10 changes: 9 additions & 1 deletion integration-tests/src/user/runtime_user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,15 @@ impl RuntimeUser {
block_timestamp: 0,
shard_id,
epoch_height: 0,
gas_price: MIN_GAS_PRICE,
// Note, previously `pessimistic_gas_price_inflation_ratio` made all gas prices to be 0,
// if the ratio was 0. Now it's disabled when `GasPriceRefundAdjustment` feature is on.
gas_price: if *self.runtime_config.fees.pessimistic_gas_price_inflation_ratio.numer()
> 0
{
MIN_GAS_PRICE
} else {
0
},
gas_limit: None,
random_seed: Default::default(),
epoch_id: Default::default(),
Expand Down
Loading
Loading