mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-01 18:17:56 +00:00
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:
Generated
+1
@@ -14641,6 +14641,7 @@ dependencies = [
|
||||
"sp-statement-store",
|
||||
"sp-storage",
|
||||
"sp-test-primitives",
|
||||
"sp-trie",
|
||||
"substrate-prometheus-endpoint",
|
||||
"substrate-test-runtime",
|
||||
"thiserror",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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],
|
||||
|
||||
Reference in New Issue
Block a user