Main logic for insertion into trie.
This commit is contained in:
parent
0eb6f97a97
commit
2b09521b56
@ -87,6 +87,18 @@ impl<'a> NibbleSlice<'a> {
|
|||||||
}
|
}
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn encoded_leftmost(&self, n: usize, is_leaf: bool) -> Bytes {
|
||||||
|
let l = min(self.len(), n);
|
||||||
|
let mut r = Bytes::with_capacity(l / 2 + 1);
|
||||||
|
let mut i = l % 2;
|
||||||
|
r.push(if i == 1 {0x10 + self.at(0)} else {0} + if is_leaf {0x20} else {0});
|
||||||
|
while i < l {
|
||||||
|
r.push(self.at(i) * 16 + self.at(i + 1));
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
r
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PartialEq for NibbleSlice<'a> {
|
impl<'a> PartialEq for NibbleSlice<'a> {
|
||||||
|
@ -167,8 +167,8 @@ impl<'a> Rlp<'a> {
|
|||||||
/// assert_eq!(dog, &[0x83, b'd', b'o', b'g']);
|
/// assert_eq!(dog, &[0x83, b'd', b'o', b'g']);
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn data(&self) -> &[u8] {
|
pub fn raw(&self) -> &[u8] {
|
||||||
self.rlp.data()
|
self.rlp.raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns number of rlp items.
|
/// Returns number of rlp items.
|
||||||
@ -348,7 +348,7 @@ impl<'a> UntrustedRlp<'a> {
|
|||||||
/// assert_eq!(dog, &[0x83, b'd', b'o', b'g']);
|
/// assert_eq!(dog, &[0x83, b'd', b'o', b'g']);
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn data(&self) -> &[u8] {
|
pub fn raw(&self) -> &[u8] {
|
||||||
self.bytes
|
self.bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
250
src/trie.rs
250
src/trie.rs
@ -4,6 +4,7 @@ use hash::*;
|
|||||||
use nibbleslice::*;
|
use nibbleslice::*;
|
||||||
use bytes::*;
|
use bytes::*;
|
||||||
use rlp::*;
|
use rlp::*;
|
||||||
|
use log::*;
|
||||||
|
|
||||||
pub const NULL_RLP: [u8; 1] = [0x80; 1];
|
pub const NULL_RLP: [u8; 1] = [0x80; 1];
|
||||||
pub const SHA3_NULL_RLP: H256 = H256( [0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21] );
|
pub const SHA3_NULL_RLP: H256 = H256( [0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21] );
|
||||||
@ -29,13 +30,44 @@ pub struct TrieDB {
|
|||||||
root: H256,
|
root: H256,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Diff {
|
enum Operation {
|
||||||
new: Vec<(H256, Bytes)>,
|
New(H256, Bytes),
|
||||||
old: Vec<H256>,
|
Delete(H256),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Diff (Vec<Operation>)
|
||||||
|
|
||||||
impl Diff {
|
impl Diff {
|
||||||
pub fn new() -> Diff { Diff { new: vec![], old: vec![] }}
|
fn new() -> Diff { Diff(vec![]) }
|
||||||
|
|
||||||
|
/// Given the RLP that encodes a node, append a reference to that node `out` and leave `diff`
|
||||||
|
/// such that the reference is valid, once applied.
|
||||||
|
fn new_node(&mut self, Bytes rlp, out: &mut RlpStream) {
|
||||||
|
if (rlp.len() >= 32) {
|
||||||
|
let rlp_sha3 = rlp.sha3();
|
||||||
|
out.append(&rlp_sha3);
|
||||||
|
self.operations.push(Operation::New(rlp_sha3, rlp));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
out.append_raw(&rlp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given the RLP that encodes a now-unused node, leave `diff` in such a state that it is noted.
|
||||||
|
fn delete_node_sha3(&mut self, old_sha3: H256) {
|
||||||
|
self.operations.push(Operation::Delete(old_sha3));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete_node(&mut self, old: &Rlp) {
|
||||||
|
if (old.is_data() && old.size() == 32) {
|
||||||
|
self.operations.push(Operation::Delete(H256::decode(old)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace_node(&mut self, old: &Rlp, Bytes rlp, out: &mut RlpStream) {
|
||||||
|
self.delete_node(old);
|
||||||
|
self.new_node(rlp, &mut out);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TrieDB {
|
impl TrieDB {
|
||||||
@ -57,70 +89,214 @@ impl TrieDB {
|
|||||||
|
|
||||||
fn add(&mut self, key: &NibbleSlice, value: &[u8]) {
|
fn add(&mut self, key: &NibbleSlice, value: &[u8]) {
|
||||||
// determine what the new root is, insert new nodes and remove old as necessary.
|
// determine what the new root is, insert new nodes and remove old as necessary.
|
||||||
let todo = {
|
let todo: Diff = Diff::new();
|
||||||
let root_rlp = self.db.lookup(&self.root).expect("Trie root not found!");
|
let root_rlp = self.inject(self.db.lookup(&self.root).expect("Trie root not found!"), key, value, &mut todo);
|
||||||
self.merge(root_rlp, key, value)
|
self.apply(todo);
|
||||||
};
|
self.set_root_rlp(&root_rlp);
|
||||||
self.apply(todo.1);
|
|
||||||
self.set_root_rlp(&todo.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(&mut self, diff: Diff) {
|
fn apply(&mut self, diff: Diff) {
|
||||||
for d in diff.old.iter() {
|
for d in diff.operations.into_iter() {
|
||||||
self.db.kill(&d);
|
match d {
|
||||||
|
Operation::Delete(h) => {
|
||||||
|
trace!("TrieDB::apply --- {:?}", &h);
|
||||||
|
self.db.kill(&h);
|
||||||
|
},
|
||||||
|
Operation::New(h, d) => {
|
||||||
|
trace!("TrieDB::apply +++ {:?} -> {:?}", &h, &d);
|
||||||
|
self.db.emplace(h, d);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for d in diff.new.into_iter() {
|
|
||||||
self.db.emplace(d.0, d.1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine the RLP of the node, assuming we're inserting `partial_key` into the
|
/// Return the bytes encoding the node represented by `rlp`. It will be unlinked from
|
||||||
/// node at `old`. This will *not* delete the old mode; it will just return the new RLP
|
/// the trie.
|
||||||
/// that includes the new node.
|
fn take_node(&self, rlp: &Rlp, &mut diff) -> Bytes {
|
||||||
|
if (rlp.is_data()) {
|
||||||
|
Bytes::decode(rlp)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let h = H256::decode(rlp);
|
||||||
|
let r = self.db.lookup(&h).as_vec();
|
||||||
|
diff.delete_node(h);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inject_and_replace(&self, old: &[u8], old_sha3: H256, partial: &NibbleSlice, value: &[u8], diff: &mut Diff, out: &mut RlpStream) {
|
||||||
|
diff.new_node(self.inject(old, partial, value, diff), &mut out);
|
||||||
|
diff.delete_node(old, old_sha3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transform an existing extension or leaf node plus a new partial/value to a two-entry branch.
|
||||||
|
///
|
||||||
|
/// **This operation will not insert the new node nor destroy the original.**
|
||||||
|
fn transmute_to_branch_and_inject(&self, orig_is_leaf: bool, orig_partial: &NibbleSlice, orig_raw_payload: &[u8], partial: &NibbleSlice, value: &[u8], diff: &mut Diff) -> Bytes {
|
||||||
|
let intermediate = match orig_is_leaf {
|
||||||
|
true => Self::transmute_leaf_to_branch(orig_partial, orig_raw_payload, &mut diff),
|
||||||
|
false => Self::transmute_extension_to_branch(orig_partial, orig_raw_payload, &mut diff),
|
||||||
|
};
|
||||||
|
self.inject(&intermediate, partial, value, &mut diff)
|
||||||
|
// TODO: implement without having to make an intermediate representation.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transform an existing extension or leaf node to an invalid single-entry branch.
|
||||||
|
///
|
||||||
|
/// **This operation will not insert the new node nor destroy the original.**
|
||||||
|
fn transmute_extension_to_branch(orig_partial: &NibbleSlice, orig_raw_payload: &[u8], diff: &mut Diff) -> Bytes {
|
||||||
|
let mut s = RLPStream::new_list(17);
|
||||||
|
assert!(!orig_partial.is_empty()); // extension nodes are not allowed to have empty partial keys.
|
||||||
|
let index = orig_partial.at(0);
|
||||||
|
// orig is extension - orig_payload is a node itself.
|
||||||
|
for i in 0..17 {
|
||||||
|
if index == i {
|
||||||
|
if orig_partial.len() > 1 {
|
||||||
|
// still need an extension
|
||||||
|
diff.new_node(compose_extension(orig_partial.mid(1), orig_raw_payload), &mut s);
|
||||||
|
} else {
|
||||||
|
// was an extension of length 1 - just redirect the payload into here.
|
||||||
|
s.append_raw(orig_payload.raw());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.append_null_data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.out()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transmute_leaf_to_branch(orig_partial: &NibbleSlice, orig_raw_payload: &[u8], diff: &mut Diff) -> Bytes {
|
||||||
|
let mut s = RLPStream::new_list(17);
|
||||||
|
let index = orig_partial.is_empty() ? 16 : orig_partial.at(0);
|
||||||
|
// orig is leaf - orig_payload is data representing the actual value.
|
||||||
|
for i in 0..17 {
|
||||||
|
if index == i {
|
||||||
|
// this is our node.
|
||||||
|
diff.new_node(compose_raw(orig_partial.mid(if i == 16 {0} else {1}), orig_raw_payload, true), &mut s);
|
||||||
|
} else {
|
||||||
|
s.append_null_data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.out()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a branch node's RLP `orig` together with a `partial` key and `value`, return the
|
||||||
|
/// RLP-encoded node that accomodates the trie with the new entry. Mutate `diff` so that
|
||||||
|
/// once applied the returned node is valid.
|
||||||
|
fn injected_into_branch(&self, orig: &Rlp, partial: &NibbleSlice, value: &[u8], diff: &mut Diff) -> Bytes {
|
||||||
|
RlpStream s;
|
||||||
|
let index = partial.is_empty() ? 16 : partial.at(0);
|
||||||
|
for i in 0..17 {
|
||||||
|
if index == i && {
|
||||||
|
// this is our node.
|
||||||
|
if (orig.at(i).is_empty()) {
|
||||||
|
// easy - original had empty slot.
|
||||||
|
diff.new_node(compose_leaf(partial.mid(if i == 16 {0} else {1}), value), &mut s);
|
||||||
|
} else if (i == 16) {
|
||||||
|
// leaf entry - just replace.
|
||||||
|
let new = compose_leaf(partial.mid(if i == 16 {0} else {1}), value);
|
||||||
|
diff.replace_node(orig.at(i).raw(), new, &mut s),
|
||||||
|
} else {
|
||||||
|
// harder - original has something there already
|
||||||
|
let new = self.inject(orig.at(i).raw(), partial.mid(1), value, &mut diff);
|
||||||
|
diff.replace_node(orig.at(i).raw(), new, &mut s)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.append_raw(orig.at(i).raw());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determine the RLP of the node, assuming we're inserting `partial` into the
|
||||||
|
/// node currently of data `old`. This will *not* delete any hash of `old` from the database;
|
||||||
|
/// it will just return the new RLP that includes the new node.
|
||||||
///
|
///
|
||||||
/// The database will be updated so as to make the returned RLP valid through inserting
|
/// The database will be updated so as to make the returned RLP valid through inserting
|
||||||
/// and deleting nodes as necessary.
|
/// and deleting nodes as necessary.
|
||||||
fn merge(&self, old: &[u8], partial_key: &NibbleSlice, value: &[u8]) -> (Bytes, Diff) {
|
///
|
||||||
let o = Rlp::new(old);
|
/// **This operation will not insert the new node now destroy the original.**
|
||||||
match o.prototype() {
|
fn inject(&self, old: &[u8], partial: &NibbleSlice, value: &[u8], diff: &mut Diff) -> Bytes {
|
||||||
|
// already have an extension. either fast_forward, cleve or transmute_to_branch.
|
||||||
|
let old_rlp = Rlp::new(old);
|
||||||
|
match old_rlp.prototype() {
|
||||||
Prototype::List(17) => {
|
Prototype::List(17) => {
|
||||||
// already have a branch. route and merge.
|
// already have a branch. route and inject.
|
||||||
unimplemented!();
|
self.injected_into_branch(old_rlp, partial, value, &mut diff)
|
||||||
},
|
},
|
||||||
Prototype::List(2) => {
|
Prototype::List(2) => {
|
||||||
let their_key_rlp = o.at(0);
|
let their_key_rlp = old_rlp.at(0);
|
||||||
let (them, _) = NibbleSlice::from_encoded(their_key_rlp.data());
|
let (them, is_leaf) = NibbleSlice::from_encoded(their_key_rlp.data());
|
||||||
match partial_key.common_prefix(&them) {
|
|
||||||
|
match partial.common_prefix(&them) {
|
||||||
|
0 if partial.is_empty() && them.is_empty() => {
|
||||||
|
// both empty: just replace.
|
||||||
|
compose_leaf(partial, value)
|
||||||
|
},
|
||||||
0 => {
|
0 => {
|
||||||
// transmute to branch here
|
// one of us isn't empty: transmute to branch here
|
||||||
|
transmute_to_branch_and_inject(is_leaf, them, old_rlp.at(1).raw())
|
||||||
},
|
},
|
||||||
cp if cp == them.len() => {
|
cp if cp == them.len() => {
|
||||||
// fast-forward
|
// fully-shared prefix for this extension:
|
||||||
|
// skip to the end of this extension and continue the inject there.
|
||||||
|
let n = self.take_node(old_rlp.at(1).raw());
|
||||||
|
let downstream_node = self.inject(&n, partial.mid(cp), value, &mut diff);
|
||||||
|
let mut s = RlpStream::new_list(2);
|
||||||
|
s.append_raw(old_rlp.at(0).raw());
|
||||||
|
diff.new_node(downstream_node, &mut s);
|
||||||
|
s.out()
|
||||||
},
|
},
|
||||||
_ => {
|
cp => {
|
||||||
// cleve into two + branch in the middle
|
// partially-shared prefix for this extension:
|
||||||
|
// split into two extensions, high and low, pass the
|
||||||
|
// low through inject with the value before inserting the result
|
||||||
|
// into high to create the new.
|
||||||
|
|
||||||
|
// TODO: optimise by doing this without creating injected_low.
|
||||||
|
|
||||||
|
// low (farther from root)
|
||||||
|
let low = Self::compose_raw(them.mid(cp), old_rlp.at(1).raw(), is_leaf);
|
||||||
|
let injected_low = self.inject(&low, partial.mid(cp), value, &mut diff);
|
||||||
|
|
||||||
|
// high (closer to root)
|
||||||
|
let mut s = RlpStream::new_list(2);
|
||||||
|
s.append(them.encoded_leftmost(cp, false));
|
||||||
|
diff.new_node(injected_low, &mut s);
|
||||||
|
s.out()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// already have an extension. either fast_forward, cleve or transmute_to_branch.
|
|
||||||
unimplemented!();
|
|
||||||
},
|
},
|
||||||
Prototype::Data(0) => {
|
Prototype::Data(0) => {
|
||||||
(Self::compose_extension(partial_key, value, true), Diff::new())
|
(Self::compose_leaf(partial, value, true), Diff::new())
|
||||||
},
|
},
|
||||||
_ => panic!("Invalid RLP for node."),
|
_ => panic!("Invalid RLP for node."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compose_extension(partial_key: &NibbleSlice, value: &[u8], is_leaf: bool) -> Bytes {
|
fn compose_raw(partial: &NibbleSlice, raw_payload: &[u8], bool is_leaf) -> Bytes {
|
||||||
println!("compose_extension {:?} {:?} {:?} ({:?})", partial_key, value, is_leaf, partial_key.encoded(is_leaf));
|
println!("compose_raw {:?} {:?} {:?} ({:?})", partial, value, is_leaf, partial.encoded(is_leaf));
|
||||||
let mut s = RlpStream::new_list(2);
|
let mut s = RlpStream::new_list(2);
|
||||||
s.append(&partial_key.encoded(is_leaf));
|
s.append(&partial.encoded(is_leaf));
|
||||||
s.append(&value.to_vec()); // WTF?!?!
|
s.append_raw(raw_payload);
|
||||||
//s.append(value); // <-- should be.
|
|
||||||
let r = s.out();
|
let r = s.out();
|
||||||
println!("output: -> {:?}", &r);
|
println!("output: -> {:?}", &r);
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compose_leaf(partial: &NibbleSlice, value: &[u8]) -> Bytes {
|
||||||
|
println!("compose_leaf {:?} {:?} ({:?})", partial, value, partial.encoded(true));
|
||||||
|
let mut s = RlpStream::new_list(2);
|
||||||
|
s.append(&partial.encoded(true));
|
||||||
|
s.append(value);
|
||||||
|
let r = s.out();
|
||||||
|
println!("output: -> {:?}", &r);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compose_extension(partial: &NibbleSlice, raw_payload: &[u8]) -> Bytes {
|
||||||
|
Self::compose_raw(partial, raw_payload, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Trie for TrieDB {
|
impl Trie for TrieDB {
|
||||||
|
Loading…
Reference in New Issue
Block a user