diff --git a/core/store/src/adapter/trie_store.rs b/core/store/src/adapter/trie_store.rs index 8b6756999e0..d34ddbd7502 100644 --- a/core/store/src/adapter/trie_store.rs +++ b/core/store/src/adapter/trie_store.rs @@ -2,6 +2,7 @@ use std::io; use std::num::NonZero; use std::sync::Arc; +use borsh::BorshDeserialize; use near_primitives::errors::{MissingTrieValueContext, StorageError}; use near_primitives::hash::CryptoHash; use near_primitives::shard_layout::{get_block_shard_uid, ShardUId}; @@ -11,10 +12,6 @@ use crate::{DBCol, KeyForStateChanges, Store, StoreUpdate, TrieChanges, STATE_SN use super::{StoreAdapter, StoreUpdateAdapter, StoreUpdateHolder}; -/// Accesses to the State column should use the mapped ShardUId (either itself or ancestor in the resharding tree), -/// according to the State mapping strategy introduced in Resharding V3. -pub struct MappedShardUId(ShardUId); - #[derive(Clone)] pub struct TrieStoreAdapter { store: Store, @@ -35,11 +32,32 @@ impl TrieStoreAdapter { TrieStoreUpdateAdapter { store_update: StoreUpdateHolder::Owned(self.store.store_update()) } } + /// Constructs db key to be used to access the State column. + /// First, it consults the `StateShardUIdMapping` column to map the `shard_uid` prefix + /// to its ancestor in the resharding tree (according to Resharding V3) + /// or map to itself if the mapping does not exist. + /// + /// Please note that the mapped shard uid is read from db each time which may seem slow. + /// In practice the `StateShardUIdMapping` is very small and should always be stored in the RocksDB cache. + /// The deserialization of ShardUId is also very cheap. + fn get_key_from_shard_uid_and_hash(&self, shard_uid: ShardUId, hash: &CryptoHash) -> [u8; 40] { + let mapped_shard_uid = self + .store + .get_ser::(DBCol::StateShardUIdMapping, &shard_uid.to_bytes()) + .expect("get_key_from_shard_uid_and_hash() failed") + .unwrap_or(shard_uid); + let mut key = [0; 40]; + key[0..8].copy_from_slice(&mapped_shard_uid.to_bytes()); + key[8..].copy_from_slice(hash.as_ref()); + key + } + /// Replaces shard_uid prefix with a mapped value according to mapping strategy in Resharding V3. /// For this, it does extra read from `DBCol::StateShardUIdMapping`. + /// + /// For more details, see `get_key_from_shard_uid_and_hash()` docs. pub fn get(&self, shard_uid: ShardUId, hash: &CryptoHash) -> Result, StorageError> { - let mapped_shard_uid = get_mapped_shard_uid(&self.store, shard_uid); - let key = get_key_from_shard_uid_and_hash(mapped_shard_uid, hash); + let key = self.get_key_from_shard_uid_and_hash(shard_uid, hash); let val = self .store .get(DBCol::State, key.as_ref()) @@ -48,6 +66,15 @@ impl TrieStoreAdapter { Ok(val.into()) } + pub fn get_ser( + &self, + shard_uid: ShardUId, + hash: &CryptoHash, + ) -> Result { + let bytes = self.get(shard_uid, hash)?; + T::try_from_slice(&bytes).map_err(|e| StorageError::StorageInconsistentState(e.to_string())) + } + pub fn get_state_snapshot_hash(&self) -> Result { let val = self .store @@ -91,20 +118,22 @@ impl<'a> TrieStoreUpdateAdapter<'a> { Self { store_update: StoreUpdateHolder::Reference(store_update) } } + fn get_key_from_shard_uid_and_hash(&self, shard_uid: ShardUId, hash: &CryptoHash) -> [u8; 40] { + self.store_update.store.trie_store().get_key_from_shard_uid_and_hash(shard_uid, hash) + } + pub fn decrement_refcount_by( &mut self, shard_uid: ShardUId, hash: &CryptoHash, decrement: NonZero, ) { - let mapped_shard_uid = get_mapped_shard_uid(&self.store_update().store, shard_uid); - let key = get_key_from_shard_uid_and_hash(mapped_shard_uid, hash); + let key = self.get_key_from_shard_uid_and_hash(shard_uid, hash); self.store_update.decrement_refcount_by(DBCol::State, key.as_ref(), decrement); } pub fn decrement_refcount(&mut self, shard_uid: ShardUId, hash: &CryptoHash) { - let mapped_shard_uid = get_mapped_shard_uid(&self.store_update().store, shard_uid); - let key = get_key_from_shard_uid_and_hash(mapped_shard_uid, hash); + let key = self.get_key_from_shard_uid_and_hash(shard_uid, hash); self.store_update.decrement_refcount(DBCol::State, key.as_ref()); } @@ -115,8 +144,7 @@ impl<'a> TrieStoreUpdateAdapter<'a> { data: &[u8], increment: NonZero, ) { - let mapped_shard_uid = get_mapped_shard_uid(&self.store_update().store, shard_uid); - let key = get_key_from_shard_uid_and_hash(mapped_shard_uid, hash); + let key = self.get_key_from_shard_uid_and_hash(shard_uid, hash); self.store_update.increment_refcount_by(DBCol::State, key.as_ref(), data, increment); } @@ -166,22 +194,6 @@ impl<'a> TrieStoreUpdateAdapter<'a> { } } -/// Reads shard_uid mapping for given shard. -/// If the mapping does not exist, it means that `shard_uid` maps to itself -pub fn get_mapped_shard_uid(store: &Store, shard_uid: ShardUId) -> MappedShardUId { - let mapped_shard_uid = store - .get_ser::(DBCol::StateShardUIdMapping, &shard_uid.to_bytes()) - .expect("get_mapped_shard_uid() failed"); - MappedShardUId(mapped_shard_uid.unwrap_or(shard_uid)) -} - -pub fn get_key_from_shard_uid_and_hash(shard_uid: MappedShardUId, hash: &CryptoHash) -> [u8; 40] { - let mut key = [0; 40]; - key[0..8].copy_from_slice(&shard_uid.0.to_bytes()); - key[8..].copy_from_slice(hash.as_ref()); - key -} - #[cfg(test)] mod tests { use assert_matches::assert_matches; diff --git a/nearcore/src/entity_debug.rs b/nearcore/src/entity_debug.rs index c9d6319e39f..d246da87d43 100644 --- a/nearcore/src/entity_debug.rs +++ b/nearcore/src/entity_debug.rs @@ -33,7 +33,7 @@ use near_primitives::views::{ BlockHeaderView, BlockView, ChunkView, ExecutionOutcomeView, ReceiptView, SignedTransactionView, }; use near_store::adapter::flat_store::encode_flat_state_db_key; -use near_store::adapter::trie_store::{get_key_from_shard_uid_and_hash, get_mapped_shard_uid}; +use near_store::adapter::StoreAdapter; use near_store::db::GENESIS_CONGESTION_INFO_KEY; use near_store::flat::delta::KeyForFlatStateDelta; use near_store::flat::{FlatStateChanges, FlatStateDeltaMetadata, FlatStorageStatus}; @@ -248,11 +248,10 @@ impl EntityDebugHandlerImpl { Ok(serialize_entity(&ExecutionOutcomeView::from(outcome.outcome))) } EntityQuery::RawTrieNodeByHash { trie_node_hash, shard_uid } => { - let mapped_shard_uid = get_mapped_shard_uid(&store, shard_uid); - let key = get_key_from_shard_uid_and_hash(mapped_shard_uid, &trie_node_hash); let node = store - .get_ser::(DBCol::State, &key)? - .ok_or_else(|| anyhow!("Trie node not found"))?; + .trie_store() + .get_ser::(shard_uid, &trie_node_hash) + .map_err(|e| anyhow!("Trie node not found: {e}"))?; Ok(serialize_raw_trie_node(node)) } EntityQuery::RawTrieRootByChunkHash { chunk_hash } => { @@ -268,21 +267,18 @@ impl EntityDebugHandlerImpl { .shard_uids() .nth(shard_index) .ok_or_else(|| anyhow!("Shard {} not found", chunk.shard_id()))?; - let mapped_shard_uid = get_mapped_shard_uid(&store, shard_uid); - let key = - get_key_from_shard_uid_and_hash(mapped_shard_uid, &chunk.prev_state_root()); let node = store - .get_ser::(DBCol::State, &key)? - .ok_or_else(|| anyhow!("State root not found"))?; + .trie_store() + .get_ser::(shard_uid, &chunk.prev_state_root()) + .map_err(|e| anyhow!("State root not found: {e}"))?; Ok(serialize_raw_trie_node(node)) } EntityQuery::RawTrieValueByHash { trie_value_hash, shard_uid } => { - let mapped_shard_uid = get_mapped_shard_uid(&store, shard_uid); - let key = get_key_from_shard_uid_and_hash(mapped_shard_uid, &trie_value_hash); let value = store - .get(DBCol::State, &key)? - .ok_or_else(|| anyhow!("Trie value not found"))?; - Ok(serialize_entity(&hex::encode(value.as_slice()))) + .trie_store() + .get(shard_uid, &trie_value_hash) + .map_err(|e| anyhow!("Trie value not found: {e}"))?; + Ok(serialize_entity(&hex::encode(value))) } EntityQuery::ReceiptById { receipt_id } => { let receipt = store @@ -457,11 +453,11 @@ impl EntityDebugHandlerImpl { state: FlatStateValue, shard_uid: ShardUId, ) -> anyhow::Result> { - let mapped_shard_uid = get_mapped_shard_uid(&store, shard_uid); Ok(match state { FlatStateValue::Ref(value) => store - .get(DBCol::State, &get_key_from_shard_uid_and_hash(mapped_shard_uid, &value.hash))? - .ok_or_else(|| anyhow!("ValueRef could not be dereferenced"))? + .trie_store() + .get(shard_uid, &value.hash) + .map_err(|e| anyhow!("ValueRef could not be dereferenced: {e}"))? .to_vec(), FlatStateValue::Inlined(data) => data, })