Skip to content

Commit cb4a6c1

Browse files
author
Geunwoo Kim
committed
Add Triedb module
1 parent 6b1289d commit cb4a6c1

File tree

4 files changed

+165
-35
lines changed

4 files changed

+165
-35
lines changed

util/merkle/src/lib.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +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;
3739
pub mod triehash;
3840

3941
pub use skewed::skewed_merkle_root;
42+
pub use triedb::TrieDB;
43+
pub use triedbmut::TrieDBMut;
4044

4145
/// Trie Errors.
4246
///
@@ -62,6 +66,24 @@ impl fmt::Display for TrieError {
6266
/// Trie result type. Boxed to avoid copying around extra space for `H256`s on successful queries.
6367
pub type Result<T> = ::std::result::Result<T, Box<TrieError>>;
6468

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+
}
6587

6688
/// A key-value datastore implemented as a database-backed modified Merkle tree.
6789
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+
}

util/merkle/src/triedbmut.rs

Lines changed: 5 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ use hashdb::DBValue;
2020
use hashdb::HashDB;
2121
use primitives::H256;
2222

23-
use super::node::Node as RlpNode;
24-
use super::{TrieError, TrieMut};
23+
use super::{Trie, TrieError, TrieMut};
2524
use nibbleslice::NibbleSlice;
25+
use node::Node as RlpNode;
26+
use triedb::TrieDB;
2627

2728

2829
fn empty_children() -> [Option<H256>; 16] {
@@ -170,34 +171,6 @@ impl<'a> TrieDBMut<'a> {
170171
}
171172
}
172173

173-
/// Get auxiliary
174-
fn get_aux(&self, path: NibbleSlice, cur_node_hash: Option<H256>) -> super::Result<Option<DBValue>> {
175-
match cur_node_hash {
176-
Some(hash) => {
177-
let node_rlp = self.db.get(&hash).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(hash)))?;
178-
179-
match RlpNode::decoded(&node_rlp) {
180-
Some(RlpNode::Leaf(partial, value)) => {
181-
if partial == path {
182-
Ok(Some(value))
183-
} else {
184-
Ok(None)
185-
}
186-
}
187-
Some(RlpNode::Branch(partial, children)) => {
188-
if path.starts_with(&partial) {
189-
self.get_aux(path.mid(partial.len() + 1), children[path.mid(partial.len()).at(0) as usize])
190-
} else {
191-
Ok(None)
192-
}
193-
}
194-
None => Ok(None),
195-
}
196-
}
197-
None => Ok(None),
198-
}
199-
}
200-
201174
/// Remove auxiliary
202175
fn remove_aux(
203176
&mut self,
@@ -334,10 +307,9 @@ impl<'a> TrieMut for TrieDBMut<'a> {
334307
}
335308

336309
fn get(&self, key: &[u8]) -> super::Result<Option<DBValue>> {
337-
let path = blake256(key);
338-
let cur_hash = *self.root;
310+
let t = TrieDB::new(self.db, self.root)?;
339311

340-
self.get_aux(NibbleSlice::new(&path), Some(cur_hash))
312+
t.get(key)
341313
}
342314

343315
fn insert(&mut self, key: &[u8], value: &[u8]) -> super::Result<Option<DBValue>> {
@@ -742,4 +714,3 @@ mod tests {
742714
}
743715
}
744716
}
745-

util/merkle/src/triehash.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ fn hash256rlp<A: AsRef<[u8]>, B: AsRef<[u8]>>(input: &[(A, B)], pre_len: usize,
142142
stream.begin_list(17);
143143

144144
// Append partial path as a first element of branch
145-
println!("{} and {}", pre_len, shared_prefix);
146145
stream.append(&hex_prefix_encode(&key[pre_len..shared_prefix]));
147146

148147
let mut begin: usize = 0;

0 commit comments

Comments
 (0)