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 = sp_core::storage::well_known_keys::DEFAULT_CHILD_STORAGE_KEY_PREFIX;
84 if iter.seek(childtrie_roots).is_ok() {
85 loop {
86 match iter.next() {
87 Some(Ok((key, value))) if key.starts_with(childtrie_roots) => {
88 let mut root = TrieHash::<L>::default();
91 if root.as_mut().len() != value.as_slice().len() {
93 return Err(Error::InvalidChildRoot(key, value))
94 }
95 root.as_mut().copy_from_slice(value.as_ref());
96 child_tries.push(root);
97 },
98 Some(Err(error)) => match *error {
101 trie_db::TrieError::IncompleteDatabase(..) => (),
102 e => return Err(Box::new(e).into()),
103 },
104 _ => break,
105 }
106 }
107 }
108 }
109
110 if !HashDBT::<L::Hash, _>::contains(db, &top_root, EMPTY_PREFIX) {
111 return Err(Error::IncompleteProof)
112 }
113
114 let mut previous_extracted_child_trie = None;
115 let mut nodes_iter = nodes_iter.peekable();
116 for child_root in child_tries.into_iter() {
117 if previous_extracted_child_trie.is_none() && nodes_iter.peek().is_some() {
118 let (top_root, _) = trie_db::decode_compact_from_iter::<L, _, _>(db, &mut nodes_iter)?;
119 previous_extracted_child_trie = Some(top_root);
120 }
121
122 if Some(child_root) == previous_extracted_child_trie {
126 previous_extracted_child_trie = None;
127 }
128 }
129
130 if let Some(child_root) = previous_extracted_child_trie {
131 return Err(Error::ExtraneousChildProof(child_root))
134 }
135
136 if nodes_iter.next().is_some() {
137 return Err(Error::ExtraneousChildNode)
138 }
139
140 Ok(top_root)
141}
142
143pub fn encode_compact<L, DB>(
151 partial_db: &DB,
152 root: &TrieHash<L>,
153) -> Result<CompactProof, Error<TrieHash<L>, CError<L>>>
154where
155 L: TrieConfiguration,
156 DB: HashDBT<L::Hash, trie_db::DBValue> + hash_db::HashDBRef<L::Hash, trie_db::DBValue>,
157{
158 let mut child_tries = Vec::new();
159 let mut compact_proof = {
160 let trie = crate::TrieDBBuilder::<L>::new(partial_db, root).build();
161
162 let mut iter = trie.iter()?;
163
164 let childtrie_roots = sp_core::storage::well_known_keys::DEFAULT_CHILD_STORAGE_KEY_PREFIX;
165 if iter.seek(childtrie_roots).is_ok() {
166 loop {
167 match iter.next() {
168 Some(Ok((key, value))) if key.starts_with(childtrie_roots) => {
169 let mut root = TrieHash::<L>::default();
170 if root.as_mut().len() != value.as_slice().len() {
171 return Err(Error::InvalidChildRoot(key.to_vec(), value.to_vec()))
173 }
174 root.as_mut().copy_from_slice(value.as_ref());
175 child_tries.push(root);
176 },
177 Some(Err(error)) => match *error {
180 trie_db::TrieError::IncompleteDatabase(..) => (),
181 e => return Err(Box::new(e).into()),
182 },
183 _ => break,
184 }
185 }
186 }
187
188 trie_db::encode_compact::<L>(&trie)?
189 };
190
191 for child_root in child_tries {
192 if !HashDBT::<L::Hash, _>::contains(partial_db, &child_root, EMPTY_PREFIX) {
193 continue
196 }
197
198 let trie = crate::TrieDBBuilder::<L>::new(partial_db, &child_root).build();
199 let child_proof = trie_db::encode_compact::<L>(&trie)?;
200
201 compact_proof.extend(child_proof);
202 }
203
204 Ok(CompactProof { encoded_nodes: compact_proof })
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210 use crate::{delta_trie_root, recorder::IgnoredNodes, HashDB, StorageProof};
211 use codec::Encode;
212 use hash_db::AsHashDB;
213 use sp_core::{Blake2Hasher, H256};
214 use std::collections::HashSet;
215 use trie_db::{DBValue, Trie, TrieDBBuilder, TrieDBMutBuilder, TrieHash, TrieMut};
216
217 type MemoryDB = crate::MemoryDB<sp_core::Blake2Hasher>;
218 type Layout = crate::LayoutV1<sp_core::Blake2Hasher>;
219 type Recorder = crate::recorder::Recorder<sp_core::Blake2Hasher>;
220
221 fn create_trie(num_keys: u32) -> (MemoryDB, TrieHash<Layout>) {
222 let mut db = MemoryDB::default();
223 let mut root = Default::default();
224
225 {
226 let mut trie = TrieDBMutBuilder::<Layout>::new(&mut db, &mut root).build();
227 for i in 0..num_keys {
228 trie.insert(
229 &i.encode(),
230 &vec![1u8; 64].into_iter().chain(i.encode()).collect::<Vec<_>>(),
231 )
232 .expect("Inserts data");
233 }
234 }
235
236 (db, root)
237 }
238
239 struct Overlay<'a> {
240 db: &'a MemoryDB,
241 write: MemoryDB,
242 }
243
244 impl hash_db::HashDB<sp_core::Blake2Hasher, DBValue> for Overlay<'_> {
245 fn get(
246 &self,
247 key: &<sp_core::Blake2Hasher as hash_db::Hasher>::Out,
248 prefix: hash_db::Prefix,
249 ) -> Option<DBValue> {
250 HashDB::get(self.db, key, prefix)
251 }
252
253 fn contains(
254 &self,
255 key: &<sp_core::Blake2Hasher as hash_db::Hasher>::Out,
256 prefix: hash_db::Prefix,
257 ) -> bool {
258 HashDB::contains(self.db, key, prefix)
259 }
260
261 fn insert(
262 &mut self,
263 prefix: hash_db::Prefix,
264 value: &[u8],
265 ) -> <sp_core::Blake2Hasher as hash_db::Hasher>::Out {
266 self.write.insert(prefix, value)
267 }
268
269 fn emplace(
270 &mut self,
271 key: <sp_core::Blake2Hasher as hash_db::Hasher>::Out,
272 prefix: hash_db::Prefix,
273 value: DBValue,
274 ) {
275 self.write.emplace(key, prefix, value);
276 }
277
278 fn remove(
279 &mut self,
280 key: &<sp_core::Blake2Hasher as hash_db::Hasher>::Out,
281 prefix: hash_db::Prefix,
282 ) {
283 self.write.remove(key, prefix);
284 }
285 }
286
287 impl AsHashDB<Blake2Hasher, DBValue> for Overlay<'_> {
288 fn as_hash_db(&self) -> &dyn HashDBT<Blake2Hasher, DBValue> {
289 self
290 }
291
292 fn as_hash_db_mut<'a>(&'a mut self) -> &'a mut (dyn HashDBT<Blake2Hasher, DBValue> + 'a) {
293 self
294 }
295 }
296
297 fn emulate_block_building(
298 state: &MemoryDB,
299 root: H256,
300 read_keys: &[u32],
301 write_keys: &[u32],
302 nodes_to_ignore: IgnoredNodes<H256>,
303 ) -> (Recorder, MemoryDB, H256) {
304 let recorder = Recorder::with_ignored_nodes(nodes_to_ignore);
305
306 {
307 let mut trie_recorder = recorder.as_trie_recorder(root);
308 let trie = TrieDBBuilder::<Layout>::new(state, &root)
309 .with_recorder(&mut trie_recorder)
310 .build();
311
312 for key in read_keys {
313 trie.get(&key.encode()).unwrap().unwrap();
314 }
315 }
316
317 let mut overlay = Overlay { db: state, write: Default::default() };
318
319 let new_root = {
320 let mut trie_recorder = recorder.as_trie_recorder(root);
321 delta_trie_root::<Layout, _, _, _, _, _>(
322 &mut overlay,
323 root,
324 write_keys.iter().map(|k| {
325 (
326 k.encode(),
327 Some(vec![2u8; 64].into_iter().chain(k.encode()).collect::<Vec<_>>()),
328 )
329 }),
330 Some(&mut trie_recorder),
331 None,
332 )
333 .unwrap()
334 };
335
336 (recorder, overlay.write, new_root)
337 }
338
339 fn build_known_nodes_list(recorder: &Recorder, transaction: &MemoryDB) -> IgnoredNodes<H256> {
340 let mut ignored_nodes =
341 IgnoredNodes::from_storage_proof::<Blake2Hasher>(&recorder.to_storage_proof());
342
343 ignored_nodes.extend(IgnoredNodes::from_memory_db::<Blake2Hasher, _>(transaction.clone()));
344
345 ignored_nodes
346 }
347
348 #[test]
349 fn ensure_multiple_tries_encode_compact_works() {
350 let (mut db, root) = create_trie(100);
351
352 let mut nodes_to_ignore = IgnoredNodes::default();
353 let (recorder, transaction, root1) = emulate_block_building(
354 &db,
355 root,
356 &[2, 4, 5, 6, 7, 8],
357 &[9, 10, 11, 12, 13, 14],
358 nodes_to_ignore.clone(),
359 );
360
361 db.consolidate(transaction.clone());
362 nodes_to_ignore.extend(build_known_nodes_list(&recorder, &transaction));
363
364 let (recorder2, transaction, root2) = emulate_block_building(
365 &db,
366 root1,
367 &[9, 10, 11, 12, 13, 14],
368 &[15, 16, 17, 18, 19, 20],
369 nodes_to_ignore.clone(),
370 );
371
372 db.consolidate(transaction.clone());
373 nodes_to_ignore.extend(build_known_nodes_list(&recorder2, &transaction));
374
375 let (recorder3, _, root3) = emulate_block_building(
376 &db,
377 root2,
378 &[20, 30, 40, 41, 42],
379 &[80, 90, 91, 92, 93],
380 nodes_to_ignore,
381 );
382
383 let proof = recorder.to_storage_proof();
384 let proof2 = recorder2.to_storage_proof();
385 let proof3 = recorder3.to_storage_proof();
386
387 let mut combined = HashSet::<Vec<u8>>::from_iter(proof.into_iter_nodes());
388 proof2.iter_nodes().for_each(|n| assert!(combined.insert(n.clone())));
389 proof3.iter_nodes().for_each(|n| assert!(combined.insert(n.clone())));
390
391 let proof = StorageProof::new(combined.into_iter());
392
393 let compact_proof = encode_compact::<Layout, _>(&proof.to_memory_db(), &root).unwrap();
394
395 assert!(proof.encoded_size() > compact_proof.encoded_size());
396
397 let mut res_db = crate::MemoryDB::<Blake2Hasher>::new(&[]);
398 decode_compact::<Layout, _, _>(
399 &mut res_db,
400 compact_proof.iter_compact_encoded_nodes(),
401 Some(&root),
402 )
403 .unwrap();
404
405 let (_, transaction, root1_proof) = emulate_block_building(
406 &res_db,
407 root,
408 &[2, 4, 5, 6, 7, 8],
409 &[9, 10, 11, 12, 13, 14],
410 Default::default(),
411 );
412
413 assert_eq!(root1, root1_proof);
414
415 res_db.consolidate(transaction);
416
417 let (_, transaction2, root2_proof) = emulate_block_building(
418 &res_db,
419 root1,
420 &[9, 10, 11, 12, 13, 14],
421 &[15, 16, 17, 18, 19, 20],
422 Default::default(),
423 );
424
425 assert_eq!(root2, root2_proof);
426
427 res_db.consolidate(transaction2);
428
429 let (_, _, root3_proof) = emulate_block_building(
430 &res_db,
431 root2,
432 &[20, 30, 40, 41, 42],
433 &[80, 90, 91, 92, 93],
434 Default::default(),
435 );
436
437 assert_eq!(root3, root3_proof);
438 }
439}