From a7960e9032666cfe8241feac020a73ef66676a7d Mon Sep 17 00:00:00 2001 From: Evgeny Kuzyakov Date: Thu, 12 Sep 2019 11:13:56 -0700 Subject: [PATCH] Contract rewards (#1258) Introduce execution rewards for every burnt_gas. Refunds are free, meaining no gas cost to create/send or execute. Return burnt_gas as part of TransactionResult. Fixing tests by fixing tests. Increase fees 10X to properly check rewards. Fix view call infinite loops --- chain/chain/src/test_utils.rs | 1 + core/primitives/src/transaction.rs | 9 +- core/primitives/src/views.rs | 3 + runtime/near-runtime-fees/src/lib.rs | 49 ++++-- runtime/near-vm-logic/src/logic.rs | 13 +- runtime/runtime/src/lib.rs | 166 +++++++++++------- test-utils/testlib/src/fees_utils.rs | 143 ++++++++------- test-utils/testlib/src/node/runtime_node.rs | 7 +- test-utils/testlib/src/standard_test_cases.rs | 78 +++++--- tests/test_cases_runtime.rs | 6 + tests/test_errors.rs | 2 +- 11 files changed, 294 insertions(+), 183 deletions(-) diff --git a/chain/chain/src/test_utils.rs b/chain/chain/src/test_utils.rs index b48b335fbc6..5216c7c8abf 100644 --- a/chain/chain/src/test_utils.rs +++ b/chain/chain/src/test_utils.rs @@ -558,6 +558,7 @@ impl RuntimeAdapter for KeyValueRuntime { logs: vec![], receipts: new_receipt_hashes, result: None, + gas_burnt: 0, }, }); } diff --git a/core/primitives/src/transaction.rs b/core/primitives/src/transaction.rs index 80e2c15b4f8..a81ec787b65 100644 --- a/core/primitives/src/transaction.rs +++ b/core/primitives/src/transaction.rs @@ -193,14 +193,16 @@ impl Default for TransactionStatus { #[derive(BorshSerialize, BorshDeserialize, PartialEq, Clone, Default)] pub struct TransactionResult { - /// Transaction status. + /// Execution status. pub status: TransactionStatus, - /// Logs from this transaction. + /// Logs from this transaction or receipt. pub logs: Vec, - /// Receipt ids generated by this transaction. + /// Receipt ids generated by this transaction or receipt. pub receipts: Vec, /// Execution Result pub result: Option>, + /// The amount of the gas burnt by the given transaction or receipt. + pub gas_burnt: Gas, } impl fmt::Debug for TransactionResult { @@ -210,6 +212,7 @@ impl fmt::Debug for TransactionResult { .field("logs", &format_args!("{}", logging::pretty_vec(&self.logs))) .field("receipts", &format_args!("{}", logging::pretty_vec(&self.receipts))) .field("result", &format_args!("{}", logging::pretty_result(&self.result))) + .field("burnt_gas", &self.gas_burnt) .finish() } } diff --git a/core/primitives/src/views.rs b/core/primitives/src/views.rs index 3694c4f2875..01381ff7b88 100644 --- a/core/primitives/src/views.rs +++ b/core/primitives/src/views.rs @@ -614,6 +614,7 @@ pub struct TransactionResultView { pub logs: Vec, pub receipts: Vec, pub result: Option, + pub gas_burnt: Gas, } impl From for TransactionResultView { @@ -623,6 +624,7 @@ impl From for TransactionResultView { logs: result.logs, receipts: result.receipts.into_iter().map(|h| h.into()).collect(), result: result.result.map(|v| to_base64(&v)), + gas_burnt: result.gas_burnt, } } } @@ -634,6 +636,7 @@ impl From for TransactionResult { logs: view.logs, receipts: view.receipts.into_iter().map(|h| h.into()).collect(), result: view.result.map(|v| from_base64(&v).unwrap()), + gas_burnt: view.gas_burnt, } } } diff --git a/runtime/near-runtime-fees/src/lib.rs b/runtime/near-runtime-fees/src/lib.rs index 2d461aed932..fb2db968b83 100644 --- a/runtime/near-runtime-fees/src/lib.rs +++ b/runtime/near-runtime-fees/src/lib.rs @@ -6,6 +6,12 @@ use serde::{Deserialize, Serialize}; pub type Gas = u64; +#[derive(Debug, Serialize, Deserialize, Clone, Hash, PartialEq, Eq)] +pub struct Fraction { + pub numerator: u64, + pub denominator: u64, +} + /// Costs associated with an object that can only be sent over the network (and executed /// by the receiver). #[derive(Debug, Serialize, Deserialize, Clone, Hash, PartialEq, Eq)] @@ -44,6 +50,9 @@ pub struct RuntimeFeesConfig { pub action_creation_config: ActionCreationConfig, pub storage_usage_config: StorageUsageConfig, + + /// Fraction of the burnt gas to reward to the contract account for execution. + pub burnt_gas_reward: Fraction, } /// Describes the cost of creating a data receipt, `DataReceipt`. @@ -115,26 +124,34 @@ pub struct StorageUsageConfig { impl Default for RuntimeFeesConfig { fn default() -> Self { Self { - action_receipt_creation_config: Fee { send_sir: 1, send_not_sir: 1, execution: 1 }, + action_receipt_creation_config: Fee { send_sir: 10, send_not_sir: 10, execution: 10 }, data_receipt_creation_config: DataReceiptCreationConfig { - base_cost: Fee { send_sir: 1, send_not_sir: 1, execution: 1 }, - cost_per_byte: Fee { send_sir: 1, send_not_sir: 1, execution: 1 }, + base_cost: Fee { send_sir: 10, send_not_sir: 10, execution: 10 }, + cost_per_byte: Fee { send_sir: 10, send_not_sir: 10, execution: 10 }, }, action_creation_config: ActionCreationConfig { - create_account_cost: Fee { send_sir: 1, send_not_sir: 1, execution: 1 }, - deploy_contract_cost: Fee { send_sir: 1, send_not_sir: 1, execution: 1 }, - deploy_contract_cost_per_byte: Fee { send_sir: 1, send_not_sir: 1, execution: 1 }, - function_call_cost: Fee { send_sir: 1, send_not_sir: 1, execution: 1 }, - function_call_cost_per_byte: Fee { send_sir: 1, send_not_sir: 1, execution: 1 }, - transfer_cost: Fee { send_sir: 1, send_not_sir: 1, execution: 1 }, - stake_cost: Fee { send_sir: 1, send_not_sir: 1, execution: 1 }, + create_account_cost: Fee { send_sir: 10, send_not_sir: 10, execution: 10 }, + deploy_contract_cost: Fee { send_sir: 10, send_not_sir: 10, execution: 10 }, + deploy_contract_cost_per_byte: Fee { + send_sir: 10, + send_not_sir: 10, + execution: 10, + }, + function_call_cost: Fee { send_sir: 10, send_not_sir: 10, execution: 10 }, + function_call_cost_per_byte: Fee { send_sir: 10, send_not_sir: 10, execution: 10 }, + transfer_cost: Fee { send_sir: 10, send_not_sir: 10, execution: 10 }, + stake_cost: Fee { send_sir: 10, send_not_sir: 10, execution: 10 }, add_key_cost: AccessKeyCreationConfig { - full_access_cost: Fee { send_sir: 1, send_not_sir: 1, execution: 1 }, - function_call_cost: Fee { send_sir: 1, send_not_sir: 1, execution: 1 }, - function_call_cost_per_byte: Fee { send_sir: 1, send_not_sir: 1, execution: 1 }, + full_access_cost: Fee { send_sir: 10, send_not_sir: 10, execution: 10 }, + function_call_cost: Fee { send_sir: 10, send_not_sir: 10, execution: 10 }, + function_call_cost_per_byte: Fee { + send_sir: 10, + send_not_sir: 10, + execution: 10, + }, }, - delete_key_cost: Fee { send_sir: 1, send_not_sir: 1, execution: 1 }, - delete_account_cost: Fee { send_sir: 1, send_not_sir: 1, execution: 1 }, + delete_key_cost: Fee { send_sir: 10, send_not_sir: 10, execution: 10 }, + delete_account_cost: Fee { send_sir: 10, send_not_sir: 10, execution: 10 }, }, storage_usage_config: StorageUsageConfig { account_cost: 100, @@ -143,6 +160,7 @@ impl Default for RuntimeFeesConfig { value_cost_per_byte: 1, code_cost_per_byte: 1, }, + burnt_gas_reward: Fraction { numerator: 3, denominator: 10 }, } } } @@ -179,6 +197,7 @@ impl RuntimeFeesConfig { value_cost_per_byte: 0, code_cost_per_byte: 0, }, + burnt_gas_reward: Fraction { numerator: 0, denominator: 1 }, } } } diff --git a/runtime/near-vm-logic/src/logic.rs b/runtime/near-vm-logic/src/logic.rs index eb1705ff186..a5753d46f43 100644 --- a/runtime/near-vm-logic/src/logic.rs +++ b/runtime/near-vm-logic/src/logic.rs @@ -820,25 +820,24 @@ impl<'a> VMLogic<'a> { self.burnt_gas.checked_add(burn_gas as _).ok_or(HostError::IntegerOverflow)?; let new_used_gas = self.used_gas.checked_add(use_gas as _).ok_or(HostError::IntegerOverflow)?; - if self.context.free_of_charge - || (new_burnt_gas < self.config.max_gas_burnt - && new_used_gas < self.context.prepaid_gas) + if new_burnt_gas <= self.config.max_gas_burnt + && (self.context.free_of_charge || new_used_gas <= self.context.prepaid_gas) { self.burnt_gas = new_burnt_gas; self.used_gas = new_used_gas; Ok(()) } else { use std::cmp::min; - let res = if new_burnt_gas >= self.config.max_gas_burnt { + let res = if new_burnt_gas > self.config.max_gas_burnt { Err(HostError::GasLimitExceeded) - } else if new_used_gas >= self.context.prepaid_gas { + } else if new_used_gas > self.context.prepaid_gas { Err(HostError::GasExceeded) } else { unreachable!() }; - self.burnt_gas = - min(self.context.prepaid_gas, min(new_burnt_gas, self.config.max_gas_burnt)); + let max_burnt_gas = min(self.config.max_gas_burnt, self.context.prepaid_gas); + self.burnt_gas = min(new_burnt_gas, max_burnt_gas); self.used_gas = min(new_used_gas, self.context.prepaid_gas); res diff --git a/runtime/runtime/src/lib.rs b/runtime/runtime/src/lib.rs index e4190db5242..86499d54722 100644 --- a/runtime/runtime/src/lib.rs +++ b/runtime/runtime/src/lib.rs @@ -65,6 +65,12 @@ pub struct ApplyState { pub gas_price: Balance, } +#[derive(Debug)] +pub struct VerificationResult { + pub gas_burnt: Gas, + pub gas_used: Gas, +} + pub struct ApplyResult { pub root: MerkleHash, pub trie_changes: TrieChanges, @@ -148,7 +154,7 @@ impl Runtime { state_update: &mut TrieUpdate, apply_state: &ApplyState, signed_transaction: &SignedTransaction, - ) -> Result<(), Box> { + ) -> Result> { let transaction = &signed_transaction.transaction; let signer_id = &transaction.signer_id; if !is_valid_account_id(&signer_id) { @@ -202,28 +208,29 @@ impl Runtime { apply_rent(&signer_id, &mut signer, apply_state.block_index, &self.config); access_key.nonce = transaction.nonce; - - let mut total_cost_gas: Gas = safe_add_gas( - self.config - .transaction_costs - .action_receipt_creation_config - .send_fee(sender_is_receiver), - self.config.transaction_costs.action_receipt_creation_config.exec_fee(), - )?; - total_cost_gas = safe_add_gas( - total_cost_gas, + let mut gas_burnt: Gas = self + .config + .transaction_costs + .action_receipt_creation_config + .send_fee(sender_is_receiver); + gas_burnt = safe_add_gas( + gas_burnt, total_send_fees( &self.config.transaction_costs, sender_is_receiver, &transaction.actions, )?, )?; - total_cost_gas = safe_add_gas( - total_cost_gas, + let mut gas_used = safe_add_gas( + gas_burnt, + self.config.transaction_costs.action_receipt_creation_config.exec_fee(), + )?; + gas_used = safe_add_gas( + gas_used, total_exec_fees(&self.config.transaction_costs, &transaction.actions)?, )?; - total_cost_gas = safe_add_gas(total_cost_gas, total_prepaid_gas(&transaction.actions)?)?; - let mut total_cost = safe_gas_to_balance(apply_state.gas_price, total_cost_gas)?; + gas_used = safe_add_gas(gas_used, total_prepaid_gas(&transaction.actions)?)?; + let mut total_cost = safe_gas_to_balance(apply_state.gas_price, gas_used)?; total_cost = safe_add_balance(total_cost, total_deposit(&transaction.actions)?)?; signer.amount = signer.amount.checked_sub(total_cost).ok_or_else(|| { format!( @@ -287,36 +294,19 @@ impl Runtime { }?; set_access_key(state_update, &signer_id, &transaction.public_key, &access_key); + + // Account reward for gas burnt. + signer.amount += (gas_burnt * self.config.transaction_costs.burnt_gas_reward.numerator + / self.config.transaction_costs.burnt_gas_reward.denominator) + as Balance + * apply_state.gas_price; + set_account(state_update, &signer_id, &signer); - Ok(()) + Ok(VerificationResult { gas_burnt, gas_used }) } /// Processes signed transaction, charges fees and generates the receipt - fn apply_signed_transaction( - &self, - state_update: &mut TrieUpdate, - apply_state: &ApplyState, - signed_transaction: &SignedTransaction, - ) -> Result> { - self.verify_and_charge_transaction(state_update, apply_state, signed_transaction)?; - let transaction = &signed_transaction.transaction; - Ok(Receipt { - predecessor_id: transaction.signer_id.clone(), - receiver_id: transaction.receiver_id.clone(), - receipt_id: create_nonce_with_nonce(&signed_transaction.get_hash(), 0), - - receipt: ReceiptEnum::Action(ActionReceipt { - signer_id: transaction.signer_id.clone(), - signer_public_key: transaction.public_key, - gas_price: apply_state.gas_price, - output_data_receivers: vec![], - input_data_ids: vec![], - actions: transaction.actions.clone(), - }), - }) - } - fn process_transaction( &self, state_update: &mut TrieUpdate, @@ -325,28 +315,55 @@ impl Runtime { new_local_receipts: &mut Vec, new_receipts: &mut Vec, ) -> Result { - let mut result = TransactionResult::default(); - match self.apply_signed_transaction(state_update, apply_state, signed_transaction) { - Ok(receipt) => { - result.receipts.push(receipt.receipt_id); - if receipt.receiver_id == signed_transaction.transaction.signer_id { - new_local_receipts.push(receipt); - } else { - new_receipts.push(receipt); + let result = + match self.verify_and_charge_transaction(state_update, apply_state, signed_transaction) + { + Ok(verification_result) => { + state_update.commit(); + let transaction = &signed_transaction.transaction; + let receipt = Receipt { + predecessor_id: transaction.signer_id.clone(), + receiver_id: transaction.receiver_id.clone(), + receipt_id: create_nonce_with_nonce(&signed_transaction.get_hash(), 0), + + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: transaction.signer_id.clone(), + signer_public_key: transaction.public_key, + gas_price: apply_state.gas_price, + output_data_receivers: vec![], + input_data_ids: vec![], + actions: transaction.actions.clone(), + }), + }; + let receipt_id = receipt.receipt_id; + if receipt.receiver_id == signed_transaction.transaction.signer_id { + new_local_receipts.push(receipt); + } else { + new_receipts.push(receipt); + } + TransactionResult { + status: TransactionStatus::Completed, + logs: vec![], + receipts: vec![receipt_id], + result: None, + gas_burnt: verification_result.gas_burnt, + } } - state_update.commit(); - result.status = TransactionStatus::Completed; - } - Err(s) => { - state_update.rollback(); - if let Some(e) = s.downcast_ref::() { - // TODO fix error type in apply_signed_transaction - return Err(e.clone()); + Err(s) => { + state_update.rollback(); + if let Some(e) = s.downcast_ref::() { + // TODO fix error type in apply_signed_transaction + return Err(e.clone()); + } + TransactionResult { + status: TransactionStatus::Failed, + logs: vec![format!("Runtime error: {}", s)], + receipts: vec![], + result: None, + gas_burnt: 0, + } } - result.logs.push(format!("Runtime error: {}", s)); - result.status = TransactionStatus::Failed; - } - }; + }; Self::print_log(&result.logs); Ok(TransactionLog { hash: signed_transaction.get_hash(), result }) } @@ -523,8 +540,14 @@ impl Runtime { } } - // Calculating and generating refunds - self.generate_refund_receipts(receipt, action_receipt, &mut result); + // If the receipt is a refund, then we consider it free without burnt gas. + if receipt.predecessor_id == system_account() { + result.gas_burnt = 0; + result.gas_used = 0; + } else { + // Calculating and generating refunds + self.generate_refund_receipts(receipt, action_receipt, &mut result); + } // Moving validator proposals validator_proposals.append(&mut result.validator_proposals); @@ -542,6 +565,19 @@ impl Runtime { } }; + // Adding burnt gas reward if the account exists. + let gas_reward = result.gas_burnt + * self.config.transaction_costs.burnt_gas_reward.numerator + / self.config.transaction_costs.burnt_gas_reward.denominator; + if gas_reward > 0 { + let mut account = get_account(state_update, account_id)?; + if let Some(ref mut account) = account { + account.amount += gas_reward as Balance * action_receipt.gas_price; + set_account(state_update, account_id, account); + state_update.commit(); + } + } + // Generating outgoing data and receipts let transaction_result = if let Ok(ReturnData::ReceiptIndex(receipt_index)) = result.result { @@ -609,6 +645,7 @@ impl Runtime { logs: result.logs, receipts: transaction_new_receipt_ids, result: transaction_result, + gas_burnt: result.gas_burnt, }, }) } @@ -625,7 +662,6 @@ impl Runtime { let exec_gas = total_exec_fees(&self.config.transaction_costs, &action_receipt.actions) .expect(OVERFLOW_CHECKED_ERR) + self.config.transaction_costs.action_receipt_creation_config.exec_fee(); - let mut deposit_refund = if result.result.is_err() { total_deposit } else { 0 }; let gas_refund = if result.result.is_err() { prepaid_gas + exec_gas - result.gas_burnt @@ -638,10 +674,10 @@ impl Runtime { deposit_refund += gas_balance_refund; gas_balance_refund = 0; } - if deposit_refund > 0 && &receipt.predecessor_id != &system_account() { + if deposit_refund > 0 { result.new_receipts.push(Receipt::new_refund(&receipt.predecessor_id, deposit_refund)); } - if gas_balance_refund > 0 && &action_receipt.signer_id != &system_account() { + if gas_balance_refund > 0 { result .new_receipts .push(Receipt::new_refund(&action_receipt.signer_id, gas_balance_refund)); diff --git a/test-utils/testlib/src/fees_utils.rs b/test-utils/testlib/src/fees_utils.rs index 9988eeeab76..1cee91cd0b6 100644 --- a/test-utils/testlib/src/fees_utils.rs +++ b/test-utils/testlib/src/fees_utils.rs @@ -1,126 +1,149 @@ //! Helper functions to compute the costs of certain actions assuming they succeed and the only //! actions in the transaction batch. use near::config::INITIAL_GAS_PRICE; -use near_primitives::types::Balance; +use near_primitives::types::{Balance, Gas}; use near_runtime_fees::RuntimeFeesConfig; const GAS_PRICE: u128 = INITIAL_GAS_PRICE; +pub fn gas_burnt_to_reward(gas_burnt: Gas) -> Balance { + let cfg = RuntimeFeesConfig::default(); + let gas_reward = gas_burnt * cfg.burnt_gas_reward.numerator / cfg.burnt_gas_reward.denominator; + gas_reward as Balance * GAS_PRICE +} + pub fn create_account_cost() -> Balance { let cfg = RuntimeFeesConfig::default(); - let gas = cfg.action_receipt_creation_config.exec_fee() - + cfg.action_receipt_creation_config.send_fee(false) - + cfg.action_creation_config.create_account_cost.exec_fee() + let exec_gas = cfg.action_receipt_creation_config.exec_fee() + + cfg.action_creation_config.create_account_cost.exec_fee(); + let send_gas = cfg.action_receipt_creation_config.send_fee(false) + cfg.action_creation_config.create_account_cost.send_fee(false); - gas as Balance * GAS_PRICE + (exec_gas + send_gas) as Balance * GAS_PRICE - gas_burnt_to_reward(send_gas) } pub fn create_account_transfer_full_key_cost() -> Balance { let cfg = RuntimeFeesConfig::default(); - let gas = cfg.action_receipt_creation_config.exec_fee() - + cfg.action_receipt_creation_config.send_fee(false) + let exec_gas = cfg.action_receipt_creation_config.exec_fee() + cfg.action_creation_config.create_account_cost.exec_fee() + + cfg.action_creation_config.transfer_cost.exec_fee() + + cfg.action_creation_config.add_key_cost.full_access_cost.exec_fee(); + let send_gas = cfg.action_receipt_creation_config.send_fee(false) + cfg.action_creation_config.create_account_cost.send_fee(false) + + cfg.action_creation_config.transfer_cost.send_fee(false) + + cfg.action_creation_config.add_key_cost.full_access_cost.send_fee(false); + (exec_gas + send_gas) as Balance * GAS_PRICE - gas_burnt_to_reward(send_gas) +} + +pub fn create_account_transfer_full_key_cost_no_reward() -> Balance { + let cfg = RuntimeFeesConfig::default(); + let exec_gas = cfg.action_receipt_creation_config.exec_fee() + + cfg.action_creation_config.create_account_cost.exec_fee() + cfg.action_creation_config.transfer_cost.exec_fee() + + cfg.action_creation_config.add_key_cost.full_access_cost.exec_fee(); + let send_gas = cfg.action_receipt_creation_config.send_fee(false) + + cfg.action_creation_config.create_account_cost.send_fee(false) + cfg.action_creation_config.transfer_cost.send_fee(false) - + cfg.action_creation_config.add_key_cost.full_access_cost.exec_fee() + cfg.action_creation_config.add_key_cost.full_access_cost.send_fee(false); - gas as Balance * GAS_PRICE + (exec_gas + send_gas) as Balance * GAS_PRICE } pub fn create_account_transfer_full_key_cost_fail_on_create_account() -> Balance { let cfg = RuntimeFeesConfig::default(); - let gas = cfg.action_receipt_creation_config.exec_fee() - + cfg.action_receipt_creation_config.send_fee(false) - + cfg.action_creation_config.create_account_cost.exec_fee() + let exec_gas = cfg.action_receipt_creation_config.exec_fee() + + cfg.action_creation_config.create_account_cost.exec_fee(); + let send_gas = cfg.action_receipt_creation_config.send_fee(false) + cfg.action_creation_config.create_account_cost.send_fee(false) + cfg.action_creation_config.transfer_cost.send_fee(false) + cfg.action_creation_config.add_key_cost.full_access_cost.send_fee(false); - gas as Balance * GAS_PRICE + (exec_gas + send_gas) as Balance * GAS_PRICE - gas_burnt_to_reward(send_gas) } pub fn deploy_contract_cost(num_bytes: u64) -> Balance { let cfg = RuntimeFeesConfig::default(); - let gas = cfg.action_receipt_creation_config.exec_fee() - + cfg.action_receipt_creation_config.send_fee(false) + let exec_gas = cfg.action_receipt_creation_config.exec_fee() + cfg.action_creation_config.deploy_contract_cost.exec_fee() - + cfg.action_creation_config.deploy_contract_cost.send_fee(false) - + num_bytes - * (cfg.action_creation_config.deploy_contract_cost_per_byte.exec_fee() - + cfg.action_creation_config.deploy_contract_cost_per_byte.send_fee(false)); - gas as Balance * GAS_PRICE + + num_bytes * cfg.action_creation_config.deploy_contract_cost_per_byte.exec_fee(); + let send_gas = cfg.action_receipt_creation_config.send_fee(true) + + cfg.action_creation_config.deploy_contract_cost.send_fee(true) + + num_bytes * cfg.action_creation_config.deploy_contract_cost_per_byte.send_fee(true); + (exec_gas + send_gas) as Balance * GAS_PRICE + - gas_burnt_to_reward(send_gas) + - gas_burnt_to_reward(exec_gas) } pub fn function_call_cost(num_bytes: u64, prepaid_gas: u64) -> Balance { let cfg = RuntimeFeesConfig::default(); - let gas = cfg.action_receipt_creation_config.exec_fee() - + cfg.action_receipt_creation_config.send_fee(false) + let exec_gas = cfg.action_receipt_creation_config.exec_fee() + cfg.action_creation_config.function_call_cost.exec_fee() + + num_bytes * cfg.action_creation_config.function_call_cost_per_byte.exec_fee(); + let send_gas = cfg.action_receipt_creation_config.send_fee(false) + cfg.action_creation_config.function_call_cost.send_fee(false) - + num_bytes - * (cfg.action_creation_config.function_call_cost_per_byte.exec_fee() - + cfg.action_creation_config.function_call_cost_per_byte.send_fee(false)) - + prepaid_gas; - gas as Balance * GAS_PRICE + + num_bytes * cfg.action_creation_config.function_call_cost_per_byte.send_fee(false); + (exec_gas + send_gas + prepaid_gas) as Balance * GAS_PRICE - gas_burnt_to_reward(send_gas) } pub fn transfer_cost() -> Balance { let cfg = RuntimeFeesConfig::default(); - let gas = cfg.action_receipt_creation_config.exec_fee() - + cfg.action_receipt_creation_config.send_fee(false) - + cfg.action_creation_config.transfer_cost.exec_fee() + let exec_gas = cfg.action_receipt_creation_config.exec_fee() + + cfg.action_creation_config.transfer_cost.exec_fee(); + let send_gas = cfg.action_receipt_creation_config.send_fee(false) + cfg.action_creation_config.transfer_cost.send_fee(false); - gas as Balance * GAS_PRICE + (exec_gas + send_gas) as Balance * GAS_PRICE - gas_burnt_to_reward(send_gas) } pub fn stake_cost() -> Balance { let cfg = RuntimeFeesConfig::default(); - let gas = cfg.action_receipt_creation_config.exec_fee() - + cfg.action_receipt_creation_config.send_fee(false) - + cfg.action_creation_config.stake_cost.exec_fee() - + cfg.action_creation_config.stake_cost.send_fee(false); - gas as Balance * GAS_PRICE + let exec_gas = cfg.action_receipt_creation_config.exec_fee() + + cfg.action_creation_config.stake_cost.exec_fee(); + let send_gas = cfg.action_receipt_creation_config.send_fee(true) + + cfg.action_creation_config.stake_cost.send_fee(true); + (exec_gas + send_gas) as Balance * GAS_PRICE + - gas_burnt_to_reward(send_gas) + - gas_burnt_to_reward(exec_gas) } pub fn add_key_cost(num_bytes: u64) -> Balance { let cfg = RuntimeFeesConfig::default(); - let gas = cfg.action_receipt_creation_config.exec_fee() - + cfg.action_receipt_creation_config.send_fee(false) + let exec_gas = cfg.action_receipt_creation_config.exec_fee() + cfg.action_creation_config.add_key_cost.function_call_cost.exec_fee() - + cfg.action_creation_config.add_key_cost.function_call_cost.send_fee(false) + num_bytes - * (cfg.action_creation_config.add_key_cost.function_call_cost_per_byte.exec_fee() - + cfg - .action_creation_config - .add_key_cost - .function_call_cost_per_byte - .send_fee(false)); - gas as Balance * GAS_PRICE + * cfg.action_creation_config.add_key_cost.function_call_cost_per_byte.exec_fee(); + let send_gas = cfg.action_receipt_creation_config.send_fee(true) + + cfg.action_creation_config.add_key_cost.function_call_cost.send_fee(true) + + num_bytes + * cfg.action_creation_config.add_key_cost.function_call_cost_per_byte.send_fee(true); + (exec_gas + send_gas) as Balance * GAS_PRICE + - gas_burnt_to_reward(send_gas) + - gas_burnt_to_reward(exec_gas) } pub fn add_key_full_cost() -> Balance { let cfg = RuntimeFeesConfig::default(); - let gas = cfg.action_receipt_creation_config.exec_fee() - + cfg.action_receipt_creation_config.send_fee(false) - + cfg.action_creation_config.add_key_cost.full_access_cost.exec_fee() - + cfg.action_creation_config.add_key_cost.full_access_cost.send_fee(false); - gas as Balance * GAS_PRICE + let exec_gas = cfg.action_receipt_creation_config.exec_fee() + + cfg.action_creation_config.add_key_cost.full_access_cost.exec_fee(); + let send_gas = cfg.action_receipt_creation_config.send_fee(true) + + cfg.action_creation_config.add_key_cost.full_access_cost.send_fee(true); + (exec_gas + send_gas) as Balance * GAS_PRICE + - gas_burnt_to_reward(send_gas) + - gas_burnt_to_reward(exec_gas) } pub fn delete_key_cost() -> Balance { let cfg = RuntimeFeesConfig::default(); - let gas = cfg.action_receipt_creation_config.exec_fee() - + cfg.action_receipt_creation_config.send_fee(false) - + cfg.action_creation_config.delete_key_cost.exec_fee() - + cfg.action_creation_config.delete_key_cost.send_fee(false); - gas as Balance * GAS_PRICE + let exec_gas = cfg.action_receipt_creation_config.exec_fee() + + cfg.action_creation_config.delete_key_cost.exec_fee(); + let send_gas = cfg.action_receipt_creation_config.send_fee(true) + + cfg.action_creation_config.delete_key_cost.send_fee(true); + (exec_gas + send_gas) as Balance * GAS_PRICE + - gas_burnt_to_reward(send_gas) + - gas_burnt_to_reward(exec_gas) } pub fn delete_account_cost() -> Balance { let cfg = RuntimeFeesConfig::default(); - let gas = cfg.action_receipt_creation_config.exec_fee() - + cfg.action_receipt_creation_config.send_fee(false) - + cfg.action_creation_config.delete_account_cost.exec_fee() + let exec_gas = cfg.action_receipt_creation_config.exec_fee() + + cfg.action_creation_config.delete_account_cost.exec_fee(); + let send_gas = cfg.action_receipt_creation_config.send_fee(false) + cfg.action_creation_config.delete_account_cost.send_fee(false); - gas as Balance * GAS_PRICE + (exec_gas + send_gas) as Balance * GAS_PRICE - gas_burnt_to_reward(send_gas) } diff --git a/test-utils/testlib/src/node/runtime_node.rs b/test-utils/testlib/src/node/runtime_node.rs index 13ab1c74344..18ba56a769e 100644 --- a/test-utils/testlib/src/node/runtime_node.rs +++ b/test-utils/testlib/src/node/runtime_node.rs @@ -62,7 +62,7 @@ impl Node for RuntimeNode { #[cfg(test)] mod tests { - use crate::fees_utils::transfer_cost; + use crate::fees_utils::{gas_burnt_to_reward, transfer_cost}; use crate::node::runtime_node::RuntimeNode; use crate::node::Node; use crate::runtime_utils::{alice_account, bob_account}; @@ -71,7 +71,7 @@ mod tests { pub fn test_send_money() { let node = RuntimeNode::new(&"alice.near".to_string()); let node_user = node.user(); - node_user.send_money(alice_account(), bob_account(), 1); + let transaction_result = node_user.send_money(alice_account(), bob_account(), 1); let transfer_cost = transfer_cost(); let (alice1, bob1) = ( node.view_balance(&alice_account()).unwrap(), @@ -83,7 +83,8 @@ mod tests { node.view_balance(&bob_account()).unwrap(), ); assert_eq!(alice2, alice1 - 1 - transfer_cost); - assert_eq!(bob2, bob1 + 1); + let reward = gas_burnt_to_reward(transaction_result.transactions[1].result.gas_burnt); + assert_eq!(bob2, bob1 + 1 + reward); } } diff --git a/test-utils/testlib/src/standard_test_cases.rs b/test-utils/testlib/src/standard_test_cases.rs index f3519c4a80c..934c611b241 100644 --- a/test-utils/testlib/src/standard_test_cases.rs +++ b/test-utils/testlib/src/standard_test_cases.rs @@ -5,8 +5,8 @@ use near_crypto::{InMemorySigner, KeyType}; use near_primitives::account::{AccessKey, AccessKeyPermission, FunctionCallPermission}; use near_primitives::hash::hash; use near_primitives::types::Balance; -use near_primitives::views::AccountView; use near_primitives::views::FinalTransactionStatus; +use near_primitives::views::{AccountView, FinalTransactionResult}; use crate::fees_utils::*; use crate::node::Node; @@ -24,7 +24,7 @@ fn add_access_key( node_user: &dyn User, access_key: &AccessKey, signer2: &InMemorySigner, -) { +) -> FinalTransactionResult { let root = node_user.get_state_root(); let account_id = &node.account_id().unwrap(); let transaction_result = @@ -33,6 +33,7 @@ fn add_access_key( assert_eq!(transaction_result.transactions.len(), 2); let new_root = node_user.get_state_root(); assert_ne!(root, new_root); + transaction_result } pub fn test_smart_contract_simple(node: impl Node) { @@ -171,7 +172,7 @@ pub fn test_upload_contract(node: impl Node) { account_id.clone(), eve_dot_alice_account(), node.signer().public_key(), - 10000, + 1000000, ); assert_eq!(transaction_result.status, FinalTransactionStatus::Completed); assert_eq!(transaction_result.transactions.len(), 2); @@ -207,7 +208,7 @@ pub fn test_send_money(node: impl Node) { let account_id = &node.account_id().unwrap(); let node_user = node.user(); let root = node_user.get_state_root(); - let money_used = 10; + let money_used = 10000; let transfer_cost = transfer_cost(); let transaction_result = node_user.send_money(account_id.clone(), bob_account(), money_used); assert_eq!(transaction_result.status, FinalTransactionStatus::Completed); @@ -227,11 +228,12 @@ pub fn test_send_money(node: impl Node) { storage_usage: 64919, } ); + let reward = gas_burnt_to_reward(transaction_result.transactions[1].result.gas_burnt); let result2 = node_user.view_account(&bob_account()).unwrap(); assert_eq!( result2, AccountView { - amount: TESTING_INIT_BALANCE + money_used - TESTING_INIT_STAKE, + amount: TESTING_INIT_BALANCE + money_used - TESTING_INIT_STAKE + reward, staked: TESTING_INIT_STAKE, code_hash: default_code_hash().into(), storage_paid_at: 0, @@ -240,6 +242,23 @@ pub fn test_send_money(node: impl Node) { ); } +pub fn test_smart_contract_reward(node: impl Node) { + let node_user = node.user(); + let root = node_user.get_state_root(); + let bob = node_user.view_account(&bob_account()).unwrap(); + assert_eq!(bob.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE); + let transaction_result = + node_user.function_call(alice_account(), bob_account(), "run_test", vec![], 1000000, 0); + assert_eq!(transaction_result.status, FinalTransactionStatus::Completed); + assert_eq!(transaction_result.transactions.len(), 3); + let new_root = node_user.get_state_root(); + assert_ne!(root, new_root); + + let bob = node_user.view_account(&bob_account()).unwrap(); + let reward = gas_burnt_to_reward(transaction_result.transactions[1].result.gas_burnt); + assert_eq!(bob.amount, TESTING_INIT_BALANCE - TESTING_INIT_STAKE + reward); +} + pub fn test_send_money_over_balance(node: impl Node) { let account_id = &node.account_id().unwrap(); let node_user = node.user(); @@ -291,7 +310,7 @@ pub fn test_create_account(node: impl Node) { let account_id = &node.account_id().unwrap(); let node_user = node.user(); let root = node_user.get_state_root(); - let money_used = 10; + let money_used = 1000; let transaction_result = node_user.create_account( account_id.clone(), eve_dot_alice_account(), @@ -316,35 +335,35 @@ pub fn test_create_account(node: impl Node) { ) ); + let reward = gas_burnt_to_reward(transaction_result.transactions[1].result.gas_burnt); let result2 = node_user.view_account(&eve_dot_alice_account()).unwrap(); - assert_eq!((result2.amount, result2.staked), (money_used, 0)); + assert_eq!((result2.amount, result2.staked), (money_used + reward, 0)); } pub fn test_create_account_again(node: impl Node) { let account_id = &node.account_id().unwrap(); let node_user = node.user(); let root = node_user.get_state_root(); - let money_used = 10; - node_user.create_account( + let money_used = 1000; + let transaction_result = node_user.create_account( account_id.clone(), eve_dot_alice_account(), node.signer().public_key(), money_used, ); + assert_eq!(transaction_result.status, FinalTransactionStatus::Completed); + assert_eq!(transaction_result.transactions.len(), 2); let create_account_cost = create_account_transfer_full_key_cost(); let result1 = node_user.view_account(account_id).unwrap(); - assert_eq!( - (result1.amount, result1.staked), - ( - TESTING_INIT_BALANCE - money_used - TESTING_INIT_STAKE - create_account_cost, - TESTING_INIT_STAKE - ) - ); + let new_expected_balance = + TESTING_INIT_BALANCE - money_used - TESTING_INIT_STAKE - create_account_cost; + assert_eq!((result1.amount, result1.staked), (new_expected_balance, TESTING_INIT_STAKE)); assert_eq!(node_user.get_access_key_nonce_for_signer(account_id).unwrap(), 1); + let reward = gas_burnt_to_reward(transaction_result.transactions[1].result.gas_burnt); let result2 = node_user.view_account(&eve_dot_alice_account()).unwrap(); - assert_eq!((result2.amount, result2.staked), (money_used, 0)); + assert_eq!((result2.amount, result2.staked), (money_used + reward, 0)); let transaction_result = node_user.create_account( account_id.clone(), @@ -366,14 +385,7 @@ pub fn test_create_account_again(node: impl Node) { let result1 = node_user.view_account(account_id).unwrap(); assert_eq!( (result1.amount, result1.staked), - ( - TESTING_INIT_BALANCE - - money_used - - TESTING_INIT_STAKE - - create_account_cost - - additional_cost, - TESTING_INIT_STAKE - ) + (new_expected_balance - additional_cost, TESTING_INIT_STAKE) ); } @@ -415,7 +427,7 @@ pub fn test_create_account_failure_already_exists(node: impl Node) { let account_id = &node.account_id().unwrap(); let node_user = node.user(); let root = node_user.get_state_root(); - let money_used = 10; + let money_used = 1000; let transaction_result = node_user.create_account( account_id.clone(), @@ -436,10 +448,11 @@ pub fn test_create_account_failure_already_exists(node: impl Node) { (TESTING_INIT_BALANCE - TESTING_INIT_STAKE - create_account_cost, TESTING_INIT_STAKE) ); + let reward = gas_burnt_to_reward(transaction_result.transactions[1].result.gas_burnt); let result2 = node_user.view_account(&bob_account()).unwrap(); assert_eq!( (result2.amount, result2.staked), - (TESTING_INIT_BALANCE - TESTING_INIT_STAKE, TESTING_INIT_STAKE) + (TESTING_INIT_BALANCE - TESTING_INIT_STAKE + reward, TESTING_INIT_STAKE) ); } @@ -610,6 +623,7 @@ pub fn test_add_access_key_with_allowance(node: impl Node) { let account = node_user.view_account(account_id).unwrap(); let initial_balance = account.amount; let add_access_key_cost = add_key_cost(0); + println!("{}", add_access_key_cost); add_access_key(&node, node_user.as_ref(), &access_key, &signer2); let account = node_user.view_account(account_id).unwrap(); @@ -685,7 +699,13 @@ pub fn test_access_key_smart_contract(node: impl Node) { AccessKey { nonce: 1, permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { - allowance: Some(FUNCTION_CALL_AMOUNT - function_call_cost), + allowance: Some( + FUNCTION_CALL_AMOUNT + - function_call_cost + - gas_burnt_to_reward( + transaction_result.transactions[0].result.gas_burnt + ) + ), receiver_id: bob_account(), method_names: vec![], }), @@ -904,7 +924,7 @@ pub fn test_delete_account_no_account(node: impl Node) { } pub fn test_delete_account_while_staking(node: impl Node) { - let money_used = 1000; + let money_used = 10000; let node_user = node.user(); let _ = node_user.create_account( alice_account(), diff --git a/tests/test_cases_runtime.rs b/tests/test_cases_runtime.rs index c5ab7c47011..5853ee2116a 100644 --- a/tests/test_cases_runtime.rs +++ b/tests/test_cases_runtime.rs @@ -124,6 +124,12 @@ mod test { test_send_money(node); } + #[test] + fn test_smart_contract_reward_runtime() { + let node = create_runtime_node(); + test_smart_contract_reward(node); + } + #[test] fn test_send_money_over_balance_runtime() { let node = create_runtime_node(); diff --git a/tests/test_errors.rs b/tests/test_errors.rs index 3c62de0ede3..ce86e37bc72 100644 --- a/tests/test_errors.rs +++ b/tests/test_errors.rs @@ -55,7 +55,7 @@ fn test_deliver_tx_error_log() { let node = start_node(); let signer = Arc::new(InMemorySigner::from_seed("alice.near", KeyType::ED25519, "alice.near")); let block_hash = node.user().get_best_block_hash().unwrap(); - let cost = testlib::fees_utils::create_account_transfer_full_key_cost(); + let cost = testlib::fees_utils::create_account_transfer_full_key_cost_no_reward(); let tx = SignedTransaction::from_actions( 1, "alice.near".to_string(),