Skip to content

Commit 7663b96

Browse files
author
GeunWoo Kim
authored
Merge pull request #494 from GNUp/forpush
Add triehash & triedb
2 parents 5504542 + d6a3974 commit 7663b96

File tree

4 files changed

+667
-96
lines changed

4 files changed

+667
-96
lines changed

util/merkle/src/lib.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,20 @@ extern crate trie_standardmap as standardmap;
2727

2828
use std::fmt;
2929

30+
use ccrypto::BLAKE_NULL_RLP;
3031
use hashdb::DBValue;
3132
use primitives::H256;
3233

3334
mod nibbleslice;
3435
pub mod node;
3536
mod skewed;
37+
pub mod triedb;
3638
pub mod triedbmut;
39+
pub mod triehash;
3740

3841
pub use skewed::skewed_merkle_root;
42+
pub use triedb::TrieDB;
43+
pub use triedbmut::TrieDBMut;
3944

4045
/// Trie Errors.
4146
///
@@ -61,6 +66,24 @@ impl fmt::Display for TrieError {
6166
/// Trie result type. Boxed to avoid copying around extra space for `H256`s on successful queries.
6267
pub type Result<T> = ::std::result::Result<T, Box<TrieError>>;
6368

69+
/// A key-value datastore implemented as a database-backed modified Merkle tree.
70+
pub trait Trie {
71+
/// Return the root of the trie.
72+
fn root(&self) -> &H256;
73+
74+
/// Is the trie empty?
75+
fn is_empty(&self) -> bool {
76+
*self.root() == BLAKE_NULL_RLP
77+
}
78+
79+
/// Does the trie contain a given key?
80+
fn contains(&self, key: &[u8]) -> Result<bool> {
81+
self.get(key).map(|x| x.is_some())
82+
}
83+
84+
/// What is the value of the given key in this trie?
85+
fn get(&self, key: &[u8]) -> Result<Option<DBValue>>;
86+
}
6487

6588
/// A key-value datastore implemented as a database-backed modified Merkle tree.
6689
pub trait TrieMut {

util/merkle/src/triedb.rs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// Copyright 2018 Kodebox, Inc.
2+
// This file is part of CodeChain.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as
6+
// published by the Free Software Foundation, either version 3 of the
7+
// License, or (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
use ccrypto::blake256;
18+
use hashdb::DBValue;
19+
use hashdb::HashDB;
20+
use primitives::H256;
21+
22+
use super::nibbleslice::NibbleSlice;
23+
use super::node::Node as RlpNode;
24+
use super::{Trie, TrieError};
25+
/// A `Trie` implementation using a generic `HashDB` backing database.
26+
///
27+
/// Use it as a `Trie` trait object. You can use `db()` to get the backing database object.
28+
/// Use `get` and `contains` to query values associated with keys in the trie.
29+
///
30+
/// # Example
31+
/// ```
32+
/// extern crate hashdb;
33+
/// extern crate memorydb;
34+
/// extern crate primitives;
35+
/// extern crate codechain_merkle as cmerkle;
36+
///
37+
/// use cmerkle::*;
38+
/// use hashdb::*;
39+
/// use memorydb::*;
40+
/// use primitives::H256;
41+
///
42+
/// fn main() {
43+
/// let mut memdb = MemoryDB::new();
44+
/// let mut root = H256::new();
45+
/// TrieDBMut::new(&mut memdb, &mut root).insert(b"foo", b"bar").unwrap();
46+
/// let t = TrieDB::new(&memdb, &root).unwrap();
47+
/// assert!(t.contains(b"foo").unwrap());
48+
/// assert_eq!(t.get(b"foo").unwrap().unwrap(), DBValue::from_slice(b"bar"));
49+
/// }
50+
/// ```
51+
pub struct TrieDB<'db> {
52+
db: &'db HashDB,
53+
root: &'db H256,
54+
}
55+
56+
impl<'db> TrieDB<'db> {
57+
/// Create a new trie with the backing database `db` and `root`
58+
/// Returns an error if `root` does not exist
59+
pub fn new(db: &'db HashDB, root: &'db H256) -> super::Result<Self> {
60+
if !db.contains(root) {
61+
Err(Box::new(TrieError::InvalidStateRoot(*root)))
62+
} else {
63+
Ok(TrieDB {
64+
db,
65+
root,
66+
})
67+
}
68+
}
69+
70+
/// Get the backing database.
71+
pub fn db(&'db self) -> &'db HashDB {
72+
self.db
73+
}
74+
75+
/// Get auxiliary
76+
fn get_aux(&self, path: NibbleSlice, cur_node_hash: Option<H256>) -> super::Result<Option<DBValue>> {
77+
match cur_node_hash {
78+
Some(hash) => {
79+
let node_rlp = self.db.get(&hash).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(hash)))?;
80+
81+
match RlpNode::decoded(&node_rlp) {
82+
Some(RlpNode::Leaf(partial, value)) => {
83+
if partial == path {
84+
Ok(Some(value))
85+
} else {
86+
Ok(None)
87+
}
88+
}
89+
Some(RlpNode::Branch(partial, children)) => {
90+
if path.starts_with(&partial) {
91+
self.get_aux(path.mid(partial.len() + 1), children[path.mid(partial.len()).at(0) as usize])
92+
} else {
93+
Ok(None)
94+
}
95+
}
96+
None => Ok(None),
97+
}
98+
}
99+
None => Ok(None),
100+
}
101+
}
102+
}
103+
104+
impl<'db> Trie for TrieDB<'db> {
105+
fn root(&self) -> &H256 {
106+
self.root
107+
}
108+
109+
fn get(&self, key: &[u8]) -> super::Result<Option<DBValue>> {
110+
let path = blake256(key);
111+
let root = *self.root;
112+
113+
self.get_aux(NibbleSlice::new(&path), Some(root))
114+
}
115+
}
116+
117+
#[cfg(test)]
118+
mod tests {
119+
use super::super::*;
120+
use super::*;
121+
use memorydb::*;
122+
123+
#[test]
124+
fn get() {
125+
let mut memdb = MemoryDB::new();
126+
let mut root = H256::new();
127+
{
128+
let mut t = TrieDBMut::new(&mut memdb, &mut root);
129+
t.insert(b"A", b"ABC").unwrap();
130+
t.insert(b"B", b"ABCBA").unwrap();
131+
}
132+
133+
let t = TrieDB::new(&memdb, &root).unwrap();
134+
assert_eq!(t.get(b"A"), Ok(Some(DBValue::from_slice(b"ABC"))));
135+
assert_eq!(t.get(b"B"), Ok(Some(DBValue::from_slice(b"ABCBA"))));
136+
assert_eq!(t.get(b"C"), Ok(None));
137+
}
138+
}

0 commit comments

Comments
 (0)