Handle rlp decoding Result in patricia trie (#8166)
* Decode patricia node with UntrustedRlp (cherry picked from commit efb993b8e7ce087f092cb8c2f633c62ad87e4fb8) * Replace Rlp with UntrustedRlp in triedbmut * Handle node decode results in trie
This commit is contained in:
parent
a60d0e440d
commit
d293f94a6f
@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
use hashdb::HashDB;
|
use hashdb::HashDB;
|
||||||
use nibbleslice::NibbleSlice;
|
use nibbleslice::NibbleSlice;
|
||||||
use rlp::Rlp;
|
|
||||||
use ethereum_types::H256;
|
use ethereum_types::H256;
|
||||||
|
|
||||||
use super::{TrieError, Query};
|
use super::{TrieError, Query};
|
||||||
@ -56,7 +55,7 @@ impl<'a, Q: Query> Lookup<'a, Q> {
|
|||||||
// without incrementing the depth.
|
// without incrementing the depth.
|
||||||
let mut node_data = &node_data[..];
|
let mut node_data = &node_data[..];
|
||||||
loop {
|
loop {
|
||||||
match Node::decoded(node_data) {
|
match Node::decoded(node_data).expect("rlp read from db; qed") {
|
||||||
Node::Leaf(slice, value) => {
|
Node::Leaf(slice, value) => {
|
||||||
return Ok(match slice == key {
|
return Ok(match slice == key {
|
||||||
true => Some(self.query.decode(value)),
|
true => Some(self.query.decode(value)),
|
||||||
@ -82,9 +81,8 @@ impl<'a, Q: Query> Lookup<'a, Q> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if new node data is inline or hash.
|
// check if new node data is inline or hash.
|
||||||
let r = Rlp::new(node_data);
|
if let Some(h) = Node::try_decode_hash(&node_data) {
|
||||||
if r.is_data() && r.size() == 32 {
|
hash = h;
|
||||||
hash = r.as_val();
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,11 +14,12 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use ethereum_types::H256;
|
||||||
use elastic_array::ElasticArray36;
|
use elastic_array::ElasticArray36;
|
||||||
use nibbleslice::NibbleSlice;
|
use nibbleslice::NibbleSlice;
|
||||||
use nibblevec::NibbleVec;
|
use nibblevec::NibbleVec;
|
||||||
use bytes::*;
|
use bytes::*;
|
||||||
use rlp::*;
|
use rlp::{UntrustedRlp, RlpStream, Prototype, DecoderError};
|
||||||
use hashdb::DBValue;
|
use hashdb::DBValue;
|
||||||
|
|
||||||
/// Partial node key type.
|
/// Partial node key type.
|
||||||
@ -39,30 +40,30 @@ pub enum Node<'a> {
|
|||||||
|
|
||||||
impl<'a> Node<'a> {
|
impl<'a> Node<'a> {
|
||||||
/// Decode the `node_rlp` and return the Node.
|
/// Decode the `node_rlp` and return the Node.
|
||||||
pub fn decoded(node_rlp: &'a [u8]) -> Self {
|
pub fn decoded(node_rlp: &'a [u8]) -> Result<Self, DecoderError> {
|
||||||
let r = Rlp::new(node_rlp);
|
let r = UntrustedRlp::new(node_rlp);
|
||||||
match r.prototype() {
|
match r.prototype()? {
|
||||||
// either leaf or extension - decode first item with NibbleSlice::???
|
// either leaf or extension - decode first item with NibbleSlice::???
|
||||||
// and use is_leaf return to figure out which.
|
// and use is_leaf return to figure out which.
|
||||||
// if leaf, second item is a value (is_data())
|
// if leaf, second item is a value (is_data())
|
||||||
// if extension, second item is a node (either SHA3 to be looked up and
|
// if extension, second item is a node (either SHA3 to be looked up and
|
||||||
// fed back into this function or inline RLP which can be fed back into this function).
|
// fed back into this function or inline RLP which can be fed back into this function).
|
||||||
Prototype::List(2) => match NibbleSlice::from_encoded(r.at(0).data()) {
|
Prototype::List(2) => match NibbleSlice::from_encoded(r.at(0)?.data()?) {
|
||||||
(slice, true) => Node::Leaf(slice, r.at(1).data()),
|
(slice, true) => Ok(Node::Leaf(slice, r.at(1)?.data()?)),
|
||||||
(slice, false) => Node::Extension(slice, r.at(1).as_raw()),
|
(slice, false) => Ok(Node::Extension(slice, r.at(1)?.as_raw())),
|
||||||
},
|
},
|
||||||
// branch - first 16 are nodes, 17th is a value (or empty).
|
// branch - first 16 are nodes, 17th is a value (or empty).
|
||||||
Prototype::List(17) => {
|
Prototype::List(17) => {
|
||||||
let mut nodes = [&[] as &[u8]; 16];
|
let mut nodes = [&[] as &[u8]; 16];
|
||||||
for i in 0..16 {
|
for i in 0..16 {
|
||||||
nodes[i] = r.at(i).as_raw();
|
nodes[i] = r.at(i)?.as_raw();
|
||||||
}
|
}
|
||||||
Node::Branch(nodes, if r.at(16).is_empty() { None } else { Some(r.at(16).data()) })
|
Ok(Node::Branch(nodes, if r.at(16)?.is_empty() { None } else { Some(r.at(16)?.data()?) }))
|
||||||
},
|
},
|
||||||
// an empty branch index.
|
// an empty branch index.
|
||||||
Prototype::Data(0) => Node::Empty,
|
Prototype::Data(0) => Ok(Node::Empty),
|
||||||
// something went wrong.
|
// something went wrong.
|
||||||
_ => panic!("Rlp is not valid.")
|
_ => Err(DecoderError::Custom("Rlp is not valid."))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,6 +103,15 @@ impl<'a> Node<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_decode_hash(node_data: &[u8]) -> Option<H256> {
|
||||||
|
let r = UntrustedRlp::new(node_data);
|
||||||
|
if r.is_data() && r.size() == 32 {
|
||||||
|
Some(r.as_val().expect("Hash is the correct size of 32 bytes; qed"))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An owning node type. Useful for trie iterators.
|
/// An owning node type. Useful for trie iterators.
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use hashdb::*;
|
use hashdb::*;
|
||||||
use nibbleslice::NibbleSlice;
|
use nibbleslice::NibbleSlice;
|
||||||
use rlp::*;
|
|
||||||
use super::node::{Node, OwnedNode};
|
use super::node::{Node, OwnedNode};
|
||||||
use super::lookup::Lookup;
|
use super::lookup::Lookup;
|
||||||
use super::{Trie, TrieItem, TrieError, TrieIterator, Query};
|
use super::{Trie, TrieItem, TrieError, TrieIterator, Query};
|
||||||
@ -97,7 +96,10 @@ impl<'db> TrieDB<'db> {
|
|||||||
Node::Extension(ref slice, ref item) => {
|
Node::Extension(ref slice, ref item) => {
|
||||||
write!(f, "'{:?} ", slice)?;
|
write!(f, "'{:?} ", slice)?;
|
||||||
if let Ok(node) = self.get_raw_or_lookup(&*item) {
|
if let Ok(node) = self.get_raw_or_lookup(&*item) {
|
||||||
self.fmt_all(Node::decoded(&node), f, deepness)?;
|
match Node::decoded(&node) {
|
||||||
|
Ok(n) => self.fmt_all(n, f, deepness)?,
|
||||||
|
Err(err) => writeln!(f, "ERROR decoding node extension Rlp: {}", err)?,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Node::Branch(ref nodes, ref value) => {
|
Node::Branch(ref nodes, ref value) => {
|
||||||
@ -108,12 +110,19 @@ impl<'db> TrieDB<'db> {
|
|||||||
}
|
}
|
||||||
for i in 0..16 {
|
for i in 0..16 {
|
||||||
let node = self.get_raw_or_lookup(&*nodes[i]);
|
let node = self.get_raw_or_lookup(&*nodes[i]);
|
||||||
match node.as_ref().map(|n| Node::decoded(&*n)) {
|
match node.as_ref() {
|
||||||
Ok(Node::Empty) => {},
|
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
self.fmt_indent(f, deepness + 1)?;
|
match Node::decoded(&*n) {
|
||||||
write!(f, "'{:x} ", i)?;
|
Ok(Node::Empty) => {},
|
||||||
self.fmt_all(n, f, deepness + 1)?;
|
Ok(n) => {
|
||||||
|
self.fmt_indent(f, deepness + 1)?;
|
||||||
|
write!(f, "'{:x} ", i)?;
|
||||||
|
self.fmt_all(n, f, deepness + 1)?;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
write!(f, "ERROR decoding node branch Rlp: {}", e)?
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
write!(f, "ERROR: {}", e)?;
|
write!(f, "ERROR: {}", e)?;
|
||||||
@ -133,16 +142,18 @@ impl<'db> TrieDB<'db> {
|
|||||||
/// This could be a simple identity operation in the case that the node is sufficiently small, but
|
/// This could be a simple identity operation in the case that the node is sufficiently small, but
|
||||||
/// may require a database lookup.
|
/// may require a database lookup.
|
||||||
fn get_raw_or_lookup(&'db self, node: &'db [u8]) -> super::Result<DBValue> {
|
fn get_raw_or_lookup(&'db self, node: &'db [u8]) -> super::Result<DBValue> {
|
||||||
// check if its keccak + len
|
match Node::try_decode_hash(node) {
|
||||||
let r = Rlp::new(node);
|
Some(key) => {
|
||||||
match r.is_data() && r.size() == 32 {
|
|
||||||
true => {
|
|
||||||
let key = r.as_val::<H256>();
|
|
||||||
self.db.get(&key).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(key)))
|
self.db.get(&key).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(key)))
|
||||||
}
|
}
|
||||||
false => Ok(DBValue::from_slice(node))
|
None => Ok(DBValue::from_slice(node))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a node from raw rlp bytes, assumes valid rlp because encoded locally
|
||||||
|
fn decode_node(node: &'db [u8]) -> Node {
|
||||||
|
Node::decoded(node).expect("rlp read from db; qed")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> Trie for TrieDB<'db> {
|
impl<'db> Trie for TrieDB<'db> {
|
||||||
@ -167,7 +178,10 @@ impl<'db> fmt::Debug for TrieDB<'db> {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
writeln!(f, "c={:?} [", self.hash_count)?;
|
writeln!(f, "c={:?} [", self.hash_count)?;
|
||||||
let root_rlp = self.db.get(self.root).expect("Trie root not found!");
|
let root_rlp = self.db.get(self.root).expect("Trie root not found!");
|
||||||
self.fmt_all(Node::decoded(&root_rlp), f, 0)?;
|
match Node::decoded(&root_rlp) {
|
||||||
|
Ok(node) => self.fmt_all(node, f, 0)?,
|
||||||
|
Err(e) => writeln!(f, "ERROR decoding node rlp: {}", e)?,
|
||||||
|
}
|
||||||
writeln!(f, "]")
|
writeln!(f, "]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,7 +236,7 @@ impl<'a> TrieDBIterator<'a> {
|
|||||||
fn seek<'key>(&mut self, mut node_data: DBValue, mut key: NibbleSlice<'key>) -> super::Result<()> {
|
fn seek<'key>(&mut self, mut node_data: DBValue, mut key: NibbleSlice<'key>) -> super::Result<()> {
|
||||||
loop {
|
loop {
|
||||||
let (data, mid) = {
|
let (data, mid) = {
|
||||||
let node = Node::decoded(&node_data);
|
let node = TrieDB::decode_node(&node_data);
|
||||||
match node {
|
match node {
|
||||||
Node::Leaf(slice, _) => {
|
Node::Leaf(slice, _) => {
|
||||||
if slice == key {
|
if slice == key {
|
||||||
@ -284,7 +298,7 @@ impl<'a> TrieDBIterator<'a> {
|
|||||||
|
|
||||||
/// Descend into a payload.
|
/// Descend into a payload.
|
||||||
fn descend(&mut self, d: &[u8]) -> super::Result<()> {
|
fn descend(&mut self, d: &[u8]) -> super::Result<()> {
|
||||||
let node = Node::decoded(&self.db.get_raw_or_lookup(d)?).into();
|
let node = TrieDB::decode_node(&self.db.get_raw_or_lookup(d)?).into();
|
||||||
Ok(self.descend_into_node(node))
|
Ok(self.descend_into_node(node))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -382,7 +396,7 @@ impl<'a> Iterator for TrieDBIterator<'a> {
|
|||||||
self.trail.pop();
|
self.trail.pop();
|
||||||
},
|
},
|
||||||
IterStep::Descend(Ok(d)) => {
|
IterStep::Descend(Ok(d)) => {
|
||||||
self.descend_into_node(Node::decoded(&d).into())
|
self.descend_into_node(TrieDB::decode_node(&d).into())
|
||||||
},
|
},
|
||||||
IterStep::Descend(Err(e)) => {
|
IterStep::Descend(Err(e)) => {
|
||||||
return Some(Err(e))
|
return Some(Err(e))
|
||||||
|
@ -24,7 +24,7 @@ use super::node::NodeKey;
|
|||||||
use hashdb::HashDB;
|
use hashdb::HashDB;
|
||||||
use bytes::ToPretty;
|
use bytes::ToPretty;
|
||||||
use nibbleslice::NibbleSlice;
|
use nibbleslice::NibbleSlice;
|
||||||
use rlp::{Rlp, RlpStream};
|
use rlp::{UntrustedRlp, RlpStream};
|
||||||
use hashdb::DBValue;
|
use hashdb::DBValue;
|
||||||
|
|
||||||
use std::collections::{HashSet, VecDeque};
|
use std::collections::{HashSet, VecDeque};
|
||||||
@ -88,18 +88,17 @@ enum Node {
|
|||||||
impl Node {
|
impl Node {
|
||||||
// load an inline node into memory or get the hash to do the lookup later.
|
// load an inline node into memory or get the hash to do the lookup later.
|
||||||
fn inline_or_hash(node: &[u8], db: &HashDB, storage: &mut NodeStorage) -> NodeHandle {
|
fn inline_or_hash(node: &[u8], db: &HashDB, storage: &mut NodeStorage) -> NodeHandle {
|
||||||
let r = Rlp::new(node);
|
RlpNode::try_decode_hash(&node)
|
||||||
if r.is_data() && r.size() == 32 {
|
.map(NodeHandle::Hash)
|
||||||
NodeHandle::Hash(r.as_val::<H256>())
|
.unwrap_or_else(|| {
|
||||||
} else {
|
let child = Node::from_rlp(node, db, storage);
|
||||||
let child = Node::from_rlp(node, db, storage);
|
NodeHandle::InMemory(storage.alloc(Stored::New(child)))
|
||||||
NodeHandle::InMemory(storage.alloc(Stored::New(child)))
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// decode a node from rlp without getting its children.
|
// decode a node from rlp without getting its children.
|
||||||
fn from_rlp(rlp: &[u8], db: &HashDB, storage: &mut NodeStorage) -> Self {
|
fn from_rlp(rlp: &[u8], db: &HashDB, storage: &mut NodeStorage) -> Self {
|
||||||
match RlpNode::decoded(rlp) {
|
match RlpNode::decoded(rlp).expect("rlp read from db; qed") {
|
||||||
RlpNode::Empty => Node::Empty,
|
RlpNode::Empty => Node::Empty,
|
||||||
RlpNode::Leaf(k, v) => Node::Leaf(k.encoded(true), DBValue::from_slice(&v)),
|
RlpNode::Leaf(k, v) => Node::Leaf(k.encoded(true), DBValue::from_slice(&v)),
|
||||||
RlpNode::Extension(key, cb) => {
|
RlpNode::Extension(key, cb) => {
|
||||||
@ -108,7 +107,7 @@ impl Node {
|
|||||||
RlpNode::Branch(ref children_rlp, val) => {
|
RlpNode::Branch(ref children_rlp, val) => {
|
||||||
let mut child = |i| {
|
let mut child = |i| {
|
||||||
let raw = children_rlp[i];
|
let raw = children_rlp[i];
|
||||||
let child_rlp = Rlp::new(raw);
|
let child_rlp = UntrustedRlp::new(raw);
|
||||||
if !child_rlp.is_empty() {
|
if !child_rlp.is_empty() {
|
||||||
Some(Self::inline_or_hash(raw, db, storage))
|
Some(Self::inline_or_hash(raw, db, storage))
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user