From 0706e759a54e7230dc872e20ffc2a3f432a1e1a1 Mon Sep 17 00:00:00 2001 From: austinabell Date: Mon, 21 Mar 2022 12:51:56 -0400 Subject: [PATCH 01/18] WIP: Refactoring receipt handling to be done in VM logic --- core/primitives/src/lib.rs | 1 + core/primitives/src/receipt_manager.rs | 343 +++++++++ runtime/near-vm-logic/src/dependencies.rs | 728 +++++++++--------- runtime/near-vm-logic/src/logic.rs | 72 +- .../near-vm-logic/src/mocks/mock_external.rs | 415 +++++----- runtime/near-vm-runner-standalone/src/main.rs | 2 +- runtime/runtime/src/actions.rs | 10 +- runtime/runtime/src/ext.rs | 586 +++++++------- runtime/runtime/src/state_viewer/mod.rs | 3 - 9 files changed, 1233 insertions(+), 927 deletions(-) create mode 100644 core/primitives/src/receipt_manager.rs diff --git a/core/primitives/src/lib.rs b/core/primitives/src/lib.rs index 5c872ca7fb2..b921acd653c 100644 --- a/core/primitives/src/lib.rs +++ b/core/primitives/src/lib.rs @@ -16,6 +16,7 @@ pub mod network; pub use near_primitives_core::profile; pub mod rand; pub mod receipt; +pub mod receipt_manager; pub mod runtime; pub mod serialize; pub mod shard_layout; diff --git a/core/primitives/src/receipt_manager.rs b/core/primitives/src/receipt_manager.rs new file mode 100644 index 00000000000..a6d3c213d7f --- /dev/null +++ b/core/primitives/src/receipt_manager.rs @@ -0,0 +1,343 @@ +use crate::account::{AccessKey, AccessKeyPermission, FunctionCallPermission}; +use crate::hash::CryptoHash; +use crate::receipt::{ActionReceipt, DataReceiver, Receipt, ReceiptEnum}; +use crate::transaction::{ + Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction, + DeployContractAction, FunctionCallAction, StakeAction, TransferAction, +}; +use borsh::BorshDeserialize; +use near_crypto::PublicKey; +use near_primitives_core::types::{AccountId, Balance, 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; + +#[derive(Debug, Clone, PartialEq)] +struct ReceiptMetadata { + /// If present, where to route the output data + 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` + input_data_ids: Vec, + /// A list of actions to process when all input_data_ids are filled + actions: Vec, +} + +#[derive(Default, Clone, PartialEq)] +pub struct ReceiptManager { + action_receipts: Vec<(AccountId, ReceiptMetadata)>, + #[cfg(feature = "protocol_feature_function_call_weight")] + gas_weights: Vec<(FunctionCallActionIndex, GasWeight)>, +} + +#[cfg(feature = "protocol_feature_function_call_weight")] +#[derive(Debug, Clone, Copy, PartialEq)] +struct FunctionCallActionIndex { + receipt_index: usize, + action_index: usize, +} + +impl ReceiptManager { + pub fn get_receipt_receiver(&self, receipt_index: u64) -> Option<&AccountId> { + self.action_receipts.get(receipt_index as usize).map(|(id, _)| id) + } + pub fn into_receipts( + self, + predecessor_id: &AccountId, + signer_id: &AccountId, + signer_public_key: &PublicKey, + gas_price: Balance, + ) -> Vec { + self.action_receipts + .into_iter() + .map(|(receiver_id, 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(ActionReceipt { + signer_id: signer_id.clone(), + signer_public_key: signer_public_key.clone(), + gas_price, + output_data_receivers: receipt.output_data_receivers, + input_data_ids: receipt.input_data_ids, + actions: receipt.actions, + }), + }) + .collect() + } + + /// Appends an action and returns the index the action was inserted in the receipt + pub 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 + } + + // TODO pull docs for all these methods into here + pub 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(); + // TODO + let data_id = CryptoHash::default(); + 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 u64; + self.action_receipts.push((receiver_id, new_receipt)); + Ok(new_receipt_index) + } + + pub fn append_action_create_account(&mut self, receipt_index: u64) -> ExtResult<()> { + self.append_action(receipt_index, Action::CreateAccount(CreateAccountAction {})); + Ok(()) + } + + pub 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")] + pub 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(()) + } + + pub 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(()) + } + + pub fn append_action_transfer(&mut self, receipt_index: u64, deposit: u128) -> ExtResult<()> { + self.append_action(receipt_index, Action::Transfer(TransferAction { deposit })); + Ok(()) + } + + pub 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(()) + } + + pub 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(()) + } + + pub 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(()) + } + + pub 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(()) + } + + pub fn append_action_delete_account( + &mut self, + receipt_index: u64, + 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 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/near-vm-logic/src/dependencies.rs b/runtime/near-vm-logic/src/dependencies.rs index 3b1311828b6..bef07110de3 100644 --- a/runtime/near-vm-logic/src/dependencies.rs +++ b/runtime/near-vm-logic/src/dependencies.rs @@ -1,9 +1,6 @@ //! 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_core::types::{AccountId, Balance}; use near_vm_errors::VMLogicError; /// An abstraction over the memory of the smart contract. @@ -164,371 +161,371 @@ 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; + // /// 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 [`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 [`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. + // /// + // /// # 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 [`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 [`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 [`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 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 [`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 [`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<()>; + // /// 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<()>; /// Returns amount of touched trie nodes by storage operations fn get_touched_nodes_count(&self) -> u64; @@ -540,15 +537,6 @@ 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; + // #[cfg(feature = "protocol_feature_function_call_weight")] + // fn distribute_unused_gas(&mut self, gas: Gas) -> GasDistribution; } diff --git a/runtime/near-vm-logic/src/logic.rs b/runtime/near-vm-logic/src/logic.rs index bf12124e598..d8c0cbc5ae4 100644 --- a/runtime/near-vm-logic/src/logic.rs +++ b/runtime/near-vm-logic/src/logic.rs @@ -6,6 +6,7 @@ use crate::utils::split_method_names; use crate::ValuePtr; use byteorder::ByteOrder; use near_crypto::Secp256K1Signature; +use near_primitives::receipt_manager::ReceiptManager; use near_primitives::version::is_implicit_account_creation_enabled; use near_primitives_core::config::ExtCosts::*; use near_primitives_core::config::{ActionCosts, ExtCosts, VMConfig, ViewConfig}; @@ -60,14 +61,16 @@ 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, - + // /// 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, + + // TODO docs + receipt_manager: ReceiptManager, } /// Promises API allows to create a DAG-structure that defines dependencies between smart contract @@ -117,6 +120,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,9 +144,9 @@ 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(), } } @@ -1309,8 +1313,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(vec![], account_id.clone())?; + // self.receipt_to_account.insert(new_receipt_idx, account_id); self.checked_push_promise(Promise::Receipt(new_receipt_idx)) } @@ -1363,20 +1367,21 @@ 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(receipt_dependencies, account_id.clone())?; + // self.receipt_to_account.insert(new_receipt_idx, account_id); 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) + fn get_account_by_receipt(&self, receipt_idx: ReceiptIndex) -> &AccountId { + self.receipt_manager + .get_receipt_receiver(receipt_idx) .expect("promises and receipt_to_account should be consistent.") } @@ -1396,7 +1401,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)) } @@ -1431,7 +1436,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(()) } @@ -1489,7 +1494,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(()) } @@ -1522,7 +1527,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, @@ -1585,7 +1596,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, @@ -1685,7 +1696,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(); @@ -1700,7 +1711,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(()) } @@ -1746,7 +1757,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(()) } @@ -1791,7 +1802,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(()) } @@ -1857,7 +1872,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, @@ -1908,7 +1923,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(()) } @@ -1951,7 +1966,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(()) } @@ -2619,7 +2634,10 @@ impl<'a> VMLogic<'a> { let unused_gas = self.context.prepaid_gas - self.gas_counter.used_gas(); // Distribute the unused gas and prepay for the gas. - if matches!(self.ext.distribute_unused_gas(unused_gas), GasDistribution::All) { + if matches!( + self.receipt_manager.distribute_unused_gas(unused_gas), + GasDistribution::All + ) { self.gas_counter.prepay_gas(unused_gas).unwrap(); } } @@ -2638,6 +2656,9 @@ impl<'a> VMLogic<'a> { used_gas, logs: self.logs, profile, + // TODO this probably should be receipts, but we need to know the gas price from the + // initial action receipt to be able to convert, which happens outside VM + receipt_manager: self.receipt_manager, } } @@ -2670,6 +2691,7 @@ pub struct VMOutcome { pub logs: Vec, /// Data collected from making a contract call pub profile: ProfileData, + pub receipt_manager: ReceiptManager, } 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..6ec38a68f0f 100644 --- a/runtime/near-vm-logic/src/mocks/mock_external.rs +++ b/runtime/near-vm-logic/src/mocks/mock_external.rs @@ -1,8 +1,5 @@ use crate::{External, ValuePtr}; -#[cfg(feature = "protocol_feature_function_call_weight")] -use near_primitives::types::{GasDistribution, GasWeight}; use near_primitives_core::types::{AccountId, Balance, Gas}; -use near_vm_errors::HostError; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -10,17 +7,7 @@ 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, } pub struct MockedValuePtr { @@ -51,10 +38,6 @@ impl MockedExternal { 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,155 +70,155 @@ 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 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 get_touched_nodes_count(&self) -> u64 { 0 @@ -249,56 +232,56 @@ impl External for MockedExternal { 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 - } - } + // /// 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-runner-standalone/src/main.rs b/runtime/near-vm-runner-standalone/src/main.rs index b26e02e484d..91dcc9e04d6 100644 --- a/runtime/near-vm-runner-standalone/src/main.rs +++ b/runtime/near-vm-runner-standalone/src/main.rs @@ -171,7 +171,7 @@ fn main() { StandaloneOutput { outcome: outcome.clone(), err: err.map(|it| it.to_string()), - receipts: results.state.get_receipt_create_calls().clone(), + receipts: todo!(), state: State(results.state.fake_trie), } ); diff --git a/runtime/runtime/src/actions.rs b/runtime/runtime/src/actions.rs index 3f46b139ad7..c4a456779a8 100644 --- a/runtime/runtime/src/actions.rs +++ b/runtime/runtime/src/actions.rs @@ -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, @@ -239,7 +236,12 @@ 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(outcome.receipt_manager.into_receipts( + &receipt.predecessor_id, + &action_receipt.signer_id, + &action_receipt.signer_public_key, + action_receipt.gas_price, + )); } } 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..61613a20002 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,8 @@ impl<'a> RuntimeExt<'a> { last_block_hash, epoch_info_provider, current_protocol_version, - - #[cfg(feature = "protocol_feature_function_call_weight")] - gas_weights: vec![], + // #[cfg(feature = "protocol_feature_function_call_weight")] + // gas_weights: vec![], } } @@ -128,7 +97,8 @@ impl<'a> RuntimeExt<'a> { TrieKey::ContractData { account_id: self.account_id.clone(), key: key.to_vec() } } - fn new_data_id(&mut self) -> CryptoHash { + // TODO needs to be moved into trait to be used in VMLogic + pub fn new_data_id(&mut self) -> CryptoHash { let data_id = create_data_id( self.current_protocol_version, self.action_hash, @@ -140,34 +110,34 @@ impl<'a> RuntimeExt<'a> { 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 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,200 +204,200 @@ 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 }), - ); - 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 }), + // ); + // Ok(()) + // } fn get_touched_nodes_count(&self) -> u64 { self.trie_update.trie.get_touched_nodes_count() @@ -445,54 +415,54 @@ impl<'a> External for RuntimeExt<'a> { .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 - } - } + // /// 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, From 7403487e7cd8828a27ad3c616046ae2a6e37b09d Mon Sep 17 00:00:00 2001 From: austinabell Date: Tue, 22 Mar 2022 20:26:28 -0400 Subject: [PATCH 02/18] connect data_id generation --- core/primitives/src/receipt_manager.rs | 5 ++--- runtime/near-vm-logic/src/dependencies.rs | 3 +++ runtime/near-vm-logic/src/logic.rs | 18 +++++++++++++++--- .../near-vm-logic/src/mocks/mock_external.rs | 6 +++++- runtime/runtime/src/ext.rs | 4 ++++ 5 files changed, 29 insertions(+), 7 deletions(-) diff --git a/core/primitives/src/receipt_manager.rs b/core/primitives/src/receipt_manager.rs index a6d3c213d7f..c2fceafdb7c 100644 --- a/core/primitives/src/receipt_manager.rs +++ b/core/primitives/src/receipt_manager.rs @@ -91,14 +91,13 @@ impl ReceiptManager { // TODO pull docs for all these methods into here pub fn create_receipt( &mut self, + mut generate_data_id: impl FnMut() -> CryptoHash, 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(); - // TODO - let data_id = CryptoHash::default(); + let data_id = generate_data_id(); self.action_receipts .get_mut(receipt_index as usize) .ok_or_else(|| HostError::InvalidReceiptIndex { receipt_index })? diff --git a/runtime/near-vm-logic/src/dependencies.rs b/runtime/near-vm-logic/src/dependencies.rs index bef07110de3..8981fb6cdd7 100644 --- a/runtime/near-vm-logic/src/dependencies.rs +++ b/runtime/near-vm-logic/src/dependencies.rs @@ -1,5 +1,6 @@ //! External dependencies of the near-vm-logic. +use near_primitives::hash::CryptoHash; use near_primitives_core::types::{AccountId, Balance}; use near_vm_errors::VMLogicError; @@ -161,6 +162,8 @@ pub trait External { /// ``` fn storage_has_key(&mut self, key: &[u8]) -> Result; + fn generate_data_id(&mut self) -> CryptoHash; + // /// Create a receipt which will be executed after all the receipts identified by // /// `receipt_indices` are complete. // /// diff --git a/runtime/near-vm-logic/src/logic.rs b/runtime/near-vm-logic/src/logic.rs index d8c0cbc5ae4..9b7cf27fd50 100644 --- a/runtime/near-vm-logic/src/logic.rs +++ b/runtime/near-vm-logic/src/logic.rs @@ -6,6 +6,7 @@ use crate::utils::split_method_names; use crate::ValuePtr; use byteorder::ByteOrder; use near_crypto::Secp256K1Signature; +use near_primitives::hash::CryptoHash; use near_primitives::receipt_manager::ReceiptManager; use near_primitives::version::is_implicit_account_creation_enabled; use near_primitives_core::config::ExtCosts::*; @@ -1313,7 +1314,11 @@ 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.receipt_manager.create_receipt(vec![], account_id.clone())?; + let new_receipt_idx = self.receipt_manager.create_receipt( + data_id_generator(self.ext), + vec![], + account_id.clone(), + )?; // self.receipt_to_account.insert(new_receipt_idx, account_id); self.checked_push_promise(Promise::Receipt(new_receipt_idx)) @@ -1371,8 +1376,11 @@ impl<'a> VMLogic<'a> { .collect(); self.pay_gas_for_new_receipt(sir, &deps)?; - let new_receipt_idx = - self.receipt_manager.create_receipt(receipt_dependencies, account_id.clone())?; + let new_receipt_idx = self.receipt_manager.create_receipt( + data_id_generator(self.ext), + receipt_dependencies, + account_id.clone(), + )?; // self.receipt_to_account.insert(new_receipt_idx, account_id); self.checked_push_promise(Promise::Receipt(new_receipt_idx)) @@ -2681,6 +2689,10 @@ impl<'a> VMLogic<'a> { } } +fn data_id_generator(ext: &mut dyn External) -> impl FnMut() -> CryptoHash + '_ { + || ext.generate_data_id() +} + #[derive(Clone, PartialEq)] pub struct VMOutcome { pub balance: Balance, diff --git a/runtime/near-vm-logic/src/mocks/mock_external.rs b/runtime/near-vm-logic/src/mocks/mock_external.rs index 6ec38a68f0f..61b08161309 100644 --- a/runtime/near-vm-logic/src/mocks/mock_external.rs +++ b/runtime/near-vm-logic/src/mocks/mock_external.rs @@ -1,4 +1,5 @@ use crate::{External, ValuePtr}; +use near_primitives::hash::CryptoHash; use near_primitives_core::types::{AccountId, Balance, Gas}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -37,7 +38,6 @@ impl MockedExternal { pub fn new() -> Self { Self::default() } - } use crate::dependencies::Result; @@ -70,6 +70,10 @@ impl External for MockedExternal { Ok(self.fake_trie.contains_key(key)) } + fn generate_data_id(&mut self) -> CryptoHash { + todo!() + } + // 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()); diff --git a/runtime/runtime/src/ext.rs b/runtime/runtime/src/ext.rs index 61613a20002..51f856946bb 100644 --- a/runtime/runtime/src/ext.rs +++ b/runtime/runtime/src/ext.rs @@ -204,6 +204,10 @@ impl<'a> External for RuntimeExt<'a> { Ok(()) } + fn generate_data_id(&mut self) -> CryptoHash { + todo!() + } + // fn create_receipt( // &mut self, // receipt_indices: Vec, From 0c0ad325750d13d47d15e522e9cf759b66d40589 Mon Sep 17 00:00:00 2001 From: austinabell Date: Tue, 22 Mar 2022 22:43:59 -0400 Subject: [PATCH 03/18] migrate receipt manager to vmlogic --- core/primitives/src/lib.rs | 1 - runtime/near-vm-logic/src/lib.rs | 1 + runtime/near-vm-logic/src/logic.rs | 47 +++++++--- .../near-vm-logic/src/mocks/mock_external.rs | 9 +- .../near-vm-logic}/src/receipt_manager.rs | 70 ++++++++------- runtime/near-vm-logic/src/tests/promises.rs | 89 +++++++++++-------- runtime/near-vm-runner-standalone/src/main.rs | 4 +- runtime/runtime/src/actions.rs | 15 ++-- runtime/runtime/src/ext.rs | 23 ++--- 9 files changed, 150 insertions(+), 109 deletions(-) rename {core/primitives => runtime/near-vm-logic}/src/receipt_manager.rs (92%) diff --git a/core/primitives/src/lib.rs b/core/primitives/src/lib.rs index b921acd653c..5c872ca7fb2 100644 --- a/core/primitives/src/lib.rs +++ b/core/primitives/src/lib.rs @@ -16,7 +16,6 @@ pub mod network; pub use near_primitives_core::profile; pub mod rand; pub mod receipt; -pub mod receipt_manager; pub mod runtime; pub mod serialize; pub mod shard_layout; diff --git a/runtime/near-vm-logic/src/lib.rs b/runtime/near-vm-logic/src/lib.rs index 0cd95016bd9..3cb4012bbf2 100644 --- a/runtime/near-vm-logic/src/lib.rs +++ b/runtime/near-vm-logic/src/lib.rs @@ -12,6 +12,7 @@ pub mod serde_with; mod tests; pub mod types; mod utils; +pub(crate) mod receipt_manager; pub use context::VMContext; pub use dependencies::{External, MemoryLike, ValuePtr}; diff --git a/runtime/near-vm-logic/src/logic.rs b/runtime/near-vm-logic/src/logic.rs index 9b7cf27fd50..fb23121977e 100644 --- a/runtime/near-vm-logic/src/logic.rs +++ b/runtime/near-vm-logic/src/logic.rs @@ -1,13 +1,13 @@ use crate::context::VMContext; use crate::dependencies::{External, MemoryLike}; use crate::gas_counter::{FastGasCounter, GasCounter}; +use crate::receipt_manager::{ActionReceipts, ReceiptManager}; use crate::types::{PromiseIndex, PromiseResult, ReceiptIndex, ReturnData}; use crate::utils::split_method_names; use crate::ValuePtr; use byteorder::ByteOrder; -use near_crypto::Secp256K1Signature; -use near_primitives::hash::CryptoHash; -use near_primitives::receipt_manager::ReceiptManager; +use near_crypto::{PublicKey, Secp256K1Signature}; +use near_primitives::receipt::Receipt; use near_primitives::version::is_implicit_account_creation_enabled; use near_primitives_core::config::ExtCosts::*; use near_primitives_core::config::{ActionCosts, ExtCosts, VMConfig, ViewConfig}; @@ -71,7 +71,7 @@ pub struct VMLogic<'a> { current_protocol_version: ProtocolVersion, // TODO docs - receipt_manager: ReceiptManager, + pub(crate) receipt_manager: ReceiptManager, } /// Promises API allows to create a DAG-structure that defines dependencies between smart contract @@ -1315,7 +1315,7 @@ impl<'a> VMLogic<'a> { let sir = account_id == self.context.current_account_id; self.pay_gas_for_new_receipt(sir, &[])?; let new_receipt_idx = self.receipt_manager.create_receipt( - data_id_generator(self.ext), + || self.ext.generate_data_id(), vec![], account_id.clone(), )?; @@ -1377,7 +1377,7 @@ impl<'a> VMLogic<'a> { self.pay_gas_for_new_receipt(sir, &deps)?; let new_receipt_idx = self.receipt_manager.create_receipt( - data_id_generator(self.ext), + || self.ext.generate_data_id(), receipt_dependencies, account_id.clone(), )?; @@ -2666,7 +2666,7 @@ impl<'a> VMLogic<'a> { profile, // TODO this probably should be receipts, but we need to know the gas price from the // initial action receipt to be able to convert, which happens outside VM - receipt_manager: self.receipt_manager, + action_receipts: self.receipt_manager.action_receipts, } } @@ -2689,11 +2689,7 @@ impl<'a> VMLogic<'a> { } } -fn data_id_generator(ext: &mut dyn External) -> impl FnMut() -> CryptoHash + '_ { - || ext.generate_data_id() -} - -#[derive(Clone, PartialEq)] +#[derive(Clone)] pub struct VMOutcome { pub balance: Balance, pub storage_usage: StorageUsage, @@ -2703,7 +2699,19 @@ pub struct VMOutcome { pub logs: Vec, /// Data collected from making a contract call pub profile: ProfileData, - pub receipt_manager: ReceiptManager, + action_receipts: ActionReceipts, +} + +impl PartialEq for VMOutcome { + fn eq(&self, other: &Self) -> bool { + self.balance == other.balance + && self.storage_usage == other.storage_usage + && self.return_data == other.return_data + && self.burnt_gas == other.burnt_gas + && self.used_gas == other.used_gas + && self.logs == other.logs + && self.profile == other.profile + } } impl std::fmt::Debug for VMOutcome { @@ -2720,3 +2728,16 @@ impl std::fmt::Debug for VMOutcome { ) } } + +impl VMOutcome { + // TODO docs + pub fn take_receipts( + &mut self, + predecessor_id: &AccountId, + signer_id: &AccountId, + signer_public_key: &PublicKey, + gas_price: Balance, + ) -> Vec { + self.action_receipts.take_receipts(predecessor_id, signer_id, signer_public_key, gas_price) + } +} diff --git a/runtime/near-vm-logic/src/mocks/mock_external.rs b/runtime/near-vm-logic/src/mocks/mock_external.rs index 61b08161309..82a5f7b4170 100644 --- a/runtime/near-vm-logic/src/mocks/mock_external.rs +++ b/runtime/near-vm-logic/src/mocks/mock_external.rs @@ -1,5 +1,5 @@ use crate::{External, ValuePtr}; -use near_primitives::hash::CryptoHash; +use near_primitives::hash::{hash, CryptoHash}; use near_primitives_core::types::{AccountId, Balance, Gas}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -9,6 +9,7 @@ use std::collections::HashMap; pub struct MockedExternal { pub fake_trie: HashMap, Vec>, pub validators: HashMap, + data_count: u64, } pub struct MockedValuePtr { @@ -71,7 +72,11 @@ impl External for MockedExternal { } fn generate_data_id(&mut self) -> CryptoHash { - todo!() + // 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 create_receipt(&mut self, receipt_indices: Vec, receiver_id: AccountId) -> Result { diff --git a/core/primitives/src/receipt_manager.rs b/runtime/near-vm-logic/src/receipt_manager.rs similarity index 92% rename from core/primitives/src/receipt_manager.rs rename to runtime/near-vm-logic/src/receipt_manager.rs index c2fceafdb7c..7b599f7eeef 100644 --- a/core/primitives/src/receipt_manager.rs +++ b/runtime/near-vm-logic/src/receipt_manager.rs @@ -1,12 +1,12 @@ -use crate::account::{AccessKey, AccessKeyPermission, FunctionCallPermission}; -use crate::hash::CryptoHash; -use crate::receipt::{ActionReceipt, DataReceiver, Receipt, ReceiptEnum}; -use crate::transaction::{ +use borsh::BorshDeserialize; +use near_crypto::PublicKey; +use near_primitives::receipt::{ActionReceipt, DataReceiver, Receipt, ReceiptEnum}; +use near_primitives::transaction::{ Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction, DeployContractAction, FunctionCallAction, StakeAction, TransferAction, }; -use borsh::BorshDeserialize; -use near_crypto::PublicKey; +use near_primitives_core::account::{AccessKey, AccessKeyPermission, FunctionCallPermission}; +use near_primitives_core::hash::CryptoHash; use near_primitives_core::types::{AccountId, Balance, Gas}; #[cfg(feature = "protocol_feature_function_call_weight")] use near_primitives_core::types::{GasDistribution, GasWeight}; @@ -15,7 +15,7 @@ use near_vm_errors::{HostError, VMLogicError}; type ExtResult = ::std::result::Result; #[derive(Debug, Clone, PartialEq)] -struct ReceiptMetadata { +pub(crate) struct ReceiptMetadata { /// If present, where to route the output data output_data_receivers: Vec, /// A list of the input data dependencies for this Receipt to process. @@ -25,36 +25,23 @@ struct ReceiptMetadata { /// depending on `ReceivedData` is `Some(_)` or `None` input_data_ids: Vec, /// A list of actions to process when all input_data_ids are filled - actions: Vec, + pub(crate) actions: Vec, } #[derive(Default, Clone, PartialEq)] -pub struct ReceiptManager { - action_receipts: Vec<(AccountId, ReceiptMetadata)>, - #[cfg(feature = "protocol_feature_function_call_weight")] - gas_weights: Vec<(FunctionCallActionIndex, GasWeight)>, -} +pub(crate) struct ActionReceipts(pub(crate) Vec<(AccountId, ReceiptMetadata)>); -#[cfg(feature = "protocol_feature_function_call_weight")] -#[derive(Debug, Clone, Copy, PartialEq)] -struct FunctionCallActionIndex { - receipt_index: usize, - action_index: usize, -} - -impl ReceiptManager { - pub fn get_receipt_receiver(&self, receipt_index: u64) -> Option<&AccountId> { - self.action_receipts.get(receipt_index as usize).map(|(id, _)| id) - } - pub fn into_receipts( - self, +impl ActionReceipts { + pub(crate) fn take_receipts( + &mut self, predecessor_id: &AccountId, signer_id: &AccountId, signer_public_key: &PublicKey, gas_price: Balance, ) -> Vec { - self.action_receipts - .into_iter() + let ActionReceipts(receipts) = self; + receipts + .drain(..) .map(|(receiver_id, receipt)| Receipt { predecessor_id: predecessor_id.clone(), receiver_id, @@ -72,11 +59,32 @@ impl ReceiptManager { }) .collect() } +} + +#[derive(Default, Clone, PartialEq)] +pub struct ReceiptManager { + pub(crate) action_receipts: ActionReceipts, + #[cfg(feature = "protocol_feature_function_call_weight")] + gas_weights: Vec<(FunctionCallActionIndex, GasWeight)>, +} + +#[cfg(feature = "protocol_feature_function_call_weight")] +#[derive(Debug, Clone, Copy, PartialEq)] +struct FunctionCallActionIndex { + receipt_index: usize, + action_index: usize, +} + +impl ReceiptManager { + pub fn get_receipt_receiver(&self, receipt_index: u64) -> Option<&AccountId> { + self.action_receipts.0.get(receipt_index as usize).map(|(id, _)| id) + } /// Appends an action and returns the index the action was inserted in the receipt pub fn append_action(&mut self, receipt_index: u64, action: Action) -> usize { let actions = &mut self .action_receipts + .0 .get_mut(receipt_index as usize) .expect("receipt index should be present") .1 @@ -99,6 +107,7 @@ impl ReceiptManager { for receipt_index in receipt_indices { let data_id = generate_data_id(); self.action_receipts + .0 .get_mut(receipt_index as usize) .ok_or_else(|| HostError::InvalidReceiptIndex { receipt_index })? .1 @@ -109,8 +118,8 @@ impl ReceiptManager { let new_receipt = ReceiptMetadata { 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)); + let new_receipt_index = self.action_receipts.0.len() as u64; + self.action_receipts.0.push((receiver_id, new_receipt)); Ok(new_receipt_index) } @@ -305,6 +314,7 @@ impl ReceiptManager { let FunctionCallActionIndex { receipt_index, action_index } = metadata; if let Some(Action::FunctionCall(FunctionCallAction { ref mut gas, .. })) = self .action_receipts + .0 .get_mut(*receipt_index) .and_then(|(_, receipt)| receipt.actions.get_mut(*action_index)) { diff --git a/runtime/near-vm-logic/src/tests/promises.rs b/runtime/near-vm-logic/src/tests/promises.rs index 5cc2914ddb2..f0ce8cb219a 100644 --- a/runtime/near-vm-logic/src/tests/promises.rs +++ b/runtime/near-vm-logic/src/tests/promises.rs @@ -2,8 +2,31 @@ 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 near_account_id::AccountId; +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 + .0 + .iter() + .map(|(receiver_id, metadata)| ReceiptView { + receiver_id, + actions: metadata.actions.as_slice(), + }) + .collect() +} + #[test] fn test_promise_results() { let mut promise_results = vec![]; @@ -43,20 +66,19 @@ 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(), + &serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string() ); } @@ -82,23 +104,22 @@ 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(), + &serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string() ); } @@ -127,29 +148,27 @@ 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(), + &serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string() ); } @@ -183,27 +202,27 @@ 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(), + &serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string() ); } @@ -249,15 +268,15 @@ fn test_promise_batch_action_stake() { assert_eq!(logic.used_gas().unwrap(), 5138631652196); 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" } }, { @@ -270,7 +289,7 @@ fn test_promise_batch_action_stake() { } ]); assert_eq!( - &serde_json::to_string(logic_builder.ext.get_receipt_create_calls()).unwrap(), + &serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string() ); } @@ -327,15 +346,14 @@ fn test_promise_batch_action_add_key_with_function_call() { 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" } }, { @@ -354,7 +372,7 @@ fn test_promise_batch_action_add_key_with_function_call() { } ]); assert_eq!( - &serde_json::to_string(logic_builder.ext.get_receipt_create_calls()).unwrap(), + &serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string() ); } @@ -385,36 +403,29 @@ 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(), + &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 91dcc9e04d6..76caa90fda1 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::Clap; +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: todo!(), state: State(results.state.fake_trie), } ); diff --git a/runtime/runtime/src/actions.rs b/runtime/runtime/src/actions.rs index c4a456779a8..be93e13949b 100644 --- a/runtime/runtime/src/actions.rs +++ b/runtime/runtime/src/actions.rs @@ -221,7 +221,13 @@ pub(crate) fn action_function_call( } None => true, }; - if let Some(outcome) = outcome { + if let Some(mut outcome) = outcome { + let new_receipts = outcome.take_receipts( + &receipt.predecessor_id, + &action_receipt.signer_id, + &action_receipt.signer_public_key, + action_receipt.gas_price, + ); 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)?; @@ -236,12 +242,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(outcome.receipt_manager.into_receipts( - &receipt.predecessor_id, - &action_receipt.signer_id, - &action_receipt.signer_public_key, - action_receipt.gas_price, - )); + 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 51f856946bb..2943615b61f 100644 --- a/runtime/runtime/src/ext.rs +++ b/runtime/runtime/src/ext.rs @@ -97,19 +97,6 @@ impl<'a> RuntimeExt<'a> { TrieKey::ContractData { account_id: self.account_id.clone(), key: key.to_vec() } } - // TODO needs to be moved into trait to be used in VMLogic - pub 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() @@ -205,7 +192,15 @@ impl<'a> External for RuntimeExt<'a> { } fn generate_data_id(&mut self) -> CryptoHash { - todo!() + 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 } // fn create_receipt( From 3e6ed70e168cffd6528b90c1f512dfd19cc9ee7e Mon Sep 17 00:00:00 2001 From: austinabell Date: Tue, 22 Mar 2022 22:44:08 -0400 Subject: [PATCH 04/18] fmt --- runtime/near-vm-logic/src/lib.rs | 2 +- runtime/near-vm-logic/src/tests/promises.rs | 41 +++++---------------- 2 files changed, 11 insertions(+), 32 deletions(-) diff --git a/runtime/near-vm-logic/src/lib.rs b/runtime/near-vm-logic/src/lib.rs index 3cb4012bbf2..71f64ec2c78 100644 --- a/runtime/near-vm-logic/src/lib.rs +++ b/runtime/near-vm-logic/src/lib.rs @@ -7,12 +7,12 @@ mod dependencies; pub mod gas_counter; mod logic; pub mod mocks; +pub(crate) mod receipt_manager; pub mod serde_with; #[cfg(test)] mod tests; pub mod types; mod utils; -pub(crate) mod receipt_manager; pub use context::VMContext; pub use dependencies::{External, MemoryLike, ValuePtr}; diff --git a/runtime/near-vm-logic/src/tests/promises.rs b/runtime/near-vm-logic/src/tests/promises.rs index f0ce8cb219a..278c5dc1ec4 100644 --- a/runtime/near-vm-logic/src/tests/promises.rs +++ b/runtime/near-vm-logic/src/tests/promises.rs @@ -77,10 +77,7 @@ fn test_promise_batch_action_function_call() { } ] }]); - assert_eq!( - &serde_json::to_string(&vm_receipts(&logic)).unwrap(), - &expected.to_string() - ); + assert_eq!(&serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string()); } #[test] @@ -118,10 +115,7 @@ fn test_promise_batch_action_create_account() { ] } ]); - assert_eq!( - &serde_json::to_string(&vm_receipts(&logic)).unwrap(), - &expected.to_string() - ); + assert_eq!(&serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string()); } #[test] @@ -148,7 +142,7 @@ fn test_promise_batch_action_deploy_contract() { let expected = serde_json::json!( [ { - + "receiver_id": "rick.test", "actions": [ { @@ -167,10 +161,7 @@ fn test_promise_batch_action_deploy_contract() { ] } ]); - assert_eq!( - &serde_json::to_string(&vm_receipts(&logic)).unwrap(), - &expected.to_string() - ); + assert_eq!(&serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string()); } #[test] @@ -202,7 +193,7 @@ fn test_promise_batch_action_transfer() { let expected = serde_json::json!( [ { - + "receiver_id": "rick.test", "actions": [ { @@ -221,10 +212,7 @@ fn test_promise_batch_action_transfer() { ] } ]); - assert_eq!( - &serde_json::to_string(&vm_receipts(&logic)).unwrap(), - &expected.to_string() - ); + assert_eq!(&serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string()); } #[test] @@ -268,7 +256,7 @@ fn test_promise_batch_action_stake() { assert_eq!(logic.used_gas().unwrap(), 5138631652196); let expected = serde_json::json!([ { - + "receiver_id": "rick.test", "actions": [ { @@ -288,10 +276,7 @@ fn test_promise_batch_action_stake() { ] } ]); - assert_eq!( - &serde_json::to_string(&vm_receipts(&logic)).unwrap(), - &expected.to_string() - ); + assert_eq!(&serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string()); } #[test] @@ -371,10 +356,7 @@ fn test_promise_batch_action_add_key_with_function_call() { ] } ]); - assert_eq!( - &serde_json::to_string(&vm_receipts(&logic)).unwrap(), - &expected.to_string() - ); + assert_eq!(&serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string()); } #[test] @@ -424,8 +406,5 @@ fn test_promise_batch_then() { "actions": [] } ]); - assert_eq!( - &serde_json::to_string(&vm_receipts(&logic)).unwrap(), - &expected.to_string() - ); + assert_eq!(&serde_json::to_string(&vm_receipts(&logic)).unwrap(), &expected.to_string()); } From 7a0ab6bf16bfd4bd6c3ef69635cd8408d5b08df1 Mon Sep 17 00:00:00 2001 From: austinabell Date: Tue, 22 Mar 2022 23:11:11 -0400 Subject: [PATCH 05/18] fix tests --- runtime/near-vm-logic/src/receipt_manager.rs | 2 +- runtime/near-vm-logic/src/tests/promises.rs | 47 +++++++++++++------- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/runtime/near-vm-logic/src/receipt_manager.rs b/runtime/near-vm-logic/src/receipt_manager.rs index 7b599f7eeef..a10f4420f17 100644 --- a/runtime/near-vm-logic/src/receipt_manager.rs +++ b/runtime/near-vm-logic/src/receipt_manager.rs @@ -62,7 +62,7 @@ impl ActionReceipts { } #[derive(Default, Clone, PartialEq)] -pub struct ReceiptManager { +pub(crate) struct ReceiptManager { pub(crate) action_receipts: ActionReceipts, #[cfg(feature = "protocol_feature_function_call_weight")] gas_weights: Vec<(FunctionCallActionIndex, GasWeight)>, diff --git a/runtime/near-vm-logic/src/tests/promises.rs b/runtime/near-vm-logic/src/tests/promises.rs index 278c5dc1ec4..cd85714eb02 100644 --- a/runtime/near-vm-logic/src/tests/promises.rs +++ b/runtime/near-vm-logic/src/tests/promises.rs @@ -3,7 +3,9 @@ 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; @@ -223,7 +225,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( @@ -253,7 +259,7 @@ 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!([ { @@ -269,8 +275,8 @@ fn test_promise_batch_action_stake() { }, { "Stake": { - "stake": 110, - "public_key": "RLb4qQXoZPAFqzZhiLFAcGFPFC7JWcDd8xKvQHHEqLUgDXuQkr2ehKAN28MNGQN9vUZ1qGZ" + "stake": "110", + "public_key": "ed25519:5do5nkAEVhL8iteDvXNgxi4pWK78Y7DDadX11ArFNyrf" } } ] @@ -287,7 +293,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"; @@ -327,7 +338,7 @@ 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!( [ { @@ -342,15 +353,21 @@ fn test_promise_batch_action_add_key_with_function_call() { } }, { - "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" + ] + } + } + } } } ] From a74421b32d071cc9669c8910f6f373fbcafd895e Mon Sep 17 00:00:00 2001 From: austinabell Date: Thu, 24 Mar 2022 15:30:03 -0400 Subject: [PATCH 06/18] Delete dup and comment receipt manager --- runtime/near-vm-logic/src/dependencies.rs | 369 ------------------ runtime/near-vm-logic/src/logic.rs | 2 - .../near-vm-logic/src/mocks/mock_external.rs | 201 ---------- runtime/near-vm-logic/src/receipt_manager.rs | 135 ++++++- runtime/runtime/src/ext.rs | 275 ------------- 5 files changed, 134 insertions(+), 848 deletions(-) diff --git a/runtime/near-vm-logic/src/dependencies.rs b/runtime/near-vm-logic/src/dependencies.rs index 8981fb6cdd7..ddaa8879f1b 100644 --- a/runtime/near-vm-logic/src/dependencies.rs +++ b/runtime/near-vm-logic/src/dependencies.rs @@ -164,372 +164,6 @@ pub trait External { fn generate_data_id(&mut self) -> CryptoHash; - // /// 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<()>; - /// Returns amount of touched trie nodes by storage operations fn get_touched_nodes_count(&self) -> u64; @@ -539,7 +173,4 @@ pub trait External { /// Returns total stake of validators in the current epoch. fn validator_total_stake(&self) -> Result; - - // #[cfg(feature = "protocol_feature_function_call_weight")] - // fn distribute_unused_gas(&mut self, gas: Gas) -> GasDistribution; } diff --git a/runtime/near-vm-logic/src/logic.rs b/runtime/near-vm-logic/src/logic.rs index fb23121977e..7971075e175 100644 --- a/runtime/near-vm-logic/src/logic.rs +++ b/runtime/near-vm-logic/src/logic.rs @@ -2664,8 +2664,6 @@ impl<'a> VMLogic<'a> { used_gas, logs: self.logs, profile, - // TODO this probably should be receipts, but we need to know the gas price from the - // initial action receipt to be able to convert, which happens outside VM action_receipts: self.receipt_manager.action_receipts, } } diff --git a/runtime/near-vm-logic/src/mocks/mock_external.rs b/runtime/near-vm-logic/src/mocks/mock_external.rs index 82a5f7b4170..6e115a717be 100644 --- a/runtime/near-vm-logic/src/mocks/mock_external.rs +++ b/runtime/near-vm-logic/src/mocks/mock_external.rs @@ -79,156 +79,6 @@ impl External for MockedExternal { data_id } - // 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 get_touched_nodes_count(&self) -> u64 { 0 } @@ -240,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 index a10f4420f17..f93b7591fbf 100644 --- a/runtime/near-vm-logic/src/receipt_manager.rs +++ b/runtime/near-vm-logic/src/receipt_manager.rs @@ -96,7 +96,17 @@ impl ReceiptManager { actions.len() - 1 } - // TODO pull docs for all these methods into here + /// 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 fn create_receipt( &mut self, mut generate_data_id: impl FnMut() -> CryptoHash, @@ -123,11 +133,30 @@ impl ReceiptManager { 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 fn append_action_create_account(&mut self, receipt_index: u64) -> 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 fn append_action_deploy_contract( &mut self, receipt_index: u64, @@ -137,6 +166,28 @@ impl ReceiptManager { 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 fn append_action_function_call_weight( &mut self, @@ -168,6 +219,19 @@ impl ReceiptManager { 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 fn append_action_function_call( &mut self, receipt_index: u64, @@ -189,11 +253,32 @@ impl ReceiptManager { 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 fn append_action_transfer(&mut self, receipt_index: u64, deposit: u128) -> 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 fn append_action_stake( &mut self, receipt_index: u64, @@ -211,6 +296,17 @@ impl ReceiptManager { 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 fn append_action_add_key_with_full_access( &mut self, receipt_index: u64, @@ -228,6 +324,23 @@ impl ReceiptManager { 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 fn append_action_add_key_with_function_call( &mut self, receipt_index: u64, @@ -261,6 +374,16 @@ impl ReceiptManager { 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 fn append_action_delete_key( &mut self, receipt_index: u64, @@ -276,6 +399,16 @@ impl ReceiptManager { 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 fn append_action_delete_account( &mut self, receipt_index: u64, diff --git a/runtime/runtime/src/ext.rs b/runtime/runtime/src/ext.rs index 2943615b61f..f8d47beee7f 100644 --- a/runtime/runtime/src/ext.rs +++ b/runtime/runtime/src/ext.rs @@ -97,35 +97,6 @@ impl<'a> RuntimeExt<'a> { TrieKey::ContractData { account_id: self.account_id.clone(), key: key.to_vec() } } - // 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); } @@ -203,201 +174,6 @@ impl<'a> External for RuntimeExt<'a> { data_id } - // 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 }), - // ); - // Ok(()) - // } - fn get_touched_nodes_count(&self) -> u64 { self.trie_update.trie.get_touched_nodes_count() } @@ -413,55 +189,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 - // } - // } } From a8ca7dc35ec2f87a5354fd9c995959cdedd21ffb Mon Sep 17 00:00:00 2001 From: austinabell Date: Thu, 24 Mar 2022 20:34:25 -0400 Subject: [PATCH 07/18] cleanup docs and fix bug --- runtime/near-vm-logic/src/logic.rs | 9 +++------ runtime/runtime/src/actions.rs | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/runtime/near-vm-logic/src/logic.rs b/runtime/near-vm-logic/src/logic.rs index 7971075e175..d2cd31579bf 100644 --- a/runtime/near-vm-logic/src/logic.rs +++ b/runtime/near-vm-logic/src/logic.rs @@ -62,15 +62,13 @@ 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, - // TODO docs + /// Handles the receipts generated through execution. pub(crate) receipt_manager: ReceiptManager, } @@ -1319,7 +1317,6 @@ impl<'a> VMLogic<'a> { vec![], account_id.clone(), )?; - // self.receipt_to_account.insert(new_receipt_idx, account_id); self.checked_push_promise(Promise::Receipt(new_receipt_idx)) } @@ -1381,7 +1378,6 @@ impl<'a> VMLogic<'a> { receipt_dependencies, account_id.clone(), )?; - // self.receipt_to_account.insert(new_receipt_idx, account_id); self.checked_push_promise(Promise::Receipt(new_receipt_idx)) } @@ -2728,7 +2724,8 @@ impl std::fmt::Debug for VMOutcome { } impl VMOutcome { - // TODO docs + /// Takes all action receipts generated from the VM execution and converts them into receipts + /// based on the context of the transaction. pub fn take_receipts( &mut self, predecessor_id: &AccountId, diff --git a/runtime/runtime/src/actions.rs b/runtime/runtime/src/actions.rs index be93e13949b..c18c1fc59b4 100644 --- a/runtime/runtime/src/actions.rs +++ b/runtime/runtime/src/actions.rs @@ -223,7 +223,7 @@ pub(crate) fn action_function_call( }; if let Some(mut outcome) = outcome { let new_receipts = outcome.take_receipts( - &receipt.predecessor_id, + &account_id, &action_receipt.signer_id, &action_receipt.signer_public_key, action_receipt.gas_price, From 5808f2583536c9f83383d15cd281c4d5561c3505 Mon Sep 17 00:00:00 2001 From: austinabell Date: Fri, 25 Mar 2022 12:00:40 -0400 Subject: [PATCH 08/18] remove generic abstraction that is no longer needed --- runtime/near-vm-logic/src/logic.rs | 9 +++------ runtime/near-vm-logic/src/receipt_manager.rs | 5 +++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/runtime/near-vm-logic/src/logic.rs b/runtime/near-vm-logic/src/logic.rs index d2cd31579bf..3502f158661 100644 --- a/runtime/near-vm-logic/src/logic.rs +++ b/runtime/near-vm-logic/src/logic.rs @@ -1312,11 +1312,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.receipt_manager.create_receipt( - || self.ext.generate_data_id(), - vec![], - account_id.clone(), - )?; + let new_receipt_idx = + self.receipt_manager.create_receipt(self.ext, vec![], account_id.clone())?; self.checked_push_promise(Promise::Receipt(new_receipt_idx)) } @@ -1374,7 +1371,7 @@ impl<'a> VMLogic<'a> { self.pay_gas_for_new_receipt(sir, &deps)?; let new_receipt_idx = self.receipt_manager.create_receipt( - || self.ext.generate_data_id(), + self.ext, receipt_dependencies, account_id.clone(), )?; diff --git a/runtime/near-vm-logic/src/receipt_manager.rs b/runtime/near-vm-logic/src/receipt_manager.rs index f93b7591fbf..39893af027a 100644 --- a/runtime/near-vm-logic/src/receipt_manager.rs +++ b/runtime/near-vm-logic/src/receipt_manager.rs @@ -1,3 +1,4 @@ +use crate::External; use borsh::BorshDeserialize; use near_crypto::PublicKey; use near_primitives::receipt::{ActionReceipt, DataReceiver, Receipt, ReceiptEnum}; @@ -109,13 +110,13 @@ impl ReceiptManager { /// * `receiver_id` - account id of the receiver of the receipt created pub fn create_receipt( &mut self, - mut generate_data_id: impl FnMut() -> CryptoHash, + 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 = generate_data_id(); + let data_id = ext.generate_data_id(); self.action_receipts .0 .get_mut(receipt_index as usize) From 23e893af5ee61982b382a2500e088d5eefb3eb81 Mon Sep 17 00:00:00 2001 From: austinabell Date: Fri, 25 Mar 2022 12:02:15 -0400 Subject: [PATCH 09/18] remove commented out code --- runtime/runtime/src/ext.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/runtime/runtime/src/ext.rs b/runtime/runtime/src/ext.rs index f8d47beee7f..a6bd8da17c1 100644 --- a/runtime/runtime/src/ext.rs +++ b/runtime/runtime/src/ext.rs @@ -74,8 +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![], } } From bbd5dd3e16c8328bf68d086a0320e89e6c916f26 Mon Sep 17 00:00:00 2001 From: austinabell Date: Mon, 28 Mar 2022 21:49:47 -0400 Subject: [PATCH 10/18] avoid exposing vm logic field to crate --- runtime/near-vm-logic/src/logic.rs | 7 ++++++- runtime/near-vm-logic/src/tests/promises.rs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/runtime/near-vm-logic/src/logic.rs b/runtime/near-vm-logic/src/logic.rs index 3502f158661..847c1eb471d 100644 --- a/runtime/near-vm-logic/src/logic.rs +++ b/runtime/near-vm-logic/src/logic.rs @@ -69,7 +69,7 @@ pub struct VMLogic<'a> { current_protocol_version: ProtocolVersion, /// Handles the receipts generated through execution. - pub(crate) receipt_manager: ReceiptManager, + receipt_manager: ReceiptManager, } /// Promises API allows to create a DAG-structure that defines dependencies between smart contract @@ -149,6 +149,11 @@ impl<'a> VMLogic<'a> { } } + #[cfg(test)] + pub(crate) fn receipt_manager(&self) -> &ReceiptManager { + &self.receipt_manager + } + // ########################### // # Memory helper functions # // ########################### diff --git a/runtime/near-vm-logic/src/tests/promises.rs b/runtime/near-vm-logic/src/tests/promises.rs index cd85714eb02..d09b1bb6531 100644 --- a/runtime/near-vm-logic/src/tests/promises.rs +++ b/runtime/near-vm-logic/src/tests/promises.rs @@ -18,7 +18,7 @@ struct ReceiptView<'a> { fn vm_receipts<'a>(logic: &'a VMLogic) -> Vec> { logic - .receipt_manager + .receipt_manager() .action_receipts .0 .iter() From 236f3baff9cf4c0bdf1b0645c3e33cfc3fbee131 Mon Sep 17 00:00:00 2001 From: austinabell Date: Mon, 28 Mar 2022 21:55:35 -0400 Subject: [PATCH 11/18] avoid exposing rec metadata to crate --- runtime/near-vm-logic/src/receipt_manager.rs | 9 ++++++++- runtime/near-vm-logic/src/tests/promises.rs | 5 +---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/runtime/near-vm-logic/src/receipt_manager.rs b/runtime/near-vm-logic/src/receipt_manager.rs index 39893af027a..f0b99214517 100644 --- a/runtime/near-vm-logic/src/receipt_manager.rs +++ b/runtime/near-vm-logic/src/receipt_manager.rs @@ -26,7 +26,14 @@ pub(crate) struct ReceiptMetadata { /// depending on `ReceivedData` is `Some(_)` or `None` input_data_ids: Vec, /// A list of actions to process when all input_data_ids are filled - pub(crate) actions: Vec, + actions: Vec, +} + +impl ReceiptMetadata { + #[cfg(test)] + pub(crate) fn actions(&self) -> &[Action] { + &self.actions + } } #[derive(Default, Clone, PartialEq)] diff --git a/runtime/near-vm-logic/src/tests/promises.rs b/runtime/near-vm-logic/src/tests/promises.rs index d09b1bb6531..b436ad0b2bb 100644 --- a/runtime/near-vm-logic/src/tests/promises.rs +++ b/runtime/near-vm-logic/src/tests/promises.rs @@ -22,10 +22,7 @@ fn vm_receipts<'a>(logic: &'a VMLogic) -> Vec> { .action_receipts .0 .iter() - .map(|(receiver_id, metadata)| ReceiptView { - receiver_id, - actions: metadata.actions.as_slice(), - }) + .map(|(receiver_id, metadata)| ReceiptView { receiver_id, actions: metadata.actions() }) .collect() } From b9cf67f89e272ecc44673689c154036b0d11d4b4 Mon Sep 17 00:00:00 2001 From: austinabell Date: Wed, 30 Mar 2022 11:33:57 -0400 Subject: [PATCH 12/18] Update function call weight tests to be more involved prop tests --- runtime/near-vm-logic/src/gas_counter.rs | 6 ++ runtime/near-vm-logic/src/logic.rs | 13 ++++ .../near-vm-logic/src/tests/gas_counter.rs | 75 +++++++++++++++---- 3 files changed, 81 insertions(+), 13 deletions(-) diff --git a/runtime/near-vm-logic/src/gas_counter.rs b/runtime/near-vm-logic/src/gas_counter.rs index 3769cd63248..368ac4620c1 100644 --- a/runtime/near-vm-logic/src/gas_counter.rs +++ b/runtime/near-vm-logic/src/gas_counter.rs @@ -279,6 +279,12 @@ impl GasCounter { self.promises_gas + self.fast_counter.burnt_gas } + #[allow(dead_code)] + #[cfg(test)] + pub(crate) fn remaining_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/logic.rs b/runtime/near-vm-logic/src/logic.rs index 847c1eb471d..3001382b9ed 100644 --- a/runtime/near-vm-logic/src/logic.rs +++ b/runtime/near-vm-logic/src/logic.rs @@ -149,11 +149,18 @@ impl<'a> VMLogic<'a> { } } + #[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 # // ########################### @@ -2737,4 +2744,10 @@ impl VMOutcome { ) -> Vec { self.action_receipts.take_receipts(predecessor_id, signer_id, signer_public_key, gas_price) } + + #[allow(dead_code)] + #[cfg(test)] + pub(crate) fn action_receipts(&self) -> &ActionReceipts { + &self.action_receipts + } } diff --git a/runtime/near-vm-logic/src/tests/gas_counter.rs b/runtime/near-vm-logic/src/tests/gas_counter.rs index 3e2c9137373..b2605eb4974 100644 --- a/runtime/near-vm-logic/src/tests/gas_counter.rs +++ b/runtime/near-vm-logic/src/tests/gas_counter.rs @@ -103,7 +103,9 @@ fn test_hit_prepaid_gas_limit() { } #[cfg(feature = "protocol_feature_function_call_weight")] -fn function_call_weight_check(function_calls: impl IntoIterator) { +fn function_call_weight_check(function_calls: &[(Gas, u64)]) { + use crate::receipt_manager::ReceiptMetadata; + use near_primitives::transaction::{Action, FunctionCallAction}; use near_primitives::types::GasWeight; let gas_limit = 10u64.pow(14); @@ -111,46 +113,93 @@ fn function_call_weight_check(function_calls: impl IntoIterator bool) { + if let Action::FunctionCall(FunctionCallAction { gas, .. }) = receipt.actions()[0] { + assert!(cb(gas)); + } else { + panic!("expected function call action"); + } } + + let (gas_per_weight, remainder_gas) = { + let gas_to_distribute = logic.gas_counter().remaining_gas(); + let weight_sum: u64 = function_calls + .iter() + .map(|(_, weight)| weight) + // Saturating sum of weights + .fold(0, |sum, val| sum.checked_add(*val).unwrap_or(u64::MAX)); + (gas_to_distribute / weight_sum, gas_to_distribute % weight_sum) + }; + + // Test static gas assigned before + let receipts = logic.receipt_manager().action_receipts.0.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().0.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, (static_gas, weight)) in receipts.zip(function_calls) { + assert_with_gas(receipt, |gas| gas >= static_gas + weight * gas_per_weight); + } + + // Verify last receipt received all remaining gas + let (static_gas, weight) = function_calls.last().unwrap(); + let (_, last_receipt) = outcome.action_receipts().0.last().unwrap(); + assert_with_gas(last_receipt, |gas| { + gas == static_gas + weight * gas_per_weight + remainder_gas + }); + // 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_single_prop_test() { // Single function call - function_call_weight_check([(0, 1)]); + function_call_weight_check(&[(0, 1)]); // Single function with static gas - function_call_weight_check([(888, 1)]); + function_call_weight_check(&[(888, 1)]); // Large weight - function_call_weight_check([(0, 88888)]); + function_call_weight_check(&[(0, 88888)]); // Weight larger than gas limit - function_call_weight_check([(0, 11u64.pow(14))]); + function_call_weight_check(&[(0, 11u64.pow(14))]); // Split two - function_call_weight_check([(0, 3), (0, 2)]); + function_call_weight_check(&[(0, 3), (0, 2)]); // 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), (3_000_000, 2)]); // Many different gas weights - function_call_weight_check([ + function_call_weight_check(&[ (1_000_000, 3), (3_000_000, 2), (0, 1), @@ -159,10 +208,10 @@ fn function_call_weight_single_smoke_test() { ]); // Weight over u64 bounds - function_call_weight_check([(0, u64::MAX), (0, 1000)]); + function_call_weight_check(&[(0, u64::MAX), (0, 1000)]); // Weights with one zero and one non-zero - function_call_weight_check([(0, 0), (0, 1)]) + function_call_weight_check(&[(0, 0), (0, 1)]) } #[cfg(feature = "protocol_feature_function_call_weight")] From 37f0de08f49dea0a89f585cf4c4c78170181d0bb Mon Sep 17 00:00:00 2001 From: austinabell Date: Fri, 1 Apr 2022 11:01:52 -0400 Subject: [PATCH 13/18] expose action_receipts from outcome --- runtime/near-vm-logic/src/logic.rs | 25 ++----------------- runtime/near-vm-logic/src/receipt_manager.rs | 8 +++--- .../near-vm-logic/src/tests/gas_counter.rs | 4 +-- runtime/runtime/src/actions.rs | 4 +-- 4 files changed, 10 insertions(+), 31 deletions(-) diff --git a/runtime/near-vm-logic/src/logic.rs b/runtime/near-vm-logic/src/logic.rs index 3001382b9ed..27846dfd3a2 100644 --- a/runtime/near-vm-logic/src/logic.rs +++ b/runtime/near-vm-logic/src/logic.rs @@ -6,8 +6,7 @@ use crate::types::{PromiseIndex, PromiseResult, ReceiptIndex, ReturnData}; use crate::utils::split_method_names; use crate::ValuePtr; use byteorder::ByteOrder; -use near_crypto::{PublicKey, Secp256K1Signature}; -use near_primitives::receipt::Receipt; +use near_crypto::Secp256K1Signature; use near_primitives::version::is_implicit_account_creation_enabled; use near_primitives_core::config::ExtCosts::*; use near_primitives_core::config::{ActionCosts, ExtCosts, VMConfig, ViewConfig}; @@ -2702,7 +2701,7 @@ pub struct VMOutcome { pub logs: Vec, /// Data collected from making a contract call pub profile: ProfileData, - action_receipts: ActionReceipts, + pub action_receipts: ActionReceipts, } impl PartialEq for VMOutcome { @@ -2731,23 +2730,3 @@ impl std::fmt::Debug for VMOutcome { ) } } - -impl VMOutcome { - /// Takes all action receipts generated from the VM execution and converts them into receipts - /// based on the context of the transaction. - pub fn take_receipts( - &mut self, - predecessor_id: &AccountId, - signer_id: &AccountId, - signer_public_key: &PublicKey, - gas_price: Balance, - ) -> Vec { - self.action_receipts.take_receipts(predecessor_id, signer_id, signer_public_key, gas_price) - } - - #[allow(dead_code)] - #[cfg(test)] - pub(crate) fn action_receipts(&self) -> &ActionReceipts { - &self.action_receipts - } -} diff --git a/runtime/near-vm-logic/src/receipt_manager.rs b/runtime/near-vm-logic/src/receipt_manager.rs index f0b99214517..bb81de4484e 100644 --- a/runtime/near-vm-logic/src/receipt_manager.rs +++ b/runtime/near-vm-logic/src/receipt_manager.rs @@ -37,11 +37,11 @@ impl ReceiptMetadata { } #[derive(Default, Clone, PartialEq)] -pub(crate) struct ActionReceipts(pub(crate) Vec<(AccountId, ReceiptMetadata)>); +pub struct ActionReceipts(pub(crate) Vec<(AccountId, ReceiptMetadata)>); impl ActionReceipts { - pub(crate) fn take_receipts( - &mut self, + pub fn into_receipts( + self, predecessor_id: &AccountId, signer_id: &AccountId, signer_public_key: &PublicKey, @@ -49,7 +49,7 @@ impl ActionReceipts { ) -> Vec { let ActionReceipts(receipts) = self; receipts - .drain(..) + .into_iter() .map(|(receiver_id, receipt)| Receipt { predecessor_id: predecessor_id.clone(), receiver_id, diff --git a/runtime/near-vm-logic/src/tests/gas_counter.rs b/runtime/near-vm-logic/src/tests/gas_counter.rs index b2605eb4974..8061959a2f4 100644 --- a/runtime/near-vm-logic/src/tests/gas_counter.rs +++ b/runtime/near-vm-logic/src/tests/gas_counter.rs @@ -156,7 +156,7 @@ fn function_call_weight_check(function_calls: &[(Gas, u64)]) { let outcome = logic.compute_outcome_and_distribute_gas(); // Test gas is distributed after outcome calculated. - let receipts = outcome.action_receipts().0.iter().map(|(_, rec)| rec); + let receipts = outcome.action_receipts.0.iter().map(|(_, rec)| rec); // Assert lengths are equal for zip assert_eq!(receipts.len(), function_calls.len()); @@ -168,7 +168,7 @@ fn function_call_weight_check(function_calls: &[(Gas, u64)]) { // Verify last receipt received all remaining gas let (static_gas, weight) = function_calls.last().unwrap(); - let (_, last_receipt) = outcome.action_receipts().0.last().unwrap(); + let (_, last_receipt) = outcome.action_receipts.0.last().unwrap(); assert_with_gas(last_receipt, |gas| { gas == static_gas + weight * gas_per_weight + remainder_gas }); diff --git a/runtime/runtime/src/actions.rs b/runtime/runtime/src/actions.rs index c18c1fc59b4..cdff88d0571 100644 --- a/runtime/runtime/src/actions.rs +++ b/runtime/runtime/src/actions.rs @@ -221,8 +221,8 @@ pub(crate) fn action_function_call( } None => true, }; - if let Some(mut outcome) = outcome { - let new_receipts = outcome.take_receipts( + if let Some(outcome) = outcome { + let new_receipts = outcome.action_receipts.into_receipts( &account_id, &action_receipt.signer_id, &action_receipt.signer_public_key, From bb8b0f1ee52c4471bfac9b74a35d38bc22549b25 Mon Sep 17 00:00:00 2001 From: austinabell Date: Fri, 1 Apr 2022 12:20:56 -0400 Subject: [PATCH 14/18] switch to partialeq derive (include receipts) --- runtime/near-vm-logic/src/logic.rs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/runtime/near-vm-logic/src/logic.rs b/runtime/near-vm-logic/src/logic.rs index 27846dfd3a2..7f834cc64fb 100644 --- a/runtime/near-vm-logic/src/logic.rs +++ b/runtime/near-vm-logic/src/logic.rs @@ -2691,7 +2691,7 @@ impl<'a> VMLogic<'a> { } } -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub struct VMOutcome { pub balance: Balance, pub storage_usage: StorageUsage, @@ -2704,18 +2704,6 @@ pub struct VMOutcome { pub action_receipts: ActionReceipts, } -impl PartialEq for VMOutcome { - fn eq(&self, other: &Self) -> bool { - self.balance == other.balance - && self.storage_usage == other.storage_usage - && self.return_data == other.return_data - && self.burnt_gas == other.burnt_gas - && self.used_gas == other.used_gas - && self.logs == other.logs - && self.profile == other.profile - } -} - impl std::fmt::Debug for VMOutcome { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let return_data_str = match &self.return_data { From 69c7b98a595872382a8ed1eaf2405bfe63aed331 Mon Sep 17 00:00:00 2001 From: austinabell Date: Mon, 4 Apr 2022 19:31:07 -0400 Subject: [PATCH 15/18] update test to use exact gas amounts --- runtime/near-vm-logic/src/gas_counter.rs | 7 +- .../near-vm-logic/src/tests/gas_counter.rs | 91 +++++++++---------- 2 files changed, 46 insertions(+), 52 deletions(-) diff --git a/runtime/near-vm-logic/src/gas_counter.rs b/runtime/near-vm-logic/src/gas_counter.rs index 368ac4620c1..2709bee5a89 100644 --- a/runtime/near-vm-logic/src/gas_counter.rs +++ b/runtime/near-vm-logic/src/gas_counter.rs @@ -275,13 +275,14 @@ 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 } - #[allow(dead_code)] - #[cfg(test)] - pub(crate) fn remaining_gas(&self) -> Gas { + /// Remaining gas based on the amount of prepaid gas not yet used. + pub fn remaining_gas(&self) -> Gas { self.prepaid_gas - self.used_gas() } diff --git a/runtime/near-vm-logic/src/tests/gas_counter.rs b/runtime/near-vm-logic/src/tests/gas_counter.rs index 8061959a2f4..acb7cf10da2 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,20 +110,25 @@ fn test_hit_prepaid_gas_limit() { } #[cfg(feature = "protocol_feature_function_call_weight")] -fn function_call_weight_check(function_calls: &[(Gas, u64)]) { - use crate::receipt_manager::ReceiptMetadata; - use near_primitives::transaction::{Action, FunctionCallAction}; - use near_primitives::types::GasWeight; +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); let mut ratios = vec![]; // Schedule all function calls - for (static_gas, gas_weight) in 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, @@ -129,27 +141,9 @@ fn function_call_weight_check(function_calls: &[(Gas, u64)]) { ratios.push((index, *gas_weight)); } - 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_per_weight, remainder_gas) = { - let gas_to_distribute = logic.gas_counter().remaining_gas(); - let weight_sum: u64 = function_calls - .iter() - .map(|(_, weight)| weight) - // Saturating sum of weights - .fold(0, |sum, val| sum.checked_add(*val).unwrap_or(u64::MAX)); - (gas_to_distribute / weight_sum, gas_to_distribute % weight_sum) - }; - // Test static gas assigned before let receipts = logic.receipt_manager().action_receipts.0.iter().map(|(_, rec)| rec); - for (receipt, (static_gas, _)) in receipts.zip(function_calls) { + for (receipt, (static_gas, _, _)) in receipts.zip(function_calls) { assert_with_gas(receipt, |gas| gas == *static_gas); } @@ -162,56 +156,55 @@ fn function_call_weight_check(function_calls: &[(Gas, u64)]) { assert_eq!(receipts.len(), function_calls.len()); // Assert sufficient amount was given to - for (receipt, (static_gas, weight)) in receipts.zip(function_calls) { - assert_with_gas(receipt, |gas| gas >= static_gas + weight * gas_per_weight); + for (receipt, (_, _, expected)) in receipts.zip(function_calls) { + assert_with_gas(receipt, |gas| { + assert_eq!(gas, *expected); + true + }); } - // Verify last receipt received all remaining gas - let (static_gas, weight) = function_calls.last().unwrap(); - let (_, last_receipt) = outcome.action_receipts.0.last().unwrap(); - assert_with_gas(last_receipt, |gas| { - gas == static_gas + weight * gas_per_weight + remainder_gas - }); - // 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_prop_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), + (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")] From a908b1b03c549ef460235e202b300761cc50e001 Mon Sep 17 00:00:00 2001 From: austinabell Date: Mon, 4 Apr 2022 20:55:20 -0400 Subject: [PATCH 16/18] expose metadata and move receipt construction out of vm logic --- runtime/near-vm-logic/src/gas_counter.rs | 2 +- runtime/near-vm-logic/src/lib.rs | 1 + runtime/near-vm-logic/src/logic.rs | 16 ++-- runtime/near-vm-logic/src/receipt_manager.rs | 95 ++++++------------- .../near-vm-logic/src/tests/gas_counter.rs | 6 +- runtime/near-vm-logic/src/tests/promises.rs | 3 +- runtime/runtime/src/actions.rs | 27 ++++-- 7 files changed, 61 insertions(+), 89 deletions(-) diff --git a/runtime/near-vm-logic/src/gas_counter.rs b/runtime/near-vm-logic/src/gas_counter.rs index 2709bee5a89..575e54dc77f 100644 --- a/runtime/near-vm-logic/src/gas_counter.rs +++ b/runtime/near-vm-logic/src/gas_counter.rs @@ -282,7 +282,7 @@ impl GasCounter { } /// Remaining gas based on the amount of prepaid gas not yet used. - pub fn remaining_gas(&self) -> Gas { + pub fn unused_gas(&self) -> Gas { self.prepaid_gas - self.used_gas() } diff --git a/runtime/near-vm-logic/src/lib.rs b/runtime/near-vm-logic/src/lib.rs index 71f64ec2c78..97e9aa1bed3 100644 --- a/runtime/near-vm-logic/src/lib.rs +++ b/runtime/near-vm-logic/src/lib.rs @@ -21,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 7f834cc64fb..91bd66c48c5 100644 --- a/runtime/near-vm-logic/src/logic.rs +++ b/runtime/near-vm-logic/src/logic.rs @@ -1,10 +1,10 @@ use crate::context::VMContext; use crate::dependencies::{External, MemoryLike}; use crate::gas_counter::{FastGasCounter, GasCounter}; -use crate::receipt_manager::{ActionReceipts, ReceiptManager}; +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; @@ -2643,13 +2643,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.receipt_manager.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(); } } @@ -2701,7 +2699,7 @@ pub struct VMOutcome { pub logs: Vec, /// Data collected from making a contract call pub profile: ProfileData, - pub action_receipts: ActionReceipts, + pub action_receipts: Vec<(AccountId, ReceiptMetadata)>, } impl std::fmt::Debug for VMOutcome { diff --git a/runtime/near-vm-logic/src/receipt_manager.rs b/runtime/near-vm-logic/src/receipt_manager.rs index bb81de4484e..c8ffadd3bb5 100644 --- a/runtime/near-vm-logic/src/receipt_manager.rs +++ b/runtime/near-vm-logic/src/receipt_manager.rs @@ -1,14 +1,14 @@ use crate::External; use borsh::BorshDeserialize; use near_crypto::PublicKey; -use near_primitives::receipt::{ActionReceipt, DataReceiver, Receipt, ReceiptEnum}; +use near_primitives::receipt::DataReceiver; use near_primitives::transaction::{ Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction, DeployContractAction, FunctionCallAction, StakeAction, TransferAction, }; use near_primitives_core::account::{AccessKey, AccessKeyPermission, FunctionCallPermission}; use near_primitives_core::hash::CryptoHash; -use near_primitives_core::types::{AccountId, Balance, Gas}; +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}; @@ -16,62 +16,22 @@ use near_vm_errors::{HostError, VMLogicError}; type ExtResult = ::std::result::Result; #[derive(Debug, Clone, PartialEq)] -pub(crate) struct ReceiptMetadata { +pub struct ReceiptMetadata { /// If present, where to route the output data - output_data_receivers: Vec, + 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` - input_data_ids: Vec, + pub input_data_ids: Vec, /// A list of actions to process when all input_data_ids are filled - actions: Vec, -} - -impl ReceiptMetadata { - #[cfg(test)] - pub(crate) fn actions(&self) -> &[Action] { - &self.actions - } -} - -#[derive(Default, Clone, PartialEq)] -pub struct ActionReceipts(pub(crate) Vec<(AccountId, ReceiptMetadata)>); - -impl ActionReceipts { - pub fn into_receipts( - self, - predecessor_id: &AccountId, - signer_id: &AccountId, - signer_public_key: &PublicKey, - gas_price: Balance, - ) -> Vec { - let ActionReceipts(receipts) = self; - receipts - .into_iter() - .map(|(receiver_id, 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(ActionReceipt { - signer_id: signer_id.clone(), - signer_public_key: signer_public_key.clone(), - gas_price, - output_data_receivers: receipt.output_data_receivers, - input_data_ids: receipt.input_data_ids, - actions: receipt.actions, - }), - }) - .collect() - } + pub actions: Vec, } #[derive(Default, Clone, PartialEq)] pub(crate) struct ReceiptManager { - pub(crate) action_receipts: ActionReceipts, + pub(crate) action_receipts: Vec<(AccountId, ReceiptMetadata)>, #[cfg(feature = "protocol_feature_function_call_weight")] gas_weights: Vec<(FunctionCallActionIndex, GasWeight)>, } @@ -84,15 +44,14 @@ struct FunctionCallActionIndex { } impl ReceiptManager { - pub fn get_receipt_receiver(&self, receipt_index: u64) -> Option<&AccountId> { - self.action_receipts.0.get(receipt_index as usize).map(|(id, _)| id) + pub(crate) fn get_receipt_receiver(&self, receipt_index: u64) -> Option<&AccountId> { + self.action_receipts.get(receipt_index as usize).map(|(id, _)| id) } /// Appends an action and returns the index the action was inserted in the receipt - pub fn append_action(&mut self, receipt_index: u64, action: Action) -> usize { + pub(crate) fn append_action(&mut self, receipt_index: u64, action: Action) -> usize { let actions = &mut self .action_receipts - .0 .get_mut(receipt_index as usize) .expect("receipt index should be present") .1 @@ -115,7 +74,7 @@ impl ReceiptManager { /// * `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 fn create_receipt( + pub(crate) fn create_receipt( &mut self, ext: &mut dyn External, receipt_indices: Vec, @@ -125,7 +84,6 @@ impl ReceiptManager { for receipt_index in receipt_indices { let data_id = ext.generate_data_id(); self.action_receipts - .0 .get_mut(receipt_index as usize) .ok_or_else(|| HostError::InvalidReceiptIndex { receipt_index })? .1 @@ -136,8 +94,8 @@ impl ReceiptManager { let new_receipt = ReceiptMetadata { output_data_receivers: vec![], input_data_ids, actions: vec![] }; - let new_receipt_index = self.action_receipts.0.len() as u64; - self.action_receipts.0.push((receiver_id, new_receipt)); + let new_receipt_index = self.action_receipts.len() as u64; + self.action_receipts.push((receiver_id, new_receipt)); Ok(new_receipt_index) } @@ -150,7 +108,7 @@ impl ReceiptManager { /// # Panics /// /// Panics if the `receipt_index` does not refer to a known receipt. - pub fn append_action_create_account(&mut self, receipt_index: u64) -> ExtResult<()> { + pub(crate) fn append_action_create_account(&mut self, receipt_index: u64) -> ExtResult<()> { self.append_action(receipt_index, Action::CreateAccount(CreateAccountAction {})); Ok(()) } @@ -165,7 +123,7 @@ impl ReceiptManager { /// # Panics /// /// Panics if the `receipt_index` does not refer to a known receipt. - pub fn append_action_deploy_contract( + pub(crate) fn append_action_deploy_contract( &mut self, receipt_index: u64, code: Vec, @@ -197,7 +155,7 @@ impl ReceiptManager { /// /// Panics if the `receipt_index` does not refer to a known receipt. #[cfg(feature = "protocol_feature_function_call_weight")] - pub fn append_action_function_call_weight( + pub(crate) fn append_action_function_call_weight( &mut self, receipt_index: u64, method_name: Vec, @@ -240,7 +198,7 @@ impl ReceiptManager { /// # Panics /// /// Panics if the `receipt_index` does not refer to a known receipt. - pub fn append_action_function_call( + pub(crate) fn append_action_function_call( &mut self, receipt_index: u64, method_name: Vec, @@ -271,7 +229,11 @@ impl ReceiptManager { /// # Panics /// /// Panics if the `receipt_index` does not refer to a known receipt. - pub fn append_action_transfer(&mut self, receipt_index: u64, deposit: u128) -> ExtResult<()> { + pub(crate) fn append_action_transfer( + &mut self, + receipt_index: u64, + deposit: u128, + ) -> ExtResult<()> { self.append_action(receipt_index, Action::Transfer(TransferAction { deposit })); Ok(()) } @@ -287,7 +249,7 @@ impl ReceiptManager { /// # Panics /// /// Panics if the `receipt_index` does not refer to a known receipt. - pub fn append_action_stake( + pub(crate) fn append_action_stake( &mut self, receipt_index: u64, stake: u128, @@ -315,7 +277,7 @@ impl ReceiptManager { /// # Panics /// /// Panics if the `receipt_index` does not refer to a known receipt. - pub fn append_action_add_key_with_full_access( + pub(crate) fn append_action_add_key_with_full_access( &mut self, receipt_index: u64, public_key: Vec, @@ -349,7 +311,7 @@ impl ReceiptManager { /// # Panics /// /// Panics if the `receipt_index` does not refer to a known receipt. - pub fn append_action_add_key_with_function_call( + pub(crate) fn append_action_add_key_with_function_call( &mut self, receipt_index: u64, public_key: Vec, @@ -392,7 +354,7 @@ impl ReceiptManager { /// # Panics /// /// Panics if the `receipt_index` does not refer to a known receipt. - pub fn append_action_delete_key( + pub(crate) fn append_action_delete_key( &mut self, receipt_index: u64, public_key: Vec, @@ -417,7 +379,7 @@ impl ReceiptManager { /// # Panics /// /// Panics if the `receipt_index` does not refer to a known receipt. - pub fn append_action_delete_account( + pub(crate) fn append_action_delete_account( &mut self, receipt_index: u64, beneficiary_id: AccountId, @@ -444,7 +406,7 @@ impl ReceiptManager { /// /// Function returns a [GasDistribution] that indicates how the gas was distributed. #[cfg(feature = "protocol_feature_function_call_weight")] - pub fn distribute_unused_gas(&mut self, gas: u64) -> GasDistribution { + pub(crate) 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 { @@ -455,7 +417,6 @@ impl ReceiptManager { let FunctionCallActionIndex { receipt_index, action_index } = metadata; if let Some(Action::FunctionCall(FunctionCallAction { ref mut gas, .. })) = self .action_receipts - .0 .get_mut(*receipt_index) .and_then(|(_, receipt)| receipt.actions.get_mut(*action_index)) { diff --git a/runtime/near-vm-logic/src/tests/gas_counter.rs b/runtime/near-vm-logic/src/tests/gas_counter.rs index acb7cf10da2..b6eaf14d251 100644 --- a/runtime/near-vm-logic/src/tests/gas_counter.rs +++ b/runtime/near-vm-logic/src/tests/gas_counter.rs @@ -111,7 +111,7 @@ fn test_hit_prepaid_gas_limit() { #[cfg(feature = "protocol_feature_function_call_weight")] fn assert_with_gas(receipt: &ReceiptMetadata, cb: impl Fn(Gas) -> bool) { - if let Action::FunctionCall(FunctionCallAction { gas, .. }) = receipt.actions()[0] { + if let Action::FunctionCall(FunctionCallAction { gas, .. }) = receipt.actions[0] { assert!(cb(gas)); } else { panic!("expected function call action"); @@ -142,7 +142,7 @@ fn function_call_weight_check(function_calls: &[(Gas, u64, Gas)]) { } // Test static gas assigned before - let receipts = logic.receipt_manager().action_receipts.0.iter().map(|(_, rec)| rec); + 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); } @@ -150,7 +150,7 @@ fn function_call_weight_check(function_calls: &[(Gas, u64, Gas)]) { let outcome = logic.compute_outcome_and_distribute_gas(); // Test gas is distributed after outcome calculated. - let receipts = outcome.action_receipts.0.iter().map(|(_, rec)| rec); + let receipts = outcome.action_receipts.iter().map(|(_, rec)| rec); // Assert lengths are equal for zip assert_eq!(receipts.len(), function_calls.len()); diff --git a/runtime/near-vm-logic/src/tests/promises.rs b/runtime/near-vm-logic/src/tests/promises.rs index b436ad0b2bb..add7875e3ba 100644 --- a/runtime/near-vm-logic/src/tests/promises.rs +++ b/runtime/near-vm-logic/src/tests/promises.rs @@ -20,9 +20,8 @@ fn vm_receipts<'a>(logic: &'a VMLogic) -> Vec> { logic .receipt_manager() .action_receipts - .0 .iter() - .map(|(receiver_id, metadata)| ReceiptView { receiver_id, actions: metadata.actions() }) + .map(|(receiver_id, metadata)| ReceiptView { receiver_id, actions: &metadata.actions }) .collect() } diff --git a/runtime/runtime/src/actions.rs b/runtime/runtime/src/actions.rs index cdff88d0571..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::{ @@ -222,12 +222,25 @@ pub(crate) fn action_function_call( None => true, }; if let Some(outcome) = outcome { - let new_receipts = outcome.action_receipts.into_receipts( - &account_id, - &action_receipt.signer_id, - &action_receipt.signer_public_key, - action_receipt.gas_price, - ); + 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)?; From ba2aa7b2a832c0fd34a1f0eb88954ab6b78f89e2 Mon Sep 17 00:00:00 2001 From: austinabell Date: Mon, 4 Apr 2022 21:30:43 -0400 Subject: [PATCH 17/18] addr aux comments --- runtime/near-vm-logic/src/receipt_manager.rs | 152 ++++++++++-------- .../near-vm-logic/src/tests/gas_counter.rs | 6 +- 2 files changed, 90 insertions(+), 68 deletions(-) diff --git a/runtime/near-vm-logic/src/receipt_manager.rs b/runtime/near-vm-logic/src/receipt_manager.rs index c8ffadd3bb5..482a9d484eb 100644 --- a/runtime/near-vm-logic/src/receipt_manager.rs +++ b/runtime/near-vm-logic/src/receipt_manager.rs @@ -1,3 +1,4 @@ +use crate::types::ReceiptIndex; use crate::External; use borsh::BorshDeserialize; use near_crypto::PublicKey; @@ -6,6 +7,7 @@ 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}; @@ -15,6 +17,8 @@ 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 @@ -31,25 +35,48 @@ pub struct ReceiptMetadata { #[derive(Default, Clone, PartialEq)] pub(crate) struct ReceiptManager { - pub(crate) action_receipts: Vec<(AccountId, ReceiptMetadata)>, + 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: u64) -> Option<&AccountId> { + pub(crate) fn get_receipt_receiver(&self, receipt_index: ReceiptIndex) -> Option<&AccountId> { self.action_receipts.get(receipt_index as usize).map(|(id, _)| id) } /// Appends an action and returns the index the action was inserted in the receipt - pub(crate) fn append_action(&mut self, receipt_index: u64, action: Action) -> usize { + fn append_action(&mut self, receipt_index: ReceiptIndex, action: Action) -> usize { let actions = &mut self .action_receipts .get_mut(receipt_index as usize) @@ -77,9 +104,9 @@ impl ReceiptManager { pub(crate) fn create_receipt( &mut self, ext: &mut dyn External, - receipt_indices: Vec, + receipt_indices: Vec, receiver_id: AccountId, - ) -> ExtResult { + ) -> ExtResult { let mut input_data_ids = vec![]; for receipt_index in receipt_indices { let data_id = ext.generate_data_id(); @@ -94,7 +121,7 @@ impl ReceiptManager { let new_receipt = ReceiptMetadata { output_data_receivers: vec![], input_data_ids, actions: vec![] }; - let new_receipt_index = self.action_receipts.len() as u64; + let new_receipt_index = self.action_receipts.len() as ReceiptIndex; self.action_receipts.push((receiver_id, new_receipt)); Ok(new_receipt_index) } @@ -108,7 +135,10 @@ impl ReceiptManager { /// # Panics /// /// Panics if the `receipt_index` does not refer to a known receipt. - pub(crate) fn append_action_create_account(&mut self, receipt_index: u64) -> ExtResult<()> { + pub(crate) fn append_action_create_account( + &mut self, + receipt_index: ReceiptIndex, + ) -> ExtResult<()> { self.append_action(receipt_index, Action::CreateAccount(CreateAccountAction {})); Ok(()) } @@ -125,7 +155,7 @@ impl ReceiptManager { /// Panics if the `receipt_index` does not refer to a known receipt. pub(crate) fn append_action_deploy_contract( &mut self, - receipt_index: u64, + receipt_index: ReceiptIndex, code: Vec, ) -> ExtResult<()> { self.append_action(receipt_index, Action::DeployContract(DeployContractAction { code })); @@ -157,10 +187,10 @@ impl ReceiptManager { #[cfg(feature = "protocol_feature_function_call_weight")] pub(crate) fn append_action_function_call_weight( &mut self, - receipt_index: u64, + receipt_index: ReceiptIndex, method_name: Vec, args: Vec, - attached_deposit: u128, + attached_deposit: Balance, prepaid_gas: Gas, gas_weight: GasWeight, ) -> ExtResult<()> { @@ -200,10 +230,10 @@ impl ReceiptManager { /// Panics if the `receipt_index` does not refer to a known receipt. pub(crate) fn append_action_function_call( &mut self, - receipt_index: u64, + receipt_index: ReceiptIndex, method_name: Vec, args: Vec, - attached_deposit: u128, + attached_deposit: Balance, prepaid_gas: Gas, ) -> ExtResult<()> { self.append_action( @@ -231,8 +261,8 @@ impl ReceiptManager { /// Panics if the `receipt_index` does not refer to a known receipt. pub(crate) fn append_action_transfer( &mut self, - receipt_index: u64, - deposit: u128, + receipt_index: ReceiptIndex, + deposit: Balance, ) -> ExtResult<()> { self.append_action(receipt_index, Action::Transfer(TransferAction { deposit })); Ok(()) @@ -251,8 +281,8 @@ impl ReceiptManager { /// Panics if the `receipt_index` does not refer to a known receipt. pub(crate) fn append_action_stake( &mut self, - receipt_index: u64, - stake: u128, + receipt_index: ReceiptIndex, + stake: Balance, public_key: Vec, ) -> ExtResult<()> { self.append_action( @@ -279,9 +309,9 @@ impl ReceiptManager { /// 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: u64, + receipt_index: ReceiptIndex, public_key: Vec, - nonce: u64, + nonce: Nonce, ) -> ExtResult<()> { self.append_action( receipt_index, @@ -313,10 +343,10 @@ impl ReceiptManager { /// 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: u64, + receipt_index: ReceiptIndex, public_key: Vec, - nonce: u64, - allowance: Option, + nonce: Nonce, + allowance: Option, receiver_id: AccountId, method_names: Vec>, ) -> ExtResult<()> { @@ -356,7 +386,7 @@ impl ReceiptManager { /// Panics if the `receipt_index` does not refer to a known receipt. pub(crate) fn append_action_delete_key( &mut self, - receipt_index: u64, + receipt_index: ReceiptIndex, public_key: Vec, ) -> ExtResult<()> { self.append_action( @@ -381,7 +411,7 @@ impl ReceiptManager { /// Panics if the `receipt_index` does not refer to a known receipt. pub(crate) fn append_action_delete_account( &mut self, - receipt_index: u64, + receipt_index: ReceiptIndex, beneficiary_id: AccountId, ) -> ExtResult<()> { self.append_action( @@ -406,49 +436,43 @@ impl ReceiptManager { /// /// 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, gas: u64) -> GasDistribution { + 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 { - // 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 + + 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 b6eaf14d251..03190dce4c0 100644 --- a/runtime/near-vm-logic/src/tests/gas_counter.rs +++ b/runtime/near-vm-logic/src/tests/gas_counter.rs @@ -110,6 +110,7 @@ fn test_hit_prepaid_gas_limit() { } #[cfg(feature = "protocol_feature_function_call_weight")] +#[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)); @@ -157,10 +158,7 @@ fn function_call_weight_check(function_calls: &[(Gas, u64, Gas)]) { // Assert sufficient amount was given to for (receipt, (_, _, expected)) in receipts.zip(function_calls) { - assert_with_gas(receipt, |gas| { - assert_eq!(gas, *expected); - true - }); + assert_with_gas(receipt, |gas| gas == *expected); } // Verify that all gas was consumed (assumes at least one ratio is provided) From 3fdce0e4125cc9f2eb866733274fac93d6bbab65 Mon Sep 17 00:00:00 2001 From: austinabell Date: Mon, 4 Apr 2022 21:39:29 -0400 Subject: [PATCH 18/18] move expect to receipt manager logic --- runtime/near-vm-logic/src/logic.rs | 4 +--- runtime/near-vm-logic/src/receipt_manager.rs | 7 +++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/runtime/near-vm-logic/src/logic.rs b/runtime/near-vm-logic/src/logic.rs index 91bd66c48c5..27f5234d262 100644 --- a/runtime/near-vm-logic/src/logic.rs +++ b/runtime/near-vm-logic/src/logic.rs @@ -1392,9 +1392,7 @@ impl<'a> VMLogic<'a> { /// 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_manager - .get_receipt_receiver(receipt_idx) - .expect("promises and receipt_to_account should be consistent.") + self.receipt_manager.get_receipt_receiver(receipt_idx) } /// Helper function to return the receipt index corresponding to the given promise index. diff --git a/runtime/near-vm-logic/src/receipt_manager.rs b/runtime/near-vm-logic/src/receipt_manager.rs index 482a9d484eb..d9a3f4bcb9d 100644 --- a/runtime/near-vm-logic/src/receipt_manager.rs +++ b/runtime/near-vm-logic/src/receipt_manager.rs @@ -71,8 +71,11 @@ fn get_fuction_call_action_mut( } impl ReceiptManager { - pub(crate) fn get_receipt_receiver(&self, receipt_index: ReceiptIndex) -> Option<&AccountId> { - self.action_receipts.get(receipt_index as usize).map(|(id, _)| id) + 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