diff --git a/chain/chain/src/chain_update.rs b/chain/chain/src/chain_update.rs index 9d9b9bb592a..b03946b0947 100644 --- a/chain/chain/src/chain_update.rs +++ b/chain/chain/src/chain_update.rs @@ -157,8 +157,7 @@ impl<'a> ChainUpdate<'a> { shard_id, apply_result.proof, apply_result.applied_receipts_hash, - apply_result.contract_accesses, - apply_result.contract_deploys, + apply_result.contract_updates, ); } } @@ -188,8 +187,7 @@ impl<'a> ChainUpdate<'a> { shard_uid.shard_id(), apply_result.proof, apply_result.applied_receipts_hash, - apply_result.contract_accesses, - apply_result.contract_deploys, + apply_result.contract_updates, ); } } diff --git a/chain/chain/src/resharding/manager.rs b/chain/chain/src/resharding/manager.rs index 0d3f5318ae5..049be53f932 100644 --- a/chain/chain/src/resharding/manager.rs +++ b/chain/chain/src/resharding/manager.rs @@ -226,10 +226,7 @@ impl ReshardingManager { new_shard_uid.shard_id(), Some(partial_storage), CryptoHash::default(), - // No contract code is accessed during resharding. - // TODO(#11099): Confirm if sending no contracts is ok here. - Default::default(), - // No contract code is deployed during resharding. + // No contract code is accessed or deployed during resharding. // TODO(#11099): Confirm if sending no contracts is ok here. Default::default(), ); diff --git a/chain/chain/src/runtime/mod.rs b/chain/chain/src/runtime/mod.rs index 7bf7ceac6a4..7bec806078e 100644 --- a/chain/chain/src/runtime/mod.rs +++ b/chain/chain/src/runtime/mod.rs @@ -468,10 +468,9 @@ impl NightshadeRuntime { processed_yield_timeouts: apply_result.processed_yield_timeouts, applied_receipts_hash: hash(&borsh::to_vec(receipts).unwrap()), congestion_info: apply_result.congestion_info, - contract_accesses: apply_result.contract_accesses, bandwidth_requests: apply_result.bandwidth_requests, bandwidth_scheduler_state_hash: apply_result.bandwidth_scheduler_state_hash, - contract_deploys: apply_result.contract_deploys, + contract_updates: apply_result.contract_updates, }; Ok(result) diff --git a/chain/chain/src/store/mod.rs b/chain/chain/src/store/mod.rs index ecf60cf6dd2..918beccc626 100644 --- a/chain/chain/src/store/mod.rs +++ b/chain/chain/src/store/mod.rs @@ -1,5 +1,5 @@ use std::collections::hash_map::Entry; -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{HashMap, HashSet}; use std::io; use borsh::{BorshDeserialize, BorshSerialize}; @@ -23,7 +23,7 @@ use near_primitives::sharding::{ use near_primitives::state_sync::{ ReceiptProofResponse, ShardStateSyncResponseHeader, StateHeaderKey, StateSyncDumpProgress, }; -use near_primitives::stateless_validation::contract_distribution::CodeHash; +use near_primitives::stateless_validation::contract_distribution::ContractUpdates; use near_primitives::stateless_validation::stored_chunk_state_transition_data::{ StoredChunkStateTransitionData, StoredChunkStateTransitionDataV2, }; @@ -2010,10 +2010,10 @@ impl<'a> ChainStoreUpdate<'a> { shard_id: ShardId, partial_storage: Option, applied_receipts_hash: CryptoHash, - contract_accesses: BTreeSet, - contract_deploys: BTreeSet, + contract_updates: ContractUpdates, ) { if let Some(partial_storage) = partial_storage { + let ContractUpdates { contract_accesses, contract_deploys } = contract_updates; self.state_transition_data.insert( (block_hash, shard_id), StoredChunkStateTransitionData::V2(StoredChunkStateTransitionDataV2 { diff --git a/chain/chain/src/test_utils/kv_runtime.rs b/chain/chain/src/test_utils/kv_runtime.rs index dc68100afdf..cf8046adff4 100644 --- a/chain/chain/src/test_utils/kv_runtime.rs +++ b/chain/chain/src/test_utils/kv_runtime.rs @@ -1388,8 +1388,7 @@ impl RuntimeAdapter for KeyValueRuntime { congestion_info: Self::get_congestion_info(PROTOCOL_VERSION), bandwidth_requests: BandwidthRequests::default_for_protocol_version(PROTOCOL_VERSION), bandwidth_scheduler_state_hash: CryptoHash::default(), - contract_accesses: Default::default(), - contract_deploys: Default::default(), + contract_updates: Default::default(), }) } diff --git a/chain/chain/src/types.rs b/chain/chain/src/types.rs index 599b7a90582..087b1b4b2b0 100644 --- a/chain/chain/src/types.rs +++ b/chain/chain/src/types.rs @@ -1,5 +1,3 @@ -use std::collections::BTreeSet; - use borsh::{BorshDeserialize, BorshSerialize}; use near_async::time::{Duration, Utc}; use near_chain_configs::GenesisConfig; @@ -26,7 +24,7 @@ use near_primitives::receipt::{PromiseYieldTimeout, Receipt}; use near_primitives::sandbox::state_patch::SandboxStatePatch; use near_primitives::shard_layout::ShardUId; use near_primitives::state_part::PartId; -use near_primitives::stateless_validation::contract_distribution::CodeHash; +use near_primitives::stateless_validation::contract_distribution::ContractUpdates; use near_primitives::transaction::{ExecutionOutcomeWithId, SignedTransaction}; use near_primitives::types::validator_stake::{ValidatorStake, ValidatorStakeIter}; use near_primitives::types::{ @@ -112,10 +110,8 @@ pub struct ApplyChunkResult { pub bandwidth_requests: Option, /// Used only for a sanity check. pub bandwidth_scheduler_state_hash: CryptoHash, - /// Code-hashes of the contracts accessed (called) while applying the chunk. - pub contract_accesses: BTreeSet, - /// Code-hashes of the contracts deployed while applying the chunk. - pub contract_deploys: BTreeSet, + /// Contracts accessed and deployed while applying the chunk. + pub contract_updates: ContractUpdates, } impl ApplyChunkResult { diff --git a/chain/client/src/stateless_validation/partial_witness/partial_witness_actor.rs b/chain/client/src/stateless_validation/partial_witness/partial_witness_actor.rs index 84ed9f590a4..98ce8fa047d 100644 --- a/chain/client/src/stateless_validation/partial_witness/partial_witness_actor.rs +++ b/chain/client/src/stateless_validation/partial_witness/partial_witness_actor.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeSet; +use std::collections::HashSet; use std::sync::Arc; use itertools::Itertools; @@ -368,7 +368,7 @@ impl PartialWitnessActor { let runtime_config = self .runtime .get_runtime_config(self.epoch_manager.get_epoch_protocol_version(&key.epoch_id)?)?; - let missing_contract_hashes = BTreeSet::from_iter( + let missing_contract_hashes = HashSet::from_iter( accesses .contracts() .iter() diff --git a/chain/client/src/stateless_validation/partial_witness/partial_witness_tracker.rs b/chain/client/src/stateless_validation/partial_witness/partial_witness_tracker.rs index 8bfe0d4a208..28588de9eec 100644 --- a/chain/client/src/stateless_validation/partial_witness/partial_witness_tracker.rs +++ b/chain/client/src/stateless_validation/partial_witness/partial_witness_tracker.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeSet; +use std::collections::HashSet; use std::num::NonZeroUsize; use std::sync::Arc; @@ -43,7 +43,7 @@ enum AccessedContractsState { Unknown, /// Received `ChunkContractAccesses` and sent `ContractCodeRequest`, /// waiting for response from the chunk producer. - Requested { contract_hashes: BTreeSet, requested_at: Instant }, + Requested { contract_hashes: HashSet, requested_at: Instant }, /// Received a valid `ContractCodeResponse`. Received(Vec), } @@ -145,7 +145,7 @@ struct CacheEntry { enum CacheUpdate { WitnessPart(PartialEncodedStateWitness, Arc), - AccessedContractHashes(BTreeSet), + AccessedContractHashes(HashSet), AccessedContractCodes(Vec), } @@ -234,7 +234,7 @@ impl CacheEntry { } } - fn set_requested_contracts(&mut self, contract_hashes: BTreeSet) { + fn set_requested_contracts(&mut self, contract_hashes: HashSet) { match &self.accessed_contracts { AccessedContractsState::Unknown => { self.accessed_contracts = AccessedContractsState::Requested { @@ -251,7 +251,7 @@ impl CacheEntry { fn set_received_contracts(&mut self, contract_codes: Vec) { match &self.accessed_contracts { AccessedContractsState::Requested { contract_hashes, requested_at } => { - let actual = BTreeSet::from_iter( + let actual = HashSet::from_iter( contract_codes.iter().map(|code| CodeHash(CryptoHash::hash_bytes(&code.0))), ); let expected = contract_hashes; @@ -380,7 +380,7 @@ impl PartialEncodedStateWitnessTracker { pub fn store_accessed_contract_hashes( &mut self, key: ChunkProductionKey, - hashes: BTreeSet, + hashes: HashSet, ) -> Result<(), Error> { tracing::debug!(target: "client", ?key, ?hashes, "store_accessed_contract_hashes"); let update = CacheUpdate::AccessedContractHashes(hashes); diff --git a/chain/client/src/stateless_validation/state_witness_producer.rs b/chain/client/src/stateless_validation/state_witness_producer.rs index cc73c3d1fec..b31cfe8b50b 100644 --- a/chain/client/src/stateless_validation/state_witness_producer.rs +++ b/chain/client/src/stateless_validation/state_witness_producer.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{HashMap, HashSet}; use std::sync::Arc; use near_async::messaging::{CanSend, IntoSender}; @@ -12,7 +12,7 @@ use near_primitives::hash::{hash, CryptoHash}; use near_primitives::receipt::Receipt; use near_primitives::sharding::{ChunkHash, ReceiptProof, ShardChunk, ShardChunkHeader}; use near_primitives::stateless_validation::contract_distribution::{ - ChunkContractAccesses, CodeHash, + ChunkContractAccesses, ContractUpdates, }; use near_primitives::stateless_validation::state_witness::{ ChunkStateTransition, ChunkStateWitness, @@ -37,8 +37,7 @@ struct StateTransitionData { main_transition: ChunkStateTransition, implicit_transitions: Vec, applied_receipts_hash: CryptoHash, - contract_accesses: BTreeSet, - contract_deploys: BTreeSet, + contract_updates: ContractUpdates, } /// Result of creating witness. @@ -48,10 +47,8 @@ struct StateTransitionData { pub(crate) struct CreateWitnessResult { /// State witness created. pub(crate) state_witness: ChunkStateWitness, - /// Code-hashes of contracts accessed while applying the previous chunk. - pub(crate) contract_accesses: BTreeSet, - /// Code-hashes of contracts deployed while applying the previous chunk. - pub(crate) contract_deploys: BTreeSet, + /// Contracts accessed and deployed while applying the chunk. + pub(crate) contract_updates: ContractUpdates, } impl Client { @@ -76,14 +73,13 @@ impl Client { let my_signer = validator_signer.as_ref().ok_or(Error::NotAValidator(format!("send state witness")))?; - let CreateWitnessResult { state_witness, contract_accesses, contract_deploys } = self - .create_state_witness( - my_signer.validator_id().clone(), - prev_block_header, - prev_chunk_header, - chunk, - transactions_storage_proof, - )?; + let CreateWitnessResult { state_witness, contract_updates } = self.create_state_witness( + my_signer.validator_id().clone(), + prev_block_header, + prev_chunk_header, + chunk, + transactions_storage_proof, + )?; if self.config.save_latest_witnesses { self.chain.chain_store.save_latest_chunk_state_witness(&state_witness)?; @@ -108,12 +104,12 @@ impl Client { if ProtocolFeature::ExcludeContractCodeFromStateWitness.enabled(protocol_version) { // TODO(#11099): Currently we consume contract_deploys only for the following log message. Distribute it to validators // that will not validate the current witness so that they can follow-up with requesting the contract code. - tracing::debug!(target: "client", ?contract_accesses, ?contract_deploys, "Contract accesses and deploys while sending state witness"); - if !contract_accesses.is_empty() { + tracing::debug!(target: "client", ?contract_updates, "Contract accesses and deploys while sending state witness"); + if !contract_updates.contract_accesses.is_empty() { self.send_contract_accesses_to_chunk_validators( epoch_id, &chunk_header, - contract_accesses, + contract_updates, my_signer.as_ref(), ); } @@ -143,8 +139,7 @@ impl Client { main_transition, implicit_transitions, applied_receipts_hash, - contract_accesses, - contract_deploys, + contract_updates, } = self.collect_state_transition_data(&chunk_header, prev_chunk_header)?; let new_transactions = chunk.transactions().to_vec(); @@ -178,7 +173,7 @@ impl Client { new_transactions, new_transactions_validation_state, ); - Ok(CreateWitnessResult { state_witness, contract_accesses, contract_deploys }) + Ok(CreateWitnessResult { state_witness, contract_updates }) } /// Collect state transition data necessary to produce state witness for @@ -219,7 +214,7 @@ impl Client { if current_shard_id != next_shard_id { // If shard id changes, we need to get implicit state // transition from current shard id to the next shard id. - let (chunk_state_transition, _, _, _) = + let (chunk_state_transition, _, _) = self.get_state_transition(¤t_block_hash, &next_epoch_id, next_shard_id)?; implicit_transitions.push(chunk_state_transition); } @@ -231,7 +226,7 @@ impl Client { } // Add implicit state transition. - let (chunk_state_transition, _, _, _) = self.get_state_transition( + let (chunk_state_transition, _, _) = self.get_state_transition( ¤t_block_hash, ¤t_epoch_id, current_shard_id, @@ -247,19 +242,17 @@ impl Client { implicit_transitions.reverse(); // Get the main state transition. - let (main_transition, receipts_hash, contract_accesses, contract_deploys) = - if prev_chunk_header.is_genesis() { - self.get_genesis_state_transition(&main_block, &epoch_id, shard_id)? - } else { - self.get_state_transition(&main_block, &epoch_id, shard_id)? - }; + let (main_transition, receipts_hash, contract_updates) = if prev_chunk_header.is_genesis() { + self.get_genesis_state_transition(&main_block, &epoch_id, shard_id)? + } else { + self.get_state_transition(&main_block, &epoch_id, shard_id)? + }; Ok(StateTransitionData { main_transition, implicit_transitions, applied_receipts_hash: receipts_hash, - contract_accesses: BTreeSet::from_iter(contract_accesses.into_iter()), - contract_deploys: BTreeSet::from_iter(contract_deploys.into_iter()), + contract_updates, }) } @@ -269,8 +262,7 @@ impl Client { block_hash: &CryptoHash, epoch_id: &EpochId, shard_id: ShardId, - ) -> Result<(ChunkStateTransition, CryptoHash, BTreeSet, BTreeSet), Error> - { + ) -> Result<(ChunkStateTransition, CryptoHash, ContractUpdates), Error> { let shard_uid = self.chain.epoch_manager.shard_id_to_uid(shard_id, epoch_id)?; let stored_chunk_state_transition_data = self .chain @@ -303,6 +295,10 @@ impl Client { contract_deploys, }) => (base_state, receipts_hash, contract_accesses, contract_deploys), }; + let contract_updates = ContractUpdates { + contract_accesses: contract_accesses.into_iter().collect(), + contract_deploys: contract_deploys.into_iter().collect(), + }; Ok(( ChunkStateTransition { block_hash: *block_hash, @@ -310,8 +306,7 @@ impl Client { post_state_root: *self.chain.get_chunk_extra(block_hash, &shard_uid)?.state_root(), }, receipts_hash, - BTreeSet::from_iter(contract_accesses.into_iter()), - BTreeSet::from_iter(contract_deploys.into_iter()), + contract_updates, )) } @@ -320,8 +315,7 @@ impl Client { block_hash: &CryptoHash, epoch_id: &EpochId, shard_id: ShardId, - ) -> Result<(ChunkStateTransition, CryptoHash, BTreeSet, BTreeSet), Error> - { + ) -> Result<(ChunkStateTransition, CryptoHash, ContractUpdates), Error> { let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, &epoch_id)?; Ok(( ChunkStateTransition { @@ -331,7 +325,6 @@ impl Client { }, hash(&borsh::to_vec::<[Receipt]>(&[]).unwrap()), Default::default(), - Default::default(), )) } @@ -426,9 +419,11 @@ impl Client { &self, epoch_id: &EpochId, chunk_header: &ShardChunkHeader, - contract_accesses: BTreeSet, + contract_updates: ContractUpdates, my_signer: &ValidatorSigner, ) { + let ContractUpdates { contract_accesses, .. } = contract_updates; + let chunk_production_key = ChunkProductionKey { epoch_id: *epoch_id, shard_id: chunk_header.shard_id(), diff --git a/core/primitives/src/stateless_validation/contract_distribution.rs b/core/primitives/src/stateless_validation/contract_distribution.rs index 4ad7dd92acd..912fb867698 100644 --- a/core/primitives/src/stateless_validation/contract_distribution.rs +++ b/core/primitives/src/stateless_validation/contract_distribution.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeSet; +use std::collections::HashSet; use borsh::{BorshDeserialize, BorshSerialize}; use bytesize::ByteSize; @@ -24,7 +24,7 @@ pub enum ChunkContractAccesses { impl ChunkContractAccesses { pub fn new( next_chunk: ChunkProductionKey, - contracts: BTreeSet, + contracts: HashSet, signer: &ValidatorSigner, ) -> Self { Self::V1(ChunkContractAccessesV1::new(next_chunk, contracts, signer)) @@ -59,7 +59,7 @@ pub struct ChunkContractAccessesV1 { impl ChunkContractAccessesV1 { fn new( next_chunk: ChunkProductionKey, - contracts: BTreeSet, + contracts: HashSet, signer: &ValidatorSigner, ) -> Self { let inner = ChunkContractAccessesInner::new(next_chunk, contracts); @@ -85,7 +85,7 @@ pub struct ChunkContractAccessesInner { } impl ChunkContractAccessesInner { - fn new(next_chunk: ChunkProductionKey, contracts: BTreeSet) -> Self { + fn new(next_chunk: ChunkProductionKey, contracts: HashSet) -> Self { Self { next_chunk, contracts: contracts.into_iter().collect(), @@ -107,7 +107,7 @@ pub enum ChunkContractDeployments { impl ChunkContractDeployments { pub fn new( next_chunk: ChunkProductionKey, - contracts: BTreeSet, + contracts: HashSet, signer: &ValidatorSigner, ) -> Self { Self::V1(ChunkContractDeploymentsV1::new(next_chunk, contracts, signer)) @@ -136,7 +136,7 @@ pub struct ChunkContractDeploymentsV1 { impl ChunkContractDeploymentsV1 { fn new( next_chunk: ChunkProductionKey, - contracts: BTreeSet, + contracts: HashSet, signer: &ValidatorSigner, ) -> Self { let inner = ChunkContractDeploymentsInner::new(next_chunk, contracts); @@ -158,7 +158,7 @@ pub struct ChunkContractDeploymentsInner { } impl ChunkContractDeploymentsInner { - fn new(next_chunk: ChunkProductionKey, contracts: BTreeSet) -> Self { + fn new(next_chunk: ChunkProductionKey, contracts: HashSet) -> Self { Self { next_chunk, contracts: contracts.into_iter().collect(), @@ -179,7 +179,7 @@ pub enum ContractCodeRequest { impl ContractCodeRequest { pub fn new( next_chunk: ChunkProductionKey, - contracts: BTreeSet, + contracts: HashSet, signer: &ValidatorSigner, ) -> Self { Self::V1(ContractCodeRequestV1::new(next_chunk, contracts, signer)) @@ -214,7 +214,7 @@ pub struct ContractCodeRequestV1 { impl ContractCodeRequestV1 { fn new( next_chunk: ChunkProductionKey, - contracts: BTreeSet, + contracts: HashSet, signer: &ValidatorSigner, ) -> Self { let inner = @@ -243,7 +243,7 @@ impl ContractCodeRequestInner { fn new( requester: AccountId, next_chunk: ChunkProductionKey, - contracts: BTreeSet, + contracts: HashSet, ) -> Self { Self { requester, @@ -382,3 +382,12 @@ impl Into for CodeHash { /// Raw bytes of the (uncompiled) contract code. #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)] pub struct CodeBytes(pub std::sync::Arc<[u8]>); + +/// Contains the accesses and changes (eg. deployments) to the contracts while applying a chunk. +#[derive(Debug, Default)] +pub struct ContractUpdates { + /// Code-hashes of the contracts accessed (called) while applying the chunk. + pub contract_accesses: HashSet, + /// Code-hashes of the contracts deployed while applying the chunk. + pub contract_deploys: HashSet, +} diff --git a/core/store/src/contract.rs b/core/store/src/contract.rs index 10854be9250..a5abff262be 100644 --- a/core/store/src/contract.rs +++ b/core/store/src/contract.rs @@ -1,9 +1,9 @@ use crate::{metrics, TrieStorage}; use near_primitives::errors::StorageError; use near_primitives::hash::CryptoHash; -use near_primitives::stateless_validation::contract_distribution::CodeHash; +use near_primitives::stateless_validation::contract_distribution::{CodeHash, ContractUpdates}; use near_vm_runner::ContractCode; -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::{BTreeMap, HashSet}; use std::sync::{Arc, Mutex}; /// Tracks the uncommitted and committed deployments and calls to contracts, while applying the receipts in a chunk. @@ -35,7 +35,7 @@ struct ContractsTracker { /// /// We do not distinguish between committed and uncommitted calls, because we need /// to record all calls to validate both successful and failing function calls. - contract_calls: BTreeSet, + contract_calls: HashSet, } impl ContractsTracker { @@ -68,22 +68,14 @@ impl ContractsTracker { } /// Finalizes this tracker and returns the calls and committed deployments. - fn finalize(mut self) -> ContractStorageResult { - ContractStorageResult { - contract_calls: std::mem::take(&mut self.contract_calls), + fn finalize(mut self) -> ContractUpdates { + ContractUpdates { + contract_accesses: std::mem::take(&mut self.contract_calls), contract_deploys: self.committed_deploys.into_keys().collect(), } } } -/// Result of finalizing the contract storage, containing the contract calls and committed deployments. -pub struct ContractStorageResult { - /// List of code-hashes for the contract calls while applying the chunk. - pub contract_calls: BTreeSet, - /// List of code-hashes for the (committed) contract deployments while applying the chunk. - pub contract_deploys: BTreeSet, -} - /// Reads contract code from the trie by its hash. /// /// Cloning is cheap. @@ -178,7 +170,7 @@ impl ContractStorage { /// /// It also finalizes and destructs the inner`ContractsTracker` so there must be no other deployments or /// calls to contracts after this returns. - pub(crate) fn finalize(self) -> ContractStorageResult { + pub(crate) fn finalize(self) -> ContractUpdates { let mut guard = self.tracker.lock().expect("no panics"); let tracker = guard.take().expect("finalize must be called only once"); tracker.finalize() diff --git a/core/store/src/trie/update.rs b/core/store/src/trie/update.rs index 0ee0440e1cf..563b287c559 100644 --- a/core/store/src/trie/update.rs +++ b/core/store/src/trie/update.rs @@ -1,16 +1,16 @@ pub use self::iterator::TrieUpdateIterator; use super::accounting_cache::TrieAccountingCacheSwitch; use super::{OptimizedValueRef, Trie, TrieWithReadLock}; -use crate::contract::{ContractStorage, ContractStorageResult}; +use crate::contract::ContractStorage; use crate::trie::{KeyLookupMode, TrieChanges}; use crate::StorageError; -use near_primitives::stateless_validation::contract_distribution::CodeHash; +use near_primitives::stateless_validation::contract_distribution::ContractUpdates; use near_primitives::trie_key::TrieKey; use near_primitives::types::{ RawStateChange, RawStateChanges, RawStateChangesWithTrieKey, StateChangeCause, StateRoot, TrieCacheMode, }; -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeMap; mod iterator; @@ -58,10 +58,8 @@ pub struct TrieUpdateResult { pub trie: Trie, pub trie_changes: TrieChanges, pub state_changes: Vec, - /// Code-hashes of the contracts accessed (called). - pub contract_accesses: BTreeSet, - /// Code-hashes of the contracts deployed. - pub contract_deploys: BTreeSet, + /// Contracts accessed and deployed while applying the chunk. + pub contract_updates: ContractUpdates, } impl TrieUpdate { @@ -192,15 +190,8 @@ impl TrieUpdate { span.record("mem_reads", iops_delta.mem_reads); span.record("db_reads", iops_delta.db_reads); } - let ContractStorageResult { contract_calls, contract_deploys } = - contract_storage.finalize(); - Ok(TrieUpdateResult { - trie, - trie_changes, - state_changes, - contract_accesses: contract_calls, - contract_deploys, - }) + let contract_updates = contract_storage.finalize(); + Ok(TrieUpdateResult { trie, trie_changes, state_changes, contract_updates }) } /// Returns Error if the underlying storage fails diff --git a/runtime/runtime/src/lib.rs b/runtime/runtime/src/lib.rs index 75e2f9931d2..16ca49e96d6 100644 --- a/runtime/runtime/src/lib.rs +++ b/runtime/runtime/src/lib.rs @@ -36,7 +36,7 @@ use near_primitives::receipt::{ use near_primitives::runtime::migration_data::{MigrationData, MigrationFlags}; use near_primitives::sandbox::state_patch::SandboxStatePatch; use near_primitives::state_record::StateRecord; -use near_primitives::stateless_validation::contract_distribution::CodeHash; +use near_primitives::stateless_validation::contract_distribution::ContractUpdates; #[cfg(feature = "protocol_feature_nonrefundable_transfer_nep491")] use near_primitives::transaction::NonrefundableStorageTransferAction; use near_primitives::transaction::{ @@ -72,7 +72,7 @@ use near_vm_runner::ContractRuntimeCache; use near_vm_runner::ProfileDataV3; use pipelining::ReceiptPreparationPipeline; use std::cmp::max; -use std::collections::{BTreeSet, HashMap, HashSet, VecDeque}; +use std::collections::{HashMap, HashSet, VecDeque}; use std::sync::Arc; use tracing::{debug, instrument}; @@ -202,8 +202,8 @@ pub struct ApplyResult { pub bandwidth_requests: Option, /// Used only for a sanity check. pub bandwidth_scheduler_state_hash: CryptoHash, - pub contract_accesses: BTreeSet, - pub contract_deploys: BTreeSet, + /// Contracts accessed and deployed while applying the chunk. + pub contract_updates: ContractUpdates, } #[derive(Debug)] @@ -2080,13 +2080,8 @@ impl Runtime { metrics::CHUNK_RECORDED_SIZE_UPPER_BOUND .with_label_values(&[shard_id_str.as_str()]) .observe(chunk_recorded_size_upper_bound); - let TrieUpdateResult { - trie, - trie_changes, - state_changes, - contract_accesses, - contract_deploys, - } = state_update.finalize()?; + let TrieUpdateResult { trie, trie_changes, state_changes, contract_updates } = + state_update.finalize()?; if let Some(prefetcher) = &processing_state.prefetcher { // Only clear the prefetcher queue after finalize is done because as part of receipt @@ -2146,8 +2141,7 @@ impl Runtime { .as_ref() .map(|o| o.scheduler_state_hash) .unwrap_or_default(), - contract_accesses, - contract_deploys, + contract_updates, }) } } @@ -2235,7 +2229,7 @@ fn missing_chunk_apply_result( processing_state: ApplyProcessingState, bandwidth_scheduler_output: &Option, ) -> Result { - let TrieUpdateResult { trie, trie_changes, state_changes, contract_accesses, contract_deploys } = + let TrieUpdateResult { trie, trie_changes, state_changes, contract_updates } = processing_state.state_update.finalize()?; let proof = trie.recorded_storage(); @@ -2276,8 +2270,7 @@ fn missing_chunk_apply_result( .as_ref() .map(|o| o.scheduler_state_hash) .unwrap_or_default(), - contract_accesses, - contract_deploys, + contract_updates, }); } diff --git a/runtime/runtime/src/tests/apply.rs b/runtime/runtime/src/tests/apply.rs index f77d929c1d1..0067f939ee9 100644 --- a/runtime/runtime/src/tests/apply.rs +++ b/runtime/runtime/src/tests/apply.rs @@ -38,7 +38,7 @@ use near_store::{ Trie, }; use near_vm_runner::{ContractCode, FilesystemContractRuntimeCache}; -use std::collections::{BTreeSet, HashMap}; +use std::collections::{HashMap, HashSet}; use std::sync::Arc; use testlib::runtime_utils::{alice_account, bob_account}; @@ -1272,9 +1272,12 @@ fn test_exclude_contract_code_from_witness() { .unwrap(); assert_eq!(apply_result.delayed_receipts_count, 0); - assert_eq!(apply_result.contract_accesses, BTreeSet::new()); + assert_eq!(apply_result.contract_updates.contract_accesses, HashSet::new()); // Since both accounts deploy the same contract, we expect only one contract deploy. - assert_eq!(apply_result.contract_deploys, BTreeSet::from([CodeHash(*contract_code.hash())])); + assert_eq!( + apply_result.contract_updates.contract_deploys, + HashSet::from([CodeHash(*contract_code.hash())]) + ); let mut store_update = tries.store_update(); let root = @@ -1311,8 +1314,11 @@ fn test_exclude_contract_code_from_witness() { assert_eq!(apply_result.delayed_receipts_count, 0); // Since both accounts call the same contract, we expect only one contract access. - assert_eq!(apply_result.contract_accesses, BTreeSet::from([CodeHash(*contract_code.hash())])); - assert_eq!(apply_result.contract_deploys, BTreeSet::new()); + assert_eq!( + apply_result.contract_updates.contract_accesses, + HashSet::from([CodeHash(*contract_code.hash())]) + ); + assert_eq!(apply_result.contract_updates.contract_deploys, HashSet::new()); // Check that both contracts are excluded from the storage proof. let partial_storage = apply_result.proof.unwrap(); @@ -1374,9 +1380,12 @@ fn test_exclude_contract_code_from_witness_with_failed_call() { .unwrap(); assert_eq!(apply_result.delayed_receipts_count, 0); - assert_eq!(apply_result.contract_accesses, BTreeSet::new()); + assert_eq!(apply_result.contract_updates.contract_accesses, HashSet::new()); // Since both accounts deploy the same contract, we expect only one contract deploy. - assert_eq!(apply_result.contract_deploys, BTreeSet::from([CodeHash(*contract_code.hash())])); + assert_eq!( + apply_result.contract_updates.contract_deploys, + HashSet::from([CodeHash(*contract_code.hash())]) + ); let mut store_update = tries.store_update(); let root = @@ -1413,8 +1422,11 @@ fn test_exclude_contract_code_from_witness_with_failed_call() { assert_eq!(apply_result.delayed_receipts_count, 1); // Since both accounts call the same contract, we expect only one contract access. - assert_eq!(apply_result.contract_accesses, BTreeSet::from([CodeHash(*contract_code.hash())])); - assert_eq!(apply_result.contract_deploys, BTreeSet::new()); + assert_eq!( + apply_result.contract_updates.contract_accesses, + HashSet::from([CodeHash(*contract_code.hash())]) + ); + assert_eq!(apply_result.contract_updates.contract_deploys, HashSet::new()); // Check that both contracts are excluded from the storage proof. let partial_storage = apply_result.proof.unwrap(); @@ -1502,10 +1514,10 @@ fn test_deploy_and_call_different_contracts() { .unwrap(); assert_eq!(apply_result.delayed_receipts_count, 0); - assert_eq!(apply_result.contract_accesses, BTreeSet::new()); + assert_eq!(apply_result.contract_updates.contract_accesses, HashSet::new()); assert_eq!( - apply_result.contract_deploys, - BTreeSet::from([ + apply_result.contract_updates.contract_deploys, + HashSet::from([ CodeHash(*first_contract_code.hash()), CodeHash(*second_contract_code.hash()) ]) @@ -1530,13 +1542,13 @@ fn test_deploy_and_call_different_contracts() { assert_eq!(apply_result.delayed_receipts_count, 0); assert_eq!( - apply_result.contract_accesses, - BTreeSet::from([ + apply_result.contract_updates.contract_accesses, + HashSet::from([ CodeHash(*first_contract_code.hash()), CodeHash(*second_contract_code.hash()) ]) ); - assert_eq!(apply_result.contract_deploys, BTreeSet::new()); + assert_eq!(apply_result.contract_updates.contract_deploys, HashSet::new()); } // Similar to test_deploy_and_call_different_contracts, but one of the function calls fails. @@ -1611,10 +1623,10 @@ fn test_deploy_and_call_different_contracts_with_failed_call() { .unwrap(); assert_eq!(apply_result.delayed_receipts_count, 0); - assert_eq!(apply_result.contract_accesses, BTreeSet::new()); + assert_eq!(apply_result.contract_updates.contract_accesses, HashSet::new()); assert_eq!( - apply_result.contract_deploys, - BTreeSet::from([ + apply_result.contract_updates.contract_deploys, + HashSet::from([ CodeHash(*first_contract_code.hash()), CodeHash(*second_contract_code.hash()) ]) @@ -1639,10 +1651,10 @@ fn test_deploy_and_call_different_contracts_with_failed_call() { assert_eq!(apply_result.delayed_receipts_count, 1); assert_eq!( - apply_result.contract_accesses, - BTreeSet::from([CodeHash(*first_contract_code.hash())]) + apply_result.contract_updates.contract_accesses, + HashSet::from([CodeHash(*first_contract_code.hash())]) ); - assert_eq!(apply_result.contract_deploys, BTreeSet::new()); + assert_eq!(apply_result.contract_updates.contract_deploys, HashSet::new()); } // Tests excluding contract code from state witness and recording of contract deployments and function calls @@ -1718,15 +1730,15 @@ fn test_deploy_and_call_in_apply() { assert_eq!(apply_result.delayed_receipts_count, 0); assert_eq!( - apply_result.contract_accesses, - BTreeSet::from([ + apply_result.contract_updates.contract_accesses, + HashSet::from([ CodeHash(*first_contract_code.hash()), CodeHash(*second_contract_code.hash()) ]) ); assert_eq!( - apply_result.contract_deploys, - BTreeSet::from([ + apply_result.contract_updates.contract_deploys, + HashSet::from([ CodeHash(*first_contract_code.hash()), CodeHash(*second_contract_code.hash()) ]) @@ -1806,13 +1818,13 @@ fn test_deploy_and_call_in_apply_with_failed_call() { assert_eq!(apply_result.delayed_receipts_count, 1); assert_eq!( - apply_result.contract_accesses, - BTreeSet::from([CodeHash(*first_contract_code.hash())]) + apply_result.contract_updates.contract_accesses, + HashSet::from([CodeHash(*first_contract_code.hash())]) ); // We record both deployments even if the function call to one of them fails. assert_eq!( - apply_result.contract_deploys, - BTreeSet::from([ + apply_result.contract_updates.contract_deploys, + HashSet::from([ CodeHash(*first_contract_code.hash()), CodeHash(*second_contract_code.hash()) ]) @@ -1862,8 +1874,14 @@ fn test_deploy_and_call_in_same_receipt() { .unwrap(); assert_eq!(apply_result.delayed_receipts_count, 0); - assert_eq!(apply_result.contract_accesses, BTreeSet::from([CodeHash(*contract_code.hash())])); - assert_eq!(apply_result.contract_deploys, BTreeSet::from([CodeHash(*contract_code.hash()),])); + assert_eq!( + apply_result.contract_updates.contract_accesses, + HashSet::from([CodeHash(*contract_code.hash())]) + ); + assert_eq!( + apply_result.contract_updates.contract_deploys, + HashSet::from([CodeHash(*contract_code.hash()),]) + ); } // Tests the case in which deploy and call are contained in the same receipt and function call fails due to exceeding gas limit. @@ -1911,8 +1929,11 @@ fn test_deploy_and_call_in_same_receipt_with_failed_call() { .unwrap(); assert_eq!(apply_result.delayed_receipts_count, 0); - assert_eq!(apply_result.contract_accesses, BTreeSet::from([CodeHash(*contract_code.hash())])); - assert_eq!(apply_result.contract_deploys, BTreeSet::new()); + assert_eq!( + apply_result.contract_updates.contract_accesses, + HashSet::from([CodeHash(*contract_code.hash())]) + ); + assert_eq!(apply_result.contract_updates.contract_deploys, HashSet::new()); } /// Check that applying nothing does not change the state trie.