#![allow(clippy::too_many_lines)]
use exonum_crypto::{hash, Hash, HashStream, HASH_SIZE};
use pretty_assertions::assert_eq;
use rand::{
self,
seq::{IteratorRandom, SliceRandom},
thread_rng, Rng, RngCore,
};
use serde::{de::DeserializeOwned, Serialize};
use serde_json::{self, json};
use std::{cmp, collections::HashSet, fmt::Debug, hash::Hash as StdHash, marker::PhantomData};
use super::{
key::{BitsRange, ChildKind, KEY_SIZE},
node::BranchNode,
MapProof, MapProofError, ProofPath,
};
use crate::{
access::CopyAccessExt,
proof_map::{Hashed, ProofMapIndex, Raw, ToProofPath},
BinaryKey, BinaryValue, Database, Fork, HashTag, ObjectHash, TemporaryDB,
};
const IDX_NAME: &str = "idx_name";
impl ObjectHash for [u8; HASH_SIZE] {
fn object_hash(&self) -> Hash {
hash(self)
}
}
fn generate_random_data(len: usize) -> Vec<([u8; KEY_SIZE], Vec<u8>)> {
let mut rng = thread_rng();
let mut exists_keys = HashSet::new();
let mut base = [0; KEY_SIZE];
rng.fill_bytes(&mut base);
let kv_generator = |_| {
let mut v = vec![0; 8];
let mut k = base;
let byte: usize = rng.gen_range(0, 31);
k[byte] = rng.gen::<u8>();
rng.fill_bytes(&mut v);
while exists_keys.contains(&k) {
rng.fill_bytes(&mut k);
}
exists_keys.insert(k);
(k, v)
};
(0..len).map(kv_generator).collect::<Vec<_>>()
}
fn generate_random_data_keys<R: Rng>(
exists_keys: &mut HashSet<Vec<u8>>,
len: usize,
rng: &mut R,
) -> Vec<(Vec<u8>, Vec<u8>)> {
let kv_generator = |_| {
let mut new_key = vec![0; rng.gen_range(0, 64)];
rng.fill_bytes(&mut new_key);
while exists_keys.contains(&new_key) {
new_key = vec![0; rng.gen_range(0, 64)];
rng.fill_bytes(&mut new_key);
}
exists_keys.insert(new_key.clone());
let mut v = vec![0; 8];
rng.fill_bytes(&mut v);
(new_key, v)
};
(0..len).map(kv_generator).collect::<Vec<_>>()
}
struct ProofMapTester<S> {
_s: PhantomData<S>,
}
impl<S> ProofMapTester<S>
where
S: ToProofPath<[u8; 32]>,
{
fn test_map_methods() {
let db = TemporaryDB::default();
let fork = db.fork();
let mut index = fork.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
assert_eq!(index.get(&[1; 32]), None);
assert!(!index.contains(&[1; 32]));
index.put(&[1; 32], 1_u8);
assert_eq!(index.get(&[1; 32]), Some(1_u8));
assert!(index.contains(&[1; 32]));
index.remove(&[1; 32]);
assert!(!index.contains(&[1; 32]));
assert_eq!(index.get(&[1; 32]), None);
index.put(&[1; 32], 1_u8);
index.put(&[2; 32], 2_u8);
index.put(&[3; 32], 3_u8);
index.remove(&[3; 32]);
index.remove(&[2; 32]);
index.clear();
assert!(!index.contains(&[2; 32]));
assert!(!index.contains(&[3; 32]));
}
fn test_insert_trivial() {
let db1 = TemporaryDB::default();
let db2 = TemporaryDB::default();
let fork1 = db1.fork();
let fork2 = db2.fork();
let mut index1 = fork1.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
index1.put(&[255; 32], vec![1]);
index1.put(&[254; 32], vec![2]);
let mut index2 = fork2.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
index2.put(&[254; 32], vec![2]);
index2.put(&[255; 32], vec![1]);
assert_eq!(index1.get(&[255; 32]), Some(vec![1]));
assert_eq!(index1.get(&[254; 32]), Some(vec![2]));
assert_eq!(index2.get(&[255; 32]), Some(vec![1]));
assert_eq!(index2.get(&[254; 32]), Some(vec![2]));
assert_ne!(index1.object_hash(), HashTag::empty_map_hash());
assert_eq!(index1.object_hash(), index2.object_hash());
}
fn test_insert_same_key() {
let db = TemporaryDB::default();
let fork = db.fork();
let mut table = fork.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
assert_eq!(table.object_hash(), HashTag::empty_map_hash());
let hash = HashStream::new()
.update(&[HashTag::MapBranchNode as u8])
.update(&[128, 2])
.update(&[255; HASH_SIZE])
.update(HashTag::hash_leaf(&[2]).as_ref())
.hash();
table.put(&[255; 32], vec![1]);
table.put(&[255; 32], vec![2]);
assert_eq!(table.get(&[255; 32]), Some(vec![2]));
assert_eq!(table.object_hash(), HashTag::hash_map_node(hash));
}
fn test_insert_simple() {
let db1 = TemporaryDB::default();
let db2 = TemporaryDB::default();
let fork1 = db1.fork();
let fork2 = db2.fork();
let mut index1 = fork1.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
index1.put(&[255; 32], vec![3]);
index1.put(&[254; 32], vec![2]);
index1.put(&[250; 32], vec![1]);
index1.put(&[254; 32], vec![5]);
let mut index2 = fork2.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
index2.put(&[250; 32], vec![1]);
index2.put(&[254; 32], vec![2]);
index2.put(&[255; 32], vec![3]);
index2.put(&[254; 32], vec![5]);
assert!(index1.object_hash() != Hash::zero());
assert_eq!(index1.object_hash(), index2.object_hash());
}
fn test_insert_reverse() {
let db1 = TemporaryDB::default();
let db2 = TemporaryDB::default();
let fork1 = db1.fork();
let mut index1 = fork1.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
index1.put(&[42; 32], vec![1]);
index1.put(&[64; 32], vec![2]);
index1.put(&[240; 32], vec![3]);
index1.put(&[245; 32], vec![4]);
index1.put(&[250; 32], vec![5]);
index1.put(&[255; 32], vec![6]);
let fork2 = db2.fork();
let mut index2 = fork2.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
index2.put(&[255; 32], vec![6]);
index2.put(&[250; 32], vec![5]);
index2.put(&[245; 32], vec![4]);
index2.put(&[240; 32], vec![3]);
index2.put(&[64; 32], vec![2]);
index2.put(&[42; 32], vec![1]);
assert!(index2.object_hash() != Hash::zero());
assert_eq!(index2.object_hash(), index1.object_hash());
}
fn test_remove_trivial() {
let db1 = TemporaryDB::default();
let db2 = TemporaryDB::default();
let fork1 = db1.fork();
let mut index1 = fork1.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
index1.put(&[255; 32], vec![6]);
index1.remove(&[255; 32]);
let fork2 = db2.fork();
let mut index2 = fork2.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
index2.put(&[255; 32], vec![6]);
index2.remove(&[255; 32]);
assert_eq!(index1.object_hash(), HashTag::empty_map_hash());
assert_eq!(index2.object_hash(), HashTag::empty_map_hash());
}
fn test_remove_simple() {
let db1 = TemporaryDB::default();
let db2 = TemporaryDB::default();
let fork1 = db1.fork();
let mut index1 = fork1.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
index1.put(&[255; 32], vec![1]);
index1.put(&[250; 32], vec![2]);
index1.put(&[245; 32], vec![3]);
index1.remove(&[255; 32]);
index1.remove(&[245; 32]);
let fork2 = db2.fork();
let mut index2 = fork2.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
index2.put(&[250; 32], vec![2]);
index2.put(&[255; 32], vec![1]);
index2.put(&[245; 32], vec![3]);
index2.remove(&[255; 32]);
index2.remove(&[245; 32]);
assert_eq!(index2.get(&[250; 32]), Some(vec![2]));
assert_eq!(index1.get(&[250; 32]), Some(vec![2]));
assert!(index1.get(&[255; 32]).is_none());
assert!(index2.get(&[255; 32]).is_none());
assert!(index1.get(&[245; 32]).is_none());
assert!(index2.get(&[245; 32]).is_none());
assert_eq!(index1.object_hash(), index2.object_hash());
}
fn test_remove_reverse() {
let db1 = TemporaryDB::default();
let db2 = TemporaryDB::default();
let fork1 = db1.fork();
let mut index1 = fork1.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
index1.put(&[42; 32], vec![1]);
index1.put(&[64; 32], vec![2]);
index1.put(&[240; 32], vec![3]);
index1.put(&[245; 32], vec![4]);
index1.put(&[250; 32], vec![5]);
index1.put(&[255; 32], vec![6]);
index1.remove(&[255; 32]);
index1.remove(&[250; 32]);
index1.remove(&[245; 32]);
index1.remove(&[240; 32]);
index1.remove(&[64; 32]);
index1.remove(&[42; 32]);
let fork2 = db2.fork();
let mut index2 = fork2.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
index2.put(&[255; 32], vec![6]);
index2.put(&[250; 32], vec![5]);
index2.put(&[245; 32], vec![4]);
index2.put(&[240; 32], vec![3]);
index2.put(&[64; 32], vec![2]);
index2.put(&[42; 32], vec![1]);
index2.remove(&[42; 32]);
index2.remove(&[64; 32]);
index2.remove(&[240; 32]);
index2.remove(&[245; 32]);
index2.remove(&[250; 32]);
index2.remove(&[255; 32]);
assert_eq!(index2.object_hash(), index1.object_hash());
}
fn test_clear() {
let db = TemporaryDB::default();
let fork = db.fork();
let mut index = fork.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
index.put(&[1; 32], 1);
let merkle_root = index.merkle_root();
index.clear();
assert_eq!(index.merkle_root(), Hash::zero());
index.put(&[1; 32], 1);
assert_eq!(index.merkle_root(), merkle_root);
}
fn test_fuzz_insert() {
let db1 = TemporaryDB::default();
let db2 = TemporaryDB::default();
let mut data = generate_random_data(100);
let mut rng = rand::thread_rng();
let fork1 = db1.fork();
let mut index1 = fork1.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
for item in &data {
index1.put(&item.0, item.1.clone());
}
let fork2 = db2.fork();
let mut index2 = fork2.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
data.shuffle(&mut rng);
for item in &data {
index2.put(&item.0, item.1.clone());
}
for item in &data {
let v1 = index1.get(&item.0);
let v2 = index2.get(&item.0);
assert_eq!(v1.as_ref(), Some(&item.1));
assert_eq!(v2.as_ref(), Some(&item.1));
}
assert!(index2.object_hash() != HashTag::empty_map_hash());
assert_eq!(index2.object_hash(), index1.object_hash());
data.shuffle(&mut rng);
for item in &data {
index1.put(&item.0, vec![1]);
}
data.shuffle(&mut rng);
for item in &data {
index2.put(&item.0, vec![1]);
}
for item in &data {
let v1 = index1.get(&item.0);
let v2 = index2.get(&item.0);
assert_eq!(v1.as_ref(), Some(&vec![1]));
assert_eq!(v2.as_ref(), Some(&vec![1]));
}
assert_eq!(index2.object_hash(), index1.object_hash());
}
fn test_build_proof_in_empty_tree() {
let db = TemporaryDB::default();
let fork = db.fork();
let mut table = fork.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
table.put(&[230; 32], vec![1]);
table.remove(&[230; 32]);
let proof = table.get_proof([244; 32]);
assert_eq!(proof.proof_unchecked(), vec![]);
check_map_proof(&proof, None, &table);
}
fn test_build_multiproof_in_empty_tree() {
let db = TemporaryDB::default();
let fork = db.fork();
let mut table = fork.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
table.put(&[230; 32], vec![1]);
table.remove(&[230; 32]);
let keys = vec![[0; 32], [230; 32], [244; 32]];
let proof = table.get_multiproof(keys.clone());
assert_eq!(proof.proof_unchecked(), vec![]);
check_map_multiproof(&proof, keys, &table);
}
fn test_build_proof_in_single_node_tree() {
let db = TemporaryDB::default();
let fork = db.fork();
let mut table = fork.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
table.put(&[230; 32], vec![1]);
let proof = table.get_proof([230; 32]);
assert_eq!(proof.proof_unchecked(), vec![]);
check_map_proof(&proof, Some([230; 32]), &table);
let proof = table.get_proof([128; 32]);
assert_eq!(
proof.proof_unchecked(),
vec![(S::transform_key(&[230; 32]), HashTag::hash_leaf(&[1]))]
);
check_map_proof(&proof, None, &table);
}
fn test_build_multiproof_in_single_node_tree() {
let db = TemporaryDB::default();
let fork = db.fork();
let mut table = fork.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
table.put(&[230; 32], vec![1]);
let keys = vec![[230; 32], [1; 32], [129; 32]];
let proof = table.get_multiproof(keys.clone());
assert_eq!(proof.proof_unchecked(), vec![]);
check_map_multiproof(&proof, keys, &table);
let keys = vec![[229; 32], [1; 32], [129; 32]];
let proof = table.get_multiproof(keys.clone());
assert_eq!(
proof.proof_unchecked(),
vec![(S::transform_key(&[230; 32]), HashTag::hash_leaf(&[1]))]
);
check_map_multiproof(&proof, keys, &table);
}
fn test_build_proof_in_complex_tree() {
let db = TemporaryDB::default();
let fork = db.fork();
let mut table = fork.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
table.put(&[128; 32], vec![1]); table.put(&[32; 32], vec![2]); let proof = table.get_proof([128; 32]);
assert_eq!(
proof.proof_unchecked(),
vec![(S::transform_key(&[32; 32]), HashTag::hash_leaf(&[2]))]
);
check_map_proof(&proof, Some([128; 32]), &table);
let proof = table.get_proof([32; 32]);
assert_eq!(
proof.proof_unchecked(),
vec![(S::transform_key(&[128; 32]), HashTag::hash_leaf(&[1]))]
);
check_map_proof(&proof, Some([32; 32]), &table);
let proof = table.get_proof([0; 32]);
let exp_proof = vec![
(S::transform_key(&[128; 32]), HashTag::hash_leaf(&[1])),
(S::transform_key(&[32; 32]), HashTag::hash_leaf(&[2])),
];
assert_eq!(proof.proof_unchecked(), exp_proof);
check_map_proof(&proof, None, &table);
let proof = table.get_proof([64; 32]); assert_eq!(proof.proof_unchecked(), exp_proof);
check_map_proof(&proof, None, &table);
let proof = table.get_proof([255; 32]);
assert_eq!(proof.proof_unchecked(), exp_proof);
check_map_proof(&proof, None, &table);
let left_key = {
let mut key = [0; 32];
key[0] = 128;
table.put(&key, vec![3]);
key
};
let left_hash = {
let mut node = BranchNode::empty();
node.set_child(
ChildKind::Left,
&S::transform_key(&left_key),
&HashTag::hash_leaf(&[3]),
);
node.set_child(
ChildKind::Right,
&S::transform_key(&[128; 32]),
&HashTag::hash_leaf(&[1]),
);
node.object_hash()
};
let proof = table.get_proof([128; 32]);
assert_eq!(
proof.proof_unchecked(),
vec![
(S::transform_key(&left_key), HashTag::hash_leaf(&[3])),
(S::transform_key(&[32; 32]), HashTag::hash_leaf(&[2])),
]
);
check_map_proof(&proof, Some([128; 32]), &table);
let proof = table.get_proof([64; 32]);
assert_eq!(
proof.proof_unchecked(),
vec![
(S::transform_key(&left_key).prefix(15), left_hash),
(S::transform_key(&[32; 32]), HashTag::hash_leaf(&[2])),
]
);
check_map_proof(&proof, None, &table);
let proof = table.get_proof([32; 32]);
assert_eq!(
proof.proof_unchecked(),
vec![(S::transform_key(&left_key).prefix(15), left_hash)]
);
check_map_proof(&proof, Some([32; 32]), &table);
let proof = table.get_proof([1; 32]);
assert_eq!(
proof.proof_unchecked(),
vec![
(S::transform_key(&left_key).prefix(15), left_hash),
(S::transform_key(&[32; 32]), HashTag::hash_leaf(&[2])),
]
);
check_map_proof(&proof, None, &table);
let right_key = {
let mut key = [0; 32];
key[0] = 32;
key[1] = 0b1001_0000; table.put(&key, vec![4]);
key
};
let right_hash = {
let mut node = BranchNode::empty();
node.set_child(
ChildKind::Left,
&S::transform_key(&[32; 32]),
&HashTag::hash_leaf(&[2]),
);
node.set_child(
ChildKind::Right,
&S::transform_key(&right_key),
&HashTag::hash_leaf(&[4]),
);
node.object_hash()
};
let proof = table.get_proof([128; 32]);
assert_eq!(
proof.proof_unchecked(),
vec![
(S::transform_key(&left_key), HashTag::hash_leaf(&[3])),
(S::transform_key(&right_key).prefix(12), right_hash),
]
);
check_map_proof(&proof, Some([128; 32]), &table);
let proof = table.get_proof([64; 32]);
assert_eq!(
proof.proof_unchecked(),
vec![
(S::transform_key(&left_key).prefix(15), left_hash),
(S::transform_key(&right_key).prefix(12), right_hash),
]
);
check_map_proof(&proof, None, &table);
let nonexisting_key = {
let mut key = [0; 32];
key[0] = 128;
key[1] = 128;
key[29] = 29;
key
};
let proof = table.get_proof(nonexisting_key);
assert_eq!(
proof.proof_unchecked(),
vec![
(S::transform_key(&left_key), HashTag::hash_leaf(&[3])),
(S::transform_key(&[128; 32]), HashTag::hash_leaf(&[1])),
(S::transform_key(&right_key).prefix(12), right_hash),
]
);
check_map_proof(&proof, None, &table);
let subtree_hash = table.merkle_root();
table.put(&[129; 32], vec![5]);
let proof = table.get_proof([129; 32]);
assert_eq!(
proof.proof_unchecked(),
vec![(S::transform_key(&[0; 32]).prefix(5), subtree_hash)]
);
check_map_proof(&proof, Some([129; 32]), &table);
let proof = table.get_proof([1; 32]);
assert_eq!(
proof.proof_unchecked(),
vec![
(S::transform_key(&[0; 32]).prefix(5), subtree_hash),
(S::transform_key(&[129; 32]), HashTag::hash_leaf(&[5])),
]
);
check_map_proof(&proof, None, &table);
let proof = table.get_proof([32; 32]);
assert_eq!(
proof.proof_unchecked(),
vec![
(S::transform_key(&left_key).prefix(15), left_hash),
(S::transform_key(&right_key), HashTag::hash_leaf(&[4])),
(S::transform_key(&[129; 32]), HashTag::hash_leaf(&[5])),
]
);
check_map_proof(&proof, Some([32; 32]), &table);
}
fn test_build_multiproof_simple() {
let db = TemporaryDB::default();
let fork = db.fork();
let mut table = fork.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
table.put(&[128; 32], vec![1]);
table.put(&[32; 32], vec![2]);
let keys = vec![[0; 32], [128; 32]];
let proof = table.get_multiproof(keys.clone());
assert_eq!(
proof.proof_unchecked(),
vec![(S::transform_key(&[32; 32]), HashTag::hash_leaf(&[2]))]
);
check_map_multiproof(&proof, keys, &table);
let keys = vec![[64; 32], [0; 32], [128; 32], [129; 32]];
let proof = table.get_multiproof(keys.clone());
assert_eq!(
proof.proof_unchecked(),
vec![(S::transform_key(&[32; 32]), HashTag::hash_leaf(&[2]))]
);
check_map_multiproof(&proof, keys, &table);
let keys = vec![[64; 32], [0; 32], [128; 32], [129; 32], [32; 32]];
let proof = table.get_multiproof(keys.clone());
assert_eq!(proof.proof_unchecked(), vec![]);
check_map_multiproof(&proof, keys, &table);
let keys = vec![[64; 32], [0; 32], [129; 32]];
let proof = table.get_multiproof(keys.clone());
assert_eq!(
proof.proof_unchecked(),
vec![
(S::transform_key(&[128; 32]), HashTag::hash_leaf(&[1])),
(S::transform_key(&[32; 32]), HashTag::hash_leaf(&[2])),
]
);
check_map_multiproof(&proof, keys, &table);
let keys = vec![[64; 32], [129; 32]];
let proof = table.get_multiproof(keys.clone());
assert_eq!(
proof.proof_unchecked(),
vec![
(S::transform_key(&[128; 32]), HashTag::hash_leaf(&[1])),
(S::transform_key(&[32; 32]), HashTag::hash_leaf(&[2])),
]
);
check_map_multiproof(&proof, keys, &table);
let keys = vec![[64; 32], [64; 32]];
let proof = table.get_multiproof(keys);
assert_eq!(
proof.proof_unchecked(),
vec![
(S::transform_key(&[128; 32]), HashTag::hash_leaf(&[1])),
(S::transform_key(&[32; 32]), HashTag::hash_leaf(&[2])),
]
);
check_map_multiproof(&proof, vec![[64; 32]], &table);
let keys = vec![[128; 32], [64; 32], [128; 32]];
let proof = table.get_multiproof(keys);
assert_eq!(
proof.proof_unchecked(),
vec![(S::transform_key(&[32; 32]), HashTag::hash_leaf(&[2]))]
);
check_map_multiproof(&proof, vec![[128; 32], [64; 32]], &table);
let keys = vec![[255; 32], [129; 32]];
let proof = table.get_multiproof(keys.clone());
assert_eq!(
proof.proof_unchecked(),
vec![
(S::transform_key(&[128; 32]), HashTag::hash_leaf(&[1])),
(S::transform_key(&[32; 32]), HashTag::hash_leaf(&[2])),
]
);
check_map_multiproof(&proof, keys, &table);
let left_key = {
let mut key = [0; 32];
key[0] = 128;
table.put(&key, vec![3]);
key
};
let left_hash = {
let mut node = BranchNode::empty();
node.set_child(
ChildKind::Left,
&S::transform_key(&left_key),
&HashTag::hash_leaf(&[3]),
);
node.set_child(
ChildKind::Right,
&S::transform_key(&[128; 32]),
&HashTag::hash_leaf(&[1]),
);
node.object_hash()
};
let proof = table.get_multiproof(vec![[0; 32]]);
assert_eq!(
proof.proof_unchecked(),
vec![
(S::transform_key(&[128; 32]).prefix(15), left_hash),
(S::transform_key(&[32; 32]), HashTag::hash_leaf(&[2])),
]
);
check_map_multiproof(&proof, vec![[0; 32]], &table);
let keys = {
let mut key = [128; 32];
key[1] = 8;
vec![[0; 32], key, [64; 32]]
};
let proof = table.get_multiproof(keys.clone());
assert_eq!(
proof.proof_unchecked(),
vec![
(S::transform_key(&[128; 32]).prefix(15), left_hash),
(S::transform_key(&[32; 32]), HashTag::hash_leaf(&[2])),
]
);
check_map_multiproof(&proof, keys, &table);
let keys = vec![[0; 32], [128; 32], [64; 32]];
let proof = table.get_multiproof(keys.clone());
assert_eq!(
proof.proof_unchecked(),
vec![
(S::transform_key(&left_key), HashTag::hash_leaf(&[3])),
(S::transform_key(&[32; 32]), HashTag::hash_leaf(&[2])),
]
);
check_map_multiproof(&proof, keys, &table);
let keys = vec![[0; 32], [128; 32], [32; 32], [64; 32], [129; 32]];
let proof = table.get_multiproof(keys.clone());
assert_eq!(
proof.proof_unchecked(),
vec![(S::transform_key(&left_key), HashTag::hash_leaf(&[3]))]
);
check_map_multiproof(&proof, keys, &table);
let keys = vec![[0; 32], [32; 32], [255; 32]];
let proof = table.get_multiproof(keys.clone());
assert_eq!(
proof.proof_unchecked(),
vec![(S::transform_key(&[128; 32]).prefix(15), left_hash)]
);
check_map_multiproof(&proof, keys, &table);
let keys = {
let mut key = [0; 32];
key[0] = 128;
key[1] = 128;
vec![[0; 32], [32; 32], key]
};
let proof = table.get_multiproof(keys.clone());
assert_eq!(
proof.proof_unchecked(),
vec![
(S::transform_key(&left_key), HashTag::hash_leaf(&[3])),
(S::transform_key(&[128; 32]), HashTag::hash_leaf(&[1])),
]
);
check_map_multiproof(&proof, keys, &table);
}
fn test_fuzz_delete() {
let db1 = TemporaryDB::default();
let db2 = TemporaryDB::default();
let mut data = generate_random_data(100);
let mut rng = rand::thread_rng();
let fork1 = db1.fork();
let mut index1 = fork1.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
for item in &data {
index1.put(&item.0, item.1.clone());
}
let fork2 = db2.fork();
let mut index2 = fork2.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
data.shuffle(&mut rng);
for item in &data {
index2.put(&item.0, item.1.clone());
}
let saved_hash = index1.object_hash();
let mut keys_to_remove = data
.iter()
.take(50)
.map(|(key, _)| *key)
.collect::<Vec<_>>();
keys_to_remove.shuffle(&mut rng);
for key in &keys_to_remove {
index1.remove(key);
}
keys_to_remove.shuffle(&mut rng);
for key in &keys_to_remove {
index2.remove(key);
}
for key in &keys_to_remove {
assert!(index1.get(key).is_none());
assert!(index2.get(key).is_none());
}
assert!(index2.object_hash() != Hash::zero());
assert_eq!(index2.object_hash(), index1.object_hash());
for item in &data {
index1.put(&item.0, item.1.clone());
}
data.shuffle(&mut rng);
for item in &data {
index2.put(&item.0, item.1.clone());
}
for item in &data {
let v1 = index1.get(&item.0);
let v2 = index2.get(&item.0);
assert_eq!(v1.as_ref(), Some(&item.1));
assert_eq!(v2.as_ref(), Some(&item.1));
}
assert_eq!(index2.object_hash(), index1.object_hash());
assert_eq!(index2.object_hash(), saved_hash);
}
fn test_fuzz_insert_after_delete() {
let db = TemporaryDB::default();
let fork = db.fork();
let mut index = fork.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
let data = generate_random_data(100);
for item in &data[0..50] {
index.put(&item.0, item.1.clone());
}
let saved_hash = index.object_hash();
for item in &data[50..] {
index.put(&item.0, item.1.clone());
}
for item in &data[50..] {
index.remove(&item.0);
}
for item in &data[0..50] {
let v1 = index.get(&item.0);
assert_eq!(v1.as_ref(), Some(&item.1));
}
for item in &data[50..] {
let v1 = index.get(&item.0);
assert_eq!(v1.as_ref(), None);
}
assert_eq!(index.object_hash(), saved_hash);
}
fn test_iter() {
let db = TemporaryDB::default();
let fork = db.fork();
let mut map_index = fork.get_generic_proof_map::<_, _, _, S>(IDX_NAME);
let k0 = [0; 32];
let k1 = [1; 32];
let k2 = [2; 32];
let k3 = [3; 32];
let k4 = [4; 32];
map_index.put(&k1, 1_u8);
map_index.put(&k2, 2_u8);
map_index.put(&k3, 3_u8);
assert_eq!(
map_index.iter().collect::<Vec<_>>(),
vec![(k1, 1), (k2, 2), (k3, 3)]
);
assert_eq!(
map_index.iter_from(&k0).collect::<Vec<_>>(),
vec![(k1, 1), (k2, 2), (k3, 3)]
);
assert_eq!(
map_index.iter_from(&k1).collect::<Vec<_>>(),
vec![(k1, 1), (k2, 2), (k3, 3)]
);
assert_eq!(
map_index.iter_from(&k2).collect::<Vec<_>>(),
vec![(k2, 2), (k3, 3)]
);
assert_eq!(map_index.iter_from(&k4).count(), 0);
assert_eq!(map_index.keys().collect::<Vec<_>>(), vec![k1, k2, k3]);
assert_eq!(
map_index.keys_from(&k0).collect::<Vec<_>>(),
vec![k1, k2, k3]
);
assert_eq!(
map_index.keys_from(&k1).collect::<Vec<_>>(),
vec![k1, k2, k3]
);
assert_eq!(map_index.keys_from(&k2).collect::<Vec<_>>(), vec![k2, k3]);
assert_eq!(
map_index.keys_from(&k4).collect::<Vec<_>>(),
Vec::<[u8; 32]>::new()
);
assert_eq!(map_index.values().collect::<Vec<_>>(), vec![1, 2, 3]);
assert_eq!(
map_index.values_from(&k0).collect::<Vec<_>>(),
vec![1, 2, 3]
);
assert_eq!(
map_index.values_from(&k1).collect::<Vec<u8>>(),
vec![1, 2, 3]
);
assert_eq!(map_index.values_from(&k2).collect::<Vec<_>>(), vec![2, 3]);
assert_eq!(map_index.values_from(&k4).count(), 0);
}
}
#[test]
fn test_map_methods_raw() {
ProofMapTester::<Raw>::test_map_methods()
}
#[test]
fn test_map_methods_hashed() {
ProofMapTester::<Hashed>::test_map_methods()
}
#[test]
fn test_insert_trivial_raw() {
ProofMapTester::<Raw>::test_insert_trivial()
}
#[test]
fn test_insert_trivial_hashed() {
ProofMapTester::<Hashed>::test_insert_trivial()
}
#[test]
fn test_insert_same_key_raw() {
ProofMapTester::<Raw>::test_insert_same_key()
}
#[test]
fn test_insert_simple_raw() {
ProofMapTester::<Raw>::test_insert_simple()
}
#[test]
fn test_insert_simple_hashed() {
ProofMapTester::<Hashed>::test_insert_simple()
}
#[test]
fn test_insert_reverse_raw() {
ProofMapTester::<Raw>::test_insert_reverse()
}
#[test]
fn test_insert_reverse_hashed() {
ProofMapTester::<Hashed>::test_insert_reverse()
}
#[test]
fn test_remove_trivial_raw() {
ProofMapTester::<Raw>::test_remove_trivial()
}
#[test]
fn test_remove_trivial_hashed() {
ProofMapTester::<Hashed>::test_remove_trivial()
}
#[test]
fn test_remove_simple_raw() {
ProofMapTester::<Raw>::test_remove_simple()
}
#[test]
fn test_remove_simple_hashed() {
ProofMapTester::<Hashed>::test_remove_simple()
}
#[test]
fn test_remove_reverse_raw() {
ProofMapTester::<Raw>::test_remove_reverse()
}
#[test]
fn test_remove_reverse_hashed() {
ProofMapTester::<Hashed>::test_remove_reverse()
}
#[test]
fn test_clear_raw() {
ProofMapTester::<Raw>::test_clear()
}
#[test]
fn test_clear_hashed() {
ProofMapTester::<Hashed>::test_clear()
}
#[test]
fn test_fuzz_insert_raw() {
ProofMapTester::<Raw>::test_fuzz_insert()
}
#[test]
fn test_fuzz_insert_hashed() {
ProofMapTester::<Hashed>::test_fuzz_insert()
}
#[test]
fn test_build_proof_in_empty_tree_raw() {
ProofMapTester::<Raw>::test_build_proof_in_empty_tree()
}
#[test]
fn test_build_proof_in_empty_tree_hashed() {
ProofMapTester::<Hashed>::test_build_proof_in_empty_tree()
}
#[test]
fn test_build_multiproof_in_empty_tree_raw() {
ProofMapTester::<Raw>::test_build_multiproof_in_empty_tree()
}
#[test]
fn test_build_multiproof_in_empty_tree_hashed() {
ProofMapTester::<Hashed>::test_build_multiproof_in_empty_tree()
}
#[test]
fn test_build_multiproof_in_single_node_tree_raw() {
ProofMapTester::<Raw>::test_build_multiproof_in_single_node_tree()
}
#[test]
fn test_build_multiproof_in_single_node_tree_hashed() {
ProofMapTester::<Hashed>::test_build_multiproof_in_single_node_tree()
}
#[test]
fn test_build_proof_in_complex_tree_raw() {
ProofMapTester::<Raw>::test_build_proof_in_complex_tree()
}
#[test]
fn test_build_multiproof_simple_raw() {
ProofMapTester::<Raw>::test_build_multiproof_simple()
}
#[test]
fn test_fuzz_delete_raw() {
ProofMapTester::<Raw>::test_fuzz_delete()
}
#[test]
fn test_fuzz_delete_hashed() {
ProofMapTester::<Hashed>::test_fuzz_delete()
}
#[test]
fn test_fuzz_insert_after_delete_raw() {
ProofMapTester::<Raw>::test_fuzz_insert_after_delete()
}
#[test]
fn test_fuzz_insert_after_delete_hashed() {
ProofMapTester::<Hashed>::test_fuzz_insert_after_delete()
}
#[test]
fn test_iter_raw() {
ProofMapTester::<Raw>::test_iter()
}
#[test]
fn test_iter_hashed() {
ProofMapTester::<Hashed>::test_iter()
}
#[test]
fn index_as_iterator() {
let db = TemporaryDB::default();
let fork = db.fork();
let mut map = fork.get_proof_map(IDX_NAME);
map.put(&1_u8, 1_u8);
map.put(&2_u8, 2_u8);
map.put(&3_u8, 3_u8);
for (key, value) in &map {
assert_eq!(key, value);
}
assert_eq!((&map).into_iter().count(), 3);
assert_eq!(map.keys().collect::<Vec<_>>(), vec![1, 2, 3]);
assert_eq!(map.iter().collect::<Vec<_>>(), vec![(1, 1), (2, 2), (3, 3)]);
let mut map = fork.get_proof_map((IDX_NAME, &0_u8));
map.put("1", 1_u8);
map.put("2", 2_u8);
map.put("3", 3_u8);
for (key, value) in &map {
assert_eq!(key, value.to_string());
}
assert_eq!((&map).into_iter().count(), 3);
assert_eq!(map.keys().collect::<Vec<_>>(), vec!["1", "2", "3"]);
assert_eq!(
map.iter().collect::<Vec<_>>(),
vec![
("1".to_owned(), 1),
("2".to_owned(), 2),
("3".to_owned(), 3)
]
);
let mut map = fork.get_raw_proof_map((IDX_NAME, &1_u8));
map.put(&[1; 32], 1_u8);
map.put(&[2; 32], 2_u8);
map.put(&[3; 32], 3_u8);
for (key, value) in &map {
assert_eq!(key[0], value);
}
assert_eq!((&map).into_iter().count(), 3);
assert_eq!(
map.keys().collect::<Vec<_>>(),
vec![[1; 32], [2; 32], [3; 32]]
);
assert_eq!(
map.iter().collect::<Vec<_>>(),
vec![([1; 32], 1), ([2; 32], 2), ([3; 32], 3)]
);
}
#[test]
fn test_build_proof_in_single_node_tree_raw() {
ProofMapTester::<Raw>::test_build_proof_in_single_node_tree()
}
#[test]
fn test_build_proof_in_single_node_tree_hashed() {
ProofMapTester::<Hashed>::test_build_proof_in_single_node_tree()
}
#[test]
fn test_merkle_root_leaf() {
let db = TemporaryDB::default();
let fork = db.fork();
let mut index = fork.get_proof_map(IDX_NAME);
let key = vec![1, 2, 3];
let value = vec![4, 5, 6];
index.put(&key, value.clone());
let path = Hashed::transform_key(&key);
let mut path_buffer = [0; HASH_SIZE + 2];
path.write_compressed(&mut path_buffer);
let merkle_root = HashStream::new()
.update(&[HashTag::MapBranchNode as u8])
.update(&path_buffer[..])
.update(HashTag::hash_leaf(&value).as_ref())
.hash();
assert_eq!(HashTag::hash_map_node(merkle_root), index.object_hash());
}
fn check_map_proof<K, V, S>(
proof: &MapProof<K::Owned, V, S>,
key: Option<K>,
table: &ProofMapIndex<&Fork, K, V, S>,
) where
K: BinaryKey,
K::Owned: Serialize + DeserializeOwned + PartialEq + Debug + Clone,
V: BinaryValue + ObjectHash + PartialEq + Debug + Serialize + DeserializeOwned,
S: ToProofPath<K> + ToProofPath<K::Owned>,
{
let serialized_proof = serde_json::to_value(&proof).unwrap();
let deserialized_proof: MapProof<K::Owned, V, S> =
serde_json::from_value(serialized_proof).unwrap();
let entries = match key {
Some(key) => {
let value = table.get(&key).unwrap();
vec![(key, value)]
}
None => vec![],
};
let proof = proof.check().unwrap();
assert_eq!(
proof
.entries()
.map(|(k, v)| (k.to_owned(), v))
.collect::<Vec<_>>(),
entries
.iter()
.map(|(k, v)| (k.to_owned(), v))
.collect::<Vec<_>>()
);
assert_eq!(proof.index_hash(), table.object_hash());
let deserialized_proof = deserialized_proof.check().unwrap();
assert_eq!(
deserialized_proof.entries().collect::<Vec<_>>(),
proof.entries().collect::<Vec<_>>()
);
assert_eq!(deserialized_proof.index_hash(), proof.index_hash());
}
fn check_map_multiproof<K, V, S>(
proof: &MapProof<K, V, S>,
keys: Vec<K>,
table: &ProofMapIndex<&Fork, K, V, S>,
) where
K: BinaryKey + ObjectHash + PartialEq + Debug,
V: BinaryValue + ObjectHash + PartialEq + Debug,
S: ToProofPath<K>,
{
let (entries, missing_keys) = {
let mut entries: Vec<(K, V)> = Vec::new();
let mut missing_keys: Vec<K> = Vec::new();
for key in keys {
if table.contains(&key) {
let value = table.get(&key).unwrap();
entries.push((key, value));
} else {
missing_keys.push(key);
}
}
entries.sort_unstable_by(|&(ref x, _), &(ref y, _)| {
S::transform_key(x)
.partial_cmp(&S::transform_key(y))
.unwrap()
});
missing_keys.sort_unstable_by(|x, y| {
S::transform_key(x)
.partial_cmp(&S::transform_key(y))
.unwrap()
});
(entries, missing_keys)
};
let proof = proof.check().unwrap();
assert_eq!(proof.index_hash(), table.object_hash());
assert_eq!(missing_keys.iter().collect::<Vec<&_>>(), {
let mut actual_keys = proof.missing_keys().collect::<Vec<_>>();
actual_keys.sort_unstable_by(|&x, &y| {
S::transform_key(x)
.partial_cmp(&S::transform_key(y))
.unwrap()
});
actual_keys
});
assert_eq!(
entries
.iter()
.map(|&(ref k, ref v)| (k, v))
.collect::<Vec<_>>(),
{
let mut actual_entries = proof.entries().collect::<Vec<_>>();
actual_entries.sort_unstable_by(|&(x, _), &(y, _)| {
S::transform_key(x)
.partial_cmp(&S::transform_key(y))
.unwrap()
});
actual_entries
}
);
}
const MAX_CHECKED_ELEMENTS: usize = 1_024;
fn check_proofs_for_data<K, V>(db: &dyn Database, data: &[(K, V)], nonexisting_keys: &[K])
where
K: BinaryKey<Owned = K> + ObjectHash + Clone + PartialEq + Debug + Serialize + DeserializeOwned,
V: BinaryValue + ObjectHash + Clone + PartialEq + Debug + Serialize + DeserializeOwned,
{
let fork = db.fork();
let mut table = fork.get_proof_map(IDX_NAME);
for (key, value) in data {
table.put(key, value.clone());
}
let batch_size = data.len();
let indexes = if batch_size < MAX_CHECKED_ELEMENTS {
(0..batch_size).collect()
} else {
let mut rng = thread_rng();
(0..batch_size).choose_multiple(&mut rng, MAX_CHECKED_ELEMENTS)
};
for i in indexes {
let key = data[i].0.clone();
let proof = table.get_proof(key.to_owned());
check_map_proof(&proof, Some(key), &table);
}
for key in nonexisting_keys {
if !table.contains(key) {
let proof = table.get_proof(key.to_owned());
check_map_proof(&proof, None, &table);
}
}
}
fn check_multiproofs_for_data<K, V>(db: &dyn Database, data: &[(K, V)], nonexisting_keys: &[K])
where
K: BinaryKey<Owned = K> + ObjectHash + Clone + Ord + PartialEq + StdHash + Debug + Serialize,
V: BinaryValue + ObjectHash + Clone + PartialEq + Debug + Serialize,
{
let fork = db.fork();
let mut table = fork.get_proof_map(IDX_NAME);
for &(ref key, ref value) in data {
table.put(key, value.clone());
}
let mut rng = thread_rng();
for proof_size in (0..11).map(|x| x * 10 + 1) {
let keys = data
.iter()
.map(|(k, _)| k.clone())
.choose_multiple(&mut rng, proof_size);
let proof = table.get_multiproof(keys.clone());
check_map_multiproof(&proof, keys, &table);
let mut keys = data
.iter()
.map(|(k, _)| k.clone())
.choose_multiple(&mut rng, proof_size);
let non_keys = nonexisting_keys
.iter()
.cloned()
.choose_multiple(&mut rng, proof_size);
keys.extend(non_keys);
let proof = table.get_multiproof(keys.clone());
check_map_multiproof(&proof, keys, &table);
}
}
#[test]
fn test_invalid_map_proofs() {
use self::MapProofError::*;
let h = hash(&[1]);
let proof: MapProof<[u8; 32], Vec<u8>, Raw> =
MapProof::new().add_proof_entry(Raw::transform_key(&[1; 32]).prefix(240), h);
match proof.check().unwrap_err() {
NonTerminalNode(..) => {}
e => panic!("expected non-terminal node error, got {}", e),
}
let json = json!({
"proof": [
{ "path": "11", "hash": Hash::default() },
{ "path": "0", "hash": Hash::default() },
],
"entries": []
});
let proof: MapProof<[u8; 32], Vec<u8>, Raw> = serde_json::from_value(json).unwrap();
match proof.check().unwrap_err() {
InvalidOrdering(..) => {}
e => panic!("expected invalid ordering error, got {}", e),
}
let proof: MapProof<[u8; 32], Vec<u8>, Raw> = MapProof::new()
.add_proof_entry(Raw::transform_key(&[1; 32]).prefix(3), h)
.add_proof_entry(Raw::transform_key(&[1; 32]).prefix(77), h);
match proof.check().unwrap_err() {
EmbeddedPaths { .. } => {}
e => panic!("expected embedded paths error, got {}", e),
}
let proof: MapProof<[u8; 32], Vec<u8>, Raw> = MapProof::new()
.add_proof_entry(Raw::transform_key(&[1; 32]).prefix(3), h)
.add_entry([1; 32], vec![1, 2, 3]);
match proof.check().unwrap_err() {
EmbeddedPaths { .. } => {}
e => panic!("expected embedded paths error, got {}", e),
}
let proof: MapProof<[u8; 32], Vec<u8>, Raw> = MapProof::new()
.add_proof_entry(Raw::transform_key(&[1; 32]).prefix(3), h)
.add_entry([1; 32], vec![1, 2, 3]);
match proof.check().unwrap_err() {
EmbeddedPaths { .. } => {}
e => panic!("expected embedded paths error, got {}", e),
}
let proof: MapProof<[u8; 32], Vec<u8>, Raw> = MapProof::new()
.add_proof_entry(Raw::transform_key(&[0; 32]).prefix(10), h)
.add_proof_entry(Raw::transform_key(&[1; 32]), h)
.add_entry([1; 32], vec![1, 2, 3]);
match proof.check().unwrap_err() {
DuplicatePath(..) => {}
e => panic!("expected duplicate path error, got {}", e),
}
let proof: MapProof<[u8; 32], Vec<u8>, Raw> = MapProof::new()
.add_proof_entry(Raw::transform_key(&[0; 32]).prefix(10), h)
.add_entry([1; 32], vec![1, 2, 3])
.add_entry([1; 32], vec![1, 2, 3]);
match proof.check().unwrap_err() {
DuplicatePath(..) => {}
e => panic!("expected duplicate path error, got {}", e),
}
}
#[test]
fn test_fuzz_insert_build_proofs_in_table_filled_with_hashes() {
let db = TemporaryDB::default();
let mut rng = thread_rng();
let batch_sizes = (7..9).map(|x| 1 << x);
for batch_size in batch_sizes {
let mut exists_keys = HashSet::default();
let data: Vec<(Hash, Hash)> =
generate_random_data_keys(&mut exists_keys, batch_size, &mut rng)
.into_iter()
.map(|(key, val)| (hash(&key), hash(&val)))
.collect();
let nonexisting_count = cmp::min(MAX_CHECKED_ELEMENTS, batch_size);
let nonexisting_keys: Vec<_> =
generate_random_data_keys(&mut exists_keys, nonexisting_count / 2, &mut rng)
.into_iter()
.flat_map(|(key, val)| vec![hash(&key), hash(&val)])
.collect();
check_proofs_for_data(&db, &data, &nonexisting_keys);
}
}
#[test]
fn test_fuzz_insert_build_proofs() {
let db = TemporaryDB::default();
let mut rng = thread_rng();
let batch_sizes = (7..9).map(|x| (1 << x) - 1);
for batch_size in batch_sizes {
let mut exists_keys = HashSet::default();
let data = generate_random_data_keys(&mut exists_keys, batch_size, &mut rng);
let nonexisting_count = cmp::min(MAX_CHECKED_ELEMENTS, batch_size);
let nonexisting_keys: Vec<_> =
generate_random_data_keys(&mut exists_keys, nonexisting_count, &mut rng)
.into_iter()
.map(|(key, _)| key)
.collect();
check_proofs_for_data(&db, &data, &nonexisting_keys);
}
}
#[test]
fn test_fuzz_insert_build_multiproofs() {
let db = TemporaryDB::default();
let mut rng = thread_rng();
let batch_sizes = (7..9).map(|x| 1 << x);
for batch_size in batch_sizes {
let mut exists_keys = HashSet::default();
let data = generate_random_data_keys(&mut exists_keys, batch_size, &mut rng);
let nonexisting_count = cmp::min(MAX_CHECKED_ELEMENTS, batch_size);
let nonexisting_keys: Vec<_> =
generate_random_data_keys(&mut exists_keys, nonexisting_count, &mut rng)
.into_iter()
.map(|(key, _)| key)
.collect();
check_multiproofs_for_data(&db, &data, &nonexisting_keys);
}
}
#[test]
fn test_fuzz_delete_build_proofs() {
const SAMPLE_SIZE: usize = 200;
let db = TemporaryDB::default();
let mut rng = thread_rng();
let mut exists_keys = HashSet::default();
let data = generate_random_data_keys(&mut exists_keys, SAMPLE_SIZE, &mut rng);
let fork = db.fork();
let mut table = fork.get_proof_map(IDX_NAME);
for item in &data {
table.put(&item.0, item.1.clone());
}
let (keys_to_remove, keys_to_remove_seq) = {
let mut keys = data
.iter()
.map(|item| item.0.clone())
.choose_multiple(&mut rng, SAMPLE_SIZE / 5);
keys.shuffle(&mut rng);
let seq_keys = keys.split_off(SAMPLE_SIZE / 10);
(keys, seq_keys)
};
for key in &keys_to_remove {
table.remove(key);
}
for key in keys_to_remove {
let proof = table.get_proof(key);
check_map_proof(&proof, None, &table);
}
for key in keys_to_remove_seq {
let proof = table.get_proof(key.clone());
check_map_proof(&proof, Some(key.clone()), &table);
table.remove(&key);
let proof = table.get_proof(key);
check_map_proof(&proof, None, &table);
}
}
#[test]
fn restore_after_no_op_initialization() {
let db = TemporaryDB::new();
let fork = db.fork();
fork.get_proof_map::<_, Hash, u32>(IDX_NAME);
let map = fork.readonly().get_proof_map::<_, Hash, u32>(IDX_NAME);
assert_eq!(map.iter().count(), 0);
}
#[test]
fn test_tree_with_hashed_key() {
use anyhow::ensure;
use byteorder::{ByteOrder, LittleEndian};
use exonum_crypto::Hash;
use std::{borrow::Cow, iter::FromIterator};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
struct Point {
x: u16,
y: u16,
}
impl Point {
fn new(x: u16, y: u16) -> Self {
Self { x, y }
}
}
impl BinaryKey for Point {
fn size(&self) -> usize {
4
}
fn write(&self, buffer: &mut [u8]) -> usize {
LittleEndian::write_u16(&mut buffer[0..2], self.x);
LittleEndian::write_u16(&mut buffer[2..4], self.y);
self.size()
}
fn read(buffer: &[u8]) -> Self {
let x = LittleEndian::read_u16(&buffer[0..2]);
let y = LittleEndian::read_u16(&buffer[2..4]);
Self { x, y }
}
}
impl BinaryValue for Point {
fn to_bytes(&self) -> Vec<u8> {
let mut buf = vec![0_u8; self.size()];
self.write(&mut buf);
buf
}
fn from_bytes(bytes: Cow<'_, [u8]>) -> anyhow::Result<Self> {
let bytes = bytes.as_ref();
ensure!(
bytes.len() == 4,
"Unable to decode Point: wrong buffer size"
);
Ok(Self::read(bytes))
}
}
impl ObjectHash for Point {
fn object_hash(&self) -> Hash {
let mut buffer = [0; 4];
self.write(&mut buffer);
exonum_crypto::hash(&buffer)
}
}
fn hash_isolated_node(key: &ProofPath, h: &Hash) -> Hash {
HashTag::hash_map_node(HashTag::hash_single_entry_map(key, h))
}
let db = TemporaryDB::default();
let fork = db.fork();
let mut table = fork.get_proof_map(IDX_NAME);
table.put(&Point::new(1, 2), vec![1, 2, 3]);
table.put(&Point::new(3, 4), vec![2, 3, 4]);
assert_eq!(table.get(&Point::new(1, 2)), Some(vec![1, 2, 3]));
assert_eq!(table.get(&Point::new(2, 2)), None);
let keys: HashSet<_> = table.keys().collect();
assert_eq!(
keys,
HashSet::from_iter(vec![Point::new(3, 4), Point::new(1, 2)])
);
let kvs: HashSet<_> = table.iter().collect();
assert_eq!(
kvs,
HashSet::from_iter(vec![
(Point::new(3, 4), vec![2, 3, 4]),
(Point::new(1, 2), vec![1, 2, 3]),
])
);
let proof = table.get_proof(Point::new(1, 2));
assert_eq!(
proof.proof_unchecked(),
vec![(
Hashed::transform_key(&Point::new(3, 4)),
HashTag::hash_leaf(&[2, 3, 4])
)]
);
let proof = proof.check().unwrap();
assert_eq!(
proof.all_entries().collect::<Vec<_>>(),
vec![(&Point::new(1, 2), Some(&vec![1, 2, 3]))]
);
assert_eq!(proof.index_hash(), table.object_hash());
let key = Point::new(3, 4);
let other_key = Point::new(1, 2);
table.remove(&key);
let keys: Vec<_> = table.keys().collect();
assert_eq!(keys, vec![other_key]);
assert_eq!(table.get(&key), None);
assert_eq!(table.get(&other_key), Some(vec![1, 2, 3]));
assert_eq!(
table.object_hash(),
hash_isolated_node(
&Hashed::transform_key(&other_key),
&HashTag::hash_leaf(&[1, 2, 3])
)
);
}
#[test]
fn unsized_key() {
let db = TemporaryDB::default();
let fork = db.fork();
let mut table = fork.get_proof_map::<_, str, _>(IDX_NAME);
table.put("key1", vec![1]);
let proof = table.get_proof("key1".to_owned());
assert_eq!(proof.proof_unchecked(), vec![]);
assert!(proof.check().is_ok());
let proof = table.get_multiproof(vec!["key2".to_owned(), "key3".to_owned()]);
assert_eq!(
proof.proof_unchecked(),
vec![(Hashed::transform_key("key1"), HashTag::hash_leaf(&[1]))]
);
assert!(proof.check().is_ok());
}