1use crate::{CompactProof, HashDBT, TrieConfiguration, TrieHash, EMPTY_PREFIX};
24use alloc::{boxed::Box, vec::Vec};
25use trie_db::{CError, Trie};
26
27#[derive(Debug)]
29#[cfg_attr(feature = "std", derive(thiserror::Error))]
30pub enum Error<H, CodecError> {
31 #[cfg_attr(feature = "std", error("Invalid root {0:x?}, expected {1:x?}"))]
32 RootMismatch(H, H),
33 #[cfg_attr(feature = "std", error("Missing nodes in the proof"))]
34 IncompleteProof,
35 #[cfg_attr(feature = "std", error("Child node content with no root in proof"))]
36 ExtraneousChildNode,
37 #[cfg_attr(feature = "std", error("Proof of child trie {0:x?} not in parent proof"))]
38 ExtraneousChildProof(H),
39 #[cfg_attr(feature = "std", error("Invalid root {0:x?}, expected {1:x?}"))]
40 InvalidChildRoot(Vec<u8>, Vec<u8>),
41 #[cfg_attr(feature = "std", error("Trie error: {0:?}"))]
42 TrieError(Box<trie_db::TrieError<H, CodecError>>),
43}
44
45impl<H, CodecError> From<Box<trie_db::TrieError<H, CodecError>>> for Error<H, CodecError> {
46 fn from(error: Box<trie_db::TrieError<H, CodecError>>) -> Self {
47 Error::TrieError(error)
48 }
49}
50
51pub fn decode_compact<'a, L, DB, I>(
61 db: &mut DB,
62 encoded: I,
63 expected_root: &TrieHash<L>,
64) -> Result<TrieHash<L>, Error<TrieHash<L>, CError<L>>>
65where
66 L: TrieConfiguration,
67 DB: HashDBT<L::Hash, trie_db::DBValue> + hash_db::HashDBRef<L::Hash, trie_db::DBValue>,
68 I: IntoIterator<Item = &'a [u8]>,
69{
70 let mut nodes_iter = encoded.into_iter();
71 let (top_root, _nb_used) = trie_db::decode_compact_from_iter::<L, _, _>(db, &mut nodes_iter)?;
72
73 if &top_root != expected_root {
74 return Err(Error::RootMismatch(top_root, *expected_root));
75 }
76
77 let mut child_tries = Vec::new();
78 {
79 let trie = crate::TrieDBBuilder::<L>::new(db, &top_root).build();
81
82 let mut iter = trie.iter()?;
83
84 let childtrie_roots = sp_core::storage::well_known_keys::DEFAULT_CHILD_STORAGE_KEY_PREFIX;
85 if iter.seek(childtrie_roots).is_ok() {
86 loop {
87 match iter.next() {
88 Some(Ok((key, value))) if key.starts_with(childtrie_roots) => {
89 let mut root = TrieHash::<L>::default();
92 if root.as_mut().len() != value.as_slice().len() {
94 return Err(Error::InvalidChildRoot(key, value));
95 }
96 root.as_mut().copy_from_slice(value.as_ref());
97 child_tries.push(root);
98 },
99 Some(Err(error)) => match *error {
102 trie_db::TrieError::IncompleteDatabase(..) => (),
103 e => return Err(Box::new(e).into()),
104 },
105 _ => break,
106 }
107 }
108 }
109 }
110
111 if !HashDBT::<L::Hash, _>::contains(db, &top_root, EMPTY_PREFIX) {
112 return Err(Error::IncompleteProof);
113 }
114
115 let mut previous_extracted_child_trie = None;
116 let mut nodes_iter = nodes_iter.peekable();
117 for child_root in child_tries.into_iter() {
118 if previous_extracted_child_trie.is_none() && nodes_iter.peek().is_some() {
119 let (top_root, _) = trie_db::decode_compact_from_iter::<L, _, _>(db, &mut nodes_iter)?;
120 previous_extracted_child_trie = Some(top_root);
121 }
122
123 if Some(child_root) == previous_extracted_child_trie {
127 previous_extracted_child_trie = None;
128 }
129 }
130
131 if let Some(child_root) = previous_extracted_child_trie {
132 return Err(Error::ExtraneousChildProof(child_root));
135 }
136
137 if nodes_iter.next().is_some() {
138 return Err(Error::ExtraneousChildNode);
139 }
140
141 Ok(top_root)
142}
143
144pub fn encode_compact<L, DB>(
152 partial_db: &DB,
153 root: &TrieHash<L>,
154) -> Result<CompactProof, Error<TrieHash<L>, CError<L>>>
155where
156 L: TrieConfiguration,
157 DB: HashDBT<L::Hash, trie_db::DBValue> + hash_db::HashDBRef<L::Hash, trie_db::DBValue>,
158{
159 let mut seen = trie_db::SeenHashes::<L>::default();
160 let mut child_tries = Vec::new();
161 let mut compact_proof = {
162 let trie = crate::TrieDBBuilder::<L>::new(partial_db, root).build();
163
164 let mut iter = trie.iter()?;
165
166 let childtrie_roots = sp_core::storage::well_known_keys::DEFAULT_CHILD_STORAGE_KEY_PREFIX;
167 if iter.seek(childtrie_roots).is_ok() {
168 loop {
169 match iter.next() {
170 Some(Ok((key, value))) if key.starts_with(childtrie_roots) => {
171 let mut root = TrieHash::<L>::default();
172 if root.as_mut().len() != value.as_slice().len() {
173 return Err(Error::InvalidChildRoot(key.to_vec(), value.to_vec()));
175 }
176 root.as_mut().copy_from_slice(value.as_ref());
177 child_tries.push(root);
178 },
179 Some(Err(error)) => match *error {
182 trie_db::TrieError::IncompleteDatabase(..) => (),
183 e => return Err(Box::new(e).into()),
184 },
185 _ => break,
186 }
187 }
188 }
189
190 trie_db::encode_compact_skip_duplicates::<L>(&trie, &mut seen)?
191 };
192
193 for child_root in child_tries {
194 if !HashDBT::<L::Hash, _>::contains(partial_db, &child_root, EMPTY_PREFIX) {
195 continue;
198 }
199
200 let trie = crate::TrieDBBuilder::<L>::new(partial_db, &child_root).build();
201 let child_proof = trie_db::encode_compact_skip_duplicates::<L>(&trie, &mut seen)?;
202
203 compact_proof.extend(child_proof);
204 }
205
206 Ok(CompactProof { encoded_nodes: compact_proof })
207}
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212 use crate::{delta_trie_root, recorder::IgnoredNodes, HashDB, StorageProof};
213 use codec::Encode;
214 use hash_db::AsHashDB;
215 use sp_core::{Blake2Hasher, H256};
216 use std::collections::HashSet;
217 use trie_db::{DBValue, Trie, TrieDBBuilder, TrieDBMutBuilder, TrieHash, TrieMut};
218
219 type MemoryDB = crate::MemoryDB<sp_core::Blake2Hasher>;
220 type Layout = crate::LayoutV1<sp_core::Blake2Hasher>;
221 type Recorder = crate::recorder::Recorder<sp_core::Blake2Hasher>;
222
223 fn create_trie(num_keys: u32) -> (MemoryDB, TrieHash<Layout>) {
224 let mut db = MemoryDB::default();
225 let mut root = Default::default();
226
227 {
228 let mut trie = TrieDBMutBuilder::<Layout>::new(&mut db, &mut root).build();
229 for i in 0..num_keys {
230 trie.insert(
231 &i.encode(),
232 &vec![1u8; 64].into_iter().chain(i.encode()).collect::<Vec<_>>(),
233 )
234 .expect("Inserts data");
235 }
236 }
237
238 (db, root)
239 }
240
241 #[test]
242 fn values_shared_between_top_and_child_trie_are_deduplicated() {
243 let mut db = MemoryDB::default();
244
245 let shared_value = vec![42u8; 64];
250
251 let mut child_root = Default::default();
252 {
253 let mut trie = TrieDBMutBuilder::<Layout>::new(&mut db, &mut child_root).build();
254 trie.insert(b"child_key", &shared_value).expect("Inserts data");
255 }
256
257 let mut root = Default::default();
258 {
259 let mut trie = TrieDBMutBuilder::<Layout>::new(&mut db, &mut root).build();
260 trie.insert(b"top_key", &shared_value).expect("Inserts data");
261
262 let mut child_storage_key =
263 sp_core::storage::well_known_keys::DEFAULT_CHILD_STORAGE_KEY_PREFIX.to_vec();
264 child_storage_key.extend(b"child1");
265 trie.insert(&child_storage_key, child_root.as_ref()).expect("Inserts data");
266 }
267
268 let compact_proof = encode_compact::<Layout, _>(&db, &root).unwrap();
269
270 let occurrences = compact_proof
273 .encoded_nodes
274 .iter()
275 .filter(|node| node.as_slice() == shared_value.as_slice())
276 .count();
277 assert_eq!(occurrences, 1);
278
279 let mut res_db = MemoryDB::new(&[]);
281 decode_compact::<Layout, _, _>(
282 &mut res_db,
283 compact_proof.iter_compact_encoded_nodes(),
284 &root,
285 )
286 .unwrap();
287
288 let trie = TrieDBBuilder::<Layout>::new(&res_db, &root).build();
289 assert_eq!(trie.get(b"top_key").unwrap().unwrap(), shared_value);
290
291 let child_trie = TrieDBBuilder::<Layout>::new(&res_db, &child_root).build();
292 assert_eq!(child_trie.get(b"child_key").unwrap().unwrap(), shared_value);
293 }
294
295 struct Overlay<'a> {
296 db: &'a MemoryDB,
297 write: MemoryDB,
298 }
299
300 impl hash_db::HashDB<sp_core::Blake2Hasher, DBValue> for Overlay<'_> {
301 fn get(
302 &self,
303 key: &<sp_core::Blake2Hasher as hash_db::Hasher>::Out,
304 prefix: hash_db::Prefix,
305 ) -> Option<DBValue> {
306 HashDB::get(self.db, key, prefix)
307 }
308
309 fn contains(
310 &self,
311 key: &<sp_core::Blake2Hasher as hash_db::Hasher>::Out,
312 prefix: hash_db::Prefix,
313 ) -> bool {
314 HashDB::contains(self.db, key, prefix)
315 }
316
317 fn insert(
318 &mut self,
319 prefix: hash_db::Prefix,
320 value: &[u8],
321 ) -> <sp_core::Blake2Hasher as hash_db::Hasher>::Out {
322 self.write.insert(prefix, value)
323 }
324
325 fn emplace(
326 &mut self,
327 key: <sp_core::Blake2Hasher as hash_db::Hasher>::Out,
328 prefix: hash_db::Prefix,
329 value: DBValue,
330 ) {
331 self.write.emplace(key, prefix, value);
332 }
333
334 fn remove(
335 &mut self,
336 key: &<sp_core::Blake2Hasher as hash_db::Hasher>::Out,
337 prefix: hash_db::Prefix,
338 ) {
339 self.write.remove(key, prefix);
340 }
341 }
342
343 impl AsHashDB<Blake2Hasher, DBValue> for Overlay<'_> {
344 fn as_hash_db(&self) -> &dyn HashDBT<Blake2Hasher, DBValue> {
345 self
346 }
347
348 fn as_hash_db_mut<'a>(&'a mut self) -> &'a mut (dyn HashDBT<Blake2Hasher, DBValue> + 'a) {
349 self
350 }
351 }
352
353 fn emulate_block_building(
354 state: &MemoryDB,
355 root: H256,
356 read_keys: &[u32],
357 write_keys: &[u32],
358 nodes_to_ignore: IgnoredNodes<H256>,
359 ) -> (Recorder, MemoryDB, H256) {
360 let recorder = Recorder::with_ignored_nodes(nodes_to_ignore);
361
362 {
363 let mut trie_recorder = recorder.as_trie_recorder(root);
364 let trie = TrieDBBuilder::<Layout>::new(state, &root)
365 .with_recorder(&mut trie_recorder)
366 .build();
367
368 for key in read_keys {
369 trie.get(&key.encode()).unwrap().unwrap();
370 }
371 }
372
373 let mut overlay = Overlay { db: state, write: Default::default() };
374
375 let new_root = {
376 let mut trie_recorder = recorder.as_trie_recorder(root);
377 delta_trie_root::<Layout, _, _, _, _, _>(
378 &mut overlay,
379 root,
380 write_keys.iter().map(|k| {
381 (
382 k.encode(),
383 Some(vec![2u8; 64].into_iter().chain(k.encode()).collect::<Vec<_>>()),
384 )
385 }),
386 Some(&mut trie_recorder),
387 None,
388 )
389 .unwrap()
390 };
391
392 (recorder, overlay.write, new_root)
393 }
394
395 fn build_known_nodes_list(recorder: &Recorder, transaction: &MemoryDB) -> IgnoredNodes<H256> {
396 let mut ignored_nodes =
397 IgnoredNodes::from_storage_proof::<Blake2Hasher>(&recorder.to_storage_proof());
398
399 ignored_nodes.extend(IgnoredNodes::from_memory_db::<Blake2Hasher, _>(transaction.clone()));
400
401 ignored_nodes
402 }
403
404 #[test]
405 fn ensure_multiple_tries_encode_compact_works() {
406 let (mut db, root) = create_trie(100);
407
408 let mut nodes_to_ignore = IgnoredNodes::default();
409 let (recorder, transaction, root1) = emulate_block_building(
410 &db,
411 root,
412 &[2, 4, 5, 6, 7, 8],
413 &[9, 10, 11, 12, 13, 14],
414 nodes_to_ignore.clone(),
415 );
416
417 db.consolidate(transaction.clone());
418 nodes_to_ignore.extend(build_known_nodes_list(&recorder, &transaction));
419
420 let (recorder2, transaction, root2) = emulate_block_building(
421 &db,
422 root1,
423 &[9, 10, 11, 12, 13, 14],
424 &[15, 16, 17, 18, 19, 20],
425 nodes_to_ignore.clone(),
426 );
427
428 db.consolidate(transaction.clone());
429 nodes_to_ignore.extend(build_known_nodes_list(&recorder2, &transaction));
430
431 let (recorder3, _, root3) = emulate_block_building(
432 &db,
433 root2,
434 &[20, 30, 40, 41, 42],
435 &[80, 90, 91, 92, 93],
436 nodes_to_ignore,
437 );
438
439 let proof = recorder.to_storage_proof();
440 let proof2 = recorder2.to_storage_proof();
441 let proof3 = recorder3.to_storage_proof();
442
443 let mut combined = HashSet::<Vec<u8>>::from_iter(proof.into_iter_nodes());
444 proof2.iter_nodes().for_each(|n| assert!(combined.insert(n.clone())));
445 proof3.iter_nodes().for_each(|n| assert!(combined.insert(n.clone())));
446
447 let proof = StorageProof::new(combined.into_iter());
448
449 let compact_proof = encode_compact::<Layout, _>(&proof.to_memory_db(), &root).unwrap();
450
451 assert!(proof.encoded_size() > compact_proof.encoded_size());
452
453 let mut res_db = crate::MemoryDB::<Blake2Hasher>::new(&[]);
454 decode_compact::<Layout, _, _>(
455 &mut res_db,
456 compact_proof.iter_compact_encoded_nodes(),
457 &root,
458 )
459 .unwrap();
460
461 let (_, transaction, root1_proof) = emulate_block_building(
462 &res_db,
463 root,
464 &[2, 4, 5, 6, 7, 8],
465 &[9, 10, 11, 12, 13, 14],
466 Default::default(),
467 );
468
469 assert_eq!(root1, root1_proof);
470
471 res_db.consolidate(transaction);
472
473 let (_, transaction2, root2_proof) = emulate_block_building(
474 &res_db,
475 root1,
476 &[9, 10, 11, 12, 13, 14],
477 &[15, 16, 17, 18, 19, 20],
478 Default::default(),
479 );
480
481 assert_eq!(root2, root2_proof);
482
483 res_db.consolidate(transaction2);
484
485 let (_, _, root3_proof) = emulate_block_building(
486 &res_db,
487 root2,
488 &[20, 30, 40, 41, 42],
489 &[80, 90, 91, 92, 93],
490 Default::default(),
491 );
492
493 assert_eq!(root3, root3_proof);
494 }
495}