Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 21 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d9728fb
git: Ignore target folder for main repo
lexnv Aug 25, 2023
289732a
Cargo: Update trie to github lexnv/expose_merkle_value
lexnv Aug 25, 2023
e51afac
client: Expose merkle value on storageProvider trait
lexnv Aug 25, 2023
bc39b11
primitives/backend: Expose merkle value on Backend trait
lexnv Aug 25, 2023
d23ad73
primitives/trie: Low level functions for reading the merkle value
lexnv Aug 25, 2023
fe12896
primitives/trie: Implement closest merkle value on TrieBackendEssence..
lexnv Aug 25, 2023
cb7fd4d
client: Implement closest merkle value up until `StorageProvider`
lexnv Aug 25, 2023
766de69
chainHead/storage: Query the storage for closest merkle value
lexnv Aug 25, 2023
d23e1c1
chainHead/tests: Check merkle values propagate to the chainHead_storage
lexnv Aug 25, 2023
4f215a9
Merge branch 'master' into lexnv/chainhed_trie_db_merkle
lexnv Aug 28, 2023
6493a10
Merge remote-tracking branch 'origin/master' into lexnv/chainhed_trie…
lexnv Sep 11, 2023
3607785
primitives: Adjust merkle value api
lexnv Sep 11, 2023
62f677a
Update interface with merkle value changes
lexnv Sep 11, 2023
b6dd92e
chainHead/storage: Adjust the returned values
lexnv Sep 11, 2023
445f225
Merge remote-tracking branch 'origin/master' into lexnv/chainhed_trie…
lexnv Sep 11, 2023
1e52670
bench: Use trie github link
lexnv Sep 11, 2023
520c651
chainHead/tests: Adjust testing
lexnv Sep 11, 2023
ba184a0
chainHead: Support merkle value types
lexnv Sep 11, 2023
3294a1d
chainHead: Fix build
lexnv Sep 11, 2023
3e7a60d
Merge remote-tracking branch 'origin/master' into lexnv/chainhed_trie…
lexnv Sep 13, 2023
cd0c0c1
Update substrate/client/rpc-spec-v2/src/chain_head/tests.rs
lexnv Sep 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions substrate/client/api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
16 changes: 16 additions & 0 deletions substrate/client/api/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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.
Expand Down
23 changes: 22 additions & 1 deletion substrate/client/db/src/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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)
Expand Down
17 changes: 16 additions & 1 deletion substrate/client/db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: What does it mean here, if an Error is returned, vs. an Option is returned? Under what circumstances can errors happen?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, the Option tells us if there is descendant merkle value below the provided key, since the key may not be in the database and may not have any descendants.

On the other hand, the error is a way to propagate that something went wrong during this operation. Mainly on the trie-db side:

  • unable to fetch bytes from the database (invalid state root / incomplete data)
  • corrupted data (failing to decode the node)

Here would be the main place from which those errors originate


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)
}
Expand Down
16 changes: 16 additions & 0 deletions substrate/client/db/src/record_stats_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
}
Expand Down
17 changes: 4 additions & 13 deletions substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
},
Expand Down Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
};
}

Expand Down
21 changes: 19 additions & 2 deletions substrate/client/rpc-spec-v2/src/chain_head/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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> {
Expand Down
Loading