diff --git a/runtime/near-vm-logic/src/dependencies.rs b/runtime/near-vm-logic/src/dependencies.rs index 3b1311828b6..ddaa8879f1b 100644 --- a/runtime/near-vm-logic/src/dependencies.rs +++ b/runtime/near-vm-logic/src/dependencies.rs @@ -1,9 +1,7 @@ //! External dependencies of the near-vm-logic. -use crate::types::{PublicKey, ReceiptIndex}; -use near_primitives_core::types::{AccountId, Balance, Gas}; -#[cfg(feature = "protocol_feature_function_call_weight")] -use near_primitives_core::types::{GasDistribution, GasWeight}; +use near_primitives::hash::CryptoHash; +use near_primitives_core::types::{AccountId, Balance}; use near_vm_errors::VMLogicError; /// An abstraction over the memory of the smart contract. @@ -164,371 +162,7 @@ pub trait External { /// ``` fn storage_has_key(&mut self, key: &[u8]) -> Result; - /// Create a receipt which will be executed after all the receipts identified by - /// `receipt_indices` are complete. - /// - /// If any of the [`RecepitIndex`]es do not refer to a known receipt, this function will fail - /// with an error. - /// - /// # Arguments - /// - /// * `receipt_indices` - a list of receipt indices the new receipt is depend on - /// - /// # Example - /// ``` - /// # use near_vm_logic::mocks::mock_external::MockedExternal; - /// # use near_vm_logic::External; - /// - /// # let mut external = MockedExternal::new(); - /// let receipt_index_one = external.create_receipt(vec![], "charli.near".parse().unwrap()).unwrap(); - /// let receipt_index_two = external.create_receipt(vec![receipt_index_one], "bob.near".parse().unwrap()); - /// - /// ``` - fn create_receipt( - &mut self, - receipt_indices: Vec, - receiver_id: AccountId, - ) -> Result; - - /// Attach the [`CreateAccountAction`] action to an existing receipt. - /// - /// # Arguments - /// - /// * `receipt_index` - an index of Receipt to append an action - /// - /// # Example - /// ``` - /// # use near_vm_logic::mocks::mock_external::MockedExternal; - /// # use near_vm_logic::External; - /// - /// # let mut external = MockedExternal::new(); - /// let receipt_index = external.create_receipt(vec![], "charli.near".parse().unwrap()).unwrap(); - /// external.append_action_create_account(receipt_index).unwrap(); - /// - /// ``` - /// - /// # Panics - /// - /// Panics if the `receipt_index` does not refer to a known receipt. - fn append_action_create_account(&mut self, receipt_index: ReceiptIndex) -> Result<()>; - - /// Attach the [`DeployContractAction`] action to an existing receipt. - /// - /// # Arguments - /// - /// * `receipt_index` - an index of Receipt to append an action - /// * `code` - a Wasm code to attach - /// - /// # Example - /// - /// ``` - /// # use near_vm_logic::mocks::mock_external::MockedExternal; - /// # use near_vm_logic::External; - /// - /// # let mut external = MockedExternal::new(); - /// let receipt_index = external.create_receipt(vec![], "charli.near".parse().unwrap()).unwrap(); - /// external.append_action_deploy_contract(receipt_index, b"some valid Wasm code".to_vec()).unwrap(); - /// ``` - /// - /// # Panics - /// - /// Panics if the `receipt_index` does not refer to a known receipt. - fn append_action_deploy_contract( - &mut self, - receipt_index: ReceiptIndex, - code: Vec, - ) -> Result<()>; - - /// Attach the [`FunctionCallAction`] action to an existing receipt. - /// - /// # Arguments - /// - /// * `receipt_index` - an index of Receipt to append an action - /// * `method_name` - a name of the contract method to call - /// * `arguments` - a Wasm code to attach - /// * `attached_deposit` - amount of tokens to transfer with the call - /// * `prepaid_gas` - amount of prepaid gas to attach to the call - /// - /// # Example - /// - /// ``` - /// # use near_vm_logic::mocks::mock_external::MockedExternal; - /// # use near_vm_logic::External; - /// - /// # let mut external = MockedExternal::new(); - /// let receipt_index = external.create_receipt(vec![], "charli.near".parse().unwrap()).unwrap(); - /// external.append_action_function_call( - /// receipt_index, - /// b"method_name".to_vec(), - /// b"{serialised: arguments}".to_vec(), - /// 100000u128, - /// 100u64 - /// ).unwrap(); - /// ``` - /// - /// # Panics - /// - /// Panics if the `receipt_index` does not refer to a known receipt. - fn append_action_function_call( - &mut self, - receipt_index: ReceiptIndex, - method_name: Vec, - arguments: Vec, - attached_deposit: Balance, - prepaid_gas: Gas, - ) -> Result<()>; - - /// Attach the [`FunctionCallAction`] action to an existing receipt. This method has similar - /// functionality to [`append_action_function_call`](Self::append_action_function_call) except - /// that it allows specifying a weight to use leftover gas from the current execution. - /// - /// `prepaid_gas` and `gas_weight` can either be specified or both. If a `gas_weight` is - /// specified, the action should be allocated gas in - /// [`distribute_unused_gas`](Self::distribute_unused_gas). - /// - /// For more information, see [crate::VMLogic::promise_batch_action_function_call_weight]. - /// - /// # Arguments - /// - /// * `receipt_index` - an index of Receipt to append an action - /// * `method_name` - a name of the contract method to call - /// * `arguments` - a Wasm code to attach - /// * `attached_deposit` - amount of tokens to transfer with the call - /// * `prepaid_gas` - amount of prepaid gas to attach to the call - /// * `gas_weight` - relative weight of unused gas to distribute to the function call action - /// - /// # Example - /// - /// ``` - /// # use near_vm_logic::mocks::mock_external::MockedExternal; - /// # use near_vm_logic::External; - /// - /// # let mut external = MockedExternal::new(); - /// let receipt_index = external.create_receipt(vec![], "charli.near".parse().unwrap()).unwrap(); - /// external.append_action_function_call_weight( - /// receipt_index, - /// b"method_name".to_vec(), - /// b"{serialised: arguments}".to_vec(), - /// 100000u128, - /// 100u64, - /// 2, - /// ).unwrap(); - /// ``` - /// - /// # Panics - /// - /// Panics if the `receipt_index` does not refer to a known receipt. - #[cfg(feature = "protocol_feature_function_call_weight")] - fn append_action_function_call_weight( - &mut self, - receipt_index: ReceiptIndex, - method_name: Vec, - arguments: Vec, - attached_deposit: Balance, - prepaid_gas: Gas, - gas_weight: GasWeight, - ) -> Result<()>; - - /// Attach the [`TransferAction`] action to an existing receipt. - /// - /// # Arguments - /// - /// * `receipt_index` - an index of Receipt to append an action - /// * `amount` - amount of tokens to transfer - /// - /// # Example - /// - /// ``` - /// # use near_vm_logic::mocks::mock_external::MockedExternal; - /// # use near_vm_logic::External; - /// - /// # let mut external = MockedExternal::new(); - /// let receipt_index = external.create_receipt(vec![], "charli.near".parse().unwrap()).unwrap(); - /// external.append_action_transfer( - /// receipt_index, - /// 100000u128, - /// ).unwrap(); - /// ``` - /// - /// # Panics - /// - /// Panics if the `receipt_index` does not refer to a known receipt. - fn append_action_transfer( - &mut self, - receipt_index: ReceiptIndex, - amount: Balance, - ) -> Result<()>; - - /// Attach the [`StakeAction`] action to an existing receipt. - /// - /// # Arguments - /// - /// * `receipt_index` - an index of Receipt to append an action - /// * `stake` - amount of tokens to stake - /// * `public_key` - a validator public key - /// - /// # Example - /// - /// ``` - /// # use near_vm_logic::mocks::mock_external::MockedExternal; - /// # use near_vm_logic::External; - /// - /// # let mut external = MockedExternal::new(); - /// let receipt_index = external.create_receipt(vec![], "charli.near".parse().unwrap()).unwrap(); - /// external.append_action_stake( - /// receipt_index, - /// 100000u128, - /// b"some public key".to_vec() - /// ).unwrap(); - /// ``` - /// - /// # Panics - /// - /// Panics if the `receipt_index` does not refer to a known receipt. - fn append_action_stake( - &mut self, - receipt_index: ReceiptIndex, - stake: Balance, - public_key: PublicKey, - ) -> Result<()>; - - /// Attach the [`AddKeyAction`] action to an existing receipt. - /// - /// # Arguments - /// - /// * `receipt_index` - an index of Receipt to append an action - /// * `public_key` - a public key for an access key - /// * `nonce` - a nonce - /// - /// # Example - /// - /// ``` - /// # use near_vm_logic::mocks::mock_external::MockedExternal; - /// # use near_vm_logic::External; - /// - /// # let mut external = MockedExternal::new(); - /// let receipt_index = external.create_receipt(vec![], "charli.near".parse().unwrap()).unwrap(); - /// external.append_action_add_key_with_full_access( - /// receipt_index, - /// b"some public key".to_vec(), - /// 0u64 - /// ).unwrap(); - /// ``` - /// - /// # Panics - /// - /// Panics if the `receipt_index` does not refer to a known receipt. - fn append_action_add_key_with_full_access( - &mut self, - receipt_index: ReceiptIndex, - public_key: PublicKey, - nonce: u64, - ) -> Result<()>; - - /// Attach the [`AddKeyAction`] action an existing receipt. - /// - /// The access key associated with the action will have the - /// [`AccessKeyPermission::FunctionCall`] permission scope. - /// - /// # Arguments - /// - /// * `receipt_index` - an index of Receipt to append an action - /// * `public_key` - a public key for an access key - /// * `nonce` - a nonce - /// * `allowance` - amount of tokens allowed to spend by this access key - /// * `receiver_id` - a contract witch will be allowed to call with this access key - /// * `method_names` - a list of method names is allowed to call with this access key (empty = any method) - /// - /// # Example - /// - /// ``` - /// # use near_vm_logic::mocks::mock_external::MockedExternal; - /// # use near_vm_logic::External; - /// - /// # let mut external = MockedExternal::new(); - /// let receipt_index = external.create_receipt(vec![], "charli.near".parse().unwrap()).unwrap(); - /// external.append_action_add_key_with_function_call( - /// receipt_index, - /// b"some public key".to_vec(), - /// 0u64, - /// None, - /// "bob.near".parse().unwrap(), - /// vec![b"foo".to_vec(), b"bar".to_vec()] - /// ).unwrap(); - /// ``` - /// - /// # Panics - /// - /// Panics if the `receipt_index` does not refer to a known receipt. - fn append_action_add_key_with_function_call( - &mut self, - receipt_index: ReceiptIndex, - public_key: PublicKey, - nonce: u64, - allowance: Option, - receiver_id: AccountId, - method_names: Vec>, - ) -> Result<()>; - - /// Attach the [`DeleteKeyAction`] action to an existing receipt. - /// - /// # Arguments - /// - /// * `receipt_index` - an index of Receipt to append an action - /// * `public_key` - a public key for an access key to delete - /// - /// # Example - /// - /// ``` - /// # use near_vm_logic::mocks::mock_external::MockedExternal; - /// # use near_vm_logic::External; - /// - /// # let mut external = MockedExternal::new(); - /// let receipt_index = external.create_receipt(vec![], "charli.near".parse().unwrap()).unwrap(); - /// external.append_action_delete_key( - /// receipt_index, - /// b"some public key".to_vec() - /// ).unwrap(); - /// ``` - /// - /// # Panics - /// - /// Panics if the `receipt_index` does not refer to a known receipt. - fn append_action_delete_key( - &mut self, - receipt_index: ReceiptIndex, - public_key: PublicKey, - ) -> Result<()>; - - /// Attach the [`DeleteAccountAction`] action to an existing receipt - /// - /// # Arguments - /// - /// * `receipt_index` - an index of Receipt to append an action - /// * `beneficiary_id` - an account id to which the rest of the funds of the removed account will be transferred - /// - /// # Example - /// - /// ``` - /// # use near_vm_logic::mocks::mock_external::MockedExternal; - /// # use near_vm_logic::External; - /// - /// # let mut external = MockedExternal::new(); - /// let receipt_index = external.create_receipt(vec![], "charli.near".parse().unwrap()).unwrap(); - /// external.append_action_delete_account( - /// receipt_index, - /// "sam".parse().unwrap() - /// ).unwrap(); - /// ``` - /// - /// # Panics - /// - /// Panics if the `receipt_index` does not refer to a known receipt. - fn append_action_delete_account( - &mut self, - receipt_index: ReceiptIndex, - beneficiary_id: AccountId, - ) -> Result<()>; + fn generate_data_id(&mut self) -> CryptoHash; /// Returns amount of touched trie nodes by storage operations fn get_touched_nodes_count(&self) -> u64; @@ -539,16 +173,4 @@ pub trait External { /// Returns total stake of validators in the current epoch. fn validator_total_stake(&self) -> Result; - - /// Distribute the gas among the scheduled function calls that specify a gas weight. - /// - /// # Arguments - /// - /// * `gas` - amount of unused gas to distribute - /// - /// # Returns - /// - /// Function returns a [GasDistribution] that indicates how the gas was distributed. - #[cfg(feature = "protocol_feature_function_call_weight")] - fn distribute_unused_gas(&mut self, gas: Gas) -> GasDistribution; } diff --git a/runtime/near-vm-logic/src/gas_counter.rs b/runtime/near-vm-logic/src/gas_counter.rs index 3769cd63248..575e54dc77f 100644 --- a/runtime/near-vm-logic/src/gas_counter.rs +++ b/runtime/near-vm-logic/src/gas_counter.rs @@ -275,10 +275,17 @@ impl GasCounter { pub fn burnt_gas(&self) -> Gas { self.fast_counter.burnt_gas } + + /// Amount of gas used through promises and amount burned. pub fn used_gas(&self) -> Gas { self.promises_gas + self.fast_counter.burnt_gas } + /// Remaining gas based on the amount of prepaid gas not yet used. + pub fn unused_gas(&self) -> Gas { + self.prepaid_gas - self.used_gas() + } + pub fn profile_data(&self) -> ProfileData { self.profile.clone() } diff --git a/runtime/near-vm-logic/src/lib.rs b/runtime/near-vm-logic/src/lib.rs index 0cd95016bd9..97e9aa1bed3 100644 --- a/runtime/near-vm-logic/src/lib.rs +++ b/runtime/near-vm-logic/src/lib.rs @@ -7,6 +7,7 @@ mod dependencies; pub mod gas_counter; mod logic; pub mod mocks; +pub(crate) mod receipt_manager; pub mod serde_with; #[cfg(test)] mod tests; @@ -20,6 +21,7 @@ pub use near_primitives_core::config::*; pub use near_primitives_core::profile; pub use near_primitives_core::types::ProtocolVersion; pub use near_vm_errors::{HostError, VMLogicError}; +pub use receipt_manager::ReceiptMetadata; pub use types::ReturnData; pub use gas_counter::with_ext_cost_counter; diff --git a/runtime/near-vm-logic/src/logic.rs b/runtime/near-vm-logic/src/logic.rs index ad47b4e1dc8..ef3037831d2 100644 --- a/runtime/near-vm-logic/src/logic.rs +++ b/runtime/near-vm-logic/src/logic.rs @@ -1,9 +1,10 @@ use crate::context::VMContext; use crate::dependencies::{External, MemoryLike}; use crate::gas_counter::{FastGasCounter, GasCounter}; +use crate::receipt_manager::ReceiptManager; use crate::types::{PromiseIndex, PromiseResult, ReceiptIndex, ReturnData}; use crate::utils::split_method_names; -use crate::ValuePtr; +use crate::{ReceiptMetadata, ValuePtr}; use byteorder::ByteOrder; use near_crypto::Secp256K1Signature; use near_primitives::version::is_implicit_account_creation_enabled; @@ -60,14 +61,14 @@ pub struct VMLogic<'a> { /// The DAG of promises, indexed by promise id. promises: Vec, - /// Record the accounts towards which the receipts are directed. - receipt_to_account: HashMap, - /// Tracks the total log length. The sum of length of all logs. total_log_length: u64, /// Current protocol version that is used for the function call. current_protocol_version: ProtocolVersion, + + /// Handles the receipts generated through execution. + receipt_manager: ReceiptManager, } /// Promises API allows to create a DAG-structure that defines dependencies between smart contract @@ -117,6 +118,7 @@ impl<'a> VMLogic<'a> { Some(ViewConfig { max_gas_burnt: max_gas_burnt_view }) => max_gas_burnt_view, None => config.limit_config.max_gas_burnt, }; + let current_account_locked_balance = context.account_locked_balance; let gas_counter = GasCounter::new( config.ext_costs.clone(), @@ -140,12 +142,24 @@ impl<'a> VMLogic<'a> { logs: vec![], registers: HashMap::new(), promises: vec![], - receipt_to_account: HashMap::new(), total_log_length: 0, current_protocol_version, + receipt_manager: ReceiptManager::default(), } } + #[allow(dead_code)] + #[cfg(test)] + pub(crate) fn receipt_manager(&self) -> &ReceiptManager { + &self.receipt_manager + } + + #[allow(dead_code)] + #[cfg(test)] + pub(crate) fn gas_counter(&self) -> &GasCounter { + &self.gas_counter + } + // ########################### // # Memory helper functions # // ########################### @@ -1314,8 +1328,8 @@ impl<'a> VMLogic<'a> { let account_id = self.read_and_parse_account_id(account_id_ptr, account_id_len)?; let sir = account_id == self.context.current_account_id; self.pay_gas_for_new_receipt(sir, &[])?; - let new_receipt_idx = self.ext.create_receipt(vec![], account_id.clone())?; - self.receipt_to_account.insert(new_receipt_idx, account_id); + let new_receipt_idx = + self.receipt_manager.create_receipt(self.ext, vec![], account_id.clone())?; self.checked_push_promise(Promise::Receipt(new_receipt_idx)) } @@ -1368,21 +1382,22 @@ impl<'a> VMLogic<'a> { let sir = account_id == self.context.current_account_id; let deps: Vec<_> = receipt_dependencies .iter() - .map(|receipt_idx| self.get_account_by_receipt(receipt_idx) == &account_id) + .map(|&receipt_idx| self.get_account_by_receipt(receipt_idx) == &account_id) .collect(); self.pay_gas_for_new_receipt(sir, &deps)?; - let new_receipt_idx = self.ext.create_receipt(receipt_dependencies, account_id.clone())?; - self.receipt_to_account.insert(new_receipt_idx, account_id); + let new_receipt_idx = self.receipt_manager.create_receipt( + self.ext, + receipt_dependencies, + account_id.clone(), + )?; self.checked_push_promise(Promise::Receipt(new_receipt_idx)) } /// Helper function to return the account id towards which the receipt is directed. - fn get_account_by_receipt(&self, receipt_idx: &ReceiptIndex) -> &AccountId { - self.receipt_to_account - .get(receipt_idx) - .expect("promises and receipt_to_account should be consistent.") + fn get_account_by_receipt(&self, receipt_idx: ReceiptIndex) -> &AccountId { + self.receipt_manager.get_receipt_receiver(receipt_idx) } /// Helper function to return the receipt index corresponding to the given promise index. @@ -1401,7 +1416,7 @@ impl<'a> VMLogic<'a> { Promise::NotReceipt(_) => Err(HostError::CannotAppendActionToJointPromise), }?; - let account_id = self.get_account_by_receipt(&receipt_idx); + let account_id = self.get_account_by_receipt(receipt_idx); let sir = account_id == &self.context.current_account_id; Ok((receipt_idx, sir)) } @@ -1436,7 +1451,7 @@ impl<'a> VMLogic<'a> { ActionCosts::create_account, )?; - self.ext.append_action_create_account(receipt_idx)?; + self.receipt_manager.append_action_create_account(receipt_idx)?; Ok(()) } @@ -1494,7 +1509,7 @@ impl<'a> VMLogic<'a> { ActionCosts::deploy_contract, )?; - self.ext.append_action_deploy_contract(receipt_idx, code)?; + self.receipt_manager.append_action_deploy_contract(receipt_idx, code)?; Ok(()) } @@ -1527,7 +1542,13 @@ impl<'a> VMLogic<'a> { gas: Gas, ) -> Result<()> { let append_action_fn = |vm: &mut Self, receipt_idx, method_name, arguments, amount, gas| { - vm.ext.append_action_function_call(receipt_idx, method_name, arguments, amount, gas) + vm.receipt_manager.append_action_function_call( + receipt_idx, + method_name, + arguments, + amount, + gas, + ) }; self.internal_promise_batch_action_function_call( promise_idx, @@ -1590,7 +1611,7 @@ impl<'a> VMLogic<'a> { gas_weight: GasWeight, ) -> Result<()> { let append_action_fn = |vm: &mut Self, receipt_idx, method_name, arguments, amount, gas| { - vm.ext.append_action_function_call_weight( + vm.receipt_manager.append_action_function_call_weight( receipt_idx, method_name, arguments, @@ -1690,7 +1711,7 @@ impl<'a> VMLogic<'a> { let amount = self.memory_get_u128(amount_ptr)?; let (receipt_idx, sir) = self.promise_idx_to_receipt_idx_with_sir(promise_idx)?; - let receiver_id = self.get_account_by_receipt(&receipt_idx); + let receiver_id = self.get_account_by_receipt(receipt_idx); let is_receiver_implicit = is_implicit_account_creation_enabled(self.current_protocol_version) && receiver_id.is_implicit(); @@ -1705,7 +1726,7 @@ impl<'a> VMLogic<'a> { self.deduct_balance(amount)?; - self.ext.append_action_transfer(receipt_idx, amount)?; + self.receipt_manager.append_action_transfer(receipt_idx, amount)?; Ok(()) } @@ -1751,7 +1772,7 @@ impl<'a> VMLogic<'a> { ActionCosts::stake, )?; - self.ext.append_action_stake(receipt_idx, amount, public_key)?; + self.receipt_manager.append_action_stake(receipt_idx, amount, public_key)?; Ok(()) } @@ -1796,7 +1817,11 @@ impl<'a> VMLogic<'a> { ActionCosts::add_key, )?; - self.ext.append_action_add_key_with_full_access(receipt_idx, public_key, nonce)?; + self.receipt_manager.append_action_add_key_with_full_access( + receipt_idx, + public_key, + nonce, + )?; Ok(()) } @@ -1862,7 +1887,7 @@ impl<'a> VMLogic<'a> { ActionCosts::function_call, )?; - self.ext.append_action_add_key_with_function_call( + self.receipt_manager.append_action_add_key_with_function_call( receipt_idx, public_key, nonce, @@ -1913,7 +1938,7 @@ impl<'a> VMLogic<'a> { ActionCosts::delete_key, )?; - self.ext.append_action_delete_key(receipt_idx, public_key)?; + self.receipt_manager.append_action_delete_key(receipt_idx, public_key)?; Ok(()) } @@ -1956,7 +1981,7 @@ impl<'a> VMLogic<'a> { ActionCosts::delete_account, )?; - self.ext.append_action_delete_account(receipt_idx, beneficiary_id)?; + self.receipt_manager.append_action_delete_account(receipt_idx, beneficiary_id)?; Ok(()) } @@ -2621,10 +2646,11 @@ impl<'a> VMLogic<'a> { #[cfg(feature = "protocol_feature_function_call_weight")] if !self.context.is_view() { // Distribute unused gas to scheduled function calls - let unused_gas = self.context.prepaid_gas - self.gas_counter.used_gas(); + let unused_gas = self.gas_counter.unused_gas(); - // Distribute the unused gas and prepay for the gas. - if matches!(self.ext.distribute_unused_gas(unused_gas), GasDistribution::All) { + // Spend all remaining gas by distributing it among function calls that specify + // a gas weight + if let GasDistribution::All = self.receipt_manager.distribute_unused_gas(unused_gas) { self.gas_counter.prepay_gas(unused_gas).unwrap(); } } @@ -2643,6 +2669,7 @@ impl<'a> VMLogic<'a> { used_gas, logs: self.logs, profile, + action_receipts: self.receipt_manager.action_receipts, } } @@ -2675,6 +2702,7 @@ pub struct VMOutcome { pub logs: Vec, /// Data collected from making a contract call pub profile: ProfileData, + pub action_receipts: Vec<(AccountId, ReceiptMetadata)>, } impl std::fmt::Debug for VMOutcome { diff --git a/runtime/near-vm-logic/src/mocks/mock_external.rs b/runtime/near-vm-logic/src/mocks/mock_external.rs index c348af4829f..6e115a717be 100644 --- a/runtime/near-vm-logic/src/mocks/mock_external.rs +++ b/runtime/near-vm-logic/src/mocks/mock_external.rs @@ -1,8 +1,6 @@ use crate::{External, ValuePtr}; -#[cfg(feature = "protocol_feature_function_call_weight")] -use near_primitives::types::{GasDistribution, GasWeight}; +use near_primitives::hash::{hash, CryptoHash}; use near_primitives_core::types::{AccountId, Balance, Gas}; -use near_vm_errors::HostError; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -10,17 +8,8 @@ use std::collections::HashMap; /// Emulates the trie and the mock handling code. pub struct MockedExternal { pub fake_trie: HashMap, Vec>, - receipts: Vec, pub validators: HashMap, - #[cfg(feature = "protocol_feature_function_call_weight")] - gas_weights: Vec<(FunctionCallActionIndex, GasWeight)>, -} - -#[derive(Clone)] -#[cfg(feature = "protocol_feature_function_call_weight")] -struct FunctionCallActionIndex { - receipt_index: usize, - action_index: usize, + data_count: u64, } pub struct MockedValuePtr { @@ -50,11 +39,6 @@ impl MockedExternal { pub fn new() -> Self { Self::default() } - - /// Get calls to receipt create that were performed during contract call. - pub fn get_receipt_create_calls(&self) -> &Vec { - &self.receipts - } } use crate::dependencies::Result; @@ -87,154 +71,12 @@ impl External for MockedExternal { Ok(self.fake_trie.contains_key(key)) } - fn create_receipt(&mut self, receipt_indices: Vec, receiver_id: AccountId) -> Result { - if let Some(index) = receipt_indices.iter().find(|&&el| el >= self.receipts.len() as u64) { - return Err(HostError::InvalidReceiptIndex { receipt_index: *index }.into()); - } - let res = self.receipts.len() as u64; - self.receipts.push(Receipt { receipt_indices, receiver_id, actions: vec![] }); - Ok(res) - } - - fn append_action_create_account(&mut self, receipt_index: u64) -> Result<()> { - self.receipts.get_mut(receipt_index as usize).unwrap().actions.push(Action::CreateAccount); - Ok(()) - } - - fn append_action_deploy_contract(&mut self, receipt_index: u64, code: Vec) -> Result<()> { - self.receipts - .get_mut(receipt_index as usize) - .unwrap() - .actions - .push(Action::DeployContract(DeployContractAction { code })); - Ok(()) - } - - fn append_action_function_call( - &mut self, - receipt_index: u64, - method_name: Vec, - arguments: Vec, - attached_deposit: u128, - prepaid_gas: u64, - ) -> Result<()> { - self.receipts.get_mut(receipt_index as usize).unwrap().actions.push(Action::FunctionCall( - FunctionCallAction { - method_name, - args: arguments, - deposit: attached_deposit, - gas: prepaid_gas, - }, - )); - Ok(()) - } - - #[cfg(feature = "protocol_feature_function_call_weight")] - fn append_action_function_call_weight( - &mut self, - receipt_index: u64, - method_name: Vec, - arguments: Vec, - attached_deposit: u128, - prepaid_gas: Gas, - gas_weight: GasWeight, - ) -> Result<()> { - let receipt_index = receipt_index as usize; - let receipt = self.receipts.get_mut(receipt_index).unwrap(); - if gas_weight.0 > 0 { - self.gas_weights.push(( - FunctionCallActionIndex { receipt_index, action_index: receipt.actions.len() }, - gas_weight, - )); - } - - receipt.actions.push(Action::FunctionCall(FunctionCallAction { - method_name, - args: arguments, - deposit: attached_deposit, - gas: prepaid_gas, - })); - Ok(()) - } - - fn append_action_transfer(&mut self, receipt_index: u64, amount: u128) -> Result<()> { - self.receipts - .get_mut(receipt_index as usize) - .unwrap() - .actions - .push(Action::Transfer(TransferAction { deposit: amount })); - Ok(()) - } - - fn append_action_stake( - &mut self, - receipt_index: u64, - stake: u128, - public_key: Vec, - ) -> Result<()> { - self.receipts - .get_mut(receipt_index as usize) - .unwrap() - .actions - .push(Action::Stake(StakeAction { stake, public_key })); - Ok(()) - } - - fn append_action_add_key_with_full_access( - &mut self, - receipt_index: u64, - public_key: Vec, - nonce: u64, - ) -> Result<()> { - self.receipts - .get_mut(receipt_index as usize) - .unwrap() - .actions - .push(Action::AddKeyWithFullAccess(AddKeyWithFullAccessAction { public_key, nonce })); - Ok(()) - } - - fn append_action_add_key_with_function_call( - &mut self, - receipt_index: u64, - public_key: Vec, - nonce: u64, - allowance: Option, - receiver_id: AccountId, - method_names: Vec>, - ) -> Result<()> { - self.receipts.get_mut(receipt_index as usize).unwrap().actions.push( - Action::AddKeyWithFunctionCall(AddKeyWithFunctionCallAction { - public_key, - nonce, - allowance, - receiver_id, - method_names, - }), - ); - Ok(()) - } - - fn append_action_delete_key(&mut self, receipt_index: u64, public_key: Vec) -> Result<()> { - self.receipts - .get_mut(receipt_index as usize) - .unwrap() - .actions - .push(Action::DeleteKey(DeleteKeyAction { public_key })); - Ok(()) - } - - fn append_action_delete_account( - &mut self, - receipt_index: u64, - beneficiary_id: AccountId, - ) -> Result<()> { - self.receipts - .get_mut(receipt_index as usize) - .unwrap() - .actions - .push(Action::DeleteAccount(DeleteAccountAction { beneficiary_id })); - Ok(()) + fn generate_data_id(&mut self) -> CryptoHash { + // Generates some hash for the data ID to receive data. This hash should not be functionally + // used in any mocked contexts. + let data_id = hash(&self.data_count.to_le_bytes()); + self.data_count += 1; + data_id } fn get_touched_nodes_count(&self) -> u64 { @@ -248,57 +90,6 @@ impl External for MockedExternal { fn validator_total_stake(&self) -> Result { Ok(self.validators.values().sum()) } - - /// Distributes the gas passed in by splitting it among weights defined in `gas_weights`. - /// This will sum all weights, retrieve the gas per weight, then update each function - /// to add the respective amount of gas. Once all gas is distributed, the remainder of - /// the gas not assigned due to precision loss is added to the last function with a weight. - #[cfg(feature = "protocol_feature_function_call_weight")] - fn distribute_unused_gas(&mut self, gas: Gas) -> GasDistribution { - let gas_weight_sum: u128 = - self.gas_weights.iter().map(|(_, GasWeight(weight))| *weight as u128).sum(); - if gas_weight_sum != 0 { - // Floor division that will ensure gas allocated is <= gas to distribute - let gas_per_weight = (gas as u128 / gas_weight_sum) as u64; - - let mut distribute_gas = |metadata: &FunctionCallActionIndex, assigned_gas: u64| { - let FunctionCallActionIndex { receipt_index, action_index } = metadata; - if let Some(Action::FunctionCall(FunctionCallAction { ref mut gas, .. })) = self - .receipts - .get_mut(*receipt_index) - .and_then(|receipt| receipt.actions.get_mut(*action_index)) - { - *gas += assigned_gas; - } else { - panic!( - "Invalid index for assigning unused gas weight \ - (promise_index={}, action_index={})", - receipt_index, action_index - ); - } - }; - - let mut distributed = 0; - for (action_index, GasWeight(weight)) in &self.gas_weights { - // This can't overflow because the gas_per_weight is floor division - // of the weight sum. - let assigned_gas = gas_per_weight * weight; - - distribute_gas(action_index, assigned_gas); - - distributed += assigned_gas - } - - // Distribute remaining gas to final action. - if let Some((last_idx, _)) = self.gas_weights.last() { - distribute_gas(last_idx, gas - distributed); - } - self.gas_weights.clear(); - GasDistribution::All - } else { - GasDistribution::NoRatios - } - } } #[derive(Serialize, Deserialize, Clone, Debug)] diff --git a/runtime/near-vm-logic/src/receipt_manager.rs b/runtime/near-vm-logic/src/receipt_manager.rs new file mode 100644 index 00000000000..d9a3f4bcb9d --- /dev/null +++ b/runtime/near-vm-logic/src/receipt_manager.rs @@ -0,0 +1,481 @@ +use crate::types::ReceiptIndex; +use crate::External; +use borsh::BorshDeserialize; +use near_crypto::PublicKey; +use near_primitives::receipt::DataReceiver; +use near_primitives::transaction::{ + Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction, + DeployContractAction, FunctionCallAction, StakeAction, TransferAction, +}; +use near_primitives::types::{Balance, Nonce}; +use near_primitives_core::account::{AccessKey, AccessKeyPermission, FunctionCallPermission}; +use near_primitives_core::hash::CryptoHash; +use near_primitives_core::types::{AccountId, Gas}; +#[cfg(feature = "protocol_feature_function_call_weight")] +use near_primitives_core::types::{GasDistribution, GasWeight}; +use near_vm_errors::{HostError, VMLogicError}; + +type ExtResult = ::std::result::Result; + +type ActionReceipts = Vec<(AccountId, ReceiptMetadata)>; + +#[derive(Debug, Clone, PartialEq)] +pub struct ReceiptMetadata { + /// If present, where to route the output data + pub output_data_receivers: Vec, + /// A list of the input data dependencies for this Receipt to process. + /// If all `input_data_ids` for this receipt are delivered to the account + /// that means we have all the `ReceivedData` input which will be than converted to a + /// `PromiseResult::Successful(value)` or `PromiseResult::Failed` + /// depending on `ReceivedData` is `Some(_)` or `None` + pub input_data_ids: Vec, + /// A list of actions to process when all input_data_ids are filled + pub actions: Vec, +} + +#[derive(Default, Clone, PartialEq)] +pub(crate) struct ReceiptManager { + pub(crate) action_receipts: ActionReceipts, + #[cfg(feature = "protocol_feature_function_call_weight")] + gas_weights: Vec<(FunctionCallActionIndex, GasWeight)>, +} + +/// Indexes the [`ReceiptManager`]'s action receipts and actions. +#[cfg(feature = "protocol_feature_function_call_weight")] +#[derive(Debug, Clone, Copy, PartialEq)] +struct FunctionCallActionIndex { + /// Index of [`ReceiptMetadata`] in the action receipts of [`ReceiptManager`]. + receipt_index: usize, + /// Index of the [`Action`] within the [`ReceiptMetadata`]. + action_index: usize, +} + +#[cfg(feature = "protocol_feature_function_call_weight")] +fn get_fuction_call_action_mut( + action_receipts: &mut ActionReceipts, + index: FunctionCallActionIndex, +) -> &mut FunctionCallAction { + let FunctionCallActionIndex { receipt_index, action_index } = index; + if let Some(Action::FunctionCall(action)) = action_receipts + .get_mut(receipt_index) + .and_then(|(_, receipt)| receipt.actions.get_mut(action_index)) + { + action + } else { + panic!( + "Invalid function call index \ + (promise_index={}, action_index={})", + receipt_index, action_index + ); + } +} + +impl ReceiptManager { + pub(crate) fn get_receipt_receiver(&self, receipt_index: ReceiptIndex) -> &AccountId { + self.action_receipts + .get(receipt_index as usize) + .map(|(id, _)| id) + .expect("receipt index should be valid for getting receiver") + } + + /// Appends an action and returns the index the action was inserted in the receipt + fn append_action(&mut self, receipt_index: ReceiptIndex, action: Action) -> usize { + let actions = &mut self + .action_receipts + .get_mut(receipt_index as usize) + .expect("receipt index should be present") + .1 + .actions; + + actions.push(action); + + // Return index that action was inserted at + actions.len() - 1 + } + + /// Create a receipt which will be executed after all the receipts identified by + /// `receipt_indices` are complete. + /// + /// If any of the [`RecepitIndex`]es do not refer to a known receipt, this function will fail + /// with an error. + /// + /// # Arguments + /// + /// * `generate_data_id` - function to generate a data id to connect receipt output to + /// * `receipt_indices` - a list of receipt indices the new receipt is depend on + /// * `receiver_id` - account id of the receiver of the receipt created + pub(crate) fn create_receipt( + &mut self, + ext: &mut dyn External, + receipt_indices: Vec, + receiver_id: AccountId, + ) -> ExtResult { + let mut input_data_ids = vec![]; + for receipt_index in receipt_indices { + let data_id = ext.generate_data_id(); + self.action_receipts + .get_mut(receipt_index as usize) + .ok_or_else(|| HostError::InvalidReceiptIndex { receipt_index })? + .1 + .output_data_receivers + .push(DataReceiver { data_id, receiver_id: receiver_id.clone() }); + input_data_ids.push(data_id); + } + + let new_receipt = + ReceiptMetadata { output_data_receivers: vec![], input_data_ids, actions: vec![] }; + let new_receipt_index = self.action_receipts.len() as ReceiptIndex; + self.action_receipts.push((receiver_id, new_receipt)); + Ok(new_receipt_index) + } + + /// Attach the [`CreateAccountAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + pub(crate) fn append_action_create_account( + &mut self, + receipt_index: ReceiptIndex, + ) -> ExtResult<()> { + self.append_action(receipt_index, Action::CreateAccount(CreateAccountAction {})); + Ok(()) + } + + /// Attach the [`DeployContractAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `code` - a Wasm code to attach + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + pub(crate) fn append_action_deploy_contract( + &mut self, + receipt_index: ReceiptIndex, + code: Vec, + ) -> ExtResult<()> { + self.append_action(receipt_index, Action::DeployContract(DeployContractAction { code })); + Ok(()) + } + + /// Attach the [`FunctionCallAction`] action to an existing receipt. This method has similar + /// functionality to [`append_action_function_call`](Self::append_action_function_call) except + /// that it allows specifying a weight to use leftover gas from the current execution. + /// + /// `prepaid_gas` and `gas_weight` can either be specified or both. If a `gas_weight` is + /// specified, the action should be allocated gas in + /// [`distribute_unused_gas`](Self::distribute_unused_gas). + /// + /// For more information, see [crate::VMLogic::promise_batch_action_function_call_weight]. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `method_name` - a name of the contract method to call + /// * `arguments` - a Wasm code to attach + /// * `attached_deposit` - amount of tokens to transfer with the call + /// * `prepaid_gas` - amount of prepaid gas to attach to the call + /// * `gas_weight` - relative weight of unused gas to distribute to the function call action + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + #[cfg(feature = "protocol_feature_function_call_weight")] + pub(crate) fn append_action_function_call_weight( + &mut self, + receipt_index: ReceiptIndex, + method_name: Vec, + args: Vec, + attached_deposit: Balance, + prepaid_gas: Gas, + gas_weight: GasWeight, + ) -> ExtResult<()> { + let action_index = self.append_action( + receipt_index, + Action::FunctionCall(FunctionCallAction { + method_name: String::from_utf8(method_name) + .map_err(|_| HostError::InvalidMethodName)?, + args, + gas: prepaid_gas, + deposit: attached_deposit, + }), + ); + + if gas_weight.0 > 0 { + self.gas_weights.push(( + FunctionCallActionIndex { receipt_index: receipt_index as usize, action_index }, + gas_weight, + )); + } + + Ok(()) + } + + /// Attach the [`FunctionCallAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `method_name` - a name of the contract method to call + /// * `arguments` - a Wasm code to attach + /// * `attached_deposit` - amount of tokens to transfer with the call + /// * `prepaid_gas` - amount of prepaid gas to attach to the call + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + pub(crate) fn append_action_function_call( + &mut self, + receipt_index: ReceiptIndex, + method_name: Vec, + args: Vec, + attached_deposit: Balance, + prepaid_gas: Gas, + ) -> ExtResult<()> { + self.append_action( + receipt_index, + Action::FunctionCall(FunctionCallAction { + method_name: String::from_utf8(method_name) + .map_err(|_| HostError::InvalidMethodName)?, + args, + gas: prepaid_gas, + deposit: attached_deposit, + }), + ); + Ok(()) + } + + /// Attach the [`TransferAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `amount` - amount of tokens to transfer + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + pub(crate) fn append_action_transfer( + &mut self, + receipt_index: ReceiptIndex, + deposit: Balance, + ) -> ExtResult<()> { + self.append_action(receipt_index, Action::Transfer(TransferAction { deposit })); + Ok(()) + } + + /// Attach the [`StakeAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `stake` - amount of tokens to stake + /// * `public_key` - a validator public key + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + pub(crate) fn append_action_stake( + &mut self, + receipt_index: ReceiptIndex, + stake: Balance, + public_key: Vec, + ) -> ExtResult<()> { + self.append_action( + receipt_index, + Action::Stake(StakeAction { + stake, + public_key: PublicKey::try_from_slice(&public_key) + .map_err(|_| HostError::InvalidPublicKey)?, + }), + ); + Ok(()) + } + + /// Attach the [`AddKeyAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `public_key` - a public key for an access key + /// * `nonce` - a nonce + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + pub(crate) fn append_action_add_key_with_full_access( + &mut self, + receipt_index: ReceiptIndex, + public_key: Vec, + nonce: Nonce, + ) -> ExtResult<()> { + self.append_action( + receipt_index, + Action::AddKey(AddKeyAction { + public_key: PublicKey::try_from_slice(&public_key) + .map_err(|_| HostError::InvalidPublicKey)?, + access_key: AccessKey { nonce, permission: AccessKeyPermission::FullAccess }, + }), + ); + Ok(()) + } + + /// Attach the [`AddKeyAction`] action an existing receipt. + /// + /// The access key associated with the action will have the + /// [`AccessKeyPermission::FunctionCall`] permission scope. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `public_key` - a public key for an access key + /// * `nonce` - a nonce + /// * `allowance` - amount of tokens allowed to spend by this access key + /// * `receiver_id` - a contract witch will be allowed to call with this access key + /// * `method_names` - a list of method names is allowed to call with this access key (empty = any method) + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + pub(crate) fn append_action_add_key_with_function_call( + &mut self, + receipt_index: ReceiptIndex, + public_key: Vec, + nonce: Nonce, + allowance: Option, + receiver_id: AccountId, + method_names: Vec>, + ) -> ExtResult<()> { + self.append_action( + receipt_index, + Action::AddKey(AddKeyAction { + public_key: PublicKey::try_from_slice(&public_key) + .map_err(|_| HostError::InvalidPublicKey)?, + access_key: AccessKey { + nonce, + permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance, + receiver_id: receiver_id.into(), + method_names: method_names + .into_iter() + .map(|method_name| { + String::from_utf8(method_name) + .map_err(|_| HostError::InvalidMethodName) + }) + .collect::, _>>()?, + }), + }, + }), + ); + Ok(()) + } + + /// Attach the [`DeleteKeyAction`] action to an existing receipt. + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `public_key` - a public key for an access key to delete + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + pub(crate) fn append_action_delete_key( + &mut self, + receipt_index: ReceiptIndex, + public_key: Vec, + ) -> ExtResult<()> { + self.append_action( + receipt_index, + Action::DeleteKey(DeleteKeyAction { + public_key: PublicKey::try_from_slice(&public_key) + .map_err(|_| HostError::InvalidPublicKey)?, + }), + ); + Ok(()) + } + + /// Attach the [`DeleteAccountAction`] action to an existing receipt + /// + /// # Arguments + /// + /// * `receipt_index` - an index of Receipt to append an action + /// * `beneficiary_id` - an account id to which the rest of the funds of the removed account will be transferred + /// + /// # Panics + /// + /// Panics if the `receipt_index` does not refer to a known receipt. + pub(crate) fn append_action_delete_account( + &mut self, + receipt_index: ReceiptIndex, + beneficiary_id: AccountId, + ) -> ExtResult<()> { + self.append_action( + receipt_index, + Action::DeleteAccount(DeleteAccountAction { beneficiary_id }), + ); + Ok(()) + } + + /// Distribute the gas among the scheduled function calls that specify a gas weight. + /// + /// Distributes the gas passed in by splitting it among weights defined in `gas_weights`. + /// This will sum all weights, retrieve the gas per weight, then update each function + /// to add the respective amount of gas. Once all gas is distributed, the remainder of + /// the gas not assigned due to precision loss is added to the last function with a weight. + /// + /// # Arguments + /// + /// * `gas` - amount of unused gas to distribute + /// + /// # Returns + /// + /// Function returns a [GasDistribution] that indicates how the gas was distributed. + #[cfg(feature = "protocol_feature_function_call_weight")] + pub(crate) fn distribute_unused_gas(&mut self, unused_gas: Gas) -> GasDistribution { + let gas_weight_sum: u128 = + self.gas_weights.iter().map(|(_, GasWeight(weight))| *weight as u128).sum(); + + if gas_weight_sum == 0 { + return GasDistribution::NoRatios; + } + + // Floor division that will ensure gas allocated is <= gas to distribute + let gas_per_weight = (unused_gas as u128 / gas_weight_sum) as Gas; + + let mut distribute_gas = |index: &FunctionCallActionIndex, assigned_gas: Gas| { + let FunctionCallAction { gas, .. } = + get_fuction_call_action_mut(&mut self.action_receipts, *index); + + // This operation cannot overflow because the gas_per_weight calculation is a floor + // division of the total amount of gas by the weight sum and the remainder is + // distributed exactly. + *gas += assigned_gas; + }; + + let mut distributed = 0; + for (action_index, GasWeight(weight)) in &self.gas_weights { + // This can't overflow because the gas_per_weight is floor division + // of the weight sum. + let assigned_gas = gas_per_weight * weight; + + distribute_gas(action_index, assigned_gas); + + distributed += assigned_gas + } + + // Distribute remaining gas to final action. + if let Some((last_idx, _)) = self.gas_weights.last() { + distribute_gas(last_idx, unused_gas - distributed); + } + self.gas_weights.clear(); + GasDistribution::All + } +} diff --git a/runtime/near-vm-logic/src/tests/gas_counter.rs b/runtime/near-vm-logic/src/tests/gas_counter.rs index 3e2c9137373..03190dce4c0 100644 --- a/runtime/near-vm-logic/src/tests/gas_counter.rs +++ b/runtime/near-vm-logic/src/tests/gas_counter.rs @@ -4,6 +4,13 @@ use crate::tests::vm_logic_builder::VMLogicBuilder; use crate::types::Gas; use crate::{VMConfig, VMLogic}; +#[cfg(feature = "protocol_feature_function_call_weight")] +use crate::receipt_manager::ReceiptMetadata; +#[cfg(feature = "protocol_feature_function_call_weight")] +use near_primitives::transaction::{Action, FunctionCallAction}; +#[cfg(feature = "protocol_feature_function_call_weight")] +use near_primitives::types::GasWeight; + #[test] fn test_dont_burn_gas_when_exceeding_attached_gas_limit() { let gas_limit = 10u64.pow(14); @@ -103,66 +110,99 @@ fn test_hit_prepaid_gas_limit() { } #[cfg(feature = "protocol_feature_function_call_weight")] -fn function_call_weight_check(function_calls: impl IntoIterator) { - use near_primitives::types::GasWeight; +#[track_caller] +fn assert_with_gas(receipt: &ReceiptMetadata, cb: impl Fn(Gas) -> bool) { + if let Action::FunctionCall(FunctionCallAction { gas, .. }) = receipt.actions[0] { + assert!(cb(gas)); + } else { + panic!("expected function call action"); + } +} - let gas_limit = 10u64.pow(14); +#[cfg(feature = "protocol_feature_function_call_weight")] +fn function_call_weight_check(function_calls: &[(Gas, u64, Gas)]) { + let gas_limit = 10_000_000_000; - let mut logic_builder = VMLogicBuilder::default().max_gas_burnt(gas_limit); + let mut logic_builder = VMLogicBuilder::free().max_gas_burnt(gas_limit); let mut logic = logic_builder.build_with_prepaid_gas(gas_limit); - for (static_gas, gas_weight) in function_calls { + let mut ratios = vec![]; + + // Schedule all function calls + for (static_gas, gas_weight, _) in function_calls { let index = promise_batch_create(&mut logic, "rick.test").expect("should create a promise"); promise_batch_action_function_call_weight( &mut logic, index, 0, - static_gas, - GasWeight(gas_weight), + *static_gas, + GasWeight(*gas_weight), ) .expect("batch action function call should succeed"); + ratios.push((index, *gas_weight)); + } + + // Test static gas assigned before + let receipts = logic.receipt_manager().action_receipts.iter().map(|(_, rec)| rec); + for (receipt, (static_gas, _, _)) in receipts.zip(function_calls) { + assert_with_gas(receipt, |gas| gas == *static_gas); } + let outcome = logic.compute_outcome_and_distribute_gas(); + // Test gas is distributed after outcome calculated. + let receipts = outcome.action_receipts.iter().map(|(_, rec)| rec); + + // Assert lengths are equal for zip + assert_eq!(receipts.len(), function_calls.len()); + + // Assert sufficient amount was given to + for (receipt, (_, _, expected)) in receipts.zip(function_calls) { + assert_with_gas(receipt, |gas| gas == *expected); + } + // Verify that all gas was consumed (assumes at least one ratio is provided) assert_eq!(outcome.used_gas, gas_limit); } #[cfg(feature = "protocol_feature_function_call_weight")] #[test] -fn function_call_weight_single_smoke_test() { +fn function_call_weight_basic_cases_test() { + // Following tests input are in the format (static gas, gas weight, expected gas) + // and the gas limit is `10_000_000_000` + // Single function call - function_call_weight_check([(0, 1)]); + function_call_weight_check(&[(0, 1, 10_000_000_000)]); // Single function with static gas - function_call_weight_check([(888, 1)]); + function_call_weight_check(&[(888, 1, 10_000_000_000)]); // Large weight - function_call_weight_check([(0, 88888)]); + function_call_weight_check(&[(0, 88888, 10_000_000_000)]); // Weight larger than gas limit - function_call_weight_check([(0, 11u64.pow(14))]); + function_call_weight_check(&[(0, 11u64.pow(14), 10_000_000_000)]); // Split two - function_call_weight_check([(0, 3), (0, 2)]); + function_call_weight_check(&[(0, 3, 6_000_000_000), (0, 2, 4_000_000_000)]); // Split two with static gas - function_call_weight_check([(1_000_000, 3), (3_000_000, 2)]); + function_call_weight_check(&[(1_000_000, 3, 5_998_600_000), (3_000_000, 2, 4_001_400_000)]); // Many different gas weights - function_call_weight_check([ - (1_000_000, 3), - (3_000_000, 2), - (0, 1), - (1_000_000_000, 0), - (0, 4), + function_call_weight_check(&[ + (1_000_000, 3, 2_699_800_000), + (3_000_000, 2, 1_802_200_000), + (0, 1, 899_600_000), + (1_000_000_000, 0, 1_000_000_000), + (0, 4, 3_598_400_000), ]); // Weight over u64 bounds - function_call_weight_check([(0, u64::MAX), (0, 1000)]); + function_call_weight_check(&[(0, u64::MAX, 0), (0, 1000, 10_000_000_000)]); // Weights with one zero and one non-zero - function_call_weight_check([(0, 0), (0, 1)]) + function_call_weight_check(&[(0, 0, 0), (0, 1, 10_000_000_000)]) } #[cfg(feature = "protocol_feature_function_call_weight")] diff --git a/runtime/near-vm-logic/src/tests/promises.rs b/runtime/near-vm-logic/src/tests/promises.rs index 5cc2914ddb2..add7875e3ba 100644 --- a/runtime/near-vm-logic/src/tests/promises.rs +++ b/runtime/near-vm-logic/src/tests/promises.rs @@ -2,8 +2,29 @@ use crate::tests::fixtures::get_context; use crate::tests::helpers::*; use crate::tests::vm_logic_builder::VMLogicBuilder; use crate::types::PromiseResult; +use crate::VMLogic; +use borsh::BorshSerialize; +use near_account_id::AccountId; +use near_crypto::PublicKey; +use near_primitives::transaction::Action; +use serde::Serialize; use serde_json; +#[derive(Serialize)] +struct ReceiptView<'a> { + receiver_id: &'a AccountId, + actions: &'a [Action], +} + +fn vm_receipts<'a>(logic: &'a VMLogic) -> Vec> { + logic + .receipt_manager() + .action_receipts + .iter() + .map(|(receiver_id, metadata)| ReceiptView { receiver_id, actions: &metadata.actions }) + .collect() +} + #[test] fn test_promise_results() { let mut promise_results = vec![]; @@ -43,22 +64,18 @@ fn test_promise_batch_action_function_call() { .expect("should add an action to receipt"); let expected = serde_json::json!([ { - "receipt_indices":[], "receiver_id":"rick.test", "actions":[ { "FunctionCall":{ - "method_name":"promise_create","args":"args","gas":0,"deposit":0}}, + "method_name":"promise_create","args":"YXJncw==","gas":0,"deposit":"0"}}, { "FunctionCall":{ - "method_name":"promise_batch_action","args":"promise_batch_action_args","gas":0,"deposit":0} + "method_name":"promise_batch_action","args":"cHJvbWlzZV9iYXRjaF9hY3Rpb25fYXJncw==","gas":0,"deposit":"0"} } ] }]); - assert_eq!( - &serde_json::to_string(logic_builder.ext.get_receipt_create_calls()).unwrap(), - &expected.to_string() - ); + assert_eq!(&serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string()); } #[test] @@ -82,25 +99,21 @@ fn test_promise_batch_action_create_account() { assert_eq!(logic.used_gas().unwrap(), 5077478438564); let expected = serde_json::json!([ { - "receipt_indices": [], "receiver_id": "rick.test", "actions": [ { "FunctionCall": { "method_name": "promise_create", - "args": "args", + "args": "YXJncw==", "gas": 0, - "deposit": 0 + "deposit": "0" } }, - "CreateAccount" + {"CreateAccount": {}} ] } ]); - assert_eq!( - &serde_json::to_string(logic_builder.ext.get_receipt_create_calls()).unwrap(), - &expected.to_string() - ); + assert_eq!(&serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string()); } #[test] @@ -127,31 +140,26 @@ fn test_promise_batch_action_deploy_contract() { let expected = serde_json::json!( [ { - "receipt_indices": [], + "receiver_id": "rick.test", "actions": [ { "FunctionCall": { "method_name": "promise_create", - "args": "args", + "args": "YXJncw==", "gas": 0, - "deposit": 0 + "deposit": "0" } }, { "DeployContract": { - "code": [ - 115,97,109,112,108,101 - ] + "code": "c2FtcGxl" } } ] } ]); - assert_eq!( - &serde_json::to_string(logic_builder.ext.get_receipt_create_calls()).unwrap(), - &expected.to_string() - ); + assert_eq!(&serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string()); } #[test] @@ -183,29 +191,26 @@ fn test_promise_batch_action_transfer() { let expected = serde_json::json!( [ { - "receipt_indices": [], + "receiver_id": "rick.test", "actions": [ { "FunctionCall": { "method_name": "promise_create", - "args": "args", + "args": "YXJncw==", "gas": 0, - "deposit": 0 + "deposit": "0" } }, { "Transfer": { - "deposit": 110 + "deposit": "110" } } ] } ]); - assert_eq!( - &serde_json::to_string(logic_builder.ext.get_receipt_create_calls()).unwrap(), - &expected.to_string() - ); + assert_eq!(&serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string()); } #[test] @@ -216,7 +221,11 @@ fn test_promise_batch_action_stake() { let mut logic_builder = VMLogicBuilder::default(); let mut logic = logic_builder.build(context); let index = promise_create(&mut logic, b"rick.test", 0, 0).expect("should create a promise"); - let key = b"ed25519:5do5nkAEVhL8iteDvXNgxi4pWK78Y7DDadX11ArFNyrf"; + let key = "ed25519:5do5nkAEVhL8iteDvXNgxi4pWK78Y7DDadX11ArFNyrf" + .parse::() + .unwrap() + .try_to_vec() + .unwrap(); logic .promise_batch_action_stake( @@ -246,33 +255,30 @@ fn test_promise_batch_action_stake() { key.as_ptr() as _, ) .expect("should add an action to stake"); - assert_eq!(logic.used_gas().unwrap(), 5138631652196); + assert_eq!(logic.used_gas().unwrap(), 5138414976215); let expected = serde_json::json!([ { - "receipt_indices": [], + "receiver_id": "rick.test", "actions": [ { "FunctionCall": { "method_name": "promise_create", - "args": "args", + "args": "YXJncw==", "gas": 0, - "deposit": 0 + "deposit": "0" } }, { "Stake": { - "stake": 110, - "public_key": "RLb4qQXoZPAFqzZhiLFAcGFPFC7JWcDd8xKvQHHEqLUgDXuQkr2ehKAN28MNGQN9vUZ1qGZ" + "stake": "110", + "public_key": "ed25519:5do5nkAEVhL8iteDvXNgxi4pWK78Y7DDadX11ArFNyrf" } } ] } ]); - assert_eq!( - &serde_json::to_string(logic_builder.ext.get_receipt_create_calls()).unwrap(), - &expected.to_string() - ); + assert_eq!(&serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string()); } #[test] @@ -283,7 +289,12 @@ fn test_promise_batch_action_add_key_with_function_call() { let mut logic_builder = VMLogicBuilder::default(); let mut logic = logic_builder.build(context); let index = promise_create(&mut logic, b"rick.test", 0, 0).expect("should create a promise"); - let key = b"ed25519:5do5nkAEVhL8iteDvXNgxi4pWK78Y7DDadX11ArFNyrf"; + let serialized_key = "ed25519:5do5nkAEVhL8iteDvXNgxi4pWK78Y7DDadX11ArFNyrf" + .parse::() + .unwrap() + .try_to_vec() + .unwrap(); + let key = &&serialized_key; let nonce = 1; let allowance = 999u128; let receiver_id = b"sam"; @@ -323,40 +334,42 @@ fn test_promise_batch_action_add_key_with_function_call() { method_names, ) .expect("should add allowance"); - assert_eq!(logic.used_gas().unwrap(), 5126897175676); + assert_eq!(logic.used_gas().unwrap(), 5126680499695); let expected = serde_json::json!( [ { - "receipt_indices": [], "receiver_id": "rick.test", "actions": [ { "FunctionCall": { "method_name": "promise_create", - "args": "args", + "args": "YXJncw==", "gas": 0, - "deposit": 0 + "deposit": "0" } }, { - "AddKeyWithFunctionCall": { - "public_key": "RLb4qQXoZPAFqzZhiLFAcGFPFC7JWcDd8xKvQHHEqLUgDXuQkr2ehKAN28MNGQN9vUZ1qGZ", - "nonce": 1, - "allowance": 999, - "receiver_id": "sam", - "method_names": [ - "foo", - "bar" - ] + "AddKey": { + "public_key": "ed25519:5do5nkAEVhL8iteDvXNgxi4pWK78Y7DDadX11ArFNyrf", + "access_key": { + "nonce": 1, + "permission": { + "FunctionCall": { + "allowance": "999", + "receiver_id": "sam", + "method_names": [ + "foo", + "bar" + ] + } + } + } } } ] } ]); - assert_eq!( - &serde_json::to_string(logic_builder.ext.get_receipt_create_calls()).unwrap(), - &expected.to_string() - ); + assert_eq!(&serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string()); } #[test] @@ -385,36 +398,26 @@ fn test_promise_batch_then() { assert_eq!(logic.used_gas().unwrap(), 24124999601771); let expected = serde_json::json!([ { - "receipt_indices": [], "receiver_id": "rick.test", "actions": [ { "FunctionCall": { "method_name": "promise_create", - "args": "args", + "args": "YXJncw==", "gas": 0, - "deposit": 0 + "deposit": "0" } } ] }, { - "receipt_indices": [ - 0 - ], "receiver_id": "rick.test", "actions": [] }, { - "receipt_indices": [ - 0 - ], "receiver_id": "rick.test", "actions": [] } ]); - assert_eq!( - &serde_json::to_string(logic_builder.ext.get_receipt_create_calls()).unwrap(), - &expected.to_string() - ); + assert_eq!(&serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string()); } diff --git a/runtime/near-vm-runner-standalone/src/main.rs b/runtime/near-vm-runner-standalone/src/main.rs index cf89f4df066..b2f57aa0b5e 100644 --- a/runtime/near-vm-runner-standalone/src/main.rs +++ b/runtime/near-vm-runner-standalone/src/main.rs @@ -4,8 +4,8 @@ mod script; use crate::script::Script; use clap::Parser; +use near_vm_logic::ProtocolVersion; use near_vm_logic::VMOutcome; -use near_vm_logic::{mocks::mock_external::Receipt, ProtocolVersion}; use near_vm_runner::internal::VMKind; use serde::{ de::{MapAccess, Visitor}, @@ -107,7 +107,6 @@ struct CliArgs { struct StandaloneOutput { pub outcome: Option, pub err: Option, - pub receipts: Vec, pub state: State, } @@ -171,7 +170,6 @@ fn main() { StandaloneOutput { outcome: outcome.clone(), err: err.map(|it| it.to_string()), - receipts: results.state.get_receipt_create_calls().clone(), state: State(results.state.fake_trie), } ); diff --git a/runtime/runtime/src/actions.rs b/runtime/runtime/src/actions.rs index 3f46b139ad7..3f0196119e6 100644 --- a/runtime/runtime/src/actions.rs +++ b/runtime/runtime/src/actions.rs @@ -6,7 +6,7 @@ use near_primitives::checked_feature; use near_primitives::contract::ContractCode; use near_primitives::errors::{ActionError, ActionErrorKind, ContractCallError, RuntimeError}; use near_primitives::hash::CryptoHash; -use near_primitives::receipt::{ActionReceipt, Receipt}; +use near_primitives::receipt::{ActionReceipt, Receipt, ReceiptEnum}; use near_primitives::runtime::config::AccountCreationConfig; use near_primitives::runtime::fees::RuntimeFeesConfig; use near_primitives::transaction::{ @@ -140,9 +140,6 @@ pub(crate) fn action_function_call( let mut runtime_ext = RuntimeExt::new( state_update, account_id, - &action_receipt.signer_id, - &action_receipt.signer_public_key, - action_receipt.gas_price, action_hash, &apply_state.epoch_id, &apply_state.prev_block_hash, @@ -225,6 +222,25 @@ pub(crate) fn action_function_call( None => true, }; if let Some(outcome) = outcome { + let new_receipts: Vec<_> = outcome + .action_receipts + .into_iter() + .map(|(receiver_id, receipt)| Receipt { + predecessor_id: account_id.clone(), + receiver_id, + // Actual receipt ID is set in the Runtime.apply_action_receipt(...) in the + // "Generating receipt IDs" section + receipt_id: CryptoHash::default(), + receipt: ReceiptEnum::Action(ActionReceipt { + signer_id: action_receipt.signer_id.clone(), + signer_public_key: action_receipt.signer_public_key.clone(), + gas_price: action_receipt.gas_price, + output_data_receivers: receipt.output_data_receivers, + input_data_ids: receipt.input_data_ids, + actions: receipt.actions, + }), + }) + .collect(); result.gas_burnt = safe_add_gas(result.gas_burnt, outcome.burnt_gas)?; result.gas_burnt_for_function_call = safe_add_gas(result.gas_burnt_for_function_call, outcome.burnt_gas)?; @@ -239,7 +255,7 @@ pub(crate) fn action_function_call( account.set_amount(outcome.balance); account.set_storage_usage(outcome.storage_usage); result.result = Ok(outcome.return_data); - result.new_receipts.extend(runtime_ext.into_receipts(account_id)); + result.new_receipts.extend(new_receipts); } } else { assert!(!execution_succeeded, "Outcome should always be available if execution succeeded") diff --git a/runtime/runtime/src/ext.rs b/runtime/runtime/src/ext.rs index 7894901c1bf..a6bd8da17c1 100644 --- a/runtime/runtime/src/ext.rs +++ b/runtime/runtime/src/ext.rs @@ -1,35 +1,21 @@ use std::sync::Arc; -use borsh::BorshDeserialize; use tracing::debug; -use near_crypto::PublicKey; -use near_primitives::account::{AccessKey, AccessKeyPermission, FunctionCallPermission}; use near_primitives::contract::ContractCode; use near_primitives::errors::{EpochError, StorageError}; use near_primitives::hash::CryptoHash; -use near_primitives::receipt::{ActionReceipt, DataReceiver, Receipt, ReceiptEnum}; -use near_primitives::transaction::{ - Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction, - DeployContractAction, FunctionCallAction, StakeAction, TransferAction, -}; use near_primitives::trie_key::{trie_key_parsers, TrieKey}; -use near_primitives::types::{AccountId, Balance, EpochId, EpochInfoProvider, Gas, TrieCacheMode}; -#[cfg(feature = "protocol_feature_function_call_weight")] -use near_primitives::types::{GasDistribution, GasWeight}; +use near_primitives::types::{AccountId, Balance, EpochId, EpochInfoProvider, TrieCacheMode}; use near_primitives::utils::create_data_id; use near_primitives::version::ProtocolVersion; use near_store::{get_code, TrieUpdate, TrieUpdateValuePtr}; -use near_vm_errors::{AnyError, HostError, VMLogicError}; +use near_vm_errors::{AnyError, VMLogicError}; use near_vm_logic::{External, ValuePtr}; pub struct RuntimeExt<'a> { trie_update: &'a mut TrieUpdate, account_id: &'a AccountId, - action_receipts: Vec<(AccountId, ActionReceipt)>, - signer_id: &'a AccountId, - signer_public_key: &'a PublicKey, - gas_price: Balance, action_hash: &'a CryptoHash, data_count: u64, epoch_id: &'a EpochId, @@ -37,15 +23,6 @@ pub struct RuntimeExt<'a> { last_block_hash: &'a CryptoHash, epoch_info_provider: &'a dyn EpochInfoProvider, current_protocol_version: ProtocolVersion, - - #[cfg(feature = "protocol_feature_function_call_weight")] - gas_weights: Vec<(FunctionCallActionIndex, GasWeight)>, -} - -#[cfg(feature = "protocol_feature_function_call_weight")] -struct FunctionCallActionIndex { - receipt_index: usize, - action_index: usize, } /// Error used by `RuntimeExt`. @@ -80,9 +57,6 @@ impl<'a> RuntimeExt<'a> { pub fn new( trie_update: &'a mut TrieUpdate, account_id: &'a AccountId, - signer_id: &'a AccountId, - signer_public_key: &'a PublicKey, - gas_price: Balance, action_hash: &'a CryptoHash, epoch_id: &'a EpochId, prev_block_hash: &'a CryptoHash, @@ -93,10 +67,6 @@ impl<'a> RuntimeExt<'a> { RuntimeExt { trie_update, account_id, - action_receipts: vec![], - signer_id, - signer_public_key, - gas_price, action_hash, data_count: 0, epoch_id, @@ -104,9 +74,6 @@ impl<'a> RuntimeExt<'a> { last_block_hash, epoch_info_provider, current_protocol_version, - - #[cfg(feature = "protocol_feature_function_call_weight")] - gas_weights: vec![], } } @@ -128,47 +95,6 @@ impl<'a> RuntimeExt<'a> { TrieKey::ContractData { account_id: self.account_id.clone(), key: key.to_vec() } } - fn new_data_id(&mut self) -> CryptoHash { - let data_id = create_data_id( - self.current_protocol_version, - self.action_hash, - self.prev_block_hash, - self.last_block_hash, - self.data_count as usize, - ); - self.data_count += 1; - data_id - } - - pub fn into_receipts(self, predecessor_id: &AccountId) -> Vec { - self.action_receipts - .into_iter() - .map(|(receiver_id, action_receipt)| Receipt { - predecessor_id: predecessor_id.clone(), - receiver_id, - // Actual receipt ID is set in the Runtime.apply_action_receipt(...) in the - // "Generating receipt IDs" section - receipt_id: CryptoHash::default(), - receipt: ReceiptEnum::Action(action_receipt), - }) - .collect() - } - - /// Appends an action and returns the index the action was inserted in the receipt - fn append_action(&mut self, receipt_index: u64, action: Action) -> usize { - let actions = &mut self - .action_receipts - .get_mut(receipt_index as usize) - .expect("receipt index should be present") - .1 - .actions; - - actions.push(action); - - // Return index that action was inserted at - actions.len() - 1 - } - pub fn set_trie_cache_mode(&mut self, state: TrieCacheMode) { self.trie_update.set_trie_cache_mode(state); } @@ -234,199 +160,16 @@ impl<'a> External for RuntimeExt<'a> { Ok(()) } - fn create_receipt( - &mut self, - receipt_indices: Vec, - receiver_id: AccountId, - ) -> ExtResult { - let mut input_data_ids = vec![]; - for receipt_index in receipt_indices { - let data_id = self.new_data_id(); - self.action_receipts - .get_mut(receipt_index as usize) - .ok_or_else(|| HostError::InvalidReceiptIndex { receipt_index })? - .1 - .output_data_receivers - .push(DataReceiver { data_id, receiver_id: receiver_id.clone() }); - input_data_ids.push(data_id); - } - - let new_receipt = ActionReceipt { - signer_id: self.signer_id.clone(), - signer_public_key: self.signer_public_key.clone(), - gas_price: self.gas_price, - output_data_receivers: vec![], - input_data_ids, - actions: vec![], - }; - let new_receipt_index = self.action_receipts.len() as u64; - self.action_receipts.push((receiver_id, new_receipt)); - Ok(new_receipt_index) - } - - fn append_action_create_account(&mut self, receipt_index: u64) -> ExtResult<()> { - self.append_action(receipt_index, Action::CreateAccount(CreateAccountAction {})); - Ok(()) - } - - fn append_action_deploy_contract( - &mut self, - receipt_index: u64, - code: Vec, - ) -> ExtResult<()> { - self.append_action(receipt_index, Action::DeployContract(DeployContractAction { code })); - Ok(()) - } - - #[cfg(feature = "protocol_feature_function_call_weight")] - fn append_action_function_call_weight( - &mut self, - receipt_index: u64, - method_name: Vec, - args: Vec, - attached_deposit: u128, - prepaid_gas: Gas, - gas_weight: GasWeight, - ) -> ExtResult<()> { - let action_index = self.append_action( - receipt_index, - Action::FunctionCall(FunctionCallAction { - method_name: String::from_utf8(method_name) - .map_err(|_| HostError::InvalidMethodName)?, - args, - gas: prepaid_gas, - deposit: attached_deposit, - }), - ); - - if gas_weight.0 > 0 { - self.gas_weights.push(( - FunctionCallActionIndex { receipt_index: receipt_index as usize, action_index }, - gas_weight, - )); - } - - Ok(()) - } - - fn append_action_function_call( - &mut self, - receipt_index: u64, - method_name: Vec, - args: Vec, - attached_deposit: u128, - prepaid_gas: Gas, - ) -> ExtResult<()> { - self.append_action( - receipt_index, - Action::FunctionCall(FunctionCallAction { - method_name: String::from_utf8(method_name) - .map_err(|_| HostError::InvalidMethodName)?, - args, - gas: prepaid_gas, - deposit: attached_deposit, - }), - ); - Ok(()) - } - - fn append_action_transfer(&mut self, receipt_index: u64, deposit: u128) -> ExtResult<()> { - self.append_action(receipt_index, Action::Transfer(TransferAction { deposit })); - Ok(()) - } - - fn append_action_stake( - &mut self, - receipt_index: u64, - stake: u128, - public_key: Vec, - ) -> ExtResult<()> { - self.append_action( - receipt_index, - Action::Stake(StakeAction { - stake, - public_key: PublicKey::try_from_slice(&public_key) - .map_err(|_| HostError::InvalidPublicKey)?, - }), - ); - Ok(()) - } - - fn append_action_add_key_with_full_access( - &mut self, - receipt_index: u64, - public_key: Vec, - nonce: u64, - ) -> ExtResult<()> { - self.append_action( - receipt_index, - Action::AddKey(AddKeyAction { - public_key: PublicKey::try_from_slice(&public_key) - .map_err(|_| HostError::InvalidPublicKey)?, - access_key: AccessKey { nonce, permission: AccessKeyPermission::FullAccess }, - }), - ); - Ok(()) - } - - fn append_action_add_key_with_function_call( - &mut self, - receipt_index: u64, - public_key: Vec, - nonce: u64, - allowance: Option, - receiver_id: AccountId, - method_names: Vec>, - ) -> ExtResult<()> { - self.append_action( - receipt_index, - Action::AddKey(AddKeyAction { - public_key: PublicKey::try_from_slice(&public_key) - .map_err(|_| HostError::InvalidPublicKey)?, - access_key: AccessKey { - nonce, - permission: AccessKeyPermission::FunctionCall(FunctionCallPermission { - allowance, - receiver_id: receiver_id.into(), - method_names: method_names - .into_iter() - .map(|method_name| { - String::from_utf8(method_name) - .map_err(|_| HostError::InvalidMethodName) - }) - .collect::, _>>()?, - }), - }, - }), - ); - Ok(()) - } - - fn append_action_delete_key( - &mut self, - receipt_index: u64, - public_key: Vec, - ) -> ExtResult<()> { - self.append_action( - receipt_index, - Action::DeleteKey(DeleteKeyAction { - public_key: PublicKey::try_from_slice(&public_key) - .map_err(|_| HostError::InvalidPublicKey)?, - }), - ); - Ok(()) - } - - fn append_action_delete_account( - &mut self, - receipt_index: u64, - beneficiary_id: AccountId, - ) -> ExtResult<()> { - self.append_action( - receipt_index, - Action::DeleteAccount(DeleteAccountAction { beneficiary_id }), + fn generate_data_id(&mut self) -> CryptoHash { + let data_id = create_data_id( + self.current_protocol_version, + self.action_hash, + self.prev_block_hash, + self.last_block_hash, + self.data_count as usize, ); - Ok(()) + self.data_count += 1; + data_id } fn get_touched_nodes_count(&self) -> u64 { @@ -444,55 +187,4 @@ impl<'a> External for RuntimeExt<'a> { .validator_total_stake(self.epoch_id, self.prev_block_hash) .map_err(|e| ExternalError::ValidatorError(e).into()) } - - /// Distributes the gas passed in by splitting it among weights defined in `gas_weights`. - /// This will sum all weights, retrieve the gas per weight, then update each function - /// to add the respective amount of gas. Once all gas is distributed, the remainder of - /// the gas not assigned due to precision loss is added to the last function with a weight. - #[cfg(feature = "protocol_feature_function_call_weight")] - fn distribute_unused_gas(&mut self, gas: u64) -> GasDistribution { - let gas_weight_sum: u128 = - self.gas_weights.iter().map(|(_, GasWeight(weight))| *weight as u128).sum(); - if gas_weight_sum != 0 { - // Floor division that will ensure gas allocated is <= gas to distribute - let gas_per_weight = (gas as u128 / gas_weight_sum) as u64; - - let mut distribute_gas = |metadata: &FunctionCallActionIndex, assigned_gas: u64| { - let FunctionCallActionIndex { receipt_index, action_index } = metadata; - if let Some(Action::FunctionCall(FunctionCallAction { ref mut gas, .. })) = self - .action_receipts - .get_mut(*receipt_index) - .and_then(|(_, receipt)| receipt.actions.get_mut(*action_index)) - { - *gas += assigned_gas; - } else { - panic!( - "Invalid index for assigning unused gas weight \ - (promise_index={}, action_index={})", - receipt_index, action_index - ); - } - }; - - let mut distributed = 0; - for (action_index, GasWeight(weight)) in &self.gas_weights { - // This can't overflow because the gas_per_weight is floor division - // of the weight sum. - let assigned_gas = gas_per_weight * weight; - - distribute_gas(action_index, assigned_gas); - - distributed += assigned_gas - } - - // Distribute remaining gas to final action. - if let Some((last_idx, _)) = self.gas_weights.last() { - distribute_gas(last_idx, gas - distributed); - } - self.gas_weights.clear(); - GasDistribution::All - } else { - GasDistribution::NoRatios - } - } } diff --git a/runtime/runtime/src/state_viewer/mod.rs b/runtime/runtime/src/state_viewer/mod.rs index 0399ff996d9..dee8ae6ec8f 100644 --- a/runtime/runtime/src/state_viewer/mod.rs +++ b/runtime/runtime/src/state_viewer/mod.rs @@ -184,9 +184,6 @@ impl TrieViewer { let mut runtime_ext = RuntimeExt::new( &mut state_update, contract_id, - originator_id, - &public_key, - 0, &empty_hash, &view_state.epoch_id, &view_state.prev_block_hash,