Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Compact proof utilities in sp_trie. #8574

Merged
33 commits merged into from
Jun 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
22b8699
validation extension in sp_io
cheme Jan 27, 2021
4de9f7a
need paths
cheme Jan 27, 2021
f0ea2af
arc impl
cheme Jan 27, 2021
71202a9
missing host function in executor
cheme Jan 28, 2021
f848b44
io to pkdot
cheme Feb 1, 2021
1979b6a
decode function.
cheme Feb 1, 2021
927a096
encode primitive.
cheme Feb 1, 2021
b034ec0
trailing tab
cheme Feb 1, 2021
757ff30
multiple patch
cheme Feb 1, 2021
7dba01b
fix child trie logic
cheme Feb 2, 2021
1a2a637
Merge branch 'master' into validation_io, and revert skip value specific
cheme Apr 8, 2021
c948254
Merge branch 'master' into validation_io
cheme Apr 8, 2021
7ef5a41
restore master versionning
cheme Apr 8, 2021
360c324
bench compact proof size
cheme Apr 8, 2021
4edc38a
trie-db 22.3 is needed
cheme Apr 8, 2021
6f899c0
line width
cheme Apr 8, 2021
d3eb91c
split line
cheme Apr 8, 2021
4475edd
fixes for bench (additional root may not be needed as original issue was
cheme Apr 9, 2021
e6e4682
Merge branch 'master' into validation_io
cheme May 3, 2021
dba397c
Merge branch 'validation_io' of github.com:cheme/substrate into valid…
cheme May 3, 2021
c0fc8fd
revert compact from block size calculation.
cheme May 3, 2021
8f353dd
Merge branch 'master' into validation_io
cheme May 28, 2021
7ebb54d
Merge branch 'master' into validation_io
cheme Jun 4, 2021
bf91187
New error type for compression.
cheme Jun 4, 2021
c37046e
Adding test (incomplete (failing)).
cheme Jun 4, 2021
7daabb3
Merge branch 'master' into validation_io
cheme Jun 4, 2021
cfd923a
There is currently no proof recording utility in sp_trie, removing
cheme Jun 4, 2021
f25fc34
small test of child root in proof without a child proof.
cheme Jun 4, 2021
19c132e
remove empty test.
cheme Jun 6, 2021
515c2fd
remove non compact proof size
cheme Jun 6, 2021
8aadbf1
Merge branch 'master' into validation_io
cheme Jun 6, 2021
f867d7f
Missing revert.
cheme Jun 6, 2021
d639940
proof method to encode decode.
cheme Jun 7, 2021
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
31 changes: 28 additions & 3 deletions client/db/src/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ pub struct BenchmarkingState<B: BlockT> {
read_write_tracker: RefCell<ReadWriteTracker>,
whitelist: RefCell<Vec<TrackedStorageKey>>,
proof_recorder: Option<ProofRecorder<B::Hash>>,
proof_recorder_root: Cell<B::Hash>,
}

impl<B: BlockT> BenchmarkingState<B> {
Expand All @@ -129,7 +130,7 @@ impl<B: BlockT> BenchmarkingState<B> {
let mut state = BenchmarkingState {
state: RefCell::new(None),
db: Cell::new(None),
root: Cell::new(root),
root: Cell::new(root.clone()),
genesis: Default::default(),
genesis_root: Default::default(),
record: Default::default(),
Expand All @@ -139,6 +140,7 @@ impl<B: BlockT> BenchmarkingState<B> {
read_write_tracker: Default::default(),
whitelist: Default::default(),
proof_recorder: record_proof.then(Default::default),
proof_recorder_root: Cell::new(root.clone()),
};

state.add_whitelist_to_tracker();
Expand Down Expand Up @@ -166,7 +168,10 @@ impl<B: BlockT> BenchmarkingState<B> {
None => Arc::new(kvdb_memorydb::create(1)),
};
self.db.set(Some(db.clone()));
self.proof_recorder.as_ref().map(|r| r.reset());
if let Some(recorder) = &self.proof_recorder {
recorder.reset();
self.proof_recorder_root.set(self.root.get());
}
let storage_db = Arc::new(StorageDb::<B> {
db,
proof_recorder: self.proof_recorder.clone(),
Expand Down Expand Up @@ -516,7 +521,27 @@ impl<B: BlockT> StateBackend<HashFor<B>> for BenchmarkingState<B> {
}

fn proof_size(&self) -> Option<u32> {
self.proof_recorder.as_ref().map(|recorder| recorder.estimate_encoded_size() as u32)
self.proof_recorder.as_ref().map(|recorder| {
let proof_size = recorder.estimate_encoded_size() as u32;
let proof = recorder.to_storage_proof();
let proof_recorder_root = self.proof_recorder_root.get();
if proof_recorder_root == Default::default() || proof_size == 1 {
// empty trie
proof_size
} else {
if let Some(size) = proof.encoded_compact_size::<HashFor<B>>(proof_recorder_root) {
size as u32
} else {
panic!(
"proof rec root {:?}, root {:?}, genesis {:?}, rec_len {:?}",
self.proof_recorder_root.get(),
self.root.get(),
self.genesis_root,
proof_size,
);
}
}
})
}
}

Expand Down
57 changes: 55 additions & 2 deletions primitives/state-machine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1402,14 +1402,22 @@ mod tests {
}
}

fn test_compact(remote_proof: StorageProof, remote_root: &sp_core::H256) -> StorageProof {
let compact_remote_proof = remote_proof.into_compact_proof::<BlakeTwo256>(
remote_root.clone(),
).unwrap();
compact_remote_proof.to_storage_proof::<BlakeTwo256>(Some(remote_root)).unwrap().0
}

#[test]
fn prove_read_and_proof_check_works() {
let child_info = ChildInfo::new_default(b"sub1");
let child_info = &child_info;
// fetch read proof from 'remote' full node
let remote_backend = trie_backend::tests::test_trie();
let remote_root = remote_backend.storage_root(::std::iter::empty()).0;
let remote_root = remote_backend.storage_root(std::iter::empty()).0;
let remote_proof = prove_read(remote_backend, &[b"value2"]).unwrap();
let remote_proof = test_compact(remote_proof, &remote_root);
// check proof locally
let local_result1 = read_proof_check::<BlakeTwo256, _>(
remote_root,
Expand All @@ -1429,12 +1437,13 @@ mod tests {
assert_eq!(local_result2, false);
// on child trie
let remote_backend = trie_backend::tests::test_trie();
let remote_root = remote_backend.storage_root(::std::iter::empty()).0;
let remote_root = remote_backend.storage_root(std::iter::empty()).0;
let remote_proof = prove_child_read(
remote_backend,
child_info,
&[b"value3"],
).unwrap();
let remote_proof = test_compact(remote_proof, &remote_root);
let local_result1 = read_child_proof_check::<BlakeTwo256, _>(
remote_root,
remote_proof.clone(),
Expand All @@ -1457,6 +1466,50 @@ mod tests {
);
}

#[test]
fn compact_multiple_child_trie() {
// this root will be queried
let child_info1 = ChildInfo::new_default(b"sub1");
// this root will not be include in proof
let child_info2 = ChildInfo::new_default(b"sub2");
// this root will be include in proof
let child_info3 = ChildInfo::new_default(b"sub");
let mut remote_backend = trie_backend::tests::test_trie();
let (remote_root, transaction) = remote_backend.full_storage_root(
std::iter::empty(),
vec![
(&child_info1, vec![
(&b"key1"[..], Some(&b"val2"[..])),
(&b"key2"[..], Some(&b"val3"[..])),
].into_iter()),
(&child_info2, vec![
(&b"key3"[..], Some(&b"val4"[..])),
(&b"key4"[..], Some(&b"val5"[..])),
].into_iter()),
(&child_info3, vec![
(&b"key5"[..], Some(&b"val6"[..])),
(&b"key6"[..], Some(&b"val7"[..])),
].into_iter()),
].into_iter(),
);
remote_backend.backend_storage_mut().consolidate(transaction);
remote_backend.essence.set_root(remote_root.clone());
let remote_proof = prove_child_read(
remote_backend,
&child_info1,
&[b"key1"],
).unwrap();
let remote_proof = test_compact(remote_proof, &remote_root);
let local_result1 = read_child_proof_check::<BlakeTwo256, _>(
remote_root,
remote_proof.clone(),
&child_info1,
&[b"key1"],
).unwrap();
assert_eq!(local_result1.len(), 1);
assert_eq!(local_result1.get(&b"key1"[..]), Some(&Some(b"val2".to_vec())));
}

#[test]
fn child_storage_uuid() {

Expand Down
2 changes: 1 addition & 1 deletion primitives/trie/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ harness = false
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false }
sp-std = { version = "3.0.0", default-features = false, path = "../std" }
hash-db = { version = "0.15.2", default-features = false }
trie-db = { version = "0.22.2", default-features = false }
trie-db = { version = "0.22.3", default-features = false }
trie-root = { version = "0.16.0", default-features = false }
memory-db = { version = "0.26.0", default-features = false }
sp-core = { version = "3.0.0", default-features = false, path = "../core" }
Expand Down
2 changes: 1 addition & 1 deletion primitives/trie/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub enum Error {
/// Bad format.
BadFormat,
/// Decoding error.
Decode(codec::Error)
Decode(codec::Error),
}

impl From<codec::Error> for Error {
Expand Down
6 changes: 5 additions & 1 deletion primitives/trie/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ mod error;
mod node_header;
mod node_codec;
mod storage_proof;
mod trie_codec;
mod trie_stream;

use sp_std::{boxed::Box, marker::PhantomData, vec::Vec, borrow::Borrow};
Expand All @@ -35,7 +36,7 @@ pub use error::Error;
pub use trie_stream::TrieStream;
/// The Substrate format implementation of `NodeCodec`.
pub use node_codec::NodeCodec;
pub use storage_proof::StorageProof;
pub use storage_proof::{StorageProof, CompactProof};
/// Various re-exports from the `trie-db` crate.
pub use trie_db::{
Trie, TrieMut, DBValue, Recorder, CError, Query, TrieLayout, TrieConfiguration, nibble_ops, TrieDBIterator,
Expand All @@ -45,6 +46,9 @@ pub use memory_db::KeyFunction;
pub use memory_db::prefixed_key;
/// Various re-exports from the `hash-db` crate.
pub use hash_db::{HashDB as HashDBT, EMPTY_PREFIX};
/// Trie codec reexport, mainly child trie support
/// for trie compact proof.
pub use trie_codec::{decode_compact, encode_compact, Error as CompactProofError};

#[derive(Default)]
/// substrate trie layout
Expand Down
56 changes: 56 additions & 0 deletions primitives/trie/src/storage_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ pub struct StorageProof {
trie_nodes: Vec<Vec<u8>>,
}

/// Storage proof in compact form.
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
pub struct CompactProof {
bkchr marked this conversation as resolved.
Show resolved Hide resolved
pub encoded_nodes: Vec<Vec<u8>>,
}

impl StorageProof {
/// Constructs a storage proof from a subset of encoded trie nodes in a storage backend.
pub fn new(trie_nodes: Vec<Vec<u8>>) -> Self {
Expand Down Expand Up @@ -79,6 +85,56 @@ impl StorageProof {

Self { trie_nodes }
}

/// Encode as a compact proof with default
/// trie layout.
pub fn into_compact_proof<H: Hasher>(
self,
root: H::Out,
) -> Result<CompactProof, crate::CompactProofError<crate::Layout<H>>> {
crate::encode_compact::<crate::Layout<H>>(self, root)
}

/// Returns the estimated encoded size of the compact proof.
///
/// Runing this operation is a slow operation (build the whole compact proof) and should only be
/// in non sensitive path.
/// Return `None` on error.
pub fn encoded_compact_size<H: Hasher>(self, root: H::Out) -> Option<usize> {
let compact_proof = self.into_compact_proof::<H>(root);
compact_proof.ok().map(|p| p.encoded_size())
}

}

impl CompactProof {
/// Return an iterator on the compact encoded nodes.
pub fn iter_compact_encoded_nodes(&self) -> impl Iterator<Item = &[u8]> {
self.encoded_nodes.iter().map(Vec::as_slice)
}

/// Decode to a full storage_proof.
///
/// Method use a temporary `HashDB`, and `sp_trie::decode_compact`
/// is often better.
pub fn to_storage_proof<H: Hasher>(
&self,
expected_root: Option<&H::Out>,
) -> Result<(StorageProof, H::Out), crate::CompactProofError<crate::Layout<H>>> {
let mut db = crate::MemoryDB::<H>::new(&[]);
let root = crate::decode_compact::<crate::Layout<H>, _, _>(
&mut db,
self.iter_compact_encoded_nodes(),
expected_root,
)?;
Ok((StorageProof::new(db.drain().into_iter().filter_map(|kv|
if (kv.1).1 > 0 {
Some((kv.1).0)
} else {
None
}
).collect()), root))
}
}

/// An iterator over trie nodes constructed from a storage proof. The nodes are not guaranteed to
Expand Down
Loading