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

Commit 1085a90

Browse files
authored
Compact proof utilities in sp_trie. (#8574)
* validation extension in sp_io * need paths * arc impl * missing host function in executor * io to pkdot * decode function. * encode primitive. * trailing tab * multiple patch * fix child trie logic * restore master versionning * bench compact proof size * trie-db 22.3 is needed * line width * split line * fixes for bench (additional root may not be needed as original issue was with empty proof). * revert compact from block size calculation. * New error type for compression. * Adding test (incomplete (failing)). Also lacking real proof checking (no good primitives in sp-trie crate). * There is currently no proof recording utility in sp_trie, removing test. * small test of child root in proof without a child proof. * remove empty test. * remove non compact proof size * Missing revert. * proof method to encode decode.
1 parent 24a92c3 commit 1085a90

File tree

8 files changed

+407
-9
lines changed

8 files changed

+407
-9
lines changed

client/db/src/bench.rs

+28-3
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ pub struct BenchmarkingState<B: BlockT> {
117117
read_write_tracker: RefCell<ReadWriteTracker>,
118118
whitelist: RefCell<Vec<TrackedStorageKey>>,
119119
proof_recorder: Option<ProofRecorder<B::Hash>>,
120+
proof_recorder_root: Cell<B::Hash>,
120121
}
121122

122123
impl<B: BlockT> BenchmarkingState<B> {
@@ -129,7 +130,7 @@ impl<B: BlockT> BenchmarkingState<B> {
129130
let mut state = BenchmarkingState {
130131
state: RefCell::new(None),
131132
db: Cell::new(None),
132-
root: Cell::new(root),
133+
root: Cell::new(root.clone()),
133134
genesis: Default::default(),
134135
genesis_root: Default::default(),
135136
record: Default::default(),
@@ -139,6 +140,7 @@ impl<B: BlockT> BenchmarkingState<B> {
139140
read_write_tracker: Default::default(),
140141
whitelist: Default::default(),
141142
proof_recorder: record_proof.then(Default::default),
143+
proof_recorder_root: Cell::new(root.clone()),
142144
};
143145

144146
state.add_whitelist_to_tracker();
@@ -166,7 +168,10 @@ impl<B: BlockT> BenchmarkingState<B> {
166168
None => Arc::new(kvdb_memorydb::create(1)),
167169
};
168170
self.db.set(Some(db.clone()));
169-
self.proof_recorder.as_ref().map(|r| r.reset());
171+
if let Some(recorder) = &self.proof_recorder {
172+
recorder.reset();
173+
self.proof_recorder_root.set(self.root.get());
174+
}
170175
let storage_db = Arc::new(StorageDb::<B> {
171176
db,
172177
proof_recorder: self.proof_recorder.clone(),
@@ -516,7 +521,27 @@ impl<B: BlockT> StateBackend<HashFor<B>> for BenchmarkingState<B> {
516521
}
517522

518523
fn proof_size(&self) -> Option<u32> {
519-
self.proof_recorder.as_ref().map(|recorder| recorder.estimate_encoded_size() as u32)
524+
self.proof_recorder.as_ref().map(|recorder| {
525+
let proof_size = recorder.estimate_encoded_size() as u32;
526+
let proof = recorder.to_storage_proof();
527+
let proof_recorder_root = self.proof_recorder_root.get();
528+
if proof_recorder_root == Default::default() || proof_size == 1 {
529+
// empty trie
530+
proof_size
531+
} else {
532+
if let Some(size) = proof.encoded_compact_size::<HashFor<B>>(proof_recorder_root) {
533+
size as u32
534+
} else {
535+
panic!(
536+
"proof rec root {:?}, root {:?}, genesis {:?}, rec_len {:?}",
537+
self.proof_recorder_root.get(),
538+
self.root.get(),
539+
self.genesis_root,
540+
proof_size,
541+
);
542+
}
543+
}
544+
})
520545
}
521546
}
522547

primitives/state-machine/src/lib.rs

+55-2
Original file line numberDiff line numberDiff line change
@@ -1402,14 +1402,22 @@ mod tests {
14021402
}
14031403
}
14041404

1405+
fn test_compact(remote_proof: StorageProof, remote_root: &sp_core::H256) -> StorageProof {
1406+
let compact_remote_proof = remote_proof.into_compact_proof::<BlakeTwo256>(
1407+
remote_root.clone(),
1408+
).unwrap();
1409+
compact_remote_proof.to_storage_proof::<BlakeTwo256>(Some(remote_root)).unwrap().0
1410+
}
1411+
14051412
#[test]
14061413
fn prove_read_and_proof_check_works() {
14071414
let child_info = ChildInfo::new_default(b"sub1");
14081415
let child_info = &child_info;
14091416
// fetch read proof from 'remote' full node
14101417
let remote_backend = trie_backend::tests::test_trie();
1411-
let remote_root = remote_backend.storage_root(::std::iter::empty()).0;
1418+
let remote_root = remote_backend.storage_root(std::iter::empty()).0;
14121419
let remote_proof = prove_read(remote_backend, &[b"value2"]).unwrap();
1420+
let remote_proof = test_compact(remote_proof, &remote_root);
14131421
// check proof locally
14141422
let local_result1 = read_proof_check::<BlakeTwo256, _>(
14151423
remote_root,
@@ -1429,12 +1437,13 @@ mod tests {
14291437
assert_eq!(local_result2, false);
14301438
// on child trie
14311439
let remote_backend = trie_backend::tests::test_trie();
1432-
let remote_root = remote_backend.storage_root(::std::iter::empty()).0;
1440+
let remote_root = remote_backend.storage_root(std::iter::empty()).0;
14331441
let remote_proof = prove_child_read(
14341442
remote_backend,
14351443
child_info,
14361444
&[b"value3"],
14371445
).unwrap();
1446+
let remote_proof = test_compact(remote_proof, &remote_root);
14381447
let local_result1 = read_child_proof_check::<BlakeTwo256, _>(
14391448
remote_root,
14401449
remote_proof.clone(),
@@ -1457,6 +1466,50 @@ mod tests {
14571466
);
14581467
}
14591468

1469+
#[test]
1470+
fn compact_multiple_child_trie() {
1471+
// this root will be queried
1472+
let child_info1 = ChildInfo::new_default(b"sub1");
1473+
// this root will not be include in proof
1474+
let child_info2 = ChildInfo::new_default(b"sub2");
1475+
// this root will be include in proof
1476+
let child_info3 = ChildInfo::new_default(b"sub");
1477+
let mut remote_backend = trie_backend::tests::test_trie();
1478+
let (remote_root, transaction) = remote_backend.full_storage_root(
1479+
std::iter::empty(),
1480+
vec![
1481+
(&child_info1, vec![
1482+
(&b"key1"[..], Some(&b"val2"[..])),
1483+
(&b"key2"[..], Some(&b"val3"[..])),
1484+
].into_iter()),
1485+
(&child_info2, vec![
1486+
(&b"key3"[..], Some(&b"val4"[..])),
1487+
(&b"key4"[..], Some(&b"val5"[..])),
1488+
].into_iter()),
1489+
(&child_info3, vec![
1490+
(&b"key5"[..], Some(&b"val6"[..])),
1491+
(&b"key6"[..], Some(&b"val7"[..])),
1492+
].into_iter()),
1493+
].into_iter(),
1494+
);
1495+
remote_backend.backend_storage_mut().consolidate(transaction);
1496+
remote_backend.essence.set_root(remote_root.clone());
1497+
let remote_proof = prove_child_read(
1498+
remote_backend,
1499+
&child_info1,
1500+
&[b"key1"],
1501+
).unwrap();
1502+
let remote_proof = test_compact(remote_proof, &remote_root);
1503+
let local_result1 = read_child_proof_check::<BlakeTwo256, _>(
1504+
remote_root,
1505+
remote_proof.clone(),
1506+
&child_info1,
1507+
&[b"key1"],
1508+
).unwrap();
1509+
assert_eq!(local_result1.len(), 1);
1510+
assert_eq!(local_result1.get(&b"key1"[..]), Some(&Some(b"val2".to_vec())));
1511+
}
1512+
14601513
#[test]
14611514
fn child_storage_uuid() {
14621515

primitives/trie/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ harness = false
2121
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false }
2222
sp-std = { version = "3.0.0", default-features = false, path = "../std" }
2323
hash-db = { version = "0.15.2", default-features = false }
24-
trie-db = { version = "0.22.2", default-features = false }
24+
trie-db = { version = "0.22.3", default-features = false }
2525
trie-root = { version = "0.16.0", default-features = false }
2626
memory-db = { version = "0.26.0", default-features = false }
2727
sp-core = { version = "3.0.0", default-features = false, path = "../core" }

primitives/trie/src/error.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub enum Error {
2626
/// Bad format.
2727
BadFormat,
2828
/// Decoding error.
29-
Decode(codec::Error)
29+
Decode(codec::Error),
3030
}
3131

3232
impl From<codec::Error> for Error {

primitives/trie/src/lib.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ mod error;
2323
mod node_header;
2424
mod node_codec;
2525
mod storage_proof;
26+
mod trie_codec;
2627
mod trie_stream;
2728

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

4953
#[derive(Default)]
5054
/// substrate trie layout

primitives/trie/src/storage_proof.rs

+56
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ pub struct StorageProof {
3131
trie_nodes: Vec<Vec<u8>>,
3232
}
3333

34+
/// Storage proof in compact form.
35+
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
36+
pub struct CompactProof {
37+
pub encoded_nodes: Vec<Vec<u8>>,
38+
}
39+
3440
impl StorageProof {
3541
/// Constructs a storage proof from a subset of encoded trie nodes in a storage backend.
3642
pub fn new(trie_nodes: Vec<Vec<u8>>) -> Self {
@@ -79,6 +85,56 @@ impl StorageProof {
7985

8086
Self { trie_nodes }
8187
}
88+
89+
/// Encode as a compact proof with default
90+
/// trie layout.
91+
pub fn into_compact_proof<H: Hasher>(
92+
self,
93+
root: H::Out,
94+
) -> Result<CompactProof, crate::CompactProofError<crate::Layout<H>>> {
95+
crate::encode_compact::<crate::Layout<H>>(self, root)
96+
}
97+
98+
/// Returns the estimated encoded size of the compact proof.
99+
///
100+
/// Runing this operation is a slow operation (build the whole compact proof) and should only be
101+
/// in non sensitive path.
102+
/// Return `None` on error.
103+
pub fn encoded_compact_size<H: Hasher>(self, root: H::Out) -> Option<usize> {
104+
let compact_proof = self.into_compact_proof::<H>(root);
105+
compact_proof.ok().map(|p| p.encoded_size())
106+
}
107+
108+
}
109+
110+
impl CompactProof {
111+
/// Return an iterator on the compact encoded nodes.
112+
pub fn iter_compact_encoded_nodes(&self) -> impl Iterator<Item = &[u8]> {
113+
self.encoded_nodes.iter().map(Vec::as_slice)
114+
}
115+
116+
/// Decode to a full storage_proof.
117+
///
118+
/// Method use a temporary `HashDB`, and `sp_trie::decode_compact`
119+
/// is often better.
120+
pub fn to_storage_proof<H: Hasher>(
121+
&self,
122+
expected_root: Option<&H::Out>,
123+
) -> Result<(StorageProof, H::Out), crate::CompactProofError<crate::Layout<H>>> {
124+
let mut db = crate::MemoryDB::<H>::new(&[]);
125+
let root = crate::decode_compact::<crate::Layout<H>, _, _>(
126+
&mut db,
127+
self.iter_compact_encoded_nodes(),
128+
expected_root,
129+
)?;
130+
Ok((StorageProof::new(db.drain().into_iter().filter_map(|kv|
131+
if (kv.1).1 > 0 {
132+
Some((kv.1).0)
133+
} else {
134+
None
135+
}
136+
).collect()), root))
137+
}
82138
}
83139

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

0 commit comments

Comments
 (0)