use hash_db::{HashDB, Hasher, EMPTY_PREFIX};
use reference_trie::{test_layouts, ExtensionLayout};
use trie_db::{
decode_compact, encode_compact, DBValue, NodeCodec, Recorder, Trie, TrieDBBuilder,
TrieDBMutBuilder, TrieError, TrieLayout, TrieMut,
};
type MemoryDB<T> = memory_db::MemoryDB<
<T as TrieLayout>::Hash,
memory_db::HashKey<<T as TrieLayout>::Hash>,
DBValue,
>;
fn test_encode_compact<L: TrieLayout>(
entries: Vec<(&'static [u8], &'static [u8])>,
keys: Vec<&'static [u8]>,
) -> (<L::Hash as Hasher>::Out, Vec<Vec<u8>>, Vec<(&'static [u8], Option<DBValue>)>) {
let (db, root) = {
let mut db = <MemoryDB<L>>::default();
let mut root = Default::default();
{
let mut trie = <TrieDBMutBuilder<L>>::new(&mut db, &mut root).build();
for (key, value) in entries.iter() {
trie.insert(key, value).unwrap();
}
}
(db, root)
};
let mut recorder = Recorder::<L>::new();
let items = {
let mut items = Vec::with_capacity(keys.len());
let trie = <TrieDBBuilder<L>>::new(&db, &root).with_recorder(&mut recorder).build();
for key in keys {
let value = trie.get(key).unwrap();
items.push((key, value));
}
items
};
let mut partial_db = MemoryDB::<L>::default();
for record in recorder.drain() {
partial_db.insert(EMPTY_PREFIX, &record.data);
}
let compact_trie = {
let trie = <TrieDBBuilder<L>>::new(&partial_db, &root).build();
encode_compact::<L>(&trie).unwrap()
};
(root, compact_trie, items)
}
fn test_decode_compact<L: TrieLayout>(
encoded: &[Vec<u8>],
items: Vec<(&'static [u8], Option<DBValue>)>,
expected_root: <L::Hash as Hasher>::Out,
expected_used: usize,
) {
let mut db = MemoryDB::<L>::default();
let (root, used) = decode_compact::<L, _>(&mut db, encoded).unwrap();
assert_eq!(root, expected_root);
assert_eq!(used, expected_used);
let trie = <TrieDBBuilder<L>>::new(&db, &root).build();
for (key, expected_value) in items {
assert_eq!(trie.get(key).unwrap(), expected_value);
}
}
test_layouts!(trie_compact_encoding_works, trie_compact_encoding_works_internal);
fn trie_compact_encoding_works_internal<T: TrieLayout>() {
let (root, mut encoded, items) = test_encode_compact::<T>(
vec![
(b"alfa", &[0; 32]),
(b"bravo", b"bravo"),
(b"do", b"verb"),
(b"dog", b"puppy"),
(b"doge", &[0; 32]),
(b"horse", b"stallion"),
(b"house", b"building"),
],
vec![
b"do", b"dog", b"doge", b"bravo",
b"d", b"do\x10", b"halp", ],
);
encoded.push(Vec::new()); test_decode_compact::<T>(&encoded, items, root, encoded.len() - 1);
}
test_layouts!(
trie_decoding_fails_with_incomplete_database,
trie_decoding_fails_with_incomplete_database_internal
);
fn trie_decoding_fails_with_incomplete_database_internal<T: TrieLayout>() {
let (_, encoded, _) =
test_encode_compact::<T>(vec![(b"alfa", &[0; 32]), (b"bravo", b"bravo")], vec![b"alfa"]);
assert!(encoded.len() > 1);
let mut db = MemoryDB::<T>::default();
match decode_compact::<T, _>(&mut db, &encoded[..encoded.len() - 1]) {
Err(err) => match *err {
TrieError::IncompleteDatabase(_) => {},
_ => panic!("got unexpected TrieError"),
},
_ => panic!("decode was unexpectedly successful"),
}
}
#[test]
fn encoding_node_owned_and_decoding_node_works() {
let entries: Vec<(&[u8], &[u8])> = vec![
(b"alfa", &[0; 32]),
(b"bravo", b"bravo"),
(b"do", b"verb"),
(b"dog", b"puppy"),
(b"doge", &[0; 32]),
(b"horse", b"stallion"),
(b"house", b"building"),
];
let mut recorder = {
let mut db = <MemoryDB<ExtensionLayout>>::default();
let mut root = Default::default();
let mut recorder = Recorder::<ExtensionLayout>::new();
{
let mut trie = <TrieDBMutBuilder<ExtensionLayout>>::new(&mut db, &mut root).build();
for (key, value) in entries.iter() {
trie.insert(key, value).unwrap();
}
}
let trie = TrieDBBuilder::<ExtensionLayout>::new(&db, &root)
.with_recorder(&mut recorder)
.build();
for (key, _) in entries.iter() {
trie.get(key).unwrap();
}
recorder
};
for record in recorder.drain() {
let node =
<<ExtensionLayout as TrieLayout>::Codec as NodeCodec>::decode(&record.data).unwrap();
let node_owned = node.to_owned_node::<ExtensionLayout>().unwrap();
assert_eq!(record.data, node_owned.to_encoded::<<ExtensionLayout as TrieLayout>::Codec>());
}
}