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>(
59 db: &mut DB,
60 encoded: I,
61 expected_root: Option<&TrieHash<L>>,
62) -> Result<TrieHash<L>, Error<TrieHash<L>, CError<L>>>
63where
64 L: TrieConfiguration,
65 DB: HashDBT<L::Hash, trie_db::DBValue> + hash_db::HashDBRef<L::Hash, trie_db::DBValue>,
66 I: IntoIterator<Item = &'a [u8]>,
67{
68 let mut nodes_iter = encoded.into_iter();
69 let (top_root, _nb_used) = trie_db::decode_compact_from_iter::<L, _, _>(db, &mut nodes_iter)?;
70
71 if let Some(expected_root) = expected_root.filter(|expected| *expected != &top_root) {
73 return Err(Error::RootMismatch(top_root, *expected_root));
74 }
75
76 let mut child_tries = Vec::new();
77 {
78 let trie = crate::TrieDBBuilder::<L>::new(db, &top_root).build();
80
81 let mut iter = trie.iter()?;
82
83 let childtrie_roots =
84 pezsp_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 child_tries = Vec::new();
160 let mut compact_proof = {
161 let trie = crate::TrieDBBuilder::<L>::new(partial_db, root).build();
162
163 let mut iter = trie.iter()?;
164
165 let childtrie_roots =
166 pezsp_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::<L>(&trie)?
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::<L>(&trie)?;
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 pezsp_core::{Blake2Hasher, H256};
216 use std::collections::HashSet;
217 use trie_db::{DBValue, Trie, TrieDBBuilder, TrieDBMutBuilder, TrieHash, TrieMut};
218
219 type MemoryDB = crate::MemoryDB<pezsp_core::Blake2Hasher>;
220 type Layout = crate::LayoutV1<pezsp_core::Blake2Hasher>;
221 type Recorder = crate::recorder::Recorder<pezsp_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 struct Overlay<'a> {
242 db: &'a MemoryDB,
243 write: MemoryDB,
244 }
245
246 impl hash_db::HashDB<pezsp_core::Blake2Hasher, DBValue> for Overlay<'_> {
247 fn get(
248 &self,
249 key: &<pezsp_core::Blake2Hasher as hash_db::Hasher>::Out,
250 prefix: hash_db::Prefix,
251 ) -> Option<DBValue> {
252 HashDB::get(self.db, key, prefix)
253 }
254
255 fn contains(
256 &self,
257 key: &<pezsp_core::Blake2Hasher as hash_db::Hasher>::Out,
258 prefix: hash_db::Prefix,
259 ) -> bool {
260 HashDB::contains(self.db, key, prefix)
261 }
262
263 fn insert(
264 &mut self,
265 prefix: hash_db::Prefix,
266 value: &[u8],
267 ) -> <pezsp_core::Blake2Hasher as hash_db::Hasher>::Out {
268 self.write.insert(prefix, value)
269 }
270
271 fn emplace(
272 &mut self,
273 key: <pezsp_core::Blake2Hasher as hash_db::Hasher>::Out,
274 prefix: hash_db::Prefix,
275 value: DBValue,
276 ) {
277 self.write.emplace(key, prefix, value);
278 }
279
280 fn remove(
281 &mut self,
282 key: &<pezsp_core::Blake2Hasher as hash_db::Hasher>::Out,
283 prefix: hash_db::Prefix,
284 ) {
285 self.write.remove(key, prefix);
286 }
287 }
288
289 impl AsHashDB<Blake2Hasher, DBValue> for Overlay<'_> {
290 fn as_hash_db(&self) -> &dyn HashDBT<Blake2Hasher, DBValue> {
291 self
292 }
293
294 fn as_hash_db_mut<'a>(&'a mut self) -> &'a mut (dyn HashDBT<Blake2Hasher, DBValue> + 'a) {
295 self
296 }
297 }
298
299 fn emulate_block_building(
300 state: &MemoryDB,
301 root: H256,
302 read_keys: &[u32],
303 write_keys: &[u32],
304 nodes_to_ignore: IgnoredNodes<H256>,
305 ) -> (Recorder, MemoryDB, H256) {
306 let recorder = Recorder::with_ignored_nodes(nodes_to_ignore);
307
308 {
309 let mut trie_recorder = recorder.as_trie_recorder(root);
310 let trie = TrieDBBuilder::<Layout>::new(state, &root)
311 .with_recorder(&mut trie_recorder)
312 .build();
313
314 for key in read_keys {
315 trie.get(&key.encode()).unwrap().unwrap();
316 }
317 }
318
319 let mut overlay = Overlay { db: state, write: Default::default() };
320
321 let new_root = {
322 let mut trie_recorder = recorder.as_trie_recorder(root);
323 delta_trie_root::<Layout, _, _, _, _, _>(
324 &mut overlay,
325 root,
326 write_keys.iter().map(|k| {
327 (
328 k.encode(),
329 Some(vec![2u8; 64].into_iter().chain(k.encode()).collect::<Vec<_>>()),
330 )
331 }),
332 Some(&mut trie_recorder),
333 None,
334 )
335 .unwrap()
336 };
337
338 (recorder, overlay.write, new_root)
339 }
340
341 fn build_known_nodes_list(recorder: &Recorder, transaction: &MemoryDB) -> IgnoredNodes<H256> {
342 let mut ignored_nodes =
343 IgnoredNodes::from_storage_proof::<Blake2Hasher>(&recorder.to_storage_proof());
344
345 ignored_nodes.extend(IgnoredNodes::from_memory_db::<Blake2Hasher, _>(transaction.clone()));
346
347 ignored_nodes
348 }
349
350 #[test]
351 fn ensure_multiple_tries_encode_compact_works() {
352 let (mut db, root) = create_trie(100);
353
354 let mut nodes_to_ignore = IgnoredNodes::default();
355 let (recorder, transaction, root1) = emulate_block_building(
356 &db,
357 root,
358 &[2, 4, 5, 6, 7, 8],
359 &[9, 10, 11, 12, 13, 14],
360 nodes_to_ignore.clone(),
361 );
362
363 db.consolidate(transaction.clone());
364 nodes_to_ignore.extend(build_known_nodes_list(&recorder, &transaction));
365
366 let (recorder2, transaction, root2) = emulate_block_building(
367 &db,
368 root1,
369 &[9, 10, 11, 12, 13, 14],
370 &[15, 16, 17, 18, 19, 20],
371 nodes_to_ignore.clone(),
372 );
373
374 db.consolidate(transaction.clone());
375 nodes_to_ignore.extend(build_known_nodes_list(&recorder2, &transaction));
376
377 let (recorder3, _, root3) = emulate_block_building(
378 &db,
379 root2,
380 &[20, 30, 40, 41, 42],
381 &[80, 90, 91, 92, 93],
382 nodes_to_ignore,
383 );
384
385 let proof = recorder.to_storage_proof();
386 let proof2 = recorder2.to_storage_proof();
387 let proof3 = recorder3.to_storage_proof();
388
389 let mut combined = HashSet::<Vec<u8>>::from_iter(proof.into_iter_nodes());
390 proof2.iter_nodes().for_each(|n| assert!(combined.insert(n.clone())));
391 proof3.iter_nodes().for_each(|n| assert!(combined.insert(n.clone())));
392
393 let proof = StorageProof::new(combined.into_iter());
394
395 let compact_proof = encode_compact::<Layout, _>(&proof.to_memory_db(), &root).unwrap();
396
397 assert!(proof.encoded_size() > compact_proof.encoded_size());
398
399 let mut res_db = crate::MemoryDB::<Blake2Hasher>::new(&[]);
400 decode_compact::<Layout, _, _>(
401 &mut res_db,
402 compact_proof.iter_compact_encoded_nodes(),
403 Some(&root),
404 )
405 .unwrap();
406
407 let (_, transaction, root1_proof) = emulate_block_building(
408 &res_db,
409 root,
410 &[2, 4, 5, 6, 7, 8],
411 &[9, 10, 11, 12, 13, 14],
412 Default::default(),
413 );
414
415 assert_eq!(root1, root1_proof);
416
417 res_db.consolidate(transaction);
418
419 let (_, transaction2, root2_proof) = emulate_block_building(
420 &res_db,
421 root1,
422 &[9, 10, 11, 12, 13, 14],
423 &[15, 16, 17, 18, 19, 20],
424 Default::default(),
425 );
426
427 assert_eq!(root2, root2_proof);
428
429 res_db.consolidate(transaction2);
430
431 let (_, _, root3_proof) = emulate_block_building(
432 &res_db,
433 root2,
434 &[20, 30, 40, 41, 42],
435 &[80, 90, 91, 92, 93],
436 Default::default(),
437 );
438
439 assert_eq!(root3, root3_proof);
440 }
441}