chainHead: Add support for storage closest merkle descendant #14818 (#1153)

This PR adds support for fetching the closest merkle value of some key.


Builds on top of
- https://github.com/paritytech/trie/pull/199

Migrates https://github.com/paritytech/substrate/pull/14818 to the
monorepo.
Closes: https://github.com/paritytech/substrate/issues/14550
Closes: https://github.com/paritytech/polkadot-sdk/issues/1506

// @paritytech/subxt-team

---------

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Co-authored-by: Sebastian Kunert <skunert49@gmail.com>
This commit is contained in:
Alexandru Vasile
2023-09-18 13:54:44 +03:00
committed by GitHub
parent 20052e1675
commit 5d346643ca
15 changed files with 449 additions and 28 deletions
Generated
+1
View File
@@ -14641,6 +14641,7 @@ dependencies = [
"sp-statement-store",
"sp-storage",
"sp-test-primitives",
"sp-trie",
"substrate-prometheus-endpoint",
"substrate-test-runtime",
"thiserror",
+1
View File
@@ -35,6 +35,7 @@ sp-runtime = { path = "../../primitives/runtime", default-features = false}
sp-state-machine = { path = "../../primitives/state-machine" }
sp-statement-store = { path = "../../primitives/statement-store" }
sp-storage = { path = "../../primitives/storage" }
sp-trie = { path = "../../primitives/trie" }
[dev-dependencies]
thiserror = "1.0.48"
+16
View File
@@ -33,6 +33,7 @@ use sp_state_machine::{
OffchainChangesCollection, StorageCollection, StorageIterator,
};
use sp_storage::{ChildInfo, StorageData, StorageKey};
pub use sp_trie::MerkleValue;
use crate::{blockchain::Backend as BlockchainBackend, UsageInfo};
@@ -470,6 +471,21 @@ pub trait StorageProvider<Block: BlockT, B: Backend<Block>> {
child_info: &ChildInfo,
key: &StorageKey,
) -> sp_blockchain::Result<Option<Block::Hash>>;
/// Given a block's `Hash` and a key, return the closest merkle value.
fn closest_merkle_value(
&self,
hash: Block::Hash,
key: &StorageKey,
) -> sp_blockchain::Result<Option<MerkleValue<Block::Hash>>>;
/// Given a block's `Hash`, a key and a child storage key, return the closest merkle value.
fn child_closest_merkle_value(
&self,
hash: Block::Hash,
child_info: &ChildInfo,
key: &StorageKey,
) -> sp_blockchain::Result<Option<MerkleValue<Block::Hash>>>;
}
/// Client backend.
+22 -1
View File
@@ -37,7 +37,7 @@ use sp_state_machine::{
};
use sp_trie::{
cache::{CacheSize, SharedTrieCache},
prefixed_key, MemoryDB,
prefixed_key, MemoryDB, MerkleValue,
};
use std::{
cell::{Cell, RefCell},
@@ -382,6 +382,27 @@ impl<B: BlockT> StateBackend<HashingFor<B>> for BenchmarkingState<B> {
.child_storage_hash(child_info, key)
}
fn closest_merkle_value(
&self,
key: &[u8],
) -> Result<Option<MerkleValue<B::Hash>>, Self::Error> {
self.add_read_key(None, key);
self.state.borrow().as_ref().ok_or_else(state_err)?.closest_merkle_value(key)
}
fn child_closest_merkle_value(
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<Option<MerkleValue<B::Hash>>, Self::Error> {
self.add_read_key(None, key);
self.state
.borrow()
.as_ref()
.ok_or_else(state_err)?
.child_closest_merkle_value(child_info, key)
}
fn exists_storage(&self, key: &[u8]) -> Result<bool, Self::Error> {
self.add_read_key(None, key);
self.state.borrow().as_ref().ok_or_else(state_err)?.exists_storage(key)
+16 -1
View File
@@ -90,7 +90,7 @@ use sp_state_machine::{
OffchainChangesCollection, StateMachineStats, StorageCollection, StorageIterator, StorageKey,
StorageValue, UsageInfo as StateUsageInfo,
};
use sp_trie::{cache::SharedTrieCache, prefixed_key, MemoryDB, PrefixedMemoryDB};
use sp_trie::{cache::SharedTrieCache, prefixed_key, MemoryDB, MerkleValue, PrefixedMemoryDB};
// Re-export the Database trait so that one can pass an implementation of it.
pub use sc_state_db::PruningMode;
@@ -214,6 +214,21 @@ impl<B: BlockT> StateBackend<HashingFor<B>> for RefTrackingState<B> {
self.state.child_storage_hash(child_info, key)
}
fn closest_merkle_value(
&self,
key: &[u8],
) -> Result<Option<MerkleValue<B::Hash>>, Self::Error> {
self.state.closest_merkle_value(key)
}
fn child_closest_merkle_value(
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<Option<MerkleValue<B::Hash>>, Self::Error> {
self.state.child_closest_merkle_value(child_info, key)
}
fn exists_storage(&self, key: &[u8]) -> Result<bool, Self::Error> {
self.state.exists_storage(key)
}
@@ -28,6 +28,7 @@ use sp_state_machine::{
backend::{AsTrieBackend, Backend as StateBackend},
BackendTransaction, IterArgs, StorageIterator, StorageKey, StorageValue, TrieBackend,
};
use sp_trie::MerkleValue;
use std::sync::Arc;
/// State abstraction for recording stats about state access.
@@ -144,6 +145,21 @@ impl<S: StateBackend<HashingFor<B>>, B: BlockT> StateBackend<HashingFor<B>>
self.state.child_storage_hash(child_info, key)
}
fn closest_merkle_value(
&self,
key: &[u8],
) -> Result<Option<MerkleValue<B::Hash>>, Self::Error> {
self.state.closest_merkle_value(key)
}
fn child_closest_merkle_value(
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<Option<MerkleValue<B::Hash>>, Self::Error> {
self.state.child_closest_merkle_value(child_info, key)
}
fn exists_storage(&self, key: &[u8]) -> Result<bool, Self::Error> {
self.state.exists_storage(key)
}
@@ -27,7 +27,7 @@ use crate::{
api::ChainHeadApiServer,
chain_head_follow::ChainHeadFollower,
error::Error as ChainHeadRpcError,
event::{FollowEvent, MethodResponse, OperationError, StorageQuery, StorageQueryType},
event::{FollowEvent, MethodResponse, OperationError, StorageQuery},
hex_string,
subscription::{SubscriptionManagement, SubscriptionManagementError},
},
@@ -329,19 +329,10 @@ where
let items = items
.into_iter()
.map(|query| {
if query.query_type == StorageQueryType::ClosestDescendantMerkleValue {
// Note: remove this once all types are implemented.
return Err(ChainHeadRpcError::InvalidParam(
"Storage query type not supported".into(),
))
}
Ok(StorageQuery {
key: StorageKey(parse_hex_param(query.key)?),
query_type: query.query_type,
})
let key = StorageKey(parse_hex_param(query.key)?);
Ok(StorageQuery { key, query_type: query.query_type })
})
.collect::<Result<Vec<_>, _>>()?;
.collect::<Result<Vec<_>, ChainHeadRpcError>>()?;
let child_trie = child_trie
.map(|child_trie| parse_hex_param(child_trie))
@@ -145,6 +145,36 @@ where
.unwrap_or_else(|error| QueryResult::Err(error.to_string()))
}
/// Fetch the closest merkle value.
fn query_storage_merkle_value(
&self,
hash: Block::Hash,
key: &StorageKey,
child_key: Option<&ChildInfo>,
) -> QueryResult {
let result = if let Some(child_key) = child_key {
self.client.child_closest_merkle_value(hash, child_key, key)
} else {
self.client.closest_merkle_value(hash, key)
};
result
.map(|opt| {
QueryResult::Ok(opt.map(|storage_data| {
let result = match &storage_data {
sc_client_api::MerkleValue::Node(data) => hex_string(&data.as_slice()),
sc_client_api::MerkleValue::Hash(hash) => hex_string(&hash.as_ref()),
};
StorageResult {
key: hex_string(&key.0),
result: StorageResultType::ClosestDescendantMerkleValue(result),
}
}))
})
.unwrap_or_else(|error| QueryResult::Err(error.to_string()))
}
/// Iterate over at most `operation_max_storage_items` keys.
///
/// Returns the storage result with a potential next key to resume iteration.
@@ -286,13 +316,21 @@ where
return
},
},
StorageQueryType::ClosestDescendantMerkleValue =>
match self.query_storage_merkle_value(hash, &item.key, child_key.as_ref()) {
Ok(Some(value)) => storage_results.push(value),
Ok(None) => continue,
Err(error) => {
send_error::<Block>(&sender, operation.operation_id(), error);
return
},
},
StorageQueryType::DescendantsValues => self
.iter_operations
.push_back(QueryIter { next_key: item.key, ty: IterQueryType::Value }),
StorageQueryType::DescendantsHashes => self
.iter_operations
.push_back(QueryIter { next_key: item.key, ty: IterQueryType::Hash }),
_ => continue,
};
}
@@ -20,8 +20,8 @@ use parking_lot::Mutex;
use sc_client_api::{
execution_extensions::ExecutionExtensions, BlockBackend, BlockImportNotification,
BlockchainEvents, CallExecutor, ChildInfo, ExecutorProvider, FinalityNotification,
FinalityNotifications, FinalizeSummary, ImportNotifications, KeysIter, PairsIter, StorageData,
StorageEventStream, StorageKey, StorageProvider,
FinalityNotifications, FinalizeSummary, ImportNotifications, KeysIter, MerkleValue, PairsIter,
StorageData, StorageEventStream, StorageKey, StorageProvider,
};
use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedSender};
use sp_api::{CallApiAt, CallApiAtParams, NumberFor, RuntimeVersion};
@@ -198,6 +198,23 @@ impl<
) -> sp_blockchain::Result<Option<Block::Hash>> {
self.client.child_storage_hash(hash, child_info, key)
}
fn closest_merkle_value(
&self,
hash: Block::Hash,
key: &StorageKey,
) -> sp_blockchain::Result<Option<MerkleValue<Block::Hash>>> {
self.client.closest_merkle_value(hash, key)
}
fn child_closest_merkle_value(
&self,
hash: Block::Hash,
child_info: &ChildInfo,
key: &StorageKey,
) -> sp_blockchain::Result<Option<MerkleValue<Block::Hash>>> {
self.client.child_closest_merkle_value(hash, child_info, key)
}
}
impl<Block: BlockT, Client: CallApiAt<Block>> CallApiAt<Block> for ChainHeadMockClient<Client> {
@@ -43,7 +43,12 @@ use sp_core::{
Blake2Hasher, Hasher,
};
use sp_version::RuntimeVersion;
use std::{collections::HashSet, fmt::Debug, sync::Arc, time::Duration};
use std::{
collections::{HashMap, HashSet},
fmt::Debug,
sync::Arc,
time::Duration,
};
use substrate_test_runtime::Transfer;
use substrate_test_runtime_client::{
prelude::*, runtime, runtime::RuntimeApi, Backend, BlockBuilderExt, Client,
@@ -2583,3 +2588,186 @@ async fn stop_storage_operation() {
)
.await;
}
#[tokio::test]
async fn storage_closest_merkle_value() {
let (mut client, api, mut sub, sub_id, _) = setup_api().await;
/// The core of this test.
///
/// Checks keys that are exact match, keys with descedant and keys that should not return
/// values.
///
/// Returns (key, merkle value) pairs.
async fn expect_merkle_request(
api: &RpcModule<ChainHead<Backend, Block, Client<Backend>>>,
mut sub: &mut RpcSubscription,
sub_id: String,
block_hash: String,
) -> HashMap<String, String> {
// Valid call with storage at the keys.
let response: MethodResponse = api
.call(
"chainHead_unstable_storage",
rpc_params![
&sub_id,
&block_hash,
vec![
StorageQuery {
key: hex_string(b":AAAA"),
query_type: StorageQueryType::ClosestDescendantMerkleValue
},
StorageQuery {
key: hex_string(b":AAAB"),
query_type: StorageQueryType::ClosestDescendantMerkleValue
},
// Key with descedent.
StorageQuery {
key: hex_string(b":A"),
query_type: StorageQueryType::ClosestDescendantMerkleValue
},
StorageQuery {
key: hex_string(b":AA"),
query_type: StorageQueryType::ClosestDescendantMerkleValue
},
// Keys below this comment do not produce a result.
// Key that exceed the keyspace of the trie.
StorageQuery {
key: hex_string(b":AAAAX"),
query_type: StorageQueryType::ClosestDescendantMerkleValue
},
StorageQuery {
key: hex_string(b":AAABX"),
query_type: StorageQueryType::ClosestDescendantMerkleValue
},
// Key that are not part of the trie.
StorageQuery {
key: hex_string(b":AAX"),
query_type: StorageQueryType::ClosestDescendantMerkleValue
},
StorageQuery {
key: hex_string(b":AAAX"),
query_type: StorageQueryType::ClosestDescendantMerkleValue
},
]
],
)
.await
.unwrap();
let operation_id = match response {
MethodResponse::Started(started) => started.operation_id,
MethodResponse::LimitReached => panic!("Expected started response"),
};
let event = get_next_event::<FollowEvent<String>>(&mut sub).await;
let merkle_values: HashMap<_, _> = match event {
FollowEvent::OperationStorageItems(res) => {
assert_eq!(res.operation_id, operation_id);
res.items
.into_iter()
.map(|res| {
let value = match res.result {
StorageResultType::ClosestDescendantMerkleValue(value) => value,
_ => panic!("Unexpected StorageResultType"),
};
(res.key, value)
})
.collect()
},
_ => panic!("Expected OperationStorageItems event"),
};
// Finished.
assert_matches!(
get_next_event::<FollowEvent<String>>(&mut sub).await,
FollowEvent::OperationStorageDone(done) if done.operation_id == operation_id
);
// Response for AAAA, AAAB, A and AA.
assert_eq!(merkle_values.len(), 4);
// While checking for expected merkle values to align,
// the following will check that the returned keys are
// expected.
// Values for AAAA and AAAB are different.
assert_ne!(
merkle_values.get(&hex_string(b":AAAA")).unwrap(),
merkle_values.get(&hex_string(b":AAAB")).unwrap()
);
// Values for A and AA should be on the same branch node.
assert_eq!(
merkle_values.get(&hex_string(b":A")).unwrap(),
merkle_values.get(&hex_string(b":AA")).unwrap()
);
// The branch node value must be different than the leaf of either
// AAAA and AAAB.
assert_ne!(
merkle_values.get(&hex_string(b":A")).unwrap(),
merkle_values.get(&hex_string(b":AAAA")).unwrap()
);
assert_ne!(
merkle_values.get(&hex_string(b":A")).unwrap(),
merkle_values.get(&hex_string(b":AAAB")).unwrap()
);
merkle_values
}
// Import a new block with storage changes.
let mut builder = client.new_block(Default::default()).unwrap();
builder.push_storage_change(b":AAAA".to_vec(), Some(vec![1; 64])).unwrap();
builder.push_storage_change(b":AAAB".to_vec(), Some(vec![2; 64])).unwrap();
let block = builder.build().unwrap().block;
let block_hash = format!("{:?}", block.header.hash());
client.import(BlockOrigin::Own, block.clone()).await.unwrap();
// Ensure the imported block is propagated and pinned for this subscription.
assert_matches!(
get_next_event::<FollowEvent<String>>(&mut sub).await,
FollowEvent::NewBlock(_)
);
assert_matches!(
get_next_event::<FollowEvent<String>>(&mut sub).await,
FollowEvent::BestBlockChanged(_)
);
let merkle_values_lhs = expect_merkle_request(&api, &mut sub, sub_id.clone(), block_hash).await;
// Import a new block with and change AAAB value.
let mut builder = client.new_block(Default::default()).unwrap();
builder.push_storage_change(b":AAAA".to_vec(), Some(vec![1; 64])).unwrap();
builder.push_storage_change(b":AAAB".to_vec(), Some(vec![3; 64])).unwrap();
let block = builder.build().unwrap().block;
let block_hash = format!("{:?}", block.header.hash());
client.import(BlockOrigin::Own, block.clone()).await.unwrap();
// Ensure the imported block is propagated and pinned for this subscription.
assert_matches!(
get_next_event::<FollowEvent<String>>(&mut sub).await,
FollowEvent::NewBlock(_)
);
assert_matches!(
get_next_event::<FollowEvent<String>>(&mut sub).await,
FollowEvent::BestBlockChanged(_)
);
let merkle_values_rhs = expect_merkle_request(&api, &mut sub, sub_id.clone(), block_hash).await;
// Change propagated to the root.
assert_ne!(
merkle_values_lhs.get(&hex_string(b":A")).unwrap(),
merkle_values_rhs.get(&hex_string(b":A")).unwrap()
);
assert_ne!(
merkle_values_lhs.get(&hex_string(b":AAAB")).unwrap(),
merkle_values_rhs.get(&hex_string(b":AAAB")).unwrap()
);
// However the AAAA branch leaf remains unchanged.
assert_eq!(
merkle_values_lhs.get(&hex_string(b":AAAA")).unwrap(),
merkle_values_rhs.get(&hex_string(b":AAAA")).unwrap()
);
}
+22 -1
View File
@@ -78,7 +78,7 @@ use sp_state_machine::{
ChildStorageCollection, KeyValueStates, KeyValueStorageLevel, StorageCollection,
MAX_NESTED_TRIE_DEPTH,
};
use sp_trie::{CompactProof, StorageProof};
use sp_trie::{CompactProof, MerkleValue, StorageProof};
use std::{
collections::{HashMap, HashSet},
marker::PhantomData,
@@ -1545,6 +1545,27 @@ where
.child_storage_hash(child_info, &key.0)
.map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))
}
fn closest_merkle_value(
&self,
hash: <Block as BlockT>::Hash,
key: &StorageKey,
) -> blockchain::Result<Option<MerkleValue<<Block as BlockT>::Hash>>> {
self.state_at(hash)?
.closest_merkle_value(&key.0)
.map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))
}
fn child_closest_merkle_value(
&self,
hash: <Block as BlockT>::Hash,
child_info: &ChildInfo,
key: &StorageKey,
) -> blockchain::Result<Option<MerkleValue<<Block as BlockT>::Hash>>> {
self.state_at(hash)?
.child_closest_merkle_value(child_info, &key.0)
.map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))
}
}
impl<B, E, Block, RA> HeaderMetadata<Block> for Client<B, E, Block, RA>
@@ -30,7 +30,7 @@ use sp_core::storage::{ChildInfo, StateVersion, TrackedStorageKey};
#[cfg(feature = "std")]
use sp_core::traits::RuntimeCode;
use sp_std::vec::Vec;
use sp_trie::PrefixedMemoryDB;
use sp_trie::{MerkleValue, PrefixedMemoryDB};
/// A struct containing arguments for iterating over the storage.
#[derive(Default)]
@@ -195,7 +195,17 @@ pub trait Backend<H: Hasher>: sp_std::fmt::Debug {
/// Get keyed storage value hash or None if there is nothing associated.
fn storage_hash(&self, key: &[u8]) -> Result<Option<H::Out>, Self::Error>;
/// Get keyed child storage or None if there is nothing associated.
/// Get the merkle value or None if there is nothing associated.
fn closest_merkle_value(&self, key: &[u8]) -> Result<Option<MerkleValue<H::Out>>, Self::Error>;
/// Get the child merkle value or None if there is nothing associated.
fn child_closest_merkle_value(
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<Option<MerkleValue<H::Out>>, Self::Error>;
/// Get child keyed child storage or None if there is nothing associated.
fn child_storage(
&self,
child_info: &ChildInfo,
@@ -30,7 +30,6 @@ use codec::Codec;
use hash_db::HashDB;
use hash_db::Hasher;
use sp_core::storage::{ChildInfo, StateVersion};
use sp_trie::PrefixedMemoryDB;
#[cfg(feature = "std")]
use sp_trie::{
cache::{LocalTrieCache, TrieCache},
@@ -39,6 +38,7 @@ use sp_trie::{
};
#[cfg(not(feature = "std"))]
use sp_trie::{Error, NodeCodec};
use sp_trie::{MerkleValue, PrefixedMemoryDB};
use trie_db::TrieCache as TrieCacheT;
#[cfg(not(feature = "std"))]
use trie_db::{node::NodeOwned, CachedValue};
@@ -405,6 +405,18 @@ where
self.essence.child_storage(child_info, key)
}
fn closest_merkle_value(&self, key: &[u8]) -> Result<Option<MerkleValue<H::Out>>, Self::Error> {
self.essence.closest_merkle_value(key)
}
fn child_closest_merkle_value(
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<Option<MerkleValue<H::Out>>, Self::Error> {
self.essence.child_closest_merkle_value(child_info, key)
}
fn next_storage_key(&self, key: &[u8]) -> Result<Option<StorageKey>, Self::Error> {
let (is_cached, mut cache) = access_cache(&self.next_storage_key_cache, Option::take)
.map(|cache| (cache.last_key == key, cache))
@@ -32,11 +32,12 @@ use sp_std::{boxed::Box, marker::PhantomData, vec::Vec};
#[cfg(feature = "std")]
use sp_trie::recorder::Recorder;
use sp_trie::{
child_delta_trie_root, delta_trie_root, empty_child_trie_root, read_child_trie_hash,
read_child_trie_value, read_trie_value,
child_delta_trie_root, delta_trie_root, empty_child_trie_root,
read_child_trie_first_descedant_value, read_child_trie_hash, read_child_trie_value,
read_trie_first_descedant_value, read_trie_value,
trie_types::{TrieDBBuilder, TrieError},
DBValue, KeySpacedDB, NodeCodec, PrefixedMemoryDB, Trie, TrieCache, TrieDBRawIterator,
TrieRecorder,
DBValue, KeySpacedDB, MerkleValue, NodeCodec, PrefixedMemoryDB, Trie, TrieCache,
TrieDBRawIterator, TrieRecorder,
};
#[cfg(feature = "std")]
use std::{collections::HashMap, sync::Arc};
@@ -574,6 +575,39 @@ where
})
}
/// Get the closest merkle value at given key.
pub fn closest_merkle_value(&self, key: &[u8]) -> Result<Option<MerkleValue<H::Out>>> {
let map_e = |e| format!("Trie lookup error: {}", e);
self.with_recorder_and_cache(None, |recorder, cache| {
read_trie_first_descedant_value::<Layout<H>, _>(self, &self.root, key, recorder, cache)
.map_err(map_e)
})
}
/// Get the child closest merkle value at given key.
pub fn child_closest_merkle_value(
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<Option<MerkleValue<H::Out>>> {
let Some(child_root) = self.child_root(child_info)? else { return Ok(None) };
let map_e = |e| format!("Trie lookup error: {}", e);
self.with_recorder_and_cache(Some(child_root), |recorder, cache| {
read_child_trie_first_descedant_value::<Layout<H>, _>(
child_info.keyspace(),
self,
&child_root,
key,
recorder,
cache,
)
.map_err(map_e)
})
}
/// Create a raw iterator over the storage.
pub fn raw_iter(&self, args: IterArgs) -> Result<RawIter<S, H, C>> {
let root = if let Some(child_info) = args.child_info.as_ref() {
+41 -1
View File
@@ -44,7 +44,6 @@ pub use storage_proof::{CompactProof, StorageProof};
/// Trie codec reexport, mainly child trie support
/// for trie compact proof.
pub use trie_codec::{decode_compact, encode_compact, Error as CompactProofError};
pub use trie_db::proof::VerifyError;
use trie_db::proof::{generate_proof, verify_proof};
/// Various re-exports from the `trie-db` crate.
pub use trie_db::{
@@ -53,6 +52,7 @@ pub use trie_db::{
CError, DBValue, Query, Recorder, Trie, TrieCache, TrieConfiguration, TrieDBIterator,
TrieDBKeyIterator, TrieDBRawIterator, TrieLayout, TrieMut, TrieRecorder,
};
pub use trie_db::{proof::VerifyError, MerkleValue};
/// The Substrate format implementation of `TrieStream`.
pub use trie_stream::TrieStream;
@@ -295,6 +295,25 @@ pub fn read_trie_value<L: TrieLayout, DB: hash_db::HashDBRef<L::Hash, trie_db::D
.get(key)
}
/// Read the [`trie_db::MerkleValue`] of the node that is the closest descendant for
/// the provided key.
pub fn read_trie_first_descedant_value<L: TrieLayout, DB>(
db: &DB,
root: &TrieHash<L>,
key: &[u8],
recorder: Option<&mut dyn TrieRecorder<TrieHash<L>>>,
cache: Option<&mut dyn TrieCache<L::Codec>>,
) -> Result<Option<MerkleValue<TrieHash<L>>>, Box<TrieError<L>>>
where
DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>,
{
TrieDBBuilder::<L>::new(db, root)
.with_optional_cache(cache)
.with_optional_recorder(recorder)
.build()
.lookup_first_descendant(key)
}
/// Read a value from the trie with given Query.
pub fn read_trie_value_with<
L: TrieLayout,
@@ -397,6 +416,27 @@ where
.get_hash(key)
}
/// Read the [`trie_db::MerkleValue`] of the node that is the closest descendant for
/// the provided child key.
pub fn read_child_trie_first_descedant_value<L: TrieConfiguration, DB>(
keyspace: &[u8],
db: &DB,
root: &TrieHash<L>,
key: &[u8],
recorder: Option<&mut dyn TrieRecorder<TrieHash<L>>>,
cache: Option<&mut dyn TrieCache<L::Codec>>,
) -> Result<Option<MerkleValue<TrieHash<L>>>, Box<TrieError<L>>>
where
DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>,
{
let db = KeySpacedDB::new(db, keyspace);
TrieDBBuilder::<L>::new(&db, &root)
.with_optional_recorder(recorder)
.with_optional_cache(cache)
.build()
.lookup_first_descendant(key)
}
/// Read a value from the child trie with given query.
pub fn read_child_trie_value_with<L, Q, DB>(
keyspace: &[u8],