diff --git a/src/lib.rs b/src/lib.rs
index f25c23b..b13ce04 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,6 +19,7 @@ extern crate rlp_derive;
mod nibbleslice;
pub mod node;
+pub mod proof;
mod skewed;
#[allow(dead_code)]
pub mod snapshot;
diff --git a/src/proof.rs b/src/proof.rs
new file mode 100644
index 0000000..86aab5f
--- /dev/null
+++ b/src/proof.rs
@@ -0,0 +1,277 @@
+// Copyright 2020 Kodebox, Inc.
+// This file is part of CodeChain.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+use crate::nibbleslice::NibbleSlice;
+use crate::node::Node;
+use ccrypto::{blake256, BLAKE_NULL_RLP};
+use primitives::Bytes;
+use primitives::H256;
+
+// Unit of a proof.
+//#[derive(Clone, Eq, PartialEq, Debug, RlpEncodable, RlpDecodable)]
+#[derive(Clone, Eq, PartialEq, Debug)]
+pub struct CryptoProofUnit {
+ pub root: H256,
+ pub key: H256,
+ pub value: Option, // None in case of absence
+}
+
+#[derive(Clone, Eq, PartialEq, Debug)]
+pub struct CryptoProof(pub Vec);
+
+pub trait CryptoStructure {
+ fn make_proof(&self, key: &H256) -> crate::Result<(CryptoProofUnit, CryptoProof)>;
+}
+
+/// A verification logic of TrieDB's Merkle proof.
+/// For the format of proof, check the make_proof() function.
+/// It verifies the proof with a given unit of test.
+/// It should never abort or fail, but only return 'false' as a result of getting an invalid or ill-formed proof.
+pub fn verify(proof: &CryptoProof, test: &CryptoProofUnit) -> bool {
+ // step1: verify the value
+ fn step1(proof: &CryptoProof, test: &CryptoProofUnit) -> bool {
+ match Node::decoded(&proof.0.last().unwrap()) {
+ Some(x) => match x {
+ Node::Leaf(_, value) => test.value.as_ref().unwrap() == &value,
+ _ => false,
+ },
+ _ => false,
+ }
+ };
+
+ // step2: verify the root
+ fn step2(proof: &CryptoProof, test: &CryptoProofUnit) -> bool {
+ blake256(&proof.0[0]) == test.root
+ };
+
+ // step3 (presence): verify the key
+ fn step3_p(proof: &CryptoProof, test: &CryptoProofUnit) -> bool {
+ fn verify_branch(path: &NibbleSlice<'_>, hash: &H256, proof: &[Bytes]) -> bool {
+ if *hash != blake256(&proof[0]) {
+ return false
+ }
+ match Node::decoded(&proof[0]) {
+ Some(Node::Leaf(partial, _)) => path == &partial,
+ Some(Node::Branch(partial, table)) => {
+ if proof.len() < 2 {
+ // detect ill-formed proof
+ return false
+ }
+ if !path.starts_with(&partial) {
+ return false
+ }
+ match table[path.at(partial.len()) as usize] {
+ Some(x) => verify_branch(&path.mid(partial.len() + 1), &x, &proof[1..]),
+ None => false,
+ }
+ }
+ None => false,
+ }
+ };
+ verify_branch(&NibbleSlice::new(&test.key), &test.root, &proof.0)
+ };
+
+ // step3 (absence): verify the key.
+ fn step3_a(proof: &CryptoProof, test: &CryptoProofUnit) -> bool {
+ fn verify_branch(path: &NibbleSlice<'_>, hash: &H256, proof: &[Bytes]) -> bool {
+ if *hash != blake256(&proof[0]) {
+ return false
+ }
+ match Node::decoded(&proof[0]) {
+ Some(Node::Leaf(partial, _)) => path != &partial, // special case : there is only one leaf node in the trie,
+ Some(Node::Branch(partial, children)) => {
+ if !path.starts_with(&partial) {
+ return false
+ }
+ match children[path.at(partial.len()) as usize] {
+ Some(x) => proof.len() >= 2 && verify_branch(&path.mid(partial.len() + 1), &x, &proof[1..]),
+ None => proof.len() == 1,
+ }
+ }
+ None => false,
+ }
+ };
+ verify_branch(&NibbleSlice::new(&test.key), &test.root, &proof.0)
+ };
+
+ if proof.0.is_empty() {
+ return test.root == BLAKE_NULL_RLP && test.value.is_none() // special case of an empty trie.
+ }
+ if test.value.is_some() {
+ step1(proof, test) && step2(proof, test) && step3_p(proof, test)
+ } else {
+ step2(proof, test) && step3_a(proof, test)
+ }
+}
+
+
+#[cfg(test)]
+mod tests {
+ extern crate rand;
+
+ use super::*;
+ use crate::*;
+ use cdb::MemoryDB;
+ use rand::{rngs::StdRng, Rng};
+
+ fn simple_test<'db>(t: &TrieDB<'db>, key: &H256, value: Option<&[u8]>, key_proof: &H256, result: bool) {
+ let unit = CryptoProofUnit {
+ root: *t.root(),
+ key: *key,
+ value: value.map(|x| x.to_vec()),
+ };
+ let proof = t.make_proof(key_proof).unwrap();
+ assert_eq!(verify(&proof.1, &unit), result);
+ }
+
+ #[test]
+ fn empty_trie() {
+ let iteration = 100;
+ let seed = [0 as u8; 32];
+ let mut rng: StdRng = rand::SeedableRng::from_seed(seed);
+
+ for _ in 0..iteration {
+ let mut memdb = MemoryDB::new();
+ let mut root = H256::zero();
+ TrieDBMut::new(&mut memdb, &mut root);
+
+ // unused pair
+ let k1 = format!("{}", rng.gen::());
+ let v1 = format!("{}", rng.gen::());
+ let (keyu, valu) = { (blake256(&k1), v1.as_bytes()) };
+
+ let t = TrieDB::try_new(&memdb, &root).unwrap();
+
+ simple_test(&t, &keyu, Some(valu), &keyu, false);
+ simple_test(&t, &keyu, None, &keyu, true);
+ }
+ }
+
+ #[test]
+ fn single_trie() {
+ let iteration = 100;
+ let seed = [0 as u8; 32];
+ let mut rng: StdRng = rand::SeedableRng::from_seed(seed);
+
+ for _ in 0..iteration {
+ let mut memdb = MemoryDB::new();
+ let mut root = H256::zero();
+ let mut mt = TrieDBMut::new(&mut memdb, &mut root);
+
+ // unused pair
+ let ku = format!("{}", rng.gen::());
+ let vu = format!("{}", rng.gen::());
+ let (keyu, valu) = { (blake256(&ku), vu.as_bytes()) };
+
+ let k1 = format!("{}", rng.gen::());
+ let v1 = format!("{}", rng.gen::());
+ let (key1, val1) = { (blake256(&k1), v1.as_bytes()) };
+ mt.insert(&k1.as_bytes(), val1).unwrap();
+
+ if key1 == keyu || val1 == valu {
+ continue
+ }
+
+ let t = TrieDB::try_new(&memdb, &root).unwrap();
+
+ // Be careful: there are some case where the proof is not unique.
+ simple_test(&t, &key1, Some(val1), &key1, true);
+ simple_test(&t, &key1, Some(val1), &keyu, true); //be careful!
+ simple_test(&t, &key1, Some(valu), &key1, false);
+ simple_test(&t, &key1, Some(valu), &keyu, false);
+ simple_test(&t, &key1, None, &key1, false);
+ simple_test(&t, &key1, None, &keyu, false);
+ simple_test(&t, &keyu, Some(val1), &key1, false);
+ simple_test(&t, &keyu, Some(val1), &keyu, false);
+ simple_test(&t, &keyu, Some(valu), &key1, false);
+ simple_test(&t, &keyu, Some(valu), &keyu, false);
+ simple_test(&t, &keyu, None, &key1, true); //be careful!
+ simple_test(&t, &keyu, None, &keyu, true);
+ }
+ }
+
+ #[test]
+ fn some_trie() {
+ let iteration = 100;
+ let size = 234;
+ let seed = [0 as u8; 32];
+ let mut rng: StdRng = rand::SeedableRng::from_seed(seed);
+
+ for _ in 0..iteration {
+ let mut memdb = MemoryDB::new();
+ let mut root = H256::zero();
+ let mut mt = TrieDBMut::new(&mut memdb, &mut root);
+
+ // unused pair
+ let ku = format!("{}", rng.gen::());
+ let vu = format!("{}", rng.gen::());
+ let (keyu, valu) = { (blake256(&ku), vu.as_bytes()) };
+
+ let k1 = format!("{}", rng.gen::());
+ let v1 = format!("{}", rng.gen::());
+ let (key1, val1) = { (blake256(&k1), v1.as_bytes()) };
+ mt.insert(&k1.as_bytes(), val1).unwrap();
+
+ let k2 = format!("{}", rng.gen::());
+ let v2 = format!("{}", rng.gen::());
+ let (key2, val2) = { (blake256(&k2), v2.as_bytes()) };
+ mt.insert(&k2.as_bytes(), val2).unwrap();
+
+ if key1 == keyu || val1 == valu || key2 == keyu || val2 == valu {
+ continue
+ }
+
+ let mut flag = true;
+ for _ in 0..size {
+ let k = format!("{}", rng.gen::());
+ let v = format!("{}", rng.gen::());
+ mt.insert(k.as_bytes(), v.as_bytes()).unwrap();
+ if blake256(k) == keyu || v.as_bytes() == valu {
+ flag = false;
+ break
+ }
+ }
+ if !flag {
+ continue // skip this iteration
+ }
+
+ let t = TrieDB::try_new(&memdb, &root).unwrap();
+
+ simple_test(&t, &key1, Some(val1), &key1, true);
+ simple_test(&t, &key1, Some(val1), &key2, false);
+ simple_test(&t, &key1, Some(val1), &keyu, false);
+ simple_test(&t, &key1, Some(val2), &key1, false);
+ simple_test(&t, &key1, Some(val2), &key2, false);
+ simple_test(&t, &key1, Some(val2), &keyu, false);
+ simple_test(&t, &key1, None, &key1, false);
+ simple_test(&t, &key1, None, &key2, false);
+ simple_test(&t, &key1, None, &keyu, false);
+
+ simple_test(&t, &keyu, Some(val1), &key1, false);
+ simple_test(&t, &keyu, Some(val1), &key2, false);
+ simple_test(&t, &keyu, Some(val1), &keyu, false);
+ simple_test(&t, &keyu, None, &key1, false);
+ simple_test(&t, &keyu, None, &key2, false);
+ simple_test(&t, &keyu, None, &keyu, true);
+ }
+ }
+
+ // proof is created manually here
+ #[test]
+ fn some_malicious() {
+ // TODO
+ }
+}
diff --git a/src/triedb.rs b/src/triedb.rs
index 0591f40..ea7e419 100644
--- a/src/triedb.rs
+++ b/src/triedb.rs
@@ -16,10 +16,12 @@
use crate::nibbleslice::NibbleSlice;
use crate::node::Node as RlpNode;
-use crate::{Trie, TrieError};
+use crate::proof::{CryptoProof, CryptoProofUnit, CryptoStructure};
+use crate::{Node, Trie, TrieError};
use ccrypto::{blake256, BLAKE_NULL_RLP};
use cdb::HashDB;
use lru_cache::LruCache;
+use primitives::Bytes;
use primitives::H256;
use std::cell::RefCell;
@@ -98,11 +100,7 @@ impl<'db> TrieDB<'db> {
}
Some(RlpNode::Branch(partial, children)) => {
if path.starts_with(&partial) {
- self.get_aux(
- &path.mid(partial.len() + 1),
- children[path.mid(partial.len()).at(0) as usize],
- query,
- )
+ self.get_aux(&path.mid(partial.len() + 1), children[path.at(partial.len()) as usize], query)
} else {
Ok(None)
}
@@ -147,6 +145,67 @@ impl<'db> Trie for TrieDB<'db> {
}
}
+impl<'db> CryptoStructure for TrieDB<'db> {
+ /// A proof creation logic for TrieDB.
+ /// A proof is basically a list of serialized trie nodes, Vec.
+ /// It starts from the one closest to the root and to the leaf. (It may not reach the leaf in absence case.)
+ /// Each node can be decoded with RLP. (Note that RLP doesn't guarantee format detail, so you must check our serialization code.)
+ /// In case of precense, the list will contain a path from the root to the leaf with the key.
+ /// In case of absence, the list will contain a path to the last node that matches the key.
+ //
+ // (A: [nil])
+ // / \
+ // (B, g) \
+ // / \ \
+ // (C, iant) (D, mail) (E, clang)
+ //
+ // Here, the proof of key 'gmail' will be [(RLP encoding of A), (RLP encoding of B), (RLP encoding of D)]
+ // Here, the proof of key 'galbi' (absence) will be [(RLP encoding of A), (RLP encoding of B)]
+ fn make_proof(&self, key: &H256) -> crate::Result<(CryptoProofUnit, CryptoProof)> {
+ // it creates a reversed proof for the sake of a more efficient push() operation. (than concat)
+ fn make_proof_upto(
+ db: &dyn HashDB,
+ path: &NibbleSlice<'_>,
+ hash: &H256,
+ ) -> crate::Result<(Option, Vec)> {
+ let node_rlp = db.get(&hash).ok_or_else(|| TrieError::IncompleteDatabase(*hash))?;
+
+ match Node::decoded(&node_rlp) {
+ Some(Node::Leaf(partial, value)) => {
+ if &partial == path {
+ Ok((Some(value.to_vec()), vec![node_rlp]))
+ } else {
+ Ok((None, vec![node_rlp]))
+ }
+ }
+ Some(Node::Branch(partial, children)) => {
+ if path.starts_with(&partial) {
+ match children[path.at(partial.len()) as usize] {
+ Some(x) => {
+ let (value, mut reversed_proof) =
+ make_proof_upto(db, &path.mid(partial.len() + 1), &x)?;
+ reversed_proof.push(node_rlp);
+ Ok((value, reversed_proof))
+ }
+ None => Ok((None, vec![node_rlp])),
+ }
+ } else {
+ Ok((None, Vec::new()))
+ }
+ }
+ None => Ok((None, Vec::new())), // empty trie
+ }
+ }
+ let (value, reversed_proof) = make_proof_upto(self.db, &NibbleSlice::new(&key), self.root())?;
+ let unit = CryptoProofUnit {
+ root: *self.root(),
+ key: *key,
+ value,
+ };
+ Ok((unit, CryptoProof(reversed_proof.iter().rev().cloned().collect())))
+ }
+}
+
#[cfg(test)]
mod tests {
use cdb::MemoryDB;