From 1742a4334e66bbfc02aa841df1789bd3dbd2c58a Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 29 Mar 2023 08:25:03 +0200 Subject: [PATCH 001/154] init query plan --- trie-db/src/lib.rs | 1 + trie-db/src/query_plan.rs | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 trie-db/src/query_plan.rs diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index b09372b2..e0ae28eb 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -56,6 +56,7 @@ mod iterator; mod lookup; mod nibble; mod node_codec; +mod query_plan; mod trie_codec; pub use self::{ diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs new file mode 100644 index 00000000..f5e7d851 --- /dev/null +++ b/trie-db/src/query_plan.rs @@ -0,0 +1,21 @@ +// Copyright 2023, 2023 Parity Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Iterate on multiple values following a specific query plan. +//! Can be use on a trie db and possibly register proof, or +//! be use on a proof directly. +//! When use on a proof, process can be interrupted, checked and +//! restore (at the cost of additional hashes in proof). + + From 62ba01d492da22d2c67241479828306e6b30ed9f Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 29 Mar 2023 12:23:05 +0200 Subject: [PATCH 002/154] api draft --- trie-db/Cargo.toml | 1 + trie-db/src/query_plan.rs | 278 +++++++++++++++++++++++++++++++++++++- 2 files changed, 278 insertions(+), 1 deletion(-) diff --git a/trie-db/Cargo.toml b/trie-db/Cargo.toml index f8e6b00c..f39c3ceb 100644 --- a/trie-db/Cargo.toml +++ b/trie-db/Cargo.toml @@ -13,6 +13,7 @@ smallvec = { version = "1.0.0", features = ["union", "const_new"] } hash-db = { path = "../hash-db", default-features = false, version = "0.16.0"} hashbrown = { version = "0.13.2", default-features = false, features = ["ahash"] } rustc-hex = { version = "2.1.0", default-features = false, optional = true } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } # TODO remove (only to proto encoding but not appropriate). [features] default = ["std"] diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index f5e7d851..525be3cf 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -13,9 +13,285 @@ // limitations under the License. //! Iterate on multiple values following a specific query plan. -//! Can be use on a trie db and possibly register proof, or +//! Can be use on a trie db to register a proof, or //! be use on a proof directly. //! When use on a proof, process can be interrupted, checked and //! restore (at the cost of additional hashes in proof). +//! +//! Because nodes are guaranted to be accessed only once and recorded +//! in proof, we do not use a cache (or only the node caching part). +use core::marker::PhantomData; +use crate::{ + node::OwnedNode, proof::VerifyError, rstd::result::Result, CError, TrieError, TrieHash, + TrieLayout, TrieDB, +}; + +/// Item to query, in memory. +pub struct InMemQueryPlanItem { + key: Vec, + as_prefix: bool, +} + +impl InMemQueryPlanItem { + /// Get ref. + pub fn as_ref(&self) -> QueryPlanItem { + QueryPlanItem { key: &self.key, as_prefix: self.as_prefix } + } +} + +/// Item to query. +pub struct QueryPlanItem<'a> { + key: &'a [u8], + as_prefix: bool, +} + +/// Query plan in memory. +struct InMemQueryPlan { + items: Vec, + current: Option, + ensure_ordered: bool, + ignore_unordered: bool, + allow_under_prefix: bool, +} + +/// Iterator as type of mapped slice iter is very noisy. +pub struct QueryPlanItemIter<'a>(&'a Vec, usize); + +impl<'a> Iterator for QueryPlanItemIter<'a> { + type Item = QueryPlanItem<'a>; + + fn next(&mut self) -> Option { + if self.1 >= self.0.len() { + return None + } + self.1 += 1; + Some(self.0[self.1 - 1].as_ref()) + } +} + +impl InMemQueryPlan { + /// Get ref. + fn as_ref(&self) -> QueryPlan { + QueryPlan { + items: QueryPlanItemIter(&self.items, 0), + current: self.current.as_ref().map(|i| i.as_ref()), + ensure_ordered: self.ensure_ordered, + ignore_unordered: self.ignore_unordered, + allow_under_prefix: self.allow_under_prefix, + } + } +} + +/// Query plan. +struct QueryPlan<'a, I> +where + I: Iterator>, +{ + items: I, + current: Option>, + ensure_ordered: bool, + ignore_unordered: bool, + allow_under_prefix: bool, +} + +/// Different proof support. +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum ProofKind { + /// Proof is a sequence of fully encoded node, this is not + /// size efficient but allows streaming a proof better, since + /// the consumer can halt at the first invalid node. + FullNodes, + + /// Proof got its accessed hash and value removed (same scheme + /// as in trie_codec.rs ordering is same as full node, from + /// root and in lexicographic order). + /// + /// Checking this proof requires to read it fully. When stopping + /// recording, the partial proof stays valid, but it will + /// contains hashes that would not be needed if creating the + /// proof at once. + CompactNodes, + + /// Content oriented proof, no nodes are written, just a + /// sequence of accessed by lexicographical order as described + /// in compact_content_proof::Op. + /// As with compact node, checking validity of proof need to + /// load the full proof or up to the halt point. + CompactContent, +} + +#[derive(Default, Clone, Copy)] +struct Bitmap(u16); + +impl Bitmap { + fn at(&self, i: usize) -> bool { + self.0 & (1u16 << i) != 0 + } + + fn set(&mut self, i: usize, v: bool) { + if v { + self.0 |= 1u16 << i + } else { + self.0 &= !(1u16 << i) + } + } +} + +struct CompactEncodingInfos { + /// Node in memory content. + node: OwnedNode>, + /// Flags indicating whether each child is omitted in the encoded node. + omit_children: Bitmap, + /// Skip value if value node is after. + omit_value: bool, +} + +/* likely compact encoding is enough +struct ContentEncodingInfos { + /// Node in memory content. + node: OwnedNode>, + /// Flags indicating whether each child is omitted in the encoded node. + omit_children: Bitmap, + /// Skip value if value node is after. + omit_value: bool, +} +*/ + +/// Simplified recorder. +pub enum RecorderState { + /// For FullNodes proofs, just send node to this stream. + Stream(O), + /// For FullNodes proofs, just send node to this stream. + Compact(O, Vec), + /// For FullNodes proofs, just send node to this stream. + Content(O, Vec), +} + +/// When process is halted keep execution state +/// to restore later. +pub struct HaltedStateRecord { + recorder: RecorderState, + currently_query_item: Option, +} + +/// When process is halted keep execution state +/// to restore later. +pub struct HaltedStateCheck { + stack: (), + stack_content: (), + currently_query_item: Option, +} + +/// Run query plan on a full db and record it. +/// +/// TODO output and restart are mutually exclusive. -> enum +/// or remove output from halted state. +pub fn record_query_plan<'a, L: TrieLayout, O: codec::Output>( + db: &TrieDB, + content_iter: QueryPlanItemIter<'a>, + output: Option, + restart: Option>, +) -> Result<(), VerifyError, CError>> { + Ok(()) +} + +/// Proof reading iterator. +pub struct ReadProofIterator<'a, L: TrieLayout> { + _ph: PhantomData<&'a L>, +} + +/// Content return on success when reading proof. +pub enum ReadProofItem<'a> { + /// Successfull read of proof, not all content read. + Halted(HaltedStateCheck), + /// Seen value and key in proof. + /// When we set the query plan, we only return content + /// matching the query plan. + Value(&'a [u8], Vec), + /// Seen fully covered prefix in proof, this is only + /// return when we read the proof with the query input (otherwhise + /// we would need to indicate every child without a hash as a prefix). + CoveredPrefix(&'a [u8]), +} + +impl<'a, L: TrieLayout> Iterator for ReadProofIterator<'a, L> { + type Item = Result, VerifyError, CError>>; + + fn next(&mut self) -> Option { + unimplemented!() + } +} + +/// Read the proof. +pub fn prove_query_plan_iter<'a, L: TrieLayout>( + content_iter: Option>, + proof: impl Iterator, + restart: Option, + kind: ProofKind, + skip_hash_validation: bool, +) -> Result<(), VerifyError, CError>> { + if kind == ProofKind::CompactContent { + return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent + } + + Ok(()) +} + +mod compact_content_proof { + + use codec::{Decode, Encode}; + + /// Representation of each encoded action + /// for building the proof. + /// TODO ref variant for encoding ?? + #[derive(Encode, Decode, Debug)] + pub(crate) enum Op { + // key content followed by a mask for last byte. + // If mask erase some content the content need to + // be set at 0 (or error). + // Two consecutive `KeyPush` are invalid. + KeyPush(Vec, u8), /* TODO could use BackingByteVec (but Vec for new as it scale + * encode) */ + // Last call to pop is implicit (up to root), defining + // one will result in an error. + // Two consecutive `KeyPush` are invalid. + // TODO should be compact encoding of number. + KeyPop(u16), + // u8 is child index, shorthand for key push one nibble followed by key pop. + HashChild(Enc, u8), + // All value variant are only after a `KeyPush` or at first position. + HashValue(Enc), + Value(V), + // This is not strictly necessary, only if the proof is not sized, otherwhise if we know + // the stream will end it can be skipped. + EndProof, + } + + #[derive(Debug)] + #[repr(transparent)] + pub struct Enc(pub H); + + impl> Encode for Enc { + fn size_hint(&self) -> usize { + self.0.as_ref().len() + } + + fn encoded_size(&self) -> usize { + self.0.as_ref().len() + } + + fn encode_to(&self, dest: &mut T) { + dest.write(self.0.as_ref()) + } + } + + impl + Default> Decode for Enc { + fn decode(input: &mut I) -> core::result::Result { + let mut dest = H::default(); + input.read(dest.as_mut())?; + Ok(Enc(dest)) + } + } +} From dfee73c725dd128a25e0778c50defa7710c32138 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 29 Mar 2023 12:29:43 +0200 Subject: [PATCH 003/154] forgot read limit --- trie-db/src/query_plan.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 525be3cf..3510f199 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -160,7 +160,9 @@ struct ContentEncodingInfos { */ /// Simplified recorder. -pub enum RecorderState { +pub struct RecorderState(RecorderStateInner); + +enum RecorderStateInner { /// For FullNodes proofs, just send node to this stream. Stream(O), /// For FullNodes proofs, just send node to this stream. @@ -193,8 +195,10 @@ pub fn record_query_plan<'a, L: TrieLayout, O: codec::Output>( content_iter: QueryPlanItemIter<'a>, output: Option, restart: Option>, -) -> Result<(), VerifyError, CError>> { - Ok(()) + size_limit: Option, + node_limit: Option, +) -> Result>, VerifyError, CError>> { + Ok(None) } /// Proof reading iterator. From 69bf9001fe55132b4a39c462e02369206cd589ad Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 29 Mar 2023 17:44:53 +0200 Subject: [PATCH 004/154] equivalent to seek --- trie-db/src/nibble/mod.rs | 26 ++++++ trie-db/src/proof/verify.rs | 4 + trie-db/src/query_plan.rs | 182 ++++++++++++++++++++++++++++++++++-- 3 files changed, 202 insertions(+), 10 deletions(-) diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index e1d758e9..ecabad98 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -101,6 +101,19 @@ pub mod nibble_ops { upper_bound * NIBBLE_PER_BYTE } + /// Count the biggest common depth between two left aligned packed nibble slice and return + /// ordering. + pub fn biggest_depth_and_order(v1: &[u8], v2: &[u8]) -> (usize, cmp::Ordering) { + let upper_bound = cmp::min(v1.len(), v2.len()); + for a in 0..upper_bound { + if v1[a] != v2[a] { + let (common, order) = left_common_and_order(v1[a], v2[a]); + return (a * NIBBLE_PER_BYTE + common, order) + } + } + (upper_bound * NIBBLE_PER_BYTE, v1.len().cmp(&v2.len())) + } + /// Calculate the number of common nibble between two left aligned bytes. #[inline(always)] pub fn left_common(a: u8, b: u8) -> usize { @@ -113,6 +126,19 @@ pub mod nibble_ops { } } + /// Calculate the number of common nibble between two left aligned bytes. + #[inline(always)] + pub fn left_common_and_order(a: u8, b: u8) -> (usize, cmp::Ordering) { + let byte_order = a.cmp(&b); + if byte_order == cmp::Ordering::Equal { + (2, byte_order) + } else if pad_left(a) == pad_left(b) { + (1, byte_order) + } else { + (0, byte_order) + } + } + /// Shifts right aligned key to add a given left offset. /// Resulting in possibly padding at both left and right /// (example usage when combining two keys). diff --git a/trie-db/src/proof/verify.rs b/trie-db/src/proof/verify.rs index fedd0579..4790be6e 100644 --- a/trie-db/src/proof/verify.rs +++ b/trie-db/src/proof/verify.rs @@ -32,6 +32,8 @@ pub enum Error { /// The statement being verified contains multiple key-value pairs with the same key. The /// parameter is the duplicated key. DuplicateKey(Vec), + /// The statement being verified contains key not ordered properly. + UnorderedKey(Vec), /// The proof contains at least one extraneous node. ExtraneousNode, /// The proof contains at least one extraneous value which should have been omitted from the @@ -57,6 +59,8 @@ impl std::fmt::Display for Error write!(f, "Duplicate key in input statement: key={:?}", key), + Error::UnorderedKey(key) => + write!(f, "Unordered key in input statement: key={:?}", key), Error::ExtraneousNode => write!(f, "Extraneous node found in proof"), Error::ExtraneousValue(key) => write!(f, "Extraneous value found in proof should have been omitted: key={:?}", key), diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 3510f199..11b0b69f 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -24,8 +24,11 @@ use core::marker::PhantomData; use crate::{ - node::OwnedNode, proof::VerifyError, rstd::result::Result, CError, TrieError, TrieHash, - TrieLayout, TrieDB, + nibble::{nibble_ops, LeftNibbleSlice, NibbleSlice}, + node::{NodeHandle, NodePlan, OwnedNode}, + proof::VerifyError, + rstd::{cmp::*, result::Result}, + CError, DBValue, NibbleVec, Trie, TrieDB, TrieError, TrieHash, TrieLayout, }; /// Item to query, in memory. @@ -47,6 +50,27 @@ pub struct QueryPlanItem<'a> { as_prefix: bool, } +impl<'a> QueryPlanItem<'a> { + fn before(&self, other: &Self) -> (bool, usize) { + let (common_depth, ordering) = nibble_ops::biggest_depth_and_order(&self.key, &other.key); + + ( + match ordering { + Ordering::Less => { + if self.as_prefix { + // do not allow querying content inside a prefix + !other.key.starts_with(self.key) + } else { + true + } + }, + Ordering::Greater | Ordering::Equal => false, + }, + common_depth, + ) + } +} + /// Query plan in memory. struct InMemQueryPlan { items: Vec, @@ -85,7 +109,7 @@ impl InMemQueryPlan { } /// Query plan. -struct QueryPlan<'a, I> +pub struct QueryPlan<'a, I> where I: Iterator>, { @@ -139,13 +163,19 @@ impl Bitmap { } } +// TODO rename struct CompactEncodingInfos { /// Node in memory content. - node: OwnedNode>, + node: OwnedNode, /// Flags indicating whether each child is omitted in the encoded node. - omit_children: Bitmap, + accessed_children: Bitmap, /// Skip value if value node is after. - omit_value: bool, + accessed_value: bool, + /// Depth of node in nible. + depth: usize, + /// Last descended child, this is only really needed when iterating on + /// prefix. + last_descended_child: u8, } /* likely compact encoding is enough @@ -162,6 +192,7 @@ struct ContentEncodingInfos { /// Simplified recorder. pub struct RecorderState(RecorderStateInner); +// TODO may be useless enum RecorderStateInner { /// For FullNodes proofs, just send node to this stream. Stream(O), @@ -169,6 +200,10 @@ enum RecorderStateInner { Compact(O, Vec), /// For FullNodes proofs, just send node to this stream. Content(O, Vec), + /// Restore from next node prefix to descend into + /// (skipping all query plan before this definition). + /// (prefix is key and a boolean to indicate if padded). + Stateless(O, Vec, bool), } /// When process is halted keep execution state @@ -190,14 +225,139 @@ pub struct HaltedStateCheck { /// /// TODO output and restart are mutually exclusive. -> enum /// or remove output from halted state. -pub fn record_query_plan<'a, L: TrieLayout, O: codec::Output>( +pub fn record_query_plan<'a, L: TrieLayout, I: Iterator>, O: codec::Output>( db: &TrieDB, - content_iter: QueryPlanItemIter<'a>, + mut query_plan: QueryPlan<'a, I>, output: Option, restart: Option>, - size_limit: Option, - node_limit: Option, ) -> Result>, VerifyError, CError>> { + let dummy_parent_hash = TrieHash::::default(); + let mut prefix = NibbleVec::new(); + let mut stack: Vec = Vec::new(); + + let prev_query: Option = None; + while let Some(query) = query_plan.items.next() { + let (ordered, common_nibbles) = + prev_query.as_ref().map(|p| p.before(&query)).unwrap_or((true, 0)); + if !ordered { + if query_plan.ignore_unordered { + continue + } else { + return Err(VerifyError::UnorderedKey(query.key.to_vec())) // TODO not kind as param if keeping + // CompactContent + } + } + loop { + match prefix.len().cmp(&common_nibbles) { + Ordering::Equal | Ordering::Less => break, + Ordering::Greater => + if let Some(item) = stack.pop() { + let depth = stack.last().map(|i| i.depth).unwrap_or(0); + prefix.drop_lasts(prefix.len() - depth); + } else { + return Ok(None) + }, + } + } + + // descend + let mut slice_query = NibbleSlice::new_offset(&query.key, common_nibbles); + let mut touched = false; + let mut iter_prefix = false; + let mut child_index = 0; + loop { + let child_handle = if let Some(mut item) = stack.last_mut() { + if slice_query.is_empty() { + if query.as_prefix { + touched = true; + iter_prefix = true; + } else { + touched = true; + } + break + } + child_index = slice_query.at(0); + + // TODO this could be reuse from iterator, but it seems simple + // enough here too. + let node_data = item.node.data(); + + match item.node.node_plan() { + NodePlan::Empty | NodePlan::Leaf { .. } => break, + NodePlan::Extension { child, .. } => child.build(node_data), + NodePlan::NibbledBranch { children, .. } | + NodePlan::Branch { children, .. } => + if let Some(child) = &children[child_index as usize] { + slice_query.advance(1); + prefix.push(child_index); + item.accessed_children.set(child_index as usize, true); + + child.build(node_data) + } else { + break + }, + } + } else { + NodeHandle::Hash(db.root().as_ref()) + }; + + // TODO handle cache first + let child_node = db + .get_raw_or_lookup(dummy_parent_hash, child_handle, prefix.as_prefix(), false) + .map_err(|_| VerifyError::IncompleteProof)?; // actually incomplete db: TODO consider switching error + + // TODO put in proof (only if Hash or inline for content one) + + // descend in node + let mut node_depth = 0; + let mut descend_incomplete = false; + + // TODO + // + let node_data = child_node.0.data(); + + match child_node.0.node_plan() { + NodePlan::Branch { .. } => (), + | NodePlan::Empty => (), + NodePlan::Leaf { partial, .. } | + NodePlan::NibbledBranch { partial, .. } | + NodePlan::Extension { partial, .. } => { + let partial = partial.build(node_data); + node_depth = partial.len(); + prefix.append_partial(partial.right()); + + if slice_query.starts_with(&partial) { + slice_query.advance(partial.len()); + } else { + descend_incomplete = true; + } + }, + } + + stack.push(CompactEncodingInfos { + node: child_node.0, + accessed_children: Default::default(), + accessed_value: false, + depth: prefix.len() + node_depth, + last_descended_child: child_index, + }); + + if descend_incomplete { + if query.as_prefix { + iter_prefix = true; + } + break + } + } + + if touched { + // try access value + } + if iter_prefix { + // run prefix iteration + } + } + Ok(None) } @@ -214,6 +374,8 @@ pub enum ReadProofItem<'a> { /// When we set the query plan, we only return content /// matching the query plan. Value(&'a [u8], Vec), + /// No value seen for a key in the input query plan. + NoValue(&'a [u8]), /// Seen fully covered prefix in proof, this is only /// return when we read the proof with the query input (otherwhise /// we would need to indicate every child without a hash as a prefix). From 2def8cb28384d30244320d99b86b12ee7ac1a976 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 30 Mar 2023 11:05:04 +0200 Subject: [PATCH 005/154] factor a bit --- trie-db/src/lib.rs | 2 +- trie-db/src/query_plan.rs | 274 +++++++++++++++++++++++++++----------- 2 files changed, 197 insertions(+), 79 deletions(-) diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index e0ae28eb..590ad4fa 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -43,6 +43,7 @@ use node::NodeOwned; pub mod node; pub mod proof; +pub mod query_plan; pub mod recorder; pub mod sectriedb; pub mod sectriedbmut; @@ -56,7 +57,6 @@ mod iterator; mod lookup; mod nibble; mod node_codec; -mod query_plan; mod trie_codec; pub use self::{ diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 11b0b69f..72f32345 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -173,9 +173,9 @@ struct CompactEncodingInfos { accessed_value: bool, /// Depth of node in nible. depth: usize, - /// Last descended child, this is only really needed when iterating on + /// Next descended child, this is only really needed when iterating on /// prefix. - last_descended_child: u8, + next_descended_child: u8, } /* likely compact encoding is enough @@ -225,13 +225,19 @@ pub struct HaltedStateCheck { /// /// TODO output and restart are mutually exclusive. -> enum /// or remove output from halted state. -pub fn record_query_plan<'a, L: TrieLayout, I: Iterator>, O: codec::Output>( +pub fn record_query_plan< + 'a, + L: TrieLayout, + I: Iterator>, + O: codec::Output, +>( db: &TrieDB, mut query_plan: QueryPlan<'a, I>, output: Option, restart: Option>, ) -> Result>, VerifyError, CError>> { let dummy_parent_hash = TrieHash::::default(); + // TODO stack and prefix in a single struct let mut prefix = NibbleVec::new(); let mut stack: Vec = Vec::new(); @@ -251,7 +257,7 @@ pub fn record_query_plan<'a, L: TrieLayout, I: Iterator match prefix.len().cmp(&common_nibbles) { Ordering::Equal | Ordering::Less => break, Ordering::Greater => - if let Some(item) = stack.pop() { + if let Some(_item) = stack.pop() { let depth = stack.last().map(|i| i.depth).unwrap_or(0); prefix.drop_lasts(prefix.len() - depth); } else { @@ -263,104 +269,216 @@ pub fn record_query_plan<'a, L: TrieLayout, I: Iterator // descend let mut slice_query = NibbleSlice::new_offset(&query.key, common_nibbles); let mut touched = false; - let mut iter_prefix = false; - let mut child_index = 0; + let mut iter_prefix = None; loop { - let child_handle = if let Some(mut item) = stack.last_mut() { + if let Some(_item) = stack.last_mut() { if slice_query.is_empty() { if query.as_prefix { - touched = true; - iter_prefix = true; + iter_prefix = Some(stack.len()); } else { touched = true; } break } - child_index = slice_query.at(0); - - // TODO this could be reuse from iterator, but it seems simple - // enough here too. - let node_data = item.node.data(); - - match item.node.node_plan() { - NodePlan::Empty | NodePlan::Leaf { .. } => break, - NodePlan::Extension { child, .. } => child.build(node_data), - NodePlan::NibbledBranch { children, .. } | - NodePlan::Branch { children, .. } => - if let Some(child) = &children[child_index as usize] { - slice_query.advance(1); - prefix.push(child_index); - item.accessed_children.set(child_index as usize, true); - - child.build(node_data) - } else { - break - }, - } } else { - NodeHandle::Hash(db.root().as_ref()) - }; - - // TODO handle cache first - let child_node = db - .get_raw_or_lookup(dummy_parent_hash, child_handle, prefix.as_prefix(), false) - .map_err(|_| VerifyError::IncompleteProof)?; // actually incomplete db: TODO consider switching error - - // TODO put in proof (only if Hash or inline for content one) - - // descend in node - let mut node_depth = 0; - let mut descend_incomplete = false; - - // TODO - // - let node_data = child_node.0.data(); - - match child_node.0.node_plan() { - NodePlan::Branch { .. } => (), - | NodePlan::Empty => (), - NodePlan::Leaf { partial, .. } | - NodePlan::NibbledBranch { partial, .. } | - NodePlan::Extension { partial, .. } => { - let partial = partial.build(node_data); - node_depth = partial.len(); - prefix.append_partial(partial.right()); - - if slice_query.starts_with(&partial) { - slice_query.advance(partial.len()); - } else { - descend_incomplete = true; + break + } + let child_index = slice_query.at(0); + match try_stack_child( + &mut prefix, + &mut stack, + child_index, + db, + dummy_parent_hash, + Some(&mut slice_query), + )? { + TryStackChildResult::Stacked => {}, + TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked => break, + TryStackChildResult::StackedDescendIncomplete => { + if query.as_prefix { + iter_prefix = Some(stack.len()); } + break }, } - - stack.push(CompactEncodingInfos { - node: child_node.0, - accessed_children: Default::default(), - accessed_value: false, - depth: prefix.len() + node_depth, - last_descended_child: child_index, - }); - - if descend_incomplete { - if query.as_prefix { - iter_prefix = true; - } - break - } } if touched { // try access value } - if iter_prefix { + if let Some(prefix_stack_depth) = iter_prefix.take() { // run prefix iteration + + loop { + // Try access value + // TODO + + // descend + let mut stacked = true; + loop { + if stacked { + // try access value in next node + access_value(&prefix, &stack, db)?; + stacked = false; + } + + let child_index = if let Some(mut item) = stack.last_mut() { + if item.next_descended_child as usize >= crate::nibble_ops::NIBBLE_LENGTH { + continue + } + item.next_descended_child += 1; + item.next_descended_child - 1 + } else { + break + }; + match try_stack_child( + &mut prefix, + &mut stack, + child_index, + db, + dummy_parent_hash, + None, + )? { + TryStackChildResult::Stacked => { + stacked = true; + }, + TryStackChildResult::NotStackedBranch => (), + TryStackChildResult::NotStacked => break, + TryStackChildResult::StackedDescendIncomplete => { + unreachable!("no slice query") + }, + } + } + + // pop + + // TODO a pop function + if stack.len() == prefix_stack_depth { + break + } + if let Some(_item) = stack.pop() { + let depth = stack.last().map(|i| i.depth).unwrap_or(0); + prefix.drop_lasts(prefix.len() - depth); + } else { + unreachable!() + } + } } } Ok(None) } +enum TryStackChildResult { + Stacked, + NotStackedBranch, + NotStacked, + StackedDescendIncomplete, +} + +fn try_stack_child<'a, L: TrieLayout>( + prefix: &mut NibbleVec, + stack: &mut Vec, + child_index: u8, + db: &TrieDB, + parent_hash: TrieHash, + mut slice_query: Option<&mut NibbleSlice>, +) -> Result, CError>> { + let mut descend_incomplete = false; + let child_handle = if let Some(item) = stack.last_mut() { + // TODO this could be reuse from iterator, but it seems simple + // enough here too. + let node_data = item.node.data(); + + match item.node.node_plan() { + NodePlan::Empty | NodePlan::Leaf { .. } => return Ok(TryStackChildResult::NotStacked), + NodePlan::Extension { child, .. } => child.build(node_data), + NodePlan::NibbledBranch { children, .. } | NodePlan::Branch { children, .. } => + if let Some(child) = &children[child_index as usize] { + slice_query.as_mut().map(|s| s.advance(1)); + prefix.push(child_index); + item.accessed_children.set(child_index as usize, true); + child.build(node_data) + } else { + return Ok(TryStackChildResult::NotStackedBranch) + }, + } + } else { + NodeHandle::Hash(db.root().as_ref()) + }; + // TODO handle cache first + let child_node = db + .get_raw_or_lookup(parent_hash, child_handle, prefix.as_prefix(), false) + .map_err(|_| VerifyError::IncompleteProof)?; // actually incomplete db: TODO consider switching error + + // TODO put in proof (only if Hash or inline for content one) + + let mut node_depth = 0; + let node_data = child_node.0.data(); + + match child_node.0.node_plan() { + NodePlan::Branch { .. } => (), + | NodePlan::Empty => (), + NodePlan::Leaf { partial, .. } | + NodePlan::NibbledBranch { partial, .. } | + NodePlan::Extension { partial, .. } => { + let partial = partial.build(node_data); + node_depth = partial.len(); + prefix.append_partial(partial.right()); + if let Some(s) = slice_query { + if s.starts_with(&partial) { + s.advance(partial.len()); + } else { + descend_incomplete = true; + } + } + }, + } + + stack.push(CompactEncodingInfos { + node: child_node.0, + accessed_children: Default::default(), + accessed_value: false, + depth: prefix.len() + node_depth, + next_descended_child: child_index + 1, + }); + + if descend_incomplete { + Ok(TryStackChildResult::StackedDescendIncomplete) + } else { + Ok(TryStackChildResult::Stacked) + } +} + +fn access_value<'a, L: TrieLayout>( + prefix: &NibbleVec, + stack: &Vec, + db: &TrieDB, +) -> Result, CError>> { + let Some(item)= stack.last() else { + return Ok(false) + }; + // TODO this could be reuse from iterator, but it seems simple + // enough here too. + let node_data = item.node.data(); + + let value = match item.node.node_plan() { + NodePlan::Leaf { value, .. } => value.build(node_data), + NodePlan::Branch { value, .. } | NodePlan::NibbledBranch { value, .. } => { + if let Some(value) = value { + value.build(node_data) + } else { + return Ok(false) + } + }, + _ => return Ok(false), + }; + + // TODO register access to key and value + + Ok(true) +} + /// Proof reading iterator. pub struct ReadProofIterator<'a, L: TrieLayout> { _ph: PhantomData<&'a L>, From 1fc155b30d48b578dd2f65fd0fe4f2e9a95b9afd Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 30 Mar 2023 11:23:52 +0200 Subject: [PATCH 006/154] use a stack struct --- trie-db/src/query_plan.rs | 274 +++++++++++++++++++------------------- 1 file changed, 139 insertions(+), 135 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 72f32345..e917b68e 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -221,6 +221,13 @@ pub struct HaltedStateCheck { currently_query_item: Option, } +#[derive(Default)] +struct RecordStack { + items: Vec, + prefix: NibbleVec, + iter_prefix: Option, +} + /// Run query plan on a full db and record it. /// /// TODO output and restart are mutually exclusive. -> enum @@ -237,9 +244,7 @@ pub fn record_query_plan< restart: Option>, ) -> Result>, VerifyError, CError>> { let dummy_parent_hash = TrieHash::::default(); - // TODO stack and prefix in a single struct - let mut prefix = NibbleVec::new(); - let mut stack: Vec = Vec::new(); + let mut stack = RecordStack::default(); let prev_query: Option = None; while let Some(query) = query_plan.items.next() { @@ -254,13 +259,10 @@ pub fn record_query_plan< } } loop { - match prefix.len().cmp(&common_nibbles) { + match stack.prefix.len().cmp(&common_nibbles) { Ordering::Equal | Ordering::Less => break, Ordering::Greater => - if let Some(_item) = stack.pop() { - let depth = stack.last().map(|i| i.depth).unwrap_or(0); - prefix.drop_lasts(prefix.len() - depth); - } else { + if !stack.pop() { return Ok(None) }, } @@ -269,24 +271,18 @@ pub fn record_query_plan< // descend let mut slice_query = NibbleSlice::new_offset(&query.key, common_nibbles); let mut touched = false; - let mut iter_prefix = None; loop { - if let Some(_item) = stack.last_mut() { - if slice_query.is_empty() { - if query.as_prefix { - iter_prefix = Some(stack.len()); - } else { - touched = true; - } - break + if slice_query.is_empty() { + if query.as_prefix { + stack.enter_prefix_iter(); + } else { + touched = true; } - } else { break } + let child_index = slice_query.at(0); - match try_stack_child( - &mut prefix, - &mut stack, + match stack.try_stack_child( child_index, db, dummy_parent_hash, @@ -296,7 +292,7 @@ pub fn record_query_plan< TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked => break, TryStackChildResult::StackedDescendIncomplete => { if query.as_prefix { - iter_prefix = Some(stack.len()); + stack.enter_prefix_iter(); } break }, @@ -305,24 +301,21 @@ pub fn record_query_plan< if touched { // try access value + stack.access_value(db)?; } - if let Some(prefix_stack_depth) = iter_prefix.take() { + if let Some(prefix_stack_depth) = stack.iter_prefix.clone() { // run prefix iteration - loop { - // Try access value - // TODO - // descend let mut stacked = true; loop { if stacked { // try access value in next node - access_value(&prefix, &stack, db)?; + stack.access_value(db)?; stacked = false; } - let child_index = if let Some(mut item) = stack.last_mut() { + let child_index = if let Some(mut item) = stack.items.last_mut() { if item.next_descended_child as usize >= crate::nibble_ops::NIBBLE_LENGTH { continue } @@ -331,14 +324,7 @@ pub fn record_query_plan< } else { break }; - match try_stack_child( - &mut prefix, - &mut stack, - child_index, - db, - dummy_parent_hash, - None, - )? { + match stack.try_stack_child(child_index, db, dummy_parent_hash, None)? { TryStackChildResult::Stacked => { stacked = true; }, @@ -352,17 +338,11 @@ pub fn record_query_plan< // pop - // TODO a pop function - if stack.len() == prefix_stack_depth { + if !stack.pop() { break } - if let Some(_item) = stack.pop() { - let depth = stack.last().map(|i| i.depth).unwrap_or(0); - prefix.drop_lasts(prefix.len() - depth); - } else { - unreachable!() - } } + stack.exit_prefix_iter(); } } @@ -376,107 +356,131 @@ enum TryStackChildResult { StackedDescendIncomplete, } -fn try_stack_child<'a, L: TrieLayout>( - prefix: &mut NibbleVec, - stack: &mut Vec, - child_index: u8, - db: &TrieDB, - parent_hash: TrieHash, - mut slice_query: Option<&mut NibbleSlice>, -) -> Result, CError>> { - let mut descend_incomplete = false; - let child_handle = if let Some(item) = stack.last_mut() { +impl RecordStack { + fn try_stack_child<'a, L: TrieLayout>( + &mut self, + child_index: u8, + db: &TrieDB, + parent_hash: TrieHash, + mut slice_query: Option<&mut NibbleSlice>, + ) -> Result, CError>> { + let prefix = &mut self.prefix; + let stack = &mut self.items; + let mut descend_incomplete = false; + let child_handle = if let Some(item) = stack.last_mut() { + // TODO this could be reuse from iterator, but it seems simple + // enough here too. + let node_data = item.node.data(); + + match item.node.node_plan() { + NodePlan::Empty | NodePlan::Leaf { .. } => + return Ok(TryStackChildResult::NotStacked), + NodePlan::Extension { child, .. } => child.build(node_data), + NodePlan::NibbledBranch { children, .. } | NodePlan::Branch { children, .. } => + if let Some(child) = &children[child_index as usize] { + slice_query.as_mut().map(|s| s.advance(1)); + prefix.push(child_index); + item.accessed_children.set(child_index as usize, true); + child.build(node_data) + } else { + return Ok(TryStackChildResult::NotStackedBranch) + }, + } + } else { + NodeHandle::Hash(db.root().as_ref()) + }; + // TODO handle cache first + let child_node = db + .get_raw_or_lookup(parent_hash, child_handle, prefix.as_prefix(), false) + .map_err(|_| VerifyError::IncompleteProof)?; // actually incomplete db: TODO consider switching error + + // TODO put in proof (only if Hash or inline for content one) + + let mut node_depth = 0; + let node_data = child_node.0.data(); + + match child_node.0.node_plan() { + NodePlan::Branch { .. } => (), + | NodePlan::Empty => (), + NodePlan::Leaf { partial, .. } | + NodePlan::NibbledBranch { partial, .. } | + NodePlan::Extension { partial, .. } => { + let partial = partial.build(node_data); + node_depth = partial.len(); + prefix.append_partial(partial.right()); + if let Some(s) = slice_query { + if s.starts_with(&partial) { + s.advance(partial.len()); + } else { + descend_incomplete = true; + } + } + }, + } + + stack.push(CompactEncodingInfos { + node: child_node.0, + accessed_children: Default::default(), + accessed_value: false, + depth: prefix.len() + node_depth, + next_descended_child: child_index + 1, + }); + + if descend_incomplete { + Ok(TryStackChildResult::StackedDescendIncomplete) + } else { + Ok(TryStackChildResult::Stacked) + } + } + + fn access_value<'a, L: TrieLayout>( + &self, + db: &TrieDB, + ) -> Result, CError>> { + let Some(item)= self.items.last() else { + return Ok(false) + }; // TODO this could be reuse from iterator, but it seems simple // enough here too. let node_data = item.node.data(); - match item.node.node_plan() { - NodePlan::Empty | NodePlan::Leaf { .. } => return Ok(TryStackChildResult::NotStacked), - NodePlan::Extension { child, .. } => child.build(node_data), - NodePlan::NibbledBranch { children, .. } | NodePlan::Branch { children, .. } => - if let Some(child) = &children[child_index as usize] { - slice_query.as_mut().map(|s| s.advance(1)); - prefix.push(child_index); - item.accessed_children.set(child_index as usize, true); - child.build(node_data) + let value = match item.node.node_plan() { + NodePlan::Leaf { value, .. } => value.build(node_data), + NodePlan::Branch { value, .. } | NodePlan::NibbledBranch { value, .. } => { + if let Some(value) = value { + value.build(node_data) } else { - return Ok(TryStackChildResult::NotStackedBranch) - }, - } - } else { - NodeHandle::Hash(db.root().as_ref()) - }; - // TODO handle cache first - let child_node = db - .get_raw_or_lookup(parent_hash, child_handle, prefix.as_prefix(), false) - .map_err(|_| VerifyError::IncompleteProof)?; // actually incomplete db: TODO consider switching error - - // TODO put in proof (only if Hash or inline for content one) - - let mut node_depth = 0; - let node_data = child_node.0.data(); - - match child_node.0.node_plan() { - NodePlan::Branch { .. } => (), - | NodePlan::Empty => (), - NodePlan::Leaf { partial, .. } | - NodePlan::NibbledBranch { partial, .. } | - NodePlan::Extension { partial, .. } => { - let partial = partial.build(node_data); - node_depth = partial.len(); - prefix.append_partial(partial.right()); - if let Some(s) = slice_query { - if s.starts_with(&partial) { - s.advance(partial.len()); - } else { - descend_incomplete = true; + return Ok(false) } - } - }, - } + }, + _ => return Ok(false), + }; + + // TODO register access to key and value - stack.push(CompactEncodingInfos { - node: child_node.0, - accessed_children: Default::default(), - accessed_value: false, - depth: prefix.len() + node_depth, - next_descended_child: child_index + 1, - }); - - if descend_incomplete { - Ok(TryStackChildResult::StackedDescendIncomplete) - } else { - Ok(TryStackChildResult::Stacked) + Ok(true) } -} -fn access_value<'a, L: TrieLayout>( - prefix: &NibbleVec, - stack: &Vec, - db: &TrieDB, -) -> Result, CError>> { - let Some(item)= stack.last() else { - return Ok(false) - }; - // TODO this could be reuse from iterator, but it seems simple - // enough here too. - let node_data = item.node.data(); - - let value = match item.node.node_plan() { - NodePlan::Leaf { value, .. } => value.build(node_data), - NodePlan::Branch { value, .. } | NodePlan::NibbledBranch { value, .. } => { - if let Some(value) = value { - value.build(node_data) - } else { - return Ok(false) - } - }, - _ => return Ok(false), - }; + fn pop(&mut self) -> bool { + if self.iter_prefix == Some(self.items.len()) { + return false + } + if let Some(_item) = self.items.pop() { + let depth = self.items.last().map(|i| i.depth).unwrap_or(0); + self.prefix.drop_lasts(self.prefix.len() - depth); + true + } else { + false + } + } - // TODO register access to key and value + fn enter_prefix_iter(&mut self) { + self.iter_prefix = Some(self.items.len()); + } - Ok(true) + fn exit_prefix_iter(&mut self) { + self.iter_prefix = None + } } /// Proof reading iterator. From a4e6a03f766658383ceaabe34f23f12131ef3f48 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 30 Mar 2023 12:06:40 +0200 Subject: [PATCH 007/154] init a test --- trie-db/src/query_plan.rs | 69 +++++++++++++++++++++++---------------- trie-db/test/src/proof.rs | 53 ++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 29 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index e917b68e..f9c08a13 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -38,6 +38,10 @@ pub struct InMemQueryPlanItem { } impl InMemQueryPlanItem { + /// Create new item. + pub fn new(key: Vec, as_prefix: bool) -> Self { + Self { key, as_prefix } + } /// Get ref. pub fn as_ref(&self) -> QueryPlanItem { QueryPlanItem { key: &self.key, as_prefix: self.as_prefix } @@ -72,12 +76,10 @@ impl<'a> QueryPlanItem<'a> { } /// Query plan in memory. -struct InMemQueryPlan { - items: Vec, - current: Option, - ensure_ordered: bool, - ignore_unordered: bool, - allow_under_prefix: bool, +pub struct InMemQueryPlan { + pub items: Vec, + pub current: Option, + pub ignore_unordered: bool, } /// Iterator as type of mapped slice iter is very noisy. @@ -97,13 +99,11 @@ impl<'a> Iterator for QueryPlanItemIter<'a> { impl InMemQueryPlan { /// Get ref. - fn as_ref(&self) -> QueryPlan { + pub fn as_ref(&self) -> QueryPlan { QueryPlan { items: QueryPlanItemIter(&self.items, 0), current: self.current.as_ref().map(|i| i.as_ref()), - ensure_ordered: self.ensure_ordered, ignore_unordered: self.ignore_unordered, - allow_under_prefix: self.allow_under_prefix, } } } @@ -115,9 +115,7 @@ where { items: I, current: Option>, - ensure_ordered: bool, ignore_unordered: bool, - allow_under_prefix: bool, } /// Different proof support. @@ -236,13 +234,16 @@ pub fn record_query_plan< 'a, L: TrieLayout, I: Iterator>, - O: codec::Output, + // O: codec::Output, >( db: &TrieDB, mut query_plan: QueryPlan<'a, I>, - output: Option, - restart: Option>, -) -> Result>, VerifyError, CError>> { + // output: Option, + restart: Option<()>, + // restart: Option>, // TODO restore + //) -> Result>, VerifyError, CError>> { // TODO + //) restore +) -> Result, VerifyError, CError>> { let dummy_parent_hash = TrieHash::::default(); let mut stack = RecordStack::default(); @@ -367,6 +368,7 @@ impl RecordStack { let prefix = &mut self.prefix; let stack = &mut self.items; let mut descend_incomplete = false; + let mut stack_extension = false; let child_handle = if let Some(item) = stack.last_mut() { // TODO this could be reuse from iterator, but it seems simple // enough here too. @@ -375,7 +377,10 @@ impl RecordStack { match item.node.node_plan() { NodePlan::Empty | NodePlan::Leaf { .. } => return Ok(TryStackChildResult::NotStacked), - NodePlan::Extension { child, .. } => child.build(node_data), + NodePlan::Extension { child, .. } => { + stack_extension = true; + child.build(node_data) + }, NodePlan::NibbledBranch { children, .. } | NodePlan::Branch { children, .. } => if let Some(child) = &children[child_index as usize] { slice_query.as_mut().map(|s| s.advance(1)); @@ -396,7 +401,6 @@ impl RecordStack { // TODO put in proof (only if Hash or inline for content one) - let mut node_depth = 0; let node_data = child_node.0.data(); match child_node.0.node_plan() { @@ -406,9 +410,8 @@ impl RecordStack { NodePlan::NibbledBranch { partial, .. } | NodePlan::Extension { partial, .. } => { let partial = partial.build(node_data); - node_depth = partial.len(); prefix.append_partial(partial.right()); - if let Some(s) = slice_query { + if let Some(s) = slice_query.as_mut() { if s.starts_with(&partial) { s.advance(partial.len()); } else { @@ -417,14 +420,21 @@ impl RecordStack { } }, } - - stack.push(CompactEncodingInfos { - node: child_node.0, - accessed_children: Default::default(), - accessed_value: false, - depth: prefix.len() + node_depth, - next_descended_child: child_index + 1, - }); + if stack_extension { + // rec call to stack branch + let sbranch = self.try_stack_child(child_index, db, parent_hash, slice_query)?; + let TryStackChildResult::Stacked = sbranch else { + return Err(VerifyError::InvalidChildReference(b"branch in db should follow extension".to_vec())); + }; + } else { + stack.push(CompactEncodingInfos { + node: child_node.0, + accessed_children: Default::default(), + accessed_value: false, + depth: prefix.len(), + next_descended_child: child_index + 1, + }); + } if descend_incomplete { Ok(TryStackChildResult::StackedDescendIncomplete) @@ -434,10 +444,10 @@ impl RecordStack { } fn access_value<'a, L: TrieLayout>( - &self, + &mut self, db: &TrieDB, ) -> Result, CError>> { - let Some(item)= self.items.last() else { + let Some(item)= self.items.last_mut() else { return Ok(false) }; // TODO this could be reuse from iterator, but it seems simple @@ -456,6 +466,7 @@ impl RecordStack { _ => return Ok(false), }; + item.accessed_value = true; // TODO register access to key and value Ok(true) diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index cca2c70e..3e951a89 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -240,3 +240,56 @@ fn test_verify_decode_error_internal() { result => panic!("expected VerifyError::DecodeError, got {:?}", result), } } + +test_layouts!(test_query_plan, test_query_plan_internal); +fn test_query_plan_internal() { + use trie_db::query_plan::{record_query_plan, InMemQueryPlan, InMemQueryPlanItem}; + let set = test_entries(); + let (db, root) = { + let mut db = >::default(); + let mut root = Default::default(); + { + let mut trie = >::new(&mut db, &mut root).build(); + for (key, value) in set.iter() { + trie.insert(key, value).unwrap(); + } + } + (db, root) + }; + // TODO add a cache + let db = >::new(&db, &root).build(); + + let query_plans = [ + InMemQueryPlan { + items: vec![ + InMemQueryPlanItem::new(b"bravo".to_vec(), false), + InMemQueryPlanItem::new(b"doge".to_vec(), false), + InMemQueryPlanItem::new(b"horsey".to_vec(), false), + ], + current: None, + ignore_unordered: false, + }, + InMemQueryPlan { + items: vec![ + InMemQueryPlanItem::new(b"bravo".to_vec(), false), + InMemQueryPlanItem::new(b"do".to_vec(), true), + ], + current: None, + ignore_unordered: false, + }, + InMemQueryPlan { + items: vec![InMemQueryPlanItem::new(b"".to_vec(), true)], + current: None, + ignore_unordered: false, + }, + ]; + for query_plan in query_plans { + // no limit + + let query_plan_iter = query_plan.as_ref(); + let paused = record_query_plan::(&db, query_plan_iter, None).unwrap(); + assert!(paused.is_none()); + + // TODO limit 1, 2, 3 + } +} From a902d93110f30a957918bcf567e0d1da4b38cfde Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 30 Mar 2023 14:17:17 +0200 Subject: [PATCH 008/154] don t rely on codec output --- trie-db/src/query_plan.rs | 81 +++++++++++++++++++++++++++++++++------ 1 file changed, 70 insertions(+), 11 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index f9c08a13..5c715d44 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -27,7 +27,7 @@ use crate::{ nibble::{nibble_ops, LeftNibbleSlice, NibbleSlice}, node::{NodeHandle, NodePlan, OwnedNode}, proof::VerifyError, - rstd::{cmp::*, result::Result}, + rstd::{borrow::Cow, cmp::*, result::Result}, CError, DBValue, NibbleVec, Trie, TrieDB, TrieError, TrieHash, TrieLayout, }; @@ -136,6 +136,13 @@ pub enum ProofKind { /// proof at once. CompactNodes, + /// Same encoding as CompactNodes, but with an alternate ordering that allows streaming + /// node and avoid unbound memory when building proof. + /// + /// Ordering is starting at first met proof and parent up to intersection with next + /// sibling access in a branch, then next leaf, and repeating, finishing with root node. + CompactNodesStream, + /// Content oriented proof, no nodes are written, just a /// sequence of accessed by lexicographical order as described /// in compact_content_proof::Op. @@ -187,28 +194,80 @@ struct ContentEncodingInfos { } */ +/// Allows sending proof recording as it is produced. +pub trait RecorderOutput { + /// Append bytes. + fn write_bytes(&mut self, bytes: &[u8]); + + /// Append a delimited sequence of bytes (usually a node). + fn write_entry(&mut self, bytes: Cow<[u8]>); +} + +/// Simple in memory recorder. +/// Depending on type of proof, nodes or buffer should +/// be used. +/// Sequence is guaranteed by pushing buffer in nodes +/// every time a node is written. +pub struct InMemoryRecorder { + pub nodes: Vec, + pub buffer: Vec, +} + +impl RecorderOutput for InMemoryRecorder { + fn write_bytes(&mut self, bytes: &[u8]) { + if !self.buffer.is_empty() { + self.nodes.push(core::mem::take(&mut self.buffer)); + } + self.buffer.extend_from_slice(bytes) + } + + fn write_entry(&mut self, bytes: Cow<[u8]>) { + self.nodes.push(bytes.into_owned()); + } +} + /// Simplified recorder. -pub struct RecorderState(RecorderStateInner); +pub struct Recorder(RecorderStateInner); + +impl Recorder { + /// Instantiate a new recorder. + pub fn new(proof_kind: ProofKind, output: O) -> Self { + let recorder = match proof_kind { + ProofKind::FullNodes => RecorderStateInner::Stream(output), + ProofKind::CompactNodes => + RecorderStateInner::Compact { output, proof: Vec::new(), stacked_pos: Vec::new() }, + ProofKind::CompactNodesStream => RecorderStateInner::CompactStream(output, Vec::new()), + ProofKind::CompactContent => RecorderStateInner::Content(output), + }; + Self(recorder) + } +} // TODO may be useless -enum RecorderStateInner { +enum RecorderStateInner { /// For FullNodes proofs, just send node to this stream. Stream(O), + /// For FullNodes proofs, Requires keeping all proof before sending it. + Compact { + output: O, + proof: Vec>, + /// Stacked position in proof to modify proof as needed + /// when information got accessed. + stacked_pos: Vec, + }, /// For FullNodes proofs, just send node to this stream. - Compact(O, Vec), + CompactStream(O, Vec), /// For FullNodes proofs, just send node to this stream. - Content(O, Vec), - /// Restore from next node prefix to descend into - /// (skipping all query plan before this definition). - /// (prefix is key and a boolean to indicate if padded). - Stateless(O, Vec, bool), + Content(O), } /// When process is halted keep execution state /// to restore later. -pub struct HaltedStateRecord { - recorder: RecorderState, +pub struct HaltedStateRecord { + recorder: Recorder, currently_query_item: Option, + stack: Option, + restart: Option<(Vec, bool)>, } /// When process is halted keep execution state From 3e213513bc3a67a46f126331c255c22c865992a5 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 30 Mar 2023 20:50:31 +0200 Subject: [PATCH 009/154] ~ check for full node, need debugging, then need restart impl and test, and then only compact. --- trie-db/src/proof/verify.rs | 3 + trie-db/src/query_plan.rs | 530 +++++++++++++++++++++++++++++++++--- trie-db/test/src/proof.rs | 51 +++- 3 files changed, 533 insertions(+), 51 deletions(-) diff --git a/trie-db/src/proof/verify.rs b/trie-db/src/proof/verify.rs index 4790be6e..65717dc4 100644 --- a/trie-db/src/proof/verify.rs +++ b/trie-db/src/proof/verify.rs @@ -49,6 +49,8 @@ pub enum Error { IncompleteProof, /// The root hash computed from the proof is incorrect. RootMismatch(HO), + /// The hash computed from a node is incorrect. + HashMismatch(HO), /// One of the proof nodes could not be decoded. DecodeError(CE), } @@ -75,6 +77,7 @@ impl std::fmt::Display for Error write!(f, "Proof is incomplete -- expected more nodes"), Error::RootMismatch(hash) => write!(f, "Computed incorrect root {:?} from proof", hash), + Error::HashMismatch(hash) => write!(f, "Computed incorrect hash {:?} from node", hash), Error::DecodeError(err) => write!(f, "Unable to decode proof node: {}", err), } } diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 5c715d44..5163d2b3 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -25,13 +25,19 @@ use core::marker::PhantomData; use crate::{ nibble::{nibble_ops, LeftNibbleSlice, NibbleSlice}, - node::{NodeHandle, NodePlan, OwnedNode}, + node::{NodeHandle, NodePlan, OwnedNode, Value}, proof::VerifyError, - rstd::{borrow::Cow, cmp::*, result::Result}, + rstd::{ + borrow::{Borrow, Cow}, + cmp::*, + result::Result, + }, CError, DBValue, NibbleVec, Trie, TrieDB, TrieError, TrieHash, TrieLayout, }; +use hash_db::Hasher; /// Item to query, in memory. +#[derive(Default)] pub struct InMemQueryPlanItem { key: Vec, as_prefix: bool, @@ -78,7 +84,6 @@ impl<'a> QueryPlanItem<'a> { /// Query plan in memory. pub struct InMemQueryPlan { pub items: Vec, - pub current: Option, pub ignore_unordered: bool, } @@ -102,7 +107,6 @@ impl InMemQueryPlan { pub fn as_ref(&self) -> QueryPlan { QueryPlan { items: QueryPlanItemIter(&self.items, 0), - current: self.current.as_ref().map(|i| i.as_ref()), ignore_unordered: self.ignore_unordered, } } @@ -114,7 +118,6 @@ where I: Iterator>, { items: I, - current: Option>, ignore_unordered: bool, } @@ -181,6 +184,8 @@ struct CompactEncodingInfos { /// Next descended child, this is only really needed when iterating on /// prefix. next_descended_child: u8, + /// Is the node inline. + is_inline: bool, } /* likely compact encoding is enough @@ -208,6 +213,7 @@ pub trait RecorderOutput { /// be used. /// Sequence is guaranteed by pushing buffer in nodes /// every time a node is written. +#[derive(Default)] pub struct InMemoryRecorder { pub nodes: Vec, pub buffer: Vec, @@ -230,17 +236,94 @@ impl RecorderOutput for InMemoryRecorder { pub struct Recorder(RecorderStateInner); impl Recorder { + /// Get back output handle from a recorder. + pub fn output(self) -> O { + match self.0 { + RecorderStateInner::Stream(output) | + RecorderStateInner::Compact { output, .. } | + RecorderStateInner::CompactStream(output) | + RecorderStateInner::Content(output) => output, + } + } + /// Instantiate a new recorder. pub fn new(proof_kind: ProofKind, output: O) -> Self { let recorder = match proof_kind { ProofKind::FullNodes => RecorderStateInner::Stream(output), ProofKind::CompactNodes => RecorderStateInner::Compact { output, proof: Vec::new(), stacked_pos: Vec::new() }, - ProofKind::CompactNodesStream => RecorderStateInner::CompactStream(output, Vec::new()), + ProofKind::CompactNodesStream => RecorderStateInner::CompactStream(output), ProofKind::CompactContent => RecorderStateInner::Content(output), }; Self(recorder) } + + fn record_stacked_node(&mut self, item: &CompactEncodingInfos, stack_pos: usize) { + match &mut self.0 { + RecorderStateInner::Stream(output) => { + output.write_entry(item.node.data().into()); + }, + RecorderStateInner::Compact { output, proof, stacked_pos } => { + unimplemented!() + }, + RecorderStateInner::CompactStream(output) => { + unimplemented!() + }, + RecorderStateInner::Content(output) => { + unimplemented!() + }, + } + } + + fn record_popped_node(&mut self, item: &CompactEncodingInfos, stack_pos: usize) { + match &mut self.0 { + RecorderStateInner::Stream(_) => (), + RecorderStateInner::Compact { output, proof, stacked_pos } => { + unimplemented!() + }, + RecorderStateInner::CompactStream(output) => { + unimplemented!() + }, + RecorderStateInner::Content(output) => { + unimplemented!() + }, + } + } + + fn record_value_node(&mut self, value: Vec) { + match &mut self.0 { + RecorderStateInner::Stream(output) => { + output.write_entry(value.into()); + }, + RecorderStateInner::Compact { output, proof, stacked_pos } => { + unimplemented!() + }, + RecorderStateInner::CompactStream(output) => { + unimplemented!() + }, + RecorderStateInner::Content(output) => { + unimplemented!() + }, + } + } + + fn record_value_inline(&mut self, value: &[u8]) { + match &mut self.0 { + RecorderStateInner::Stream(output) => { + // not writing inline value (already + // in parent node). + }, + RecorderStateInner::Compact { output, proof, stacked_pos } => { + unimplemented!() + }, + RecorderStateInner::CompactStream(output) => { + unimplemented!() + }, + RecorderStateInner::Content(output) => { + unimplemented!() + }, + } + } } // TODO may be useless @@ -256,7 +339,7 @@ enum RecorderStateInner { stacked_pos: Vec, }, /// For FullNodes proofs, just send node to this stream. - CompactStream(O, Vec), + CompactStream(O), /// For FullNodes proofs, just send node to this stream. Content(O), } @@ -264,10 +347,34 @@ enum RecorderStateInner { /// When process is halted keep execution state /// to restore later. pub struct HaltedStateRecord { - recorder: Recorder, currently_query_item: Option, - stack: Option, - restart: Option<(Vec, bool)>, + stack: RecordStack, + // This indicate a restore point, it takes precedence over + // stack and currently_query_item. + from: Option<(Vec, bool)>, +} + +impl HaltedStateRecord { + pub fn from_start(recorder: Recorder) -> Self { + HaltedStateRecord { + currently_query_item: None, + stack: RecordStack { + recorder, + items: Vec::new(), + prefix: NibbleVec::new(), + iter_prefix: None, + }, + from: Some(Default::default()), + } + } + + pub fn is_finished(&self) -> bool { + self.from == None + } + + pub fn finish(self) -> Recorder { + self.stack.recorder + } } /// When process is halted keep execution state @@ -278,8 +385,8 @@ pub struct HaltedStateCheck { currently_query_item: Option, } -#[derive(Default)] -struct RecordStack { +struct RecordStack { + recorder: Recorder, items: Vec, prefix: NibbleVec, iter_prefix: Option, @@ -293,21 +400,27 @@ pub fn record_query_plan< 'a, L: TrieLayout, I: Iterator>, - // O: codec::Output, + O: RecorderOutput, >( db: &TrieDB, mut query_plan: QueryPlan<'a, I>, - // output: Option, - restart: Option<()>, - // restart: Option>, // TODO restore - //) -> Result>, VerifyError, CError>> { // TODO - //) restore -) -> Result, VerifyError, CError>> { + mut from: HaltedStateRecord, +) -> Result, VerifyError, CError>> { + // TODO + //) resto let dummy_parent_hash = TrieHash::::default(); - let mut stack = RecordStack::default(); + if let Some(lower_bound) = from.from.take() { + // TODO implement stack building from lower bound: fetch in stack and don't record. + // First also advance iterator to skip and init currently_query_item. + } + + let stack = &mut from.stack; let prev_query: Option = None; - while let Some(query) = query_plan.items.next() { + let mut from_query = from.currently_query_item.take(); + while let Some(query) = + from_query.as_ref().map(|f| f.as_ref()).or_else(|| query_plan.items.next()) + { let (ordered, common_nibbles) = prev_query.as_ref().map(|p| p.before(&query)).unwrap_or((true, 0)); if !ordered { @@ -323,7 +436,7 @@ pub fn record_query_plan< Ordering::Equal | Ordering::Less => break, Ordering::Greater => if !stack.pop() { - return Ok(None) + return Ok(from) }, } } @@ -359,6 +472,8 @@ pub fn record_query_plan< } } + from_query = None; + if touched { // try access value stack.access_value(db)?; @@ -406,7 +521,7 @@ pub fn record_query_plan< } } - Ok(None) + Ok(from) } enum TryStackChildResult { @@ -416,7 +531,7 @@ enum TryStackChildResult { StackedDescendIncomplete, } -impl RecordStack { +impl RecordStack { fn try_stack_child<'a, L: TrieLayout>( &mut self, child_index: u8, @@ -424,13 +539,12 @@ impl RecordStack { parent_hash: TrieHash, mut slice_query: Option<&mut NibbleSlice>, ) -> Result, CError>> { + let mut is_inline = false; let prefix = &mut self.prefix; let stack = &mut self.items; let mut descend_incomplete = false; let mut stack_extension = false; let child_handle = if let Some(item) = stack.last_mut() { - // TODO this could be reuse from iterator, but it seems simple - // enough here too. let node_data = item.node.data(); match item.node.node_plan() { @@ -453,6 +567,11 @@ impl RecordStack { } else { NodeHandle::Hash(db.root().as_ref()) }; + if let &NodeHandle::Inline(_) = &child_handle { + // TODO consider not going into inline for all proof but content. + // Returning NotStacked here sounds safe, then the is_inline field is not needed. + is_inline = true; + } // TODO handle cache first let child_node = db .get_raw_or_lookup(parent_hash, child_handle, prefix.as_prefix(), false) @@ -486,13 +605,16 @@ impl RecordStack { return Err(VerifyError::InvalidChildReference(b"branch in db should follow extension".to_vec())); }; } else { - stack.push(CompactEncodingInfos { + let infos = CompactEncodingInfos { node: child_node.0, accessed_children: Default::default(), accessed_value: false, depth: prefix.len(), next_descended_child: child_index + 1, - }); + is_inline, + }; + self.recorder.record_stacked_node(&infos, stack.len()); + stack.push(infos); } if descend_incomplete { @@ -524,10 +646,20 @@ impl RecordStack { }, _ => return Ok(false), }; - item.accessed_value = true; - // TODO register access to key and value - + match value { + Value::Node(hash_slice) => { + let mut hash = TrieHash::::default(); + hash.as_mut().copy_from_slice(hash_slice); + let Some(value) = db.db().get(&hash, self.prefix.as_prefix()) else { + return Err(VerifyError::IncompleteProof); + }; + self.recorder.record_value_node(value); + }, + Value::Inline(value) => { + self.recorder.record_value_inline(value); + }, + } Ok(true) } @@ -535,7 +667,8 @@ impl RecordStack { if self.iter_prefix == Some(self.items.len()) { return false } - if let Some(_item) = self.items.pop() { + if let Some(item) = self.items.pop() { + self.recorder.record_popped_node(&item, self.items.len()); let depth = self.items.last().map(|i| i.depth).unwrap_or(0); self.prefix.drop_lasts(self.prefix.len() - depth); true @@ -554,10 +687,59 @@ impl RecordStack { } /// Proof reading iterator. -pub struct ReadProofIterator<'a, L: TrieLayout> { +pub struct ReadProofIterator<'a, L, C, D, P> +where + L: TrieLayout, + C: Iterator>, + P: Iterator, + D: Borrow<[u8]>, +{ + query_plan: QueryPlan<'a, C>, + proof: P, + is_compact: bool, + expected_root: Option>, + next_content: Option>, + rem_next_content: bool, + stack: Vec>, + prefix: NibbleVec, + _ph: PhantomData<&'a L>, } +enum ItemStack> { + Inline(OwnedNode>, usize), + Node(OwnedNode, usize), +} + +impl> ItemStack { + fn depth(&self) -> usize { + match self { + ItemStack::Inline(_, d) => *d, + ItemStack::Node(_, d) => *d, + } + } + fn set_depth(&mut self, d: usize) { + *match self { + ItemStack::Inline(_, d) => d, + ItemStack::Node(_, d) => d, + } = d; + } + + fn data(&self) -> &[u8] { + match self { + ItemStack::Inline(n, _) => n.data(), + ItemStack::Node(n, _) => n.data(), + } + } + + fn node_plan(&self) -> &NodePlan { + match self { + ItemStack::Inline(n, _) => n.node_plan(), + ItemStack::Node(n, _) => n.node_plan(), + } + } +} + /// Content return on success when reading proof. pub enum ReadProofItem<'a> { /// Successfull read of proof, not all content read. @@ -565,36 +747,296 @@ pub enum ReadProofItem<'a> { /// Seen value and key in proof. /// When we set the query plan, we only return content /// matching the query plan. + /// TODO try ref for value?? Value(&'a [u8], Vec), /// No value seen for a key in the input query plan. NoValue(&'a [u8]), /// Seen fully covered prefix in proof, this is only /// return when we read the proof with the query input (otherwhise /// we would need to indicate every child without a hash as a prefix). - CoveredPrefix(&'a [u8]), + StartPrefix(&'a [u8]), + /// End of a previously start prefix. + EndPrefix, } -impl<'a, L: TrieLayout> Iterator for ReadProofIterator<'a, L> { +impl<'a, L, C, D, P> Iterator for ReadProofIterator<'a, L, C, D, P> +where + L: TrieLayout, + C: Iterator>, + P: Iterator, + D: Borrow<[u8]>, +{ type Item = Result, VerifyError, CError>>; fn next(&mut self) -> Option { + let mut exit = false; + // read proof + loop { + if self.rem_next_content || self.next_content.is_none() { + if let Some(next) = self.query_plan.items.next() { + let (ordered, common_nibbles) = if let Some(old) = self.next_content.as_ref() { + old.before(&next) + } else { + (true, 0) + }; + if !ordered { + if self.query_plan.ignore_unordered { + continue + } else { + return Some(Err(VerifyError::UnorderedKey(next.key.to_vec()))) // TODO not kind as param if keeping + // CompactContent + } + } + + while let Some(last) = self.stack.last() { + if last.depth() == common_nibbles { + self.prefix.drop_lasts(self.prefix.len() - last.depth()); + break + } + if last.depth() < common_nibbles { + // depth should match. + return Some(Err(VerifyError::ExtraneousNode)) + } + } + // TODO pop until common + + self.rem_next_content = false; + self.next_content = Some(next); + } else { + self.rem_next_content = false; + self.next_content = None; + exit = true; + break + } + }; + let to_check = self.next_content.as_ref().expect("Init above"); + let mut at_value = false; + let mut to_check_slice = NibbleSlice::new(to_check.key); + match self.prefix.len().cmp(&to_check_slice.len()) { + Ordering::Equal => { + at_value = true; + }, + Ordering::Greater => (), + Ordering::Less => { + unreachable!(); + }, + } + + if at_value { + self.rem_next_content = true; + if let Some(node) = self.stack.last() { + let node_data = node.data(); + + let value = match node.node_plan() { + NodePlan::Leaf { value, .. } => Some(value.build(node_data)), + NodePlan::Branch { value, .. } | NodePlan::NibbledBranch { value, .. } => + value.as_ref().map(|v| v.build(node_data)), + _ => None, + }; + if let Some(value) = value { + match value { + Value::Inline(value) => + return Some(Ok(ReadProofItem::Value(to_check.key, value.to_vec()))), + Value::Node(hash) => { + let Some(value) = self.proof.next() else { + return Some(Err(VerifyError::IncompleteProof)); + }; + if self.expected_root.is_some() { + let checked_hash = L::Hash::hash(value.borrow()); + if checked_hash.as_ref() != hash { + let mut error_hash = TrieHash::::default(); + error_hash.as_mut().copy_from_slice(hash); + return Some(Err(VerifyError::HashMismatch(error_hash))) + } + } + return Some(Ok(ReadProofItem::Value( + to_check.key, + value.borrow().to_vec(), + ))) + }, + } + } + + return Some(Ok(ReadProofItem::NoValue(to_check.key))) + } else { + unreachable!(); + } + } + + let child_index = to_check_slice.at(self.prefix.len() + 1); + let child_handle = if let Some(node) = self.stack.last_mut() { + let node_data = node.data(); + + match node.node_plan() { + NodePlan::Empty | NodePlan::Leaf { .. } => { + self.rem_next_content = true; + return Some(Ok(ReadProofItem::NoValue(to_check.key))) + }, + NodePlan::Extension { .. } => { + unreachable!("Extension never stacked") + }, + NodePlan::NibbledBranch { children, .. } | + NodePlan::Branch { children, .. } => + if let Some(child) = &children[child_index as usize] { + child.build(node_data) + } else { + self.rem_next_content = true; + return Some(Ok(ReadProofItem::NoValue(to_check.key))) + }, + } + } else { + NodeHandle::Hash(self.expected_root.as_ref().map(AsRef::as_ref).unwrap_or(&[])) + }; + + let mut node = match child_handle { + NodeHandle::Inline(data) => + // try access in inline then return + ItemStack::Inline( + match OwnedNode::new::(data.to_vec()) { + Ok(node) => node, + Err(e) => return Some(Err(VerifyError::DecodeError(e))), + }, + 0, + ), + NodeHandle::Hash(hash) => { + let Some(encoded_node) = self.proof.next() else { + exit = true; + break + }; + let node = match OwnedNode::new::(encoded_node) { + Ok(node) => node, + Err(e) => return Some(Err(VerifyError::DecodeError(e))), + }; + if self.expected_root.is_some() { + let checked_hash = L::Hash::hash(node.data()); + if checked_hash.as_ref() != hash { + let mut error_hash = TrieHash::::default(); + error_hash.as_mut().copy_from_slice(hash); + return Some(Err(VerifyError::HashMismatch(error_hash))) + } + } + ItemStack::Node(node, 0) + }, + }; + + let mut descend_incomplete = false; + let node_data = node.data(); + + match node.node_plan() { + NodePlan::Branch { .. } => (), + | NodePlan::Empty => (), + NodePlan::Leaf { partial, .. } | + NodePlan::NibbledBranch { partial, .. } | + NodePlan::Extension { partial, .. } => { + let partial = partial.build(node_data); + if to_check_slice.starts_with(&partial) { + if self.prefix.len() > 0 { + self.prefix.push(child_index); + to_check_slice.advance(1); + } + to_check_slice.advance(partial.len()); + self.prefix.append_partial(partial.right()); + } else { + descend_incomplete = true; + } + }, + } + if descend_incomplete { + self.rem_next_content = true; + return Some(Ok(ReadProofItem::NoValue(to_check.key))) + } + if let NodePlan::Extension { child, .. } = node.node_plan() { + let node_data = node.data(); + let child = child.build(node_data); + match child { + NodeHandle::Hash(hash) => { + let Some(encoded_branch) = self.proof.next() else { + return Some(Err(VerifyError::IncompleteProof)); + }; + + if self.expected_root.is_some() { + let checked_hash = L::Hash::hash(encoded_branch.borrow()); + if checked_hash.as_ref() != hash { + let mut error_hash = TrieHash::::default(); + error_hash.as_mut().copy_from_slice(hash); + return Some(Err(VerifyError::HashMismatch(error_hash))) + } + } + node = match OwnedNode::new::(encoded_branch) { + Ok(node) => ItemStack::Node(node, 0), + Err(e) => return Some(Err(VerifyError::DecodeError(e))), + }; + }, + NodeHandle::Inline(encoded_branch) => { + node = match OwnedNode::new::(encoded_branch.to_vec()) { + Ok(node) => ItemStack::Inline(node, 0), + Err(e) => return Some(Err(VerifyError::DecodeError(e))), + }; + }, + } + let NodePlan::Branch { .. } = node.node_plan() else { + return Some(Err(VerifyError::IncompleteProof)) // TODO make error type?? + }; + } + node.set_depth(self.prefix.len()); + self.stack.push(node); + } + + if exit { + if self.rem_next_content { + self.next_content = None; + // TODO unstack check for compact + + if self.proof.next().is_some() { + return Some(Err(VerifyError::ExtraneousNode)) + } + // successfully finished + return None + } else { + // incomplete proof: TODO for compact check root + unimplemented!("TODO return Halted read proof item"); + } + } unimplemented!() } } /// Read the proof. -pub fn prove_query_plan_iter<'a, L: TrieLayout>( - content_iter: Option>, - proof: impl Iterator, +/// +/// If expected root is None, then we do not check hashes at all. +pub fn verify_query_plan_iter<'a, L, C, D, P>( + mut query_plan: QueryPlan<'a, C>, + proof: P, restart: Option, kind: ProofKind, - skip_hash_validation: bool, -) -> Result<(), VerifyError, CError>> { - if kind == ProofKind::CompactContent { - return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent - } + expected_root: Option>, +) -> Result, VerifyError, CError>> +where + L: TrieLayout, + C: Iterator>, + P: Iterator, + D: Borrow<[u8]>, +{ + let is_compact = match kind { + ProofKind::CompactNodes | ProofKind::CompactContent => { + return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent + }, + ProofKind::FullNodes => false, + ProofKind::CompactNodesStream => true, + }; - Ok(()) + let next_content = query_plan.items.next(); + Ok(ReadProofIterator { + query_plan, + proof, + is_compact, + expected_root, + next_content, + rem_next_content: false, + prefix: Default::default(), + stack: Default::default(), + _ph: PhantomData, + }) } mod compact_content_proof { diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 3e951a89..e641b3f6 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -17,6 +17,7 @@ use reference_trie::{test_layouts, NoExtensionLayout}; use trie_db::{ proof::{generate_proof, verify_proof, VerifyError}, + query_plan::HaltedStateRecord, DBValue, Trie, TrieDBBuilder, TrieDBMutBuilder, TrieLayout, TrieMut, }; @@ -243,7 +244,10 @@ fn test_verify_decode_error_internal() { test_layouts!(test_query_plan, test_query_plan_internal); fn test_query_plan_internal() { - use trie_db::query_plan::{record_query_plan, InMemQueryPlan, InMemQueryPlanItem}; + use trie_db::query_plan::{ + record_query_plan, verify_query_plan_iter, InMemQueryPlan, InMemQueryPlanItem, + InMemoryRecorder, ProofKind, ReadProofItem, Recorder, + }; let set = test_entries(); let (db, root) = { let mut db = >::default(); @@ -266,7 +270,6 @@ fn test_query_plan_internal() { InMemQueryPlanItem::new(b"doge".to_vec(), false), InMemQueryPlanItem::new(b"horsey".to_vec(), false), ], - current: None, ignore_unordered: false, }, InMemQueryPlan { @@ -274,22 +277,56 @@ fn test_query_plan_internal() { InMemQueryPlanItem::new(b"bravo".to_vec(), false), InMemQueryPlanItem::new(b"do".to_vec(), true), ], - current: None, ignore_unordered: false, }, InMemQueryPlan { items: vec![InMemQueryPlanItem::new(b"".to_vec(), true)], - current: None, ignore_unordered: false, }, ]; for query_plan in query_plans { + let kind = ProofKind::FullNodes; + let recorder = Recorder::new(kind, InMemoryRecorder::default()); + let from = HaltedStateRecord::from_start(recorder); // no limit let query_plan_iter = query_plan.as_ref(); - let paused = record_query_plan::(&db, query_plan_iter, None).unwrap(); - assert!(paused.is_none()); + let from = record_query_plan::(&db, query_plan_iter, from).unwrap(); + assert!(from.is_finished()); + let proof = from.finish().output().nodes; + + let query_plan_iter = query_plan.as_ref(); + let verify_iter = verify_query_plan_iter::( + query_plan_iter, + proof.into_iter(), + None, + kind, + Some(root.clone()), + ) + .unwrap(); + let mut in_prefix = false; + for item in verify_iter { + match item.unwrap() { + ReadProofItem::Value(_key, _value) => { + // TODO remove from hashmap if not in prefix?? + }, + ReadProofItem::NoValue(_key) => { + // TODO remove from hashmap?? + }, + ReadProofItem::StartPrefix(_prefix) => { + // TODO remove from hashmap?? + in_prefix = true; + }, + ReadProofItem::EndPrefix => { + assert!(in_prefix); + in_prefix = false; + }, + ReadProofItem::Halted(_) => { + unreachable!("full proof"); + }, + } + } - // TODO limit 1, 2, 3 + // TODO limit 1, 2, 3 and restarts } } From 989011c6ab73398cefb4e0d6c6f8a05011eec821 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 31 Mar 2023 09:59:06 +0200 Subject: [PATCH 010/154] factor code a bit --- trie-db/src/query_plan.rs | 425 +++++++++++++++++++++----------------- 1 file changed, 241 insertions(+), 184 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 5163d2b3..fda9372c 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -529,6 +529,7 @@ enum TryStackChildResult { NotStackedBranch, NotStacked, StackedDescendIncomplete, + Halted, } impl RecordStack { @@ -629,8 +630,8 @@ impl RecordStack { db: &TrieDB, ) -> Result, CError>> { let Some(item)= self.items.last_mut() else { - return Ok(false) - }; + return Ok(false) + }; // TODO this could be reuse from iterator, but it seems simple // enough here too. let node_data = item.node.data(); @@ -700,9 +701,7 @@ where expected_root: Option>, next_content: Option>, rem_next_content: bool, - stack: Vec>, - prefix: NibbleVec, - + stack: ReadStack, _ph: PhantomData<&'a L>, } @@ -740,6 +739,196 @@ impl> ItemStack { } } +struct ReadStack> { + items: Vec>, + prefix: NibbleVec, + is_compact: bool, + _ph: PhantomData, +} + +fn verify_hash( + data: &[u8], + expected: &[u8], +) -> Result<(), VerifyError, CError>> { + let checked_hash = L::Hash::hash(data); + if checked_hash.as_ref() != expected { + let mut error_hash = TrieHash::::default(); + error_hash.as_mut().copy_from_slice(expected); + Err(VerifyError::HashMismatch(error_hash)) + } else { + Ok(()) + } +} + +impl> ReadStack { + fn try_stack_child( + &mut self, + child_index: u8, + proof: &mut impl Iterator, + expected_root: &Option>, + mut slice_query: Option<&mut NibbleSlice>, + ) -> Result, CError>> { + let check_hash = expected_root.is_some(); + let child_handle = if let Some(node) = self.items.last_mut() { + let node_data = node.data(); + + match node.node_plan() { + NodePlan::Empty | NodePlan::Leaf { .. } => + return Ok(TryStackChildResult::NotStacked), + NodePlan::Extension { .. } => { + unreachable!("Extension never stacked") + }, + NodePlan::NibbledBranch { children, .. } | NodePlan::Branch { children, .. } => + if let Some(child) = &children[child_index as usize] { + child.build(node_data) + } else { + return Ok(TryStackChildResult::NotStackedBranch) + }, + } + } else { + NodeHandle::Hash(expected_root.as_ref().map(AsRef::as_ref).unwrap_or(&[])) + }; + let mut node = match child_handle { + NodeHandle::Inline(data) => + // try access in inline then return + ItemStack::Inline( + match OwnedNode::new::(data.to_vec()) { + Ok(node) => node, + Err(e) => return Err(VerifyError::DecodeError(e)), + }, + 0, + ), + NodeHandle::Hash(hash) => { + let Some(encoded_node) = proof.next() else { + return Ok(TryStackChildResult::Halted); + }; + let node = match OwnedNode::new::(encoded_node) { + Ok(node) => node, + Err(e) => return Err(VerifyError::DecodeError(e)), + }; + if check_hash { + verify_hash::(node.data(), hash)?; + } + ItemStack::Node(node, 0) + }, + }; + let node_data = node.data(); + + match node.node_plan() { + NodePlan::Branch { .. } => (), + | NodePlan::Empty => (), + NodePlan::Leaf { partial, .. } | + NodePlan::NibbledBranch { partial, .. } | + NodePlan::Extension { partial, .. } => { + let partial = partial.build(node_data); + let ok = if let Some(slice) = slice_query.as_mut() { + slice.starts_with(&partial) + } else { + true + }; + if ok { + if self.prefix.len() > 0 { + self.prefix.push(child_index); + if let Some(slice) = slice_query.as_mut() { + slice.advance(1); + } + } + if let Some(slice) = slice_query.as_mut() { + slice.advance(partial.len()); + } + self.prefix.append_partial(partial.right()); + } else { + return Ok(TryStackChildResult::StackedDescendIncomplete) + } + }, + } + if let NodePlan::Extension { child, .. } = node.node_plan() { + let node_data = node.data(); + let child = child.build(node_data); + match child { + NodeHandle::Hash(hash) => { + let Some(encoded_branch) = proof.next() else { + // No halt on extension node (restart over a child index). + return Err(VerifyError::IncompleteProof); + }; + if check_hash { + verify_hash::(encoded_branch.borrow(), hash)?; + } + node = match OwnedNode::new::(encoded_branch) { + Ok(node) => ItemStack::Node(node, 0), + Err(e) => return Err(VerifyError::DecodeError(e)), + }; + }, + NodeHandle::Inline(encoded_branch) => { + node = match OwnedNode::new::(encoded_branch.to_vec()) { + Ok(node) => ItemStack::Inline(node, 0), + Err(e) => return Err(VerifyError::DecodeError(e)), + }; + }, + } + let NodePlan::Branch { .. } = node.node_plan() else { + return Err(VerifyError::IncompleteProof) // TODO make error type?? + }; + } + node.set_depth(self.prefix.len()); + self.items.push(node); + Ok(TryStackChildResult::Stacked) + } + + fn access_value( + &mut self, + proof: &mut impl Iterator, + check_hash: bool, + ) -> Result>, VerifyError, CError>> { + if let Some(node) = self.items.last() { + let node_data = node.data(); + + let value = match node.node_plan() { + NodePlan::Leaf { value, .. } => Some(value.build(node_data)), + NodePlan::Branch { value, .. } | NodePlan::NibbledBranch { value, .. } => + value.as_ref().map(|v| v.build(node_data)), + _ => return Ok(None), + }; + if let Some(value) = value { + match value { + Value::Inline(value) => return Ok(Some(value.to_vec())), + Value::Node(hash) => { + let Some(value) = proof.next() else { + return Err(VerifyError::IncompleteProof); + }; + if check_hash { + verify_hash::(value.borrow(), hash)?; + } + return Ok(Some(value.borrow().to_vec())) + }, + } + } + } else { + return Err(VerifyError::IncompleteProof) + } + + Ok(None) + } + + fn pop_until(&mut self, target: usize) -> Result<(), VerifyError, CError>> { + loop { + while let Some(last) = self.items.last() { + match last.depth().cmp(&target) { + Ordering::Greater => (), + // depth should match. + Ordering::Less => break, + Ordering::Equal => { + self.prefix.drop_lasts(self.prefix.len() - last.depth()); + return Ok(()) + }, + } + } + let _ = self.items.pop(); + } + Err(VerifyError::ExtraneousNode) + } +} + /// Content return on success when reading proof. pub enum ReadProofItem<'a> { /// Successfull read of proof, not all content read. @@ -769,7 +958,7 @@ where type Item = Result, VerifyError, CError>>; fn next(&mut self) -> Option { - let mut exit = false; + let check_hash = self.expected_root.is_some(); // read proof loop { if self.rem_next_content || self.next_content.is_none() { @@ -788,31 +977,22 @@ where } } - while let Some(last) = self.stack.last() { - if last.depth() == common_nibbles { - self.prefix.drop_lasts(self.prefix.len() - last.depth()); - break - } - if last.depth() < common_nibbles { - // depth should match. - return Some(Err(VerifyError::ExtraneousNode)) - } + let r = self.stack.pop_until(common_nibbles); + if let Err(e) = r { + return Some(Err(e)) } - // TODO pop until common - self.rem_next_content = false; self.next_content = Some(next); } else { self.rem_next_content = false; self.next_content = None; - exit = true; break } }; let to_check = self.next_content.as_ref().expect("Init above"); let mut at_value = false; let mut to_check_slice = NibbleSlice::new(to_check.key); - match self.prefix.len().cmp(&to_check_slice.len()) { + match self.stack.prefix.len().cmp(&to_check_slice.len()) { Ordering::Equal => { at_value = true; }, @@ -824,180 +1004,53 @@ where if at_value { self.rem_next_content = true; - if let Some(node) = self.stack.last() { - let node_data = node.data(); - - let value = match node.node_plan() { - NodePlan::Leaf { value, .. } => Some(value.build(node_data)), - NodePlan::Branch { value, .. } | NodePlan::NibbledBranch { value, .. } => - value.as_ref().map(|v| v.build(node_data)), - _ => None, - }; - if let Some(value) = value { - match value { - Value::Inline(value) => - return Some(Ok(ReadProofItem::Value(to_check.key, value.to_vec()))), - Value::Node(hash) => { - let Some(value) = self.proof.next() else { - return Some(Err(VerifyError::IncompleteProof)); - }; - if self.expected_root.is_some() { - let checked_hash = L::Hash::hash(value.borrow()); - if checked_hash.as_ref() != hash { - let mut error_hash = TrieHash::::default(); - error_hash.as_mut().copy_from_slice(hash); - return Some(Err(VerifyError::HashMismatch(error_hash))) - } - } - return Some(Ok(ReadProofItem::Value( - to_check.key, - value.borrow().to_vec(), - ))) - }, - } - } - - return Some(Ok(ReadProofItem::NoValue(to_check.key))) - } else { - unreachable!(); + match self.stack.access_value(&mut self.proof, check_hash) { + Ok(Some(value)) => return Some(Ok(ReadProofItem::Value(to_check.key, value))), + Ok(None) => return Some(Ok(ReadProofItem::NoValue(to_check.key))), + Err(e) => return Some(Err(e)), } } - let child_index = to_check_slice.at(self.prefix.len() + 1); - let child_handle = if let Some(node) = self.stack.last_mut() { - let node_data = node.data(); - - match node.node_plan() { - NodePlan::Empty | NodePlan::Leaf { .. } => { - self.rem_next_content = true; - return Some(Ok(ReadProofItem::NoValue(to_check.key))) - }, - NodePlan::Extension { .. } => { - unreachable!("Extension never stacked") - }, - NodePlan::NibbledBranch { children, .. } | - NodePlan::Branch { children, .. } => - if let Some(child) = &children[child_index as usize] { - child.build(node_data) - } else { - self.rem_next_content = true; - return Some(Ok(ReadProofItem::NoValue(to_check.key))) - }, - } - } else { - NodeHandle::Hash(self.expected_root.as_ref().map(AsRef::as_ref).unwrap_or(&[])) + let child_index = to_check_slice.at(self.stack.prefix.len() + 1); + let r = match self.stack.try_stack_child( + child_index, + &mut self.proof, + &self.expected_root, + Some(&mut to_check_slice), + ) { + Ok(r) => r, + Err(e) => return Some(Err(e)), }; - - let mut node = match child_handle { - NodeHandle::Inline(data) => - // try access in inline then return - ItemStack::Inline( - match OwnedNode::new::(data.to_vec()) { - Ok(node) => node, - Err(e) => return Some(Err(VerifyError::DecodeError(e))), - }, - 0, - ), - NodeHandle::Hash(hash) => { - let Some(encoded_node) = self.proof.next() else { - exit = true; - break - }; - let node = match OwnedNode::new::(encoded_node) { - Ok(node) => node, - Err(e) => return Some(Err(VerifyError::DecodeError(e))), - }; - if self.expected_root.is_some() { - let checked_hash = L::Hash::hash(node.data()); - if checked_hash.as_ref() != hash { - let mut error_hash = TrieHash::::default(); - error_hash.as_mut().copy_from_slice(hash); - return Some(Err(VerifyError::HashMismatch(error_hash))) - } - } - ItemStack::Node(node, 0) + match r { + TryStackChildResult::Stacked => (), + TryStackChildResult::StackedDescendIncomplete => { + self.rem_next_content = true; + return Some(Ok(ReadProofItem::NoValue(to_check.key))) }, - }; - - let mut descend_incomplete = false; - let node_data = node.data(); - - match node.node_plan() { - NodePlan::Branch { .. } => (), - | NodePlan::Empty => (), - NodePlan::Leaf { partial, .. } | - NodePlan::NibbledBranch { partial, .. } | - NodePlan::Extension { partial, .. } => { - let partial = partial.build(node_data); - if to_check_slice.starts_with(&partial) { - if self.prefix.len() > 0 { - self.prefix.push(child_index); - to_check_slice.advance(1); - } - to_check_slice.advance(partial.len()); - self.prefix.append_partial(partial.right()); - } else { - descend_incomplete = true; - } + TryStackChildResult::NotStacked => { + self.rem_next_content = true; + return Some(Ok(ReadProofItem::NoValue(to_check.key))) }, + TryStackChildResult::NotStackedBranch => { + self.rem_next_content = true; + return Some(Ok(ReadProofItem::NoValue(to_check.key))) + }, + TryStackChildResult::Halted => break, } - if descend_incomplete { - self.rem_next_content = true; - return Some(Ok(ReadProofItem::NoValue(to_check.key))) - } - if let NodePlan::Extension { child, .. } = node.node_plan() { - let node_data = node.data(); - let child = child.build(node_data); - match child { - NodeHandle::Hash(hash) => { - let Some(encoded_branch) = self.proof.next() else { - return Some(Err(VerifyError::IncompleteProof)); - }; - - if self.expected_root.is_some() { - let checked_hash = L::Hash::hash(encoded_branch.borrow()); - if checked_hash.as_ref() != hash { - let mut error_hash = TrieHash::::default(); - error_hash.as_mut().copy_from_slice(hash); - return Some(Err(VerifyError::HashMismatch(error_hash))) - } - } - node = match OwnedNode::new::(encoded_branch) { - Ok(node) => ItemStack::Node(node, 0), - Err(e) => return Some(Err(VerifyError::DecodeError(e))), - }; - }, - NodeHandle::Inline(encoded_branch) => { - node = match OwnedNode::new::(encoded_branch.to_vec()) { - Ok(node) => ItemStack::Inline(node, 0), - Err(e) => return Some(Err(VerifyError::DecodeError(e))), - }; - }, - } - let NodePlan::Branch { .. } = node.node_plan() else { - return Some(Err(VerifyError::IncompleteProof)) // TODO make error type?? - }; - } - node.set_depth(self.prefix.len()); - self.stack.push(node); } + if self.rem_next_content { + self.next_content = None; + // TODO unstack check for compact - if exit { - if self.rem_next_content { - self.next_content = None; - // TODO unstack check for compact - - if self.proof.next().is_some() { - return Some(Err(VerifyError::ExtraneousNode)) - } - // successfully finished - return None - } else { - // incomplete proof: TODO for compact check root - unimplemented!("TODO return Halted read proof item"); + if self.proof.next().is_some() { + return Some(Err(VerifyError::ExtraneousNode)) } + // successfully finished + return None + } else { + // incomplete proof: TODO for compact check root + unimplemented!("TODO return Halted read proof item"); } - unimplemented!() } } @@ -1033,8 +1086,12 @@ where expected_root, next_content, rem_next_content: false, - prefix: Default::default(), - stack: Default::default(), + stack: ReadStack { + items: Default::default(), + prefix: Default::default(), + is_compact, + _ph: PhantomData, + }, _ph: PhantomData, }) } From 03e0b8e4031cba36859a19bf33942d7e8afaed36 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 31 Mar 2023 10:04:16 +0200 Subject: [PATCH 011/154] missing err --- trie-db/src/query_plan.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index fda9372c..eecc97df 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -469,6 +469,9 @@ pub fn record_query_plan< } break }, + TryStackChildResult::Halted => { + unimplemented!() + }, } } @@ -508,6 +511,9 @@ pub fn record_query_plan< TryStackChildResult::StackedDescendIncomplete => { unreachable!("no slice query") }, + TryStackChildResult::Halted => { + unimplemented!() + }, } } From 033917751be49fd209a1ddddc8f57db94403ae7c Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 31 Mar 2023 11:46:24 +0200 Subject: [PATCH 012/154] iter on verify --- trie-db/src/query_plan.rs | 179 ++++++++++++++++++++++++++++---------- trie-db/test/src/proof.rs | 2 + 2 files changed, 137 insertions(+), 44 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index eecc97df..d3df20e7 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -707,47 +707,46 @@ where expected_root: Option>, next_content: Option>, rem_next_content: bool, - stack: ReadStack, - _ph: PhantomData<&'a L>, + stack: ReadStack<'a, L, D>, } -enum ItemStack> { - Inline(OwnedNode>, usize), - Node(OwnedNode, usize), +struct ItemStack> { + node: ItemStackNode, + depth: usize, + next_descended_child: u8, } -impl> ItemStack { - fn depth(&self) -> usize { - match self { - ItemStack::Inline(_, d) => *d, - ItemStack::Node(_, d) => *d, - } - } - fn set_depth(&mut self, d: usize) { - *match self { - ItemStack::Inline(_, d) => d, - ItemStack::Node(_, d) => d, - } = d; +enum ItemStackNode> { + Inline(OwnedNode>), + Node(OwnedNode), +} + +impl> From> for ItemStack { + fn from(node: ItemStackNode) -> Self { + ItemStack { node, depth: 0, next_descended_child: 0 } } +} +impl> ItemStack { fn data(&self) -> &[u8] { - match self { - ItemStack::Inline(n, _) => n.data(), - ItemStack::Node(n, _) => n.data(), + match &self.node { + ItemStackNode::Inline(n) => n.data(), + ItemStackNode::Node(n) => n.data(), } } fn node_plan(&self) -> &NodePlan { - match self { - ItemStack::Inline(n, _) => n.node_plan(), - ItemStack::Node(n, _) => n.node_plan(), + match &self.node { + ItemStackNode::Inline(n) => n.node_plan(), + ItemStackNode::Node(n) => n.node_plan(), } } } -struct ReadStack> { +struct ReadStack<'a, L: TrieLayout, D: Borrow<[u8]>> { items: Vec>, - prefix: NibbleVec, + prefix: &'a mut NibbleVec, + iter_prefix: Option<(usize, bool)>, // limit and wether we return value is_compact: bool, _ph: PhantomData, } @@ -766,7 +765,7 @@ fn verify_hash( } } -impl> ReadStack { +impl<'a, L: TrieLayout, D: Borrow<[u8]>> ReadStack<'a, L, D> { fn try_stack_child( &mut self, child_index: u8, @@ -794,16 +793,14 @@ impl> ReadStack { } else { NodeHandle::Hash(expected_root.as_ref().map(AsRef::as_ref).unwrap_or(&[])) }; - let mut node = match child_handle { + let mut node: ItemStack<_> = match child_handle { NodeHandle::Inline(data) => // try access in inline then return - ItemStack::Inline( - match OwnedNode::new::(data.to_vec()) { - Ok(node) => node, - Err(e) => return Err(VerifyError::DecodeError(e)), - }, - 0, - ), + ItemStackNode::Inline(match OwnedNode::new::(data.to_vec()) { + Ok(node) => node, + Err(e) => return Err(VerifyError::DecodeError(e)), + }) + .into(), NodeHandle::Hash(hash) => { let Some(encoded_node) = proof.next() else { return Ok(TryStackChildResult::Halted); @@ -815,7 +812,7 @@ impl> ReadStack { if check_hash { verify_hash::(node.data(), hash)?; } - ItemStack::Node(node, 0) + ItemStackNode::Node(node).into() }, }; let node_data = node.data(); @@ -861,13 +858,13 @@ impl> ReadStack { verify_hash::(encoded_branch.borrow(), hash)?; } node = match OwnedNode::new::(encoded_branch) { - Ok(node) => ItemStack::Node(node, 0), + Ok(node) => ItemStackNode::Node(node).into(), Err(e) => return Err(VerifyError::DecodeError(e)), }; }, NodeHandle::Inline(encoded_branch) => { node = match OwnedNode::new::(encoded_branch.to_vec()) { - Ok(node) => ItemStack::Inline(node, 0), + Ok(node) => ItemStackNode::Inline(node).into(), Err(e) => return Err(VerifyError::DecodeError(e)), }; }, @@ -876,7 +873,7 @@ impl> ReadStack { return Err(VerifyError::IncompleteProof) // TODO make error type?? }; } - node.set_depth(self.prefix.len()); + node.depth = self.prefix.len(); self.items.push(node); Ok(TryStackChildResult::Stacked) } @@ -916,15 +913,27 @@ impl> ReadStack { Ok(None) } + fn pop(&mut self) -> bool { + if self.iter_prefix.as_ref().map(|p| p.0 == self.items.len()).unwrap_or(false) { + return false + } + if let Some(last) = self.items.pop() { + self.prefix.drop_lasts(self.prefix.len() - last.depth); + true + } else { + false + } + } + fn pop_until(&mut self, target: usize) -> Result<(), VerifyError, CError>> { loop { while let Some(last) = self.items.last() { - match last.depth().cmp(&target) { + match last.depth.cmp(&target) { Ordering::Greater => (), // depth should match. Ordering::Less => break, Ordering::Equal => { - self.prefix.drop_lasts(self.prefix.len() - last.depth()); + self.prefix.drop_lasts(self.prefix.len() - last.depth); return Ok(()) }, } @@ -933,6 +942,14 @@ impl> ReadStack { } Err(VerifyError::ExtraneousNode) } + + fn enter_prefix_iter(&mut self) { + self.iter_prefix = Some((self.items.len(), false)); + } + + fn exit_prefix_iter(&mut self) { + self.iter_prefix = None + } } /// Content return on success when reading proof. @@ -978,8 +995,7 @@ where if self.query_plan.ignore_unordered { continue } else { - return Some(Err(VerifyError::UnorderedKey(next.key.to_vec()))) // TODO not kind as param if keeping - // CompactContent + return Some(Err(VerifyError::UnorderedKey(next.key.to_vec()))) } } @@ -995,7 +1011,73 @@ where break } }; + let did_prefix = self.stack.iter_prefix.is_some(); + while let Some((_, accessed_value)) = self.stack.iter_prefix.clone() { + // prefix iteration + if !accessed_value { + match self.stack.access_value(&mut self.proof, check_hash) { + Ok(Some(value)) => { + self.stack.iter_prefix.as_mut().map(|s| { + s.1 = true; + }); + // prefix is &'a mut NibbleVec. + let prefix: &'a NibbleVec = + unsafe { core::mem::transmute(&self.stack.prefix) }; + return Some(Ok(ReadProofItem::Value(prefix.inner(), value))) + }, + Ok(None) => (), + Err(e) => return Some(Err(e)), + }; + } + let mut stacked = false; + while let Some(child_index) = self.stack.items.last_mut().and_then(|last| { + if last.next_descended_child as usize >= crate::nibble_ops::NIBBLE_LENGTH { + None + } else { + let child_index = last.next_descended_child; + last.next_descended_child += 1; + Some(child_index) + } + }) { + let r = match self.stack.try_stack_child( + child_index, + &mut self.proof, + &self.expected_root, + None, + ) { + Ok(r) => r, + Err(e) => return Some(Err(e)), + }; + match r { + TryStackChildResult::Stacked => { + stacked = true; + break + }, + TryStackChildResult::StackedDescendIncomplete => { + unreachable!("slice query none"); + }, + TryStackChildResult::NotStacked => break, + TryStackChildResult::NotStackedBranch => (), + TryStackChildResult::Halted => { + unimplemented!() + }, + } + } + if !stacked { + if !self.stack.pop() { + // end iter + self.stack.exit_prefix_iter(); + } + break + } + } + if did_prefix { + // exit a prefix iter, next content looping + self.rem_next_content = true; + continue + } let to_check = self.next_content.as_ref().expect("Init above"); + let as_prefix = to_check.as_prefix; let mut at_value = false; let mut to_check_slice = NibbleSlice::new(to_check.key); match self.stack.prefix.len().cmp(&to_check_slice.len()) { @@ -1009,6 +1091,10 @@ where } if at_value { + if as_prefix { + self.stack.enter_prefix_iter(); + continue + } self.rem_next_content = true; match self.stack.access_value(&mut self.proof, check_hash) { Ok(Some(value)) => return Some(Ok(ReadProofItem::Value(to_check.key, value))), @@ -1030,6 +1116,10 @@ where match r { TryStackChildResult::Stacked => (), TryStackChildResult::StackedDescendIncomplete => { + if as_prefix { + self.stack.enter_prefix_iter(); + continue + } self.rem_next_content = true; return Some(Ok(ReadProofItem::NoValue(to_check.key))) }, @@ -1069,6 +1159,7 @@ pub fn verify_query_plan_iter<'a, L, C, D, P>( restart: Option, kind: ProofKind, expected_root: Option>, + prefix: &'a mut NibbleVec, ) -> Result, VerifyError, CError>> where L: TrieLayout, @@ -1094,11 +1185,11 @@ where rem_next_content: false, stack: ReadStack { items: Default::default(), - prefix: Default::default(), + prefix, is_compact, + iter_prefix: None, _ph: PhantomData, }, - _ph: PhantomData, }) } diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index e641b3f6..a758901a 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -296,12 +296,14 @@ fn test_query_plan_internal() { let proof = from.finish().output().nodes; let query_plan_iter = query_plan.as_ref(); + let mut buffer = trie_db::NibbleVec::new(); let verify_iter = verify_query_plan_iter::( query_plan_iter, proof.into_iter(), None, kind, Some(root.clone()), + &mut buffer, ) .unwrap(); let mut in_prefix = false; From 9a900bba06a53c7cf7905148482d5dab3354c0db Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 31 Mar 2023 14:58:05 +0200 Subject: [PATCH 013/154] fix --- trie-db/src/query_plan.rs | 163 ++++++++++++++++++++++++++------------ 1 file changed, 112 insertions(+), 51 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index d3df20e7..7c914aa0 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -55,6 +55,7 @@ impl InMemQueryPlanItem { } /// Item to query. +#[derive(Clone)] pub struct QueryPlanItem<'a> { key: &'a [u8], as_prefix: bool, @@ -416,11 +417,10 @@ pub fn record_query_plan< let stack = &mut from.stack; - let prev_query: Option = None; + let mut prev_query: Option = None; let mut from_query = from.currently_query_item.take(); - while let Some(query) = - from_query.as_ref().map(|f| f.as_ref()).or_else(|| query_plan.items.next()) - { + let mut from_query_ref = from_query.as_ref().map(|f| f.as_ref()); + while let Some(query) = from_query_ref.clone().or_else(|| query_plan.items.next()) { let (ordered, common_nibbles) = prev_query.as_ref().map(|p| p.before(&query)).unwrap_or((true, 0)); if !ordered { @@ -475,7 +475,8 @@ pub fn record_query_plan< } } - from_query = None; + from_query_ref = None; + prev_query = Some(query); if touched { // try access value @@ -607,6 +608,7 @@ impl RecordStack { } if stack_extension { // rec call to stack branch + unimplemented!("extension node recoding"); let sbranch = self.try_stack_child(child_index, db, parent_hash, slice_query)?; let TryStackChildResult::Stacked = sbranch else { return Err(VerifyError::InvalidChildReference(b"branch in db should follow extension".to_vec())); @@ -617,7 +619,7 @@ impl RecordStack { accessed_children: Default::default(), accessed_value: false, depth: prefix.len(), - next_descended_child: child_index + 1, + next_descended_child: 0, is_inline, }; self.recorder.record_stacked_node(&infos, stack.len()); @@ -705,11 +707,27 @@ where proof: P, is_compact: bool, expected_root: Option>, - next_content: Option>, - rem_next_content: bool, + current: Option>, + state: ReadProofState, stack: ReadStack<'a, L, D>, } +#[derive(Eq, PartialEq)] +enum ReadProofState { + /// Iteration not started. + NotStarted, + /// Iterating. + Running, + /// Switch next item. + SwitchQueryPlan, + /// Proof read. + PlanConsumed, + /// Proof read. + Halted, + /// Iteration finished. + Finished, +} + struct ItemStack> { node: ItemStackNode, depth: usize, @@ -803,8 +821,8 @@ impl<'a, L: TrieLayout, D: Borrow<[u8]>> ReadStack<'a, L, D> { .into(), NodeHandle::Hash(hash) => { let Some(encoded_node) = proof.next() else { - return Ok(TryStackChildResult::Halted); - }; + return Ok(TryStackChildResult::Halted); + }; let node = match OwnedNode::new::(encoded_node) { Ok(node) => node, Err(e) => return Err(VerifyError::DecodeError(e)), @@ -824,6 +842,11 @@ impl<'a, L: TrieLayout, D: Borrow<[u8]>> ReadStack<'a, L, D> { NodePlan::NibbledBranch { partial, .. } | NodePlan::Extension { partial, .. } => { let partial = partial.build(node_data); + if self.prefix.len() > 0 { + if let Some(slice) = slice_query.as_mut() { + slice.advance(1); + } + } let ok = if let Some(slice) = slice_query.as_mut() { slice.starts_with(&partial) } else { @@ -832,9 +855,6 @@ impl<'a, L: TrieLayout, D: Borrow<[u8]>> ReadStack<'a, L, D> { if ok { if self.prefix.len() > 0 { self.prefix.push(child_index); - if let Some(slice) = slice_query.as_mut() { - slice.advance(1); - } } if let Some(slice) = slice_query.as_mut() { slice.advance(partial.len()); @@ -870,8 +890,8 @@ impl<'a, L: TrieLayout, D: Borrow<[u8]>> ReadStack<'a, L, D> { }, } let NodePlan::Branch { .. } = node.node_plan() else { - return Err(VerifyError::IncompleteProof) // TODO make error type?? - }; + return Err(VerifyError::IncompleteProof) // TODO make error type?? + }; } node.depth = self.prefix.len(); self.items.push(node); @@ -897,8 +917,8 @@ impl<'a, L: TrieLayout, D: Borrow<[u8]>> ReadStack<'a, L, D> { Value::Inline(value) => return Ok(Some(value.to_vec())), Value::Node(hash) => { let Some(value) = proof.next() else { - return Err(VerifyError::IncompleteProof); - }; + return Err(VerifyError::IncompleteProof); + }; if check_hash { verify_hash::(value.borrow(), hash)?; } @@ -927,7 +947,7 @@ impl<'a, L: TrieLayout, D: Borrow<[u8]>> ReadStack<'a, L, D> { fn pop_until(&mut self, target: usize) -> Result<(), VerifyError, CError>> { loop { - while let Some(last) = self.items.last() { + if let Some(last) = self.items.last() { match last.depth.cmp(&target) { Ordering::Greater => (), // depth should match. @@ -937,9 +957,16 @@ impl<'a, L: TrieLayout, D: Borrow<[u8]>> ReadStack<'a, L, D> { return Ok(()) }, } + } else { + if target == 0 { + return Ok(()) + } else { + break + } } let _ = self.items.pop(); } + // TODO other error Err(VerifyError::ExtraneousNode) } @@ -981,12 +1008,19 @@ where type Item = Result, VerifyError, CError>>; fn next(&mut self) -> Option { + if self.state == ReadProofState::Finished { + return None + } let check_hash = self.expected_root.is_some(); + + let mut to_check_slice = self.current.as_ref().map(|n| NibbleSlice::new(n.key)); // read proof loop { - if self.rem_next_content || self.next_content.is_none() { + if self.state == ReadProofState::SwitchQueryPlan || + self.state == ReadProofState::NotStarted + { if let Some(next) = self.query_plan.items.next() { - let (ordered, common_nibbles) = if let Some(old) = self.next_content.as_ref() { + let (ordered, common_nibbles) = if let Some(old) = self.current.as_ref() { old.before(&next) } else { (true, 0) @@ -995,19 +1029,26 @@ where if self.query_plan.ignore_unordered { continue } else { + self.state = ReadProofState::Finished; return Some(Err(VerifyError::UnorderedKey(next.key.to_vec()))) } } let r = self.stack.pop_until(common_nibbles); if let Err(e) = r { + self.state = ReadProofState::Finished; return Some(Err(e)) } - self.rem_next_content = false; - self.next_content = Some(next); + self.state = ReadProofState::Running; + self.current = Some(next); + to_check_slice = self + .current + .as_ref() + .map(|n| NibbleSlice::new_offset(n.key, common_nibbles)); } else { - self.rem_next_content = false; - self.next_content = None; + self.state = ReadProofState::PlanConsumed; + self.current = None; + to_check_slice = None; break } }; @@ -1026,7 +1067,10 @@ where return Some(Ok(ReadProofItem::Value(prefix.inner(), value))) }, Ok(None) => (), - Err(e) => return Some(Err(e)), + Err(e) => { + self.state = ReadProofState::Finished; + return Some(Err(e)) + }, }; } let mut stacked = false; @@ -1046,7 +1090,10 @@ where None, ) { Ok(r) => r, - Err(e) => return Some(Err(e)), + Err(e) => { + self.state = ReadProofState::Finished; + return Some(Err(e)) + }, }; match r { TryStackChildResult::Stacked => { @@ -1073,19 +1120,20 @@ where } if did_prefix { // exit a prefix iter, next content looping - self.rem_next_content = true; + self.state = ReadProofState::SwitchQueryPlan; continue } - let to_check = self.next_content.as_ref().expect("Init above"); + let to_check = self.current.as_ref().expect("Init above"); + let to_check_len = to_check.key.len() * nibble_ops::NIBBLE_PER_BYTE; + let mut to_check_slice = to_check_slice.as_mut().expect("Init above"); let as_prefix = to_check.as_prefix; let mut at_value = false; - let mut to_check_slice = NibbleSlice::new(to_check.key); - match self.stack.prefix.len().cmp(&to_check_slice.len()) { + match self.stack.prefix.len().cmp(&to_check_len) { Ordering::Equal => { at_value = true; }, - Ordering::Greater => (), - Ordering::Less => { + Ordering::Less => (), + Ordering::Greater => { unreachable!(); }, } @@ -1095,15 +1143,23 @@ where self.stack.enter_prefix_iter(); continue } - self.rem_next_content = true; + self.state = ReadProofState::SwitchQueryPlan; match self.stack.access_value(&mut self.proof, check_hash) { Ok(Some(value)) => return Some(Ok(ReadProofItem::Value(to_check.key, value))), Ok(None) => return Some(Ok(ReadProofItem::NoValue(to_check.key))), - Err(e) => return Some(Err(e)), + Err(e) => { + self.state = ReadProofState::Finished; + return Some(Err(e)) + }, } } - let child_index = to_check_slice.at(self.stack.prefix.len() + 1); + let child_index = if self.stack.items.len() == 0 { + // dummy + 0 + } else { + to_check_slice.at(0) + }; let r = match self.stack.try_stack_child( child_index, &mut self.proof, @@ -1111,7 +1167,10 @@ where Some(&mut to_check_slice), ) { Ok(r) => r, - Err(e) => return Some(Err(e)), + Err(e) => { + self.state = ReadProofState::Finished; + return Some(Err(e)) + }, }; match r { TryStackChildResult::Stacked => (), @@ -1120,32 +1179,35 @@ where self.stack.enter_prefix_iter(); continue } - self.rem_next_content = true; + self.state = ReadProofState::SwitchQueryPlan; return Some(Ok(ReadProofItem::NoValue(to_check.key))) }, TryStackChildResult::NotStacked => { - self.rem_next_content = true; + self.state = ReadProofState::SwitchQueryPlan; return Some(Ok(ReadProofItem::NoValue(to_check.key))) }, TryStackChildResult::NotStackedBranch => { - self.rem_next_content = true; + self.state = ReadProofState::SwitchQueryPlan; return Some(Ok(ReadProofItem::NoValue(to_check.key))) }, TryStackChildResult::Halted => break, } } - if self.rem_next_content { - self.next_content = None; - // TODO unstack check for compact - if self.proof.next().is_some() { - return Some(Err(VerifyError::ExtraneousNode)) + if self.state == ReadProofState::Halted { + unimplemented!() + } else { + debug_assert!(self.state == ReadProofState::PlanConsumed); + if self.is_compact { + unimplemented!("check hash from stack"); + } else { + if self.proof.next().is_some() { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) + } } - // successfully finished + self.state = ReadProofState::Finished; return None - } else { - // incomplete proof: TODO for compact check root - unimplemented!("TODO return Halted read proof item"); } } } @@ -1175,14 +1237,13 @@ where ProofKind::CompactNodesStream => true, }; - let next_content = query_plan.items.next(); Ok(ReadProofIterator { query_plan, proof, is_compact, expected_root, - next_content, - rem_next_content: false, + current: None, + state: ReadProofState::NotStarted, stack: ReadStack { items: Default::default(), prefix, From 549edea5e002e84e65a82101a6093f247c3b74d1 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 31 Mar 2023 18:39:49 +0200 Subject: [PATCH 014/154] fix basis tests. --- trie-db/src/query_plan.rs | 102 ++++++++++++++++++++++++-------------- trie-db/test/src/proof.rs | 6 +-- 2 files changed, 68 insertions(+), 40 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 7c914aa0..434c2a7c 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -261,9 +261,10 @@ impl Recorder { fn record_stacked_node(&mut self, item: &CompactEncodingInfos, stack_pos: usize) { match &mut self.0 { - RecorderStateInner::Stream(output) => { - output.write_entry(item.node.data().into()); - }, + RecorderStateInner::Stream(output) => + if !item.is_inline { + output.write_entry(item.node.data().into()); + }, RecorderStateInner::Compact { output, proof, stacked_pos } => { unimplemented!() }, @@ -445,7 +446,7 @@ pub fn record_query_plan< let mut slice_query = NibbleSlice::new_offset(&query.key, common_nibbles); let mut touched = false; loop { - if slice_query.is_empty() { + if slice_query.is_empty() && !stack.items.is_empty() { if query.as_prefix { stack.enter_prefix_iter(); } else { @@ -454,7 +455,7 @@ pub fn record_query_plan< break } - let child_index = slice_query.at(0); + let child_index = if stack.items.is_empty() { 0 } else { slice_query.at(0) }; match stack.try_stack_child( child_index, db, @@ -481,12 +482,13 @@ pub fn record_query_plan< if touched { // try access value stack.access_value(db)?; + touched = false; } if let Some(prefix_stack_depth) = stack.iter_prefix.clone() { // run prefix iteration + let mut stacked = true; loop { // descend - let mut stacked = true; loop { if stacked { // try access value in next node @@ -496,7 +498,7 @@ pub fn record_query_plan< let child_index = if let Some(mut item) = stack.items.last_mut() { if item.next_descended_child as usize >= crate::nibble_ops::NIBBLE_LENGTH { - continue + break } item.next_descended_child += 1; item.next_descended_child - 1 @@ -558,10 +560,12 @@ impl RecordStack { match item.node.node_plan() { NodePlan::Empty | NodePlan::Leaf { .. } => return Ok(TryStackChildResult::NotStacked), - NodePlan::Extension { child, .. } => { - stack_extension = true; - child.build(node_data) - }, + NodePlan::Extension { child, .. } => + if child_index == 0 { + child.build(node_data) + } else { + return Ok(TryStackChildResult::NotStacked) + }, NodePlan::NibbledBranch { children, .. } | NodePlan::Branch { children, .. } => if let Some(child) = &children[child_index as usize] { slice_query.as_mut().map(|s| s.advance(1)); @@ -606,24 +610,24 @@ impl RecordStack { } }, } + if let NodePlan::Extension { .. } = child_node.0.node_plan() { + stack_extension = true; + } + let infos = CompactEncodingInfos { + node: child_node.0, + accessed_children: Default::default(), + accessed_value: false, + depth: prefix.len(), + next_descended_child: 0, + is_inline, + }; + self.recorder.record_stacked_node(&infos, stack.len()); + stack.push(infos); if stack_extension { - // rec call to stack branch - unimplemented!("extension node recoding"); - let sbranch = self.try_stack_child(child_index, db, parent_hash, slice_query)?; + let sbranch = self.try_stack_child(0, db, parent_hash, slice_query)?; let TryStackChildResult::Stacked = sbranch else { return Err(VerifyError::InvalidChildReference(b"branch in db should follow extension".to_vec())); }; - } else { - let infos = CompactEncodingInfos { - node: child_node.0, - accessed_children: Default::default(), - accessed_value: false, - depth: prefix.len(), - next_descended_child: 0, - is_inline, - }; - self.recorder.record_stacked_node(&infos, stack.len()); - stack.push(infos); } if descend_incomplete { @@ -680,6 +684,10 @@ impl RecordStack { self.recorder.record_popped_node(&item, self.items.len()); let depth = self.items.last().map(|i| i.depth).unwrap_or(0); self.prefix.drop_lasts(self.prefix.len() - depth); + if depth == item.depth { + // Two consecutive identical depth is an extension + self.pop(); + } true } else { false @@ -790,6 +798,7 @@ impl<'a, L: TrieLayout, D: Borrow<[u8]>> ReadStack<'a, L, D> { proof: &mut impl Iterator, expected_root: &Option>, mut slice_query: Option<&mut NibbleSlice>, + query_prefix: bool, ) -> Result, CError>> { let check_hash = expected_root.is_some(); let child_handle = if let Some(node) = self.items.last_mut() { @@ -835,6 +844,7 @@ impl<'a, L: TrieLayout, D: Borrow<[u8]>> ReadStack<'a, L, D> { }; let node_data = node.data(); + let mut prefix_incomplete = false; match node.node_plan() { NodePlan::Branch { .. } => (), | NodePlan::Empty => (), @@ -848,10 +858,21 @@ impl<'a, L: TrieLayout, D: Borrow<[u8]>> ReadStack<'a, L, D> { } } let ok = if let Some(slice) = slice_query.as_mut() { - slice.starts_with(&partial) + if slice.starts_with(&partial) { + true + } else if query_prefix { + prefix_incomplete = true; + partial.starts_with(slice) + } else { + false + } } else { true }; + if prefix_incomplete { + // end of query + slice_query = None; + } if ok { if self.prefix.len() > 0 { self.prefix.push(child_index); @@ -895,7 +916,11 @@ impl<'a, L: TrieLayout, D: Borrow<[u8]>> ReadStack<'a, L, D> { } node.depth = self.prefix.len(); self.items.push(node); - Ok(TryStackChildResult::Stacked) + if prefix_incomplete { + Ok(TryStackChildResult::StackedDescendIncomplete) + } else { + Ok(TryStackChildResult::Stacked) + } } fn access_value( @@ -1056,11 +1081,11 @@ where while let Some((_, accessed_value)) = self.stack.iter_prefix.clone() { // prefix iteration if !accessed_value { + self.stack.iter_prefix.as_mut().map(|s| { + s.1 = true; + }); match self.stack.access_value(&mut self.proof, check_hash) { Ok(Some(value)) => { - self.stack.iter_prefix.as_mut().map(|s| { - s.1 = true; - }); // prefix is &'a mut NibbleVec. let prefix: &'a NibbleVec = unsafe { core::mem::transmute(&self.stack.prefix) }; @@ -1073,7 +1098,6 @@ where }, }; } - let mut stacked = false; while let Some(child_index) = self.stack.items.last_mut().and_then(|last| { if last.next_descended_child as usize >= crate::nibble_ops::NIBBLE_LENGTH { None @@ -1088,6 +1112,7 @@ where &mut self.proof, &self.expected_root, None, + false, ) { Ok(r) => r, Err(e) => { @@ -1097,7 +1122,9 @@ where }; match r { TryStackChildResult::Stacked => { - stacked = true; + self.stack.iter_prefix.as_mut().map(|p| { + p.1 = false; + }); break }, TryStackChildResult::StackedDescendIncomplete => { @@ -1110,12 +1137,11 @@ where }, } } - if !stacked { + if self.stack.iter_prefix.as_ref().map(|p| p.1).unwrap_or_default() { if !self.stack.pop() { // end iter self.stack.exit_prefix_iter(); } - break } } if did_prefix { @@ -1129,9 +1155,10 @@ where let as_prefix = to_check.as_prefix; let mut at_value = false; match self.stack.prefix.len().cmp(&to_check_len) { - Ordering::Equal => { - at_value = true; - }, + Ordering::Equal => + if !self.stack.items.is_empty() { + at_value = true; + }, Ordering::Less => (), Ordering::Greater => { unreachable!(); @@ -1165,6 +1192,7 @@ where &mut self.proof, &self.expected_root, Some(&mut to_check_slice), + to_check.as_prefix, ) { Ok(r) => r, Err(e) => { diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index a758901a..b8101c3f 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -267,15 +267,15 @@ fn test_query_plan_internal() { InMemQueryPlan { items: vec![ InMemQueryPlanItem::new(b"bravo".to_vec(), false), - InMemQueryPlanItem::new(b"doge".to_vec(), false), - InMemQueryPlanItem::new(b"horsey".to_vec(), false), + InMemQueryPlanItem::new(b"do".to_vec(), true), ], ignore_unordered: false, }, InMemQueryPlan { items: vec![ InMemQueryPlanItem::new(b"bravo".to_vec(), false), - InMemQueryPlanItem::new(b"do".to_vec(), true), + InMemQueryPlanItem::new(b"doge".to_vec(), false), + InMemQueryPlanItem::new(b"horsey".to_vec(), false), ], ignore_unordered: false, }, From 446fcf6e0e0fdb147cfc563390dbbed54db49c76 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 31 Mar 2023 19:18:48 +0200 Subject: [PATCH 015/154] fixing key and avoid unsafe (smallvec internally). --- trie-db/src/query_plan.rs | 33 ++++++++++++++++----------------- trie-db/test/src/proof.rs | 23 +++++++++++------------ 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 434c2a7c..4bab0588 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -717,7 +717,7 @@ where expected_root: Option>, current: Option>, state: ReadProofState, - stack: ReadStack<'a, L, D>, + stack: ReadStack, } #[derive(Eq, PartialEq)] @@ -769,9 +769,9 @@ impl> ItemStack { } } -struct ReadStack<'a, L: TrieLayout, D: Borrow<[u8]>> { +struct ReadStack> { items: Vec>, - prefix: &'a mut NibbleVec, + prefix: NibbleVec, iter_prefix: Option<(usize, bool)>, // limit and wether we return value is_compact: bool, _ph: PhantomData, @@ -791,7 +791,7 @@ fn verify_hash( } } -impl<'a, L: TrieLayout, D: Borrow<[u8]>> ReadStack<'a, L, D> { +impl> ReadStack { fn try_stack_child( &mut self, child_index: u8, @@ -962,8 +962,9 @@ impl<'a, L: TrieLayout, D: Borrow<[u8]>> ReadStack<'a, L, D> { if self.iter_prefix.as_ref().map(|p| p.0 == self.items.len()).unwrap_or(false) { return false } - if let Some(last) = self.items.pop() { - self.prefix.drop_lasts(self.prefix.len() - last.depth); + if let Some(_last) = self.items.pop() { + let depth = self.items.last().map(|i| i.depth).unwrap_or(0); + self.prefix.drop_lasts(self.prefix.len() - depth); true } else { false @@ -1011,8 +1012,7 @@ pub enum ReadProofItem<'a> { /// Seen value and key in proof. /// When we set the query plan, we only return content /// matching the query plan. - /// TODO try ref for value?? - Value(&'a [u8], Vec), + Value(Cow<'a, [u8]>, Vec), /// No value seen for a key in the input query plan. NoValue(&'a [u8]), /// Seen fully covered prefix in proof, this is only @@ -1085,12 +1085,11 @@ where s.1 = true; }); match self.stack.access_value(&mut self.proof, check_hash) { - Ok(Some(value)) => { - // prefix is &'a mut NibbleVec. - let prefix: &'a NibbleVec = - unsafe { core::mem::transmute(&self.stack.prefix) }; - return Some(Ok(ReadProofItem::Value(prefix.inner(), value))) - }, + Ok(Some(value)) => + return Some(Ok(ReadProofItem::Value( + self.stack.prefix.inner().to_vec().into(), + value, + ))), Ok(None) => (), Err(e) => { self.state = ReadProofState::Finished; @@ -1172,7 +1171,8 @@ where } self.state = ReadProofState::SwitchQueryPlan; match self.stack.access_value(&mut self.proof, check_hash) { - Ok(Some(value)) => return Some(Ok(ReadProofItem::Value(to_check.key, value))), + Ok(Some(value)) => + return Some(Ok(ReadProofItem::Value(to_check.key.into(), value))), Ok(None) => return Some(Ok(ReadProofItem::NoValue(to_check.key))), Err(e) => { self.state = ReadProofState::Finished; @@ -1249,7 +1249,6 @@ pub fn verify_query_plan_iter<'a, L, C, D, P>( restart: Option, kind: ProofKind, expected_root: Option>, - prefix: &'a mut NibbleVec, ) -> Result, VerifyError, CError>> where L: TrieLayout, @@ -1274,7 +1273,7 @@ where state: ReadProofState::NotStarted, stack: ReadStack { items: Default::default(), - prefix, + prefix: Default::default(), is_compact, iter_prefix: None, _ph: PhantomData, diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index b8101c3f..a910635c 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -15,6 +15,7 @@ use hash_db::Hasher; use reference_trie::{test_layouts, NoExtensionLayout}; +use std::collections::BTreeMap; use trie_db::{ proof::{generate_proof, verify_proof, VerifyError}, query_plan::HaltedStateRecord, @@ -265,10 +266,7 @@ fn test_query_plan_internal() { let query_plans = [ InMemQueryPlan { - items: vec![ - InMemQueryPlanItem::new(b"bravo".to_vec(), false), - InMemQueryPlanItem::new(b"do".to_vec(), true), - ], + items: vec![InMemQueryPlanItem::new(b"".to_vec(), true)], ignore_unordered: false, }, InMemQueryPlan { @@ -280,7 +278,10 @@ fn test_query_plan_internal() { ignore_unordered: false, }, InMemQueryPlan { - items: vec![InMemQueryPlanItem::new(b"".to_vec(), true)], + items: vec![ + InMemQueryPlanItem::new(b"bravo".to_vec(), false), + InMemQueryPlanItem::new(b"do".to_vec(), true), + ], ignore_unordered: false, }, ]; @@ -296,27 +297,25 @@ fn test_query_plan_internal() { let proof = from.finish().output().nodes; let query_plan_iter = query_plan.as_ref(); - let mut buffer = trie_db::NibbleVec::new(); let verify_iter = verify_query_plan_iter::( query_plan_iter, proof.into_iter(), None, kind, Some(root.clone()), - &mut buffer, ) .unwrap(); + let mut content: BTreeMap<_, _> = set.iter().cloned().collect(); let mut in_prefix = false; for item in verify_iter { match item.unwrap() { - ReadProofItem::Value(_key, _value) => { - // TODO remove from hashmap if not in prefix?? + ReadProofItem::Value(key, value) => { + assert_eq!(content.get(&*key), Some(&value.as_ref())); }, - ReadProofItem::NoValue(_key) => { - // TODO remove from hashmap?? + ReadProofItem::NoValue(key) => { + assert_eq!(content.get(key), None); }, ReadProofItem::StartPrefix(_prefix) => { - // TODO remove from hashmap?? in_prefix = true; }, ReadProofItem::EndPrefix => { From c6484cdff28a38d91ea4aaae3e5aa3f989d8f540 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 31 Mar 2023 21:36:51 +0200 Subject: [PATCH 016/154] some api --- trie-db/src/query_plan.rs | 189 ++++++++++++++++++++++++++++++++------ trie-db/test/src/proof.rs | 94 +++++++++++-------- 2 files changed, 215 insertions(+), 68 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 4bab0588..d4278af4 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -80,6 +80,10 @@ impl<'a> QueryPlanItem<'a> { common_depth, ) } + + fn to_owned(&self) -> InMemQueryPlanItem { + InMemQueryPlanItem { key: self.key.to_vec(), as_prefix: self.as_prefix } + } } /// Query plan in memory. @@ -189,17 +193,6 @@ struct CompactEncodingInfos { is_inline: bool, } -/* likely compact encoding is enough -struct ContentEncodingInfos { - /// Node in memory content. - node: OwnedNode>, - /// Flags indicating whether each child is omitted in the encoded node. - omit_children: Bitmap, - /// Skip value if value node is after. - omit_value: bool, -} -*/ - /// Allows sending proof recording as it is produced. pub trait RecorderOutput { /// Append bytes. @@ -234,12 +227,22 @@ impl RecorderOutput for InMemoryRecorder { } /// Simplified recorder. -pub struct Recorder(RecorderStateInner); +pub struct Recorder { + output: RecorderStateInner, + limits: Limits, +} + +/// Limits to size proof to record. +struct Limits { + remaining_node: Option, + remaining_size: Option, + kind: ProofKind, +} impl Recorder { /// Get back output handle from a recorder. pub fn output(self) -> O { - match self.0 { + match self.output { RecorderStateInner::Stream(output) | RecorderStateInner::Compact { output, .. } | RecorderStateInner::CompactStream(output) | @@ -248,21 +251,30 @@ impl Recorder { } /// Instantiate a new recorder. - pub fn new(proof_kind: ProofKind, output: O) -> Self { - let recorder = match proof_kind { + pub fn new( + kind: ProofKind, + output: O, + limit_node: Option, + limit_size: Option, + ) -> Self { + let output = match kind { ProofKind::FullNodes => RecorderStateInner::Stream(output), ProofKind::CompactNodes => RecorderStateInner::Compact { output, proof: Vec::new(), stacked_pos: Vec::new() }, ProofKind::CompactNodesStream => RecorderStateInner::CompactStream(output), ProofKind::CompactContent => RecorderStateInner::Content(output), }; - Self(recorder) + let limits = Limits { remaining_node: limit_node, remaining_size: limit_size, kind }; + Self { output, limits } } - fn record_stacked_node(&mut self, item: &CompactEncodingInfos, stack_pos: usize) { - match &mut self.0 { + #[must_use] + fn record_stacked_node(&mut self, item: &CompactEncodingInfos, _stack_pos: usize) -> bool { + let mut res = false; + match &mut self.output { RecorderStateInner::Stream(output) => if !item.is_inline { + res = self.limits.add_node(item.node.data().len()); output.write_entry(item.node.data().into()); }, RecorderStateInner::Compact { output, proof, stacked_pos } => { @@ -275,10 +287,11 @@ impl Recorder { unimplemented!() }, } + res } fn record_popped_node(&mut self, item: &CompactEncodingInfos, stack_pos: usize) { - match &mut self.0 { + match &mut self.output { RecorderStateInner::Stream(_) => (), RecorderStateInner::Compact { output, proof, stacked_pos } => { unimplemented!() @@ -292,9 +305,12 @@ impl Recorder { } } - fn record_value_node(&mut self, value: Vec) { - match &mut self.0 { + #[must_use] + fn record_value_node(&mut self, value: Vec) -> bool { + let mut res = false; + match &mut self.output { RecorderStateInner::Stream(output) => { + res = self.limits.add_value(value.len()); output.write_entry(value.into()); }, RecorderStateInner::Compact { output, proof, stacked_pos } => { @@ -307,10 +323,11 @@ impl Recorder { unimplemented!() }, } + res } fn record_value_inline(&mut self, value: &[u8]) { - match &mut self.0 { + match &mut self.output { RecorderStateInner::Stream(output) => { // not writing inline value (already // in parent node). @@ -328,6 +345,78 @@ impl Recorder { } } +impl Limits { + #[must_use] + fn add_node(&mut self, size: usize) -> bool { + let mut res = false; + match self.kind { + ProofKind::FullNodes => { + if let Some(rem_size) = self.remaining_size.as_mut() { + if *rem_size >= size { + *rem_size -= size; + } else { + *rem_size = 0; + res = true; + } + } + if let Some(rem_node) = self.remaining_node.as_mut() { + if *rem_node > 1 { + *rem_node -= 1; + } else { + *rem_node = 0; + res = true; + } + } + }, + ProofKind::CompactNodes => { + unimplemented!() + }, + ProofKind::CompactNodesStream => { + unimplemented!() + }, + ProofKind::CompactContent => { + unimplemented!() + }, + } + res + } + + #[must_use] + fn add_value(&mut self, size: usize) -> bool { + let mut res = false; + match self.kind { + ProofKind::FullNodes => { + if let Some(rem_size) = self.remaining_size.as_mut() { + if *rem_size >= size { + *rem_size -= size; + } else { + *rem_size = 0; + res = true; + } + } + if let Some(rem_node) = self.remaining_node.as_mut() { + if *rem_node > 1 { + *rem_node -= 1; + } else { + *rem_node = 0; + res = true; + } + } + }, + ProofKind::CompactNodes => { + unimplemented!() + }, + ProofKind::CompactNodesStream => { + unimplemented!() + }, + ProofKind::CompactContent => { + unimplemented!() + }, + } + res + } +} + // TODO may be useless enum RecorderStateInner { /// For FullNodes proofs, just send node to this stream. @@ -357,6 +446,25 @@ pub struct HaltedStateRecord { } impl HaltedStateRecord { + /// Indicate we reuse the query plan iterator + /// and stack. + pub fn statefull(&mut self, recorder: Recorder) -> Recorder { + let result = core::mem::replace(&mut self.stack.recorder, recorder); + self.from = None; + result + } + + /// Indicate to use stateless (on a fresh proof + /// and a fresh query plan iterator). + pub fn stateless(&mut self, recorder: Recorder) -> Recorder { + let new_start = Self::from_start(recorder); + let old = core::mem::replace(self, new_start); + self.from = old.from; + self.currently_query_item = None; + old.stack.recorder + } + + /// Init from start. pub fn from_start(recorder: Recorder) -> Self { HaltedStateRecord { currently_query_item: None, @@ -365,6 +473,7 @@ impl HaltedStateRecord { items: Vec::new(), prefix: NibbleVec::new(), iter_prefix: None, + halt: false, }, from: Some(Default::default()), } @@ -392,6 +501,7 @@ struct RecordStack { items: Vec, prefix: NibbleVec, iter_prefix: Option, + halt: bool, } /// Run query plan on a full db and record it. @@ -456,6 +566,13 @@ pub fn record_query_plan< } let child_index = if stack.items.is_empty() { 0 } else { slice_query.at(0) }; + if stack.halt { + let restart_cursor = unimplemented!(); + from.from = restart_cursor; + from.currently_query_item = Some(query.to_owned()); + return Ok(from) + } + match stack.try_stack_child( child_index, db, @@ -479,11 +596,6 @@ pub fn record_query_plan< from_query_ref = None; prev_query = Some(query); - if touched { - // try access value - stack.access_value(db)?; - touched = false; - } if let Some(prefix_stack_depth) = stack.iter_prefix.clone() { // run prefix iteration let mut stacked = true; @@ -505,6 +617,14 @@ pub fn record_query_plan< } else { break }; + + if stack.halt { + let restart_cursor = unimplemented!(); + from.from = restart_cursor; + from.currently_query_item = prev_query.map(|q| q.to_owned()); + return Ok(from) + } + match stack.try_stack_child(child_index, db, dummy_parent_hash, None)? { TryStackChildResult::Stacked => { stacked = true; @@ -521,13 +641,18 @@ pub fn record_query_plan< } // pop - if !stack.pop() { break } } stack.exit_prefix_iter(); } + + if touched { + // try access value + stack.access_value(db)?; + touched = false; + } } Ok(from) @@ -621,7 +746,9 @@ impl RecordStack { next_descended_child: 0, is_inline, }; - self.recorder.record_stacked_node(&infos, stack.len()); + if self.recorder.record_stacked_node(&infos, stack.len()) { + self.halt = true; + } stack.push(infos); if stack_extension { let sbranch = self.try_stack_child(0, db, parent_hash, slice_query)?; @@ -667,7 +794,9 @@ impl RecordStack { let Some(value) = db.db().get(&hash, self.prefix.as_prefix()) else { return Err(VerifyError::IncompleteProof); }; - self.recorder.record_value_node(value); + if self.recorder.record_value_node(value) { + self.halt = true; + } }, Value::Inline(value) => { self.recorder.record_value_inline(value); diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index a910635c..921d6022 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -287,44 +287,62 @@ fn test_query_plan_internal() { ]; for query_plan in query_plans { let kind = ProofKind::FullNodes; - let recorder = Recorder::new(kind, InMemoryRecorder::default()); - let from = HaltedStateRecord::from_start(recorder); - // no limit - - let query_plan_iter = query_plan.as_ref(); - let from = record_query_plan::(&db, query_plan_iter, from).unwrap(); - assert!(from.is_finished()); - let proof = from.finish().output().nodes; - - let query_plan_iter = query_plan.as_ref(); - let verify_iter = verify_query_plan_iter::( - query_plan_iter, - proof.into_iter(), - None, - kind, - Some(root.clone()), - ) - .unwrap(); - let mut content: BTreeMap<_, _> = set.iter().cloned().collect(); - let mut in_prefix = false; - for item in verify_iter { - match item.unwrap() { - ReadProofItem::Value(key, value) => { - assert_eq!(content.get(&*key), Some(&value.as_ref())); - }, - ReadProofItem::NoValue(key) => { - assert_eq!(content.get(key), None); - }, - ReadProofItem::StartPrefix(_prefix) => { - in_prefix = true; - }, - ReadProofItem::EndPrefix => { - assert!(in_prefix); - in_prefix = false; - }, - ReadProofItem::Halted(_) => { - unreachable!("full proof"); - }, + for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] { + let limit = limit_conf.0; + let limit = (limit != 0).then(|| limit); + let recorder = Recorder::new(kind, InMemoryRecorder::default(), limit, None); + let mut from = HaltedStateRecord::from_start(recorder); + // no limit + let mut proof: Vec> = Default::default(); + loop { + let query_plan_iter = query_plan.as_ref(); + from = record_query_plan::(&db, query_plan_iter, from).unwrap(); + + if limit.is_none() { + assert!(from.is_finished()); + } + if from.is_finished() { + proof.append(&mut from.finish().output().nodes); + break + } + let rec = if limit_conf.1 { + from.stateless(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) + } else { + from.statefull(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) + }; + proof.append(&mut rec.output().nodes); + } + + let query_plan_iter = query_plan.as_ref(); + let verify_iter = verify_query_plan_iter::( + query_plan_iter, + proof.into_iter(), + None, + kind, + Some(root.clone()), + ) + .unwrap(); + let content: BTreeMap<_, _> = set.iter().cloned().collect(); + let mut in_prefix = false; + for item in verify_iter { + match item.unwrap() { + ReadProofItem::Value(key, value) => { + assert_eq!(content.get(&*key), Some(&value.as_ref())); + }, + ReadProofItem::NoValue(key) => { + assert_eq!(content.get(key), None); + }, + ReadProofItem::StartPrefix(_prefix) => { + in_prefix = true; + }, + ReadProofItem::EndPrefix => { + assert!(in_prefix); + in_prefix = false; + }, + ReadProofItem::Halted(_) => { + unreachable!("full proof"); + }, + } } } From 8d7e804bba73f9d691e45dc3c980278d20982c30 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 31 Mar 2023 22:38:47 +0200 Subject: [PATCH 017/154] in progress, better halt handling --- trie-db/src/query_plan.rs | 169 ++++++++++++++++++++++---------------- trie-db/test/src/proof.rs | 4 +- 2 files changed, 101 insertions(+), 72 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index d4278af4..d7058aaa 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -450,7 +450,6 @@ impl HaltedStateRecord { /// and stack. pub fn statefull(&mut self, recorder: Recorder) -> Recorder { let result = core::mem::replace(&mut self.stack.recorder, recorder); - self.from = None; result } @@ -475,7 +474,7 @@ impl HaltedStateRecord { iter_prefix: None, halt: false, }, - from: Some(Default::default()), + from: None, } } @@ -521,10 +520,21 @@ pub fn record_query_plan< // TODO //) resto let dummy_parent_hash = TrieHash::::default(); - if let Some(lower_bound) = from.from.take() { - // TODO implement stack building from lower bound: fetch in stack and don't record. - // First also advance iterator to skip and init currently_query_item. - } + let restore = if let Some(lower_bound) = from.from.take() { + let mut statefull = true; + if from.currently_query_item.is_none() { + statefull = false; + // TODO implement stack building from lower bound: fetch in stack and don't record. + // First also advance iterator to skip and init currently_query_item. + unimplemented!() + } + Some(( + lower_bound.0.len() * nibble_ops::NIBBLE_PER_BYTE - if lower_bound.1 { 1 } else { 2 }, + statefull, + )) + } else { + None + }; let stack = &mut from.stack; @@ -532,73 +542,90 @@ pub fn record_query_plan< let mut from_query = from.currently_query_item.take(); let mut from_query_ref = from_query.as_ref().map(|f| f.as_ref()); while let Some(query) = from_query_ref.clone().or_else(|| query_plan.items.next()) { - let (ordered, common_nibbles) = - prev_query.as_ref().map(|p| p.before(&query)).unwrap_or((true, 0)); - if !ordered { - if query_plan.ignore_unordered { - continue - } else { - return Err(VerifyError::UnorderedKey(query.key.to_vec())) // TODO not kind as param if keeping - // CompactContent - } - } - loop { - match stack.prefix.len().cmp(&common_nibbles) { - Ordering::Equal | Ordering::Less => break, - Ordering::Greater => - if !stack.pop() { - return Ok(from) - }, - } - } - - // descend - let mut slice_query = NibbleSlice::new_offset(&query.key, common_nibbles); - let mut touched = false; - loop { - if slice_query.is_empty() && !stack.items.is_empty() { - if query.as_prefix { - stack.enter_prefix_iter(); + let common_nibbles = if let Some((common_nibbles, statefull)) = restore { + if statefull {} + common_nibbles + } else { + let (ordered, common_nibbles) = + prev_query.as_ref().map(|p| p.before(&query)).unwrap_or((true, 0)); + if !ordered { + if query_plan.ignore_unordered { + continue } else { - touched = true; + return Err(VerifyError::UnorderedKey(query.key.to_vec())) // TODO not kind as param if keeping + // CompactContent } - break } - - let child_index = if stack.items.is_empty() { 0 } else { slice_query.at(0) }; - if stack.halt { - let restart_cursor = unimplemented!(); - from.from = restart_cursor; - from.currently_query_item = Some(query.to_owned()); - return Ok(from) + loop { + match stack.prefix.len().cmp(&common_nibbles) { + Ordering::Equal | Ordering::Less => break, + Ordering::Greater => + if !stack.pop() { + return Ok(from) + }, + } } + common_nibbles + }; - match stack.try_stack_child( - child_index, - db, - dummy_parent_hash, - Some(&mut slice_query), - )? { - TryStackChildResult::Stacked => {}, - TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked => break, - TryStackChildResult::StackedDescendIncomplete => { + let mut first_iter = false; + if stack.iter_prefix.is_none() { + // descend + let mut slice_query = NibbleSlice::new_offset(&query.key, common_nibbles); + let mut touched = false; + loop { + if slice_query.is_empty() && !stack.items.is_empty() { if query.as_prefix { + first_iter = true; stack.enter_prefix_iter(); + } else { + touched = true; } break - }, - TryStackChildResult::Halted => { - unimplemented!() - }, + } + + let child_index = if stack.items.is_empty() { 0 } else { slice_query.at(0) }; + match stack.try_stack_child( + child_index, + db, + dummy_parent_hash, + Some(&mut slice_query), + )? { + TryStackChildResult::Stacked => {}, + TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked => + break, + TryStackChildResult::StackedDescendIncomplete => { + if query.as_prefix { + first_iter = true; // TODO reorder to avoid this bool? + stack.enter_prefix_iter(); + } + break + }, + TryStackChildResult::Halted => { + stack.prefix.push(child_index); + stack.halt = false; + from.from = Some(( + stack.prefix.inner().to_vec(), + (stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, + )); + from.currently_query_item = Some(query.to_owned()); + return Ok(from) + }, + } } - } + if touched { + // try access value + stack.access_value(db)?; + touched = false; + } + } from_query_ref = None; prev_query = Some(query); if let Some(prefix_stack_depth) = stack.iter_prefix.clone() { // run prefix iteration - let mut stacked = true; + let mut stacked = first_iter; loop { // descend loop { @@ -618,13 +645,6 @@ pub fn record_query_plan< break }; - if stack.halt { - let restart_cursor = unimplemented!(); - from.from = restart_cursor; - from.currently_query_item = prev_query.map(|q| q.to_owned()); - return Ok(from) - } - match stack.try_stack_child(child_index, db, dummy_parent_hash, None)? { TryStackChildResult::Stacked => { stacked = true; @@ -635,7 +655,16 @@ pub fn record_query_plan< unreachable!("no slice query") }, TryStackChildResult::Halted => { - unimplemented!() + if let Some(mut item) = stack.items.last_mut() { + item.next_descended_child -= 1; + } + stack.halt = false; + from.from = Some(( + stack.prefix.inner().to_vec(), + (stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, + )); + from.currently_query_item = prev_query.map(|q| q.to_owned()); + return Ok(from) }, } } @@ -647,12 +676,6 @@ pub fn record_query_plan< } stack.exit_prefix_iter(); } - - if touched { - // try access value - stack.access_value(db)?; - touched = false; - } } Ok(from) @@ -708,6 +731,10 @@ impl RecordStack { // TODO consider not going into inline for all proof but content. // Returning NotStacked here sounds safe, then the is_inline field is not needed. is_inline = true; + } else { + if self.halt { + return Ok(TryStackChildResult::Halted) + } } // TODO handle cache first let child_node = db diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 921d6022..2acc93fa 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -287,7 +287,9 @@ fn test_query_plan_internal() { ]; for query_plan in query_plans { let kind = ProofKind::FullNodes; - for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] { + for limit_conf in + [/* (0, false), */ (1, false), (1, true), (2, false), (2, true), (3, true)] + { let limit = limit_conf.0; let limit = (limit != 0).then(|| limit); let recorder = Recorder::new(kind, InMemoryRecorder::default(), limit, None); From 062fafc6f96be1fe5794ef9528d00842e5dfbed8 Mon Sep 17 00:00:00 2001 From: cheme Date: Sat, 1 Apr 2023 09:02:35 +0200 Subject: [PATCH 018/154] fix full state restart --- trie-db/src/query_plan.rs | 22 ++++++++++++++-------- trie-db/test/src/proof.rs | 5 +++-- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index d7058aaa..c8064943 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -514,7 +514,7 @@ pub fn record_query_plan< O: RecorderOutput, >( db: &TrieDB, - mut query_plan: QueryPlan<'a, I>, + query_plan: &mut QueryPlan<'a, I>, mut from: HaltedStateRecord, ) -> Result, VerifyError, CError>> { // TODO @@ -529,7 +529,7 @@ pub fn record_query_plan< unimplemented!() } Some(( - lower_bound.0.len() * nibble_ops::NIBBLE_PER_BYTE - if lower_bound.1 { 1 } else { 2 }, + lower_bound.0.len() * nibble_ops::NIBBLE_PER_BYTE - if lower_bound.1 { 1 } else { 0 }, statefull, )) } else { @@ -542,9 +542,9 @@ pub fn record_query_plan< let mut from_query = from.currently_query_item.take(); let mut from_query_ref = from_query.as_ref().map(|f| f.as_ref()); while let Some(query) = from_query_ref.clone().or_else(|| query_plan.items.next()) { - let common_nibbles = if let Some((common_nibbles, statefull)) = restore { + let common_nibbles = if let Some((slice_at, statefull)) = restore { if statefull {} - common_nibbles + slice_at } else { let (ordered, common_nibbles) = prev_query.as_ref().map(|p| p.before(&query)).unwrap_or((true, 0)); @@ -702,6 +702,7 @@ impl RecordStack { let stack = &mut self.items; let mut descend_incomplete = false; let mut stack_extension = false; + let mut from_branch = None; let child_handle = if let Some(item) = stack.last_mut() { let node_data = item.node.data(); @@ -710,15 +711,14 @@ impl RecordStack { return Ok(TryStackChildResult::NotStacked), NodePlan::Extension { child, .. } => if child_index == 0 { + item.accessed_children.set(child_index as usize, true); child.build(node_data) } else { return Ok(TryStackChildResult::NotStacked) }, NodePlan::NibbledBranch { children, .. } | NodePlan::Branch { children, .. } => if let Some(child) = &children[child_index as usize] { - slice_query.as_mut().map(|s| s.advance(1)); - prefix.push(child_index); - item.accessed_children.set(child_index as usize, true); + from_branch = Some(&mut item.accessed_children); child.build(node_data) } else { return Ok(TryStackChildResult::NotStackedBranch) @@ -732,10 +732,16 @@ impl RecordStack { // Returning NotStacked here sounds safe, then the is_inline field is not needed. is_inline = true; } else { - if self.halt { + if self.halt && from_branch.is_some() { return Ok(TryStackChildResult::Halted) } } + if let Some(accessed_children) = from_branch { + accessed_children.set(child_index as usize, true); + + slice_query.as_mut().map(|s| s.advance(1)); + prefix.push(child_index); + } // TODO handle cache first let child_node = db .get_raw_or_lookup(parent_hash, child_handle, prefix.as_prefix(), false) diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 2acc93fa..2e3845b2 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -296,9 +296,9 @@ fn test_query_plan_internal() { let mut from = HaltedStateRecord::from_start(recorder); // no limit let mut proof: Vec> = Default::default(); + let mut query_plan_iter = query_plan.as_ref(); loop { - let query_plan_iter = query_plan.as_ref(); - from = record_query_plan::(&db, query_plan_iter, from).unwrap(); + from = record_query_plan::(&db, &mut query_plan_iter, from).unwrap(); if limit.is_none() { assert!(from.is_finished()); @@ -308,6 +308,7 @@ fn test_query_plan_internal() { break } let rec = if limit_conf.1 { + query_plan_iter = query_plan.as_ref(); from.stateless(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) } else { from.statefull(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) From bd19ee488980ffc0558efdd47affa48d55ea32a1 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Sat, 1 Apr 2023 17:06:32 +0200 Subject: [PATCH 019/154] proto restart --- trie-db/src/query_plan.rs | 78 ++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 22 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index c8064943..bb455d5f 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -230,6 +230,8 @@ impl RecorderOutput for InMemoryRecorder { pub struct Recorder { output: RecorderStateInner, limits: Limits, + // on restore only record content AFTER this position. + start_at: Option, } /// Limits to size proof to record. @@ -265,11 +267,14 @@ impl Recorder { ProofKind::CompactContent => RecorderStateInner::Content(output), }; let limits = Limits { remaining_node: limit_node, remaining_size: limit_size, kind }; - Self { output, limits } + Self { output, limits, start_at: None } } #[must_use] fn record_stacked_node(&mut self, item: &CompactEncodingInfos, _stack_pos: usize) -> bool { + if self.start_at.map(|s| item.depth > s).unwrap_or(false) { + return false; + } let mut res = false; match &mut self.output { RecorderStateInner::Stream(output) => @@ -291,6 +296,10 @@ impl Recorder { } fn record_popped_node(&mut self, item: &CompactEncodingInfos, stack_pos: usize) { + if self.start_at.map(|s| item.depth > s).unwrap_or(false) { + return; + } + match &mut self.output { RecorderStateInner::Stream(_) => (), RecorderStateInner::Compact { output, proof, stacked_pos } => { @@ -306,7 +315,11 @@ impl Recorder { } #[must_use] - fn record_value_node(&mut self, value: Vec) -> bool { + fn record_value_node(&mut self, value: Vec, depth: usize) -> bool { + if self.start_at.map(|s| depth > s).unwrap_or(false) { + return false; + } + let mut res = false; match &mut self.output { RecorderStateInner::Stream(output) => { @@ -326,7 +339,11 @@ impl Recorder { res } - fn record_value_inline(&mut self, value: &[u8]) { + fn record_value_inline(&mut self, value: &[u8], depth: usize) { + if self.start_at.map(|s| depth > s).unwrap_or(false) { + return; + } + match &mut self.output { RecorderStateInner::Stream(output) => { // not writing inline value (already @@ -519,31 +536,45 @@ pub fn record_query_plan< ) -> Result, VerifyError, CError>> { // TODO //) resto + let restore_buf; + let mut restore_buf2 = Vec::new(); let dummy_parent_hash = TrieHash::::default(); - let restore = if let Some(lower_bound) = from.from.take() { - let mut statefull = true; + let mut stateless = false; + let mut statefull = None; + let mut bound = LeftNibbleSlice::new(&[]); + if let Some(lower_bound) = from.from.take() { if from.currently_query_item.is_none() { - statefull = false; - // TODO implement stack building from lower bound: fetch in stack and don't record. - // First also advance iterator to skip and init currently_query_item. - unimplemented!() + stateless = true; + restore_buf2 = lower_bound.0.clone(); + restore_buf = lower_bound.0; + bound = LeftNibbleSlice::new(&restore_buf[..]); + if lower_bound.1 { + bound.truncate(bound.len() - 1); + restore_buf2.pop(); + } + from.stack.recorder.start_at = Some(bound.len()); + } else { + statefull = Some( + lower_bound.0.len() * nibble_ops::NIBBLE_PER_BYTE - + if lower_bound.1 { 1 } else { 0 }, + ); } - Some(( - lower_bound.0.len() * nibble_ops::NIBBLE_PER_BYTE - if lower_bound.1 { 1 } else { 0 }, - statefull, - )) - } else { - None - }; + } let stack = &mut from.stack; - let mut prev_query: Option = None; + let mut prev_query: Option = if stateless { + Some(QueryPlanItem { + key: restore_buf2.as_slice(), + as_prefix: false, // not checked only to advance. + }) + } else { + None + }; let mut from_query = from.currently_query_item.take(); let mut from_query_ref = from_query.as_ref().map(|f| f.as_ref()); while let Some(query) = from_query_ref.clone().or_else(|| query_plan.items.next()) { - let common_nibbles = if let Some((slice_at, statefull)) = restore { - if statefull {} + let common_nibbles = if let Some(slice_at) = statefull.take() { slice_at } else { let (ordered, common_nibbles) = @@ -552,6 +583,9 @@ pub fn record_query_plan< if query_plan.ignore_unordered { continue } else { + if stateless { + continue + } return Err(VerifyError::UnorderedKey(query.key.to_vec())) // TODO not kind as param if keeping // CompactContent } @@ -567,7 +601,7 @@ pub fn record_query_plan< } common_nibbles }; - + stateless = false; let mut first_iter = false; if stack.iter_prefix.is_none() { // descend @@ -827,12 +861,12 @@ impl RecordStack { let Some(value) = db.db().get(&hash, self.prefix.as_prefix()) else { return Err(VerifyError::IncompleteProof); }; - if self.recorder.record_value_node(value) { + if self.recorder.record_value_node(value, self.prefix.len()) { self.halt = true; } }, Value::Inline(value) => { - self.recorder.record_value_inline(value); + self.recorder.record_value_inline(value, self.prefix.len()); }, } Ok(true) From c1c2a47ae308579d10e5e893ded32cf351b92ee1 Mon Sep 17 00:00:00 2001 From: cheme Date: Sun, 2 Apr 2023 11:25:55 +0200 Subject: [PATCH 020/154] fix --- trie-db/src/nibble/nibblevec.rs | 9 ++++- trie-db/src/query_plan.rs | 69 ++++++++++++++++++++++++++------- 2 files changed, 62 insertions(+), 16 deletions(-) diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index f612585a..ab209d35 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -16,7 +16,7 @@ use super::NibbleVec; use crate::{ - nibble::{nibble_ops, BackingByteVec, NibbleSlice}, + nibble::{nibble_ops, BackingByteVec, LeftNibbleSlice, NibbleSlice}, node::NodeKey, node_codec::Partial, }; @@ -216,6 +216,13 @@ impl NibbleVec { } } + /// `NibbleVec` as a `LeftNibbleSlice`. + pub fn as_leftnibbleslice(&self) -> LeftNibbleSlice { + let mut result = LeftNibbleSlice::new(&self.inner); + result.truncate(self.len); + result + } + /// Do we start with the same nibbles as the whole of `them`? pub fn starts_with(&self, other: &Self) -> bool { if self.len() < other.len() { diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index bb455d5f..b5cf331d 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -273,7 +273,7 @@ impl Recorder { #[must_use] fn record_stacked_node(&mut self, item: &CompactEncodingInfos, _stack_pos: usize) -> bool { if self.start_at.map(|s| item.depth > s).unwrap_or(false) { - return false; + return false } let mut res = false; match &mut self.output { @@ -297,7 +297,7 @@ impl Recorder { fn record_popped_node(&mut self, item: &CompactEncodingInfos, stack_pos: usize) { if self.start_at.map(|s| item.depth > s).unwrap_or(false) { - return; + return } match &mut self.output { @@ -317,7 +317,7 @@ impl Recorder { #[must_use] fn record_value_node(&mut self, value: Vec, depth: usize) -> bool { if self.start_at.map(|s| depth > s).unwrap_or(false) { - return false; + return false } let mut res = false; @@ -341,7 +341,7 @@ impl Recorder { fn record_value_inline(&mut self, value: &[u8], depth: usize) { if self.start_at.map(|s| depth > s).unwrap_or(false) { - return; + return } match &mut self.output { @@ -490,6 +490,7 @@ impl HaltedStateRecord { prefix: NibbleVec::new(), iter_prefix: None, halt: false, + seek: None, }, from: None, } @@ -517,6 +518,7 @@ struct RecordStack { items: Vec, prefix: NibbleVec, iter_prefix: Option, + seek: Option, halt: bool, } @@ -536,33 +538,41 @@ pub fn record_query_plan< ) -> Result, VerifyError, CError>> { // TODO //) resto - let restore_buf; + // let restore_buf; let mut restore_buf2 = Vec::new(); let dummy_parent_hash = TrieHash::::default(); let mut stateless = false; let mut statefull = None; - let mut bound = LeftNibbleSlice::new(&[]); if let Some(lower_bound) = from.from.take() { if from.currently_query_item.is_none() { stateless = true; restore_buf2 = lower_bound.0.clone(); - restore_buf = lower_bound.0; - bound = LeftNibbleSlice::new(&restore_buf[..]); + let mut bound = NibbleVec::new(); + bound.append_optional_slice_and_nibble(Some(&NibbleSlice::new(&lower_bound.0)), None); + if lower_bound.1 { + bound.pop(); + } + /* + bound = LeftNibbleSlice::new(&restore_buf.0[..]); if lower_bound.1 { bound.truncate(bound.len() - 1); - restore_buf2.pop(); + //restore_buf2.pop(); } + */ from.stack.recorder.start_at = Some(bound.len()); + from.stack.seek = Some(bound); } else { statefull = Some( lower_bound.0.len() * nibble_ops::NIBBLE_PER_BYTE - - if lower_bound.1 { 1 } else { 0 }, + if lower_bound.1 { 2 } else { 1 }, ); } } let stack = &mut from.stack; + let mut prev_query: Option = None; + /* let mut prev_query: Option = if stateless { Some(QueryPlanItem { key: restore_buf2.as_slice(), @@ -571,9 +581,28 @@ pub fn record_query_plan< } else { None }; + */ let mut from_query = from.currently_query_item.take(); let mut from_query_ref = from_query.as_ref().map(|f| f.as_ref()); while let Some(query) = from_query_ref.clone().or_else(|| query_plan.items.next()) { + if stateless { + let bound = stack.seek.as_ref().expect("Initiated for stateless"); + let bound = bound.as_leftnibbleslice(); + let query_slice = LeftNibbleSlice::new(&query.key); + if query_slice.starts_with(&bound) { + } else if query.as_prefix { + if bound.starts_with(&query_slice) { + } else { + continue + } + } else { + continue + } + stateless = false; + if !query.as_prefix { + stack.seek = None; + } + } let common_nibbles = if let Some(slice_at) = statefull.take() { slice_at } else { @@ -583,9 +612,6 @@ pub fn record_query_plan< if query_plan.ignore_unordered { continue } else { - if stateless { - continue - } return Err(VerifyError::UnorderedKey(query.key.to_vec())) // TODO not kind as param if keeping // CompactContent } @@ -601,7 +627,6 @@ pub fn record_query_plan< } common_nibbles }; - stateless = false; let mut first_iter = false; if stack.iter_prefix.is_none() { // descend @@ -638,10 +663,12 @@ pub fn record_query_plan< TryStackChildResult::Halted => { stack.prefix.push(child_index); stack.halt = false; + stack.prefix.push(child_index); from.from = Some(( stack.prefix.inner().to_vec(), (stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, )); + stack.prefix.pop(); from.currently_query_item = Some(query.to_owned()); return Ok(from) }, @@ -693,10 +720,12 @@ pub fn record_query_plan< item.next_descended_child -= 1; } stack.halt = false; + stack.prefix.push(child_index); from.from = Some(( stack.prefix.inner().to_vec(), (stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, )); + stack.prefix.pop(); from.currently_query_item = prev_query.map(|q| q.to_owned()); return Ok(from) }, @@ -805,12 +834,22 @@ impl RecordStack { if let NodePlan::Extension { .. } = child_node.0.node_plan() { stack_extension = true; } + let next_descended_child = if let Some(seek) = self.seek.as_ref() { + if prefix.len() <= seek.len() { + seek.at(prefix.len()) + } else { + self.seek = None; + 0 + } + } else { + 0 + }; let infos = CompactEncodingInfos { node: child_node.0, accessed_children: Default::default(), accessed_value: false, depth: prefix.len(), - next_descended_child: 0, + next_descended_child, is_inline, }; if self.recorder.record_stacked_node(&infos, stack.len()) { From fe0d31292e04492219891a249becd43acb74ec10 Mon Sep 17 00:00:00 2001 From: cheme Date: Sun, 2 Apr 2023 17:25:52 +0200 Subject: [PATCH 021/154] ok for full range --- trie-db/src/query_plan.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index b5cf331d..471978c5 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -272,7 +272,7 @@ impl Recorder { #[must_use] fn record_stacked_node(&mut self, item: &CompactEncodingInfos, _stack_pos: usize) -> bool { - if self.start_at.map(|s| item.depth > s).unwrap_or(false) { + if self.start_at.map(|s| s > item.depth).unwrap_or(false) { return false } let mut res = false; @@ -296,7 +296,7 @@ impl Recorder { } fn record_popped_node(&mut self, item: &CompactEncodingInfos, stack_pos: usize) { - if self.start_at.map(|s| item.depth > s).unwrap_or(false) { + if self.start_at.map(|s| s > item.depth).unwrap_or(false) { return } @@ -316,7 +316,7 @@ impl Recorder { #[must_use] fn record_value_node(&mut self, value: Vec, depth: usize) -> bool { - if self.start_at.map(|s| depth > s).unwrap_or(false) { + if self.start_at.map(|s| s > depth).unwrap_or(false) { return false } @@ -340,7 +340,7 @@ impl Recorder { } fn record_value_inline(&mut self, value: &[u8], depth: usize) { - if self.start_at.map(|s| depth > s).unwrap_or(false) { + if self.start_at.map(|s| s > depth).unwrap_or(false) { return } @@ -835,7 +835,7 @@ impl RecordStack { stack_extension = true; } let next_descended_child = if let Some(seek) = self.seek.as_ref() { - if prefix.len() <= seek.len() { + if prefix.len() < seek.len() { seek.at(prefix.len()) } else { self.seek = None; From d3edf03e68e09bf72b0c82cf21dda3ca0d336db3 Mon Sep 17 00:00:00 2001 From: cheme Date: Sun, 2 Apr 2023 18:11:02 +0200 Subject: [PATCH 022/154] working but extension --- trie-db/src/nibble/nibblevec.rs | 4 +--- trie-db/src/query_plan.rs | 1 - trie-db/test/src/proof.rs | 12 +++++------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index ab209d35..26bf8574 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -218,9 +218,7 @@ impl NibbleVec { /// `NibbleVec` as a `LeftNibbleSlice`. pub fn as_leftnibbleslice(&self) -> LeftNibbleSlice { - let mut result = LeftNibbleSlice::new(&self.inner); - result.truncate(self.len); - result + LeftNibbleSlice::new(&self.inner).truncate(self.len) } /// Do we start with the same nibbles as the whole of `them`? diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 471978c5..bedc055c 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -661,7 +661,6 @@ pub fn record_query_plan< break }, TryStackChildResult::Halted => { - stack.prefix.push(child_index); stack.halt = false; stack.prefix.push(child_index); from.from = Some(( diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 2e3845b2..6d754473 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -265,10 +265,6 @@ fn test_query_plan_internal() { let db = >::new(&db, &root).build(); let query_plans = [ - InMemQueryPlan { - items: vec![InMemQueryPlanItem::new(b"".to_vec(), true)], - ignore_unordered: false, - }, InMemQueryPlan { items: vec![ InMemQueryPlanItem::new(b"bravo".to_vec(), false), @@ -284,12 +280,14 @@ fn test_query_plan_internal() { ], ignore_unordered: false, }, + InMemQueryPlan { + items: vec![InMemQueryPlanItem::new(b"".to_vec(), true)], + ignore_unordered: false, + }, ]; for query_plan in query_plans { let kind = ProofKind::FullNodes; - for limit_conf in - [/* (0, false), */ (1, false), (1, true), (2, false), (2, true), (3, true)] - { + for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] { let limit = limit_conf.0; let limit = (limit != 0).then(|| limit); let recorder = Recorder::new(kind, InMemoryRecorder::default(), limit, None); From 147d2c0512e55c2cc5e09e97f73c0dc0104405d4 Mon Sep 17 00:00:00 2001 From: cheme Date: Sun, 2 Apr 2023 18:42:01 +0200 Subject: [PATCH 023/154] fix record after seek --- trie-db/src/query_plan.rs | 18 ++++++++++++++---- trie-db/test/src/proof.rs | 8 ++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index bedc055c..d00da346 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -242,6 +242,16 @@ struct Limits { } impl Recorder { + /// Check and update start at record. + /// When return true, do record. + fn check_start_at(&mut self, depth: usize) -> bool { + if self.start_at.map(|s| s > depth).unwrap_or(false) { + false + } else { + self.start_at = None; + true + } + } /// Get back output handle from a recorder. pub fn output(self) -> O { match self.output { @@ -272,7 +282,7 @@ impl Recorder { #[must_use] fn record_stacked_node(&mut self, item: &CompactEncodingInfos, _stack_pos: usize) -> bool { - if self.start_at.map(|s| s > item.depth).unwrap_or(false) { + if !self.check_start_at(item.depth) { return false } let mut res = false; @@ -296,7 +306,7 @@ impl Recorder { } fn record_popped_node(&mut self, item: &CompactEncodingInfos, stack_pos: usize) { - if self.start_at.map(|s| s > item.depth).unwrap_or(false) { + if !self.check_start_at(item.depth) { return } @@ -316,7 +326,7 @@ impl Recorder { #[must_use] fn record_value_node(&mut self, value: Vec, depth: usize) -> bool { - if self.start_at.map(|s| s > depth).unwrap_or(false) { + if !self.check_start_at(depth) { return false } @@ -340,7 +350,7 @@ impl Recorder { } fn record_value_inline(&mut self, value: &[u8], depth: usize) { - if self.start_at.map(|s| s > depth).unwrap_or(false) { + if !self.check_start_at(depth) { return } diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 6d754473..0bda010b 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -273,6 +273,10 @@ fn test_query_plan_internal() { ], ignore_unordered: false, }, + InMemQueryPlan { + items: vec![InMemQueryPlanItem::new(b"".to_vec(), true)], + ignore_unordered: false, + }, InMemQueryPlan { items: vec![ InMemQueryPlanItem::new(b"bravo".to_vec(), false), @@ -280,10 +284,6 @@ fn test_query_plan_internal() { ], ignore_unordered: false, }, - InMemQueryPlan { - items: vec![InMemQueryPlanItem::new(b"".to_vec(), true)], - ignore_unordered: false, - }, ]; for query_plan in query_plans { let kind = ProofKind::FullNodes; From ad23a49de499cb9f0aebff7ed5b34c46b101f86f Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 3 Apr 2023 10:00:51 +0200 Subject: [PATCH 024/154] api --- trie-db/src/query_plan.rs | 42 ++++++++++++++----- trie-db/test/src/proof.rs | 87 +++++++++++++++++++++++---------------- 2 files changed, 84 insertions(+), 45 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index d00da346..c456c4d8 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -113,17 +113,16 @@ impl InMemQueryPlan { QueryPlan { items: QueryPlanItemIter(&self.items, 0), ignore_unordered: self.ignore_unordered, + _ph: PhantomData, } } } /// Query plan. -pub struct QueryPlan<'a, I> -where - I: Iterator>, -{ +pub struct QueryPlan<'a, I> { items: I, ignore_unordered: bool, + _ph: PhantomData<&'a ()>, } /// Different proof support. @@ -517,10 +516,18 @@ impl HaltedStateRecord { /// When process is halted keep execution state /// to restore later. -pub struct HaltedStateCheck { +pub struct HaltedStateCheck<'a, C> { stack: (), stack_content: (), currently_query_item: Option, + query_plan: QueryPlan<'a, C>, + // _ph: PhantomData, +} + +impl<'a, C> From> for HaltedStateCheck<'a, C> { + fn from(query_plan: QueryPlan<'a, C>) -> Self { + HaltedStateCheck { stack: (), stack_content: (), currently_query_item: None, query_plan } + } } struct RecordStack { @@ -1250,9 +1257,9 @@ impl> ReadStack { } /// Content return on success when reading proof. -pub enum ReadProofItem<'a> { +pub enum ReadProofItem<'a, C> { /// Successfull read of proof, not all content read. - Halted(HaltedStateCheck), + Halted(HaltedStateCheck<'a, C>), /// Seen value and key in proof. /// When we set the query plan, we only return content /// matching the query plan. @@ -1274,7 +1281,7 @@ where P: Iterator, D: Borrow<[u8]>, { - type Item = Result, VerifyError, CError>>; + type Item = Result, VerifyError, CError>>; fn next(&mut self) -> Option { if self.state == ReadProofState::Finished { @@ -1484,13 +1491,27 @@ where } } +/* +impl<'a, L, C, D, P> ReadProofIterator<'a, L, C, D, P> +where + L: TrieLayout, + C: Iterator>, + P: Iterator, + D: Borrow<[u8]>, +{ + /// On suspended check extract compenents to restart later. + pub fn halted(self) -> QueryPlan<'a, C> { + self.query_plan + } +} +*/ + /// Read the proof. /// /// If expected root is None, then we do not check hashes at all. pub fn verify_query_plan_iter<'a, L, C, D, P>( - mut query_plan: QueryPlan<'a, C>, + state: HaltedStateCheck<'a, C>, proof: P, - restart: Option, kind: ProofKind, expected_root: Option>, ) -> Result, VerifyError, CError>> @@ -1500,6 +1521,7 @@ where P: Iterator, D: Borrow<[u8]>, { + let HaltedStateCheck { mut query_plan, .. } = state; let is_compact = match kind { ProofKind::CompactNodes | ProofKind::CompactContent => { return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 0bda010b..a2b1faa7 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -18,7 +18,7 @@ use reference_trie::{test_layouts, NoExtensionLayout}; use std::collections::BTreeMap; use trie_db::{ proof::{generate_proof, verify_proof, VerifyError}, - query_plan::HaltedStateRecord, + query_plan::{HaltedStateCheck, HaltedStateRecord}, DBValue, Trie, TrieDBBuilder, TrieDBMutBuilder, TrieLayout, TrieMut, }; @@ -246,8 +246,8 @@ fn test_verify_decode_error_internal() { test_layouts!(test_query_plan, test_query_plan_internal); fn test_query_plan_internal() { use trie_db::query_plan::{ - record_query_plan, verify_query_plan_iter, InMemQueryPlan, InMemQueryPlanItem, - InMemoryRecorder, ProofKind, ReadProofItem, Recorder, + record_query_plan, verify_query_plan_iter, HaltedStateCheck, InMemQueryPlan, + InMemQueryPlanItem, InMemoryRecorder, ProofKind, QueryPlan, ReadProofItem, Recorder, }; let set = test_entries(); let (db, root) = { @@ -293,7 +293,7 @@ fn test_query_plan_internal() { let recorder = Recorder::new(kind, InMemoryRecorder::default(), limit, None); let mut from = HaltedStateRecord::from_start(recorder); // no limit - let mut proof: Vec> = Default::default(); + let mut proofs: Vec>> = Default::default(); let mut query_plan_iter = query_plan.as_ref(); loop { from = record_query_plan::(&db, &mut query_plan_iter, from).unwrap(); @@ -302,7 +302,7 @@ fn test_query_plan_internal() { assert!(from.is_finished()); } if from.is_finished() { - proof.append(&mut from.finish().output().nodes); + proofs.push(from.finish().output().nodes); break } let rec = if limit_conf.1 { @@ -311,39 +311,56 @@ fn test_query_plan_internal() { } else { from.statefull(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) }; - proof.append(&mut rec.output().nodes); + proofs.push(rec.output().nodes); } - let query_plan_iter = query_plan.as_ref(); - let verify_iter = verify_query_plan_iter::( - query_plan_iter, - proof.into_iter(), - None, - kind, - Some(root.clone()), - ) - .unwrap(); - let content: BTreeMap<_, _> = set.iter().cloned().collect(); - let mut in_prefix = false; - for item in verify_iter { - match item.unwrap() { - ReadProofItem::Value(key, value) => { - assert_eq!(content.get(&*key), Some(&value.as_ref())); - }, - ReadProofItem::NoValue(key) => { - assert_eq!(content.get(key), None); - }, - ReadProofItem::StartPrefix(_prefix) => { - in_prefix = true; - }, - ReadProofItem::EndPrefix => { - assert!(in_prefix); - in_prefix = false; - }, - ReadProofItem::Halted(_) => { - unreachable!("full proof"); - }, + let mut full_proof: Vec> = Default::default(); + + let mut query_plan_iter: QueryPlan<_> = query_plan.as_ref(); + let mut state: HaltedStateCheck<_> = query_plan_iter.into(); + loop { + let proof = if let Some(proof) = proofs.pop() { + full_proof.extend_from_slice(&proof); + continue + // proof + } else { + if proofs.len() == 0 { + break + } + proofs.clear(); + std::mem::take(&mut full_proof) + }; + let verify_iter = verify_query_plan_iter::( + state, + proof.into_iter(), + kind, + Some(root.clone()), + ) + .unwrap(); + let content: BTreeMap<_, _> = set.iter().cloned().collect(); + let mut in_prefix = false; + for item in verify_iter { + match item.unwrap() { + ReadProofItem::Value(key, value) => { + assert_eq!(content.get(&*key), Some(&value.as_ref())); + }, + ReadProofItem::NoValue(key) => { + assert_eq!(content.get(key), None); + }, + ReadProofItem::StartPrefix(_prefix) => { + in_prefix = true; + }, + ReadProofItem::EndPrefix => { + assert!(in_prefix); + in_prefix = false; + }, + ReadProofItem::Halted(resume) => { + state = resume; + continue + }, + } } + break } } From e44c8f5ed9cd34f0f3ef1329b812be7387dc1955 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 3 Apr 2023 11:01:37 +0200 Subject: [PATCH 025/154] more api change --- trie-db/src/node.rs | 2 +- trie-db/src/query_plan.rs | 126 +++++++++++++++++++++++--------------- trie-db/test/src/proof.rs | 13 ++-- 3 files changed, 84 insertions(+), 57 deletions(-) diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 19ed9162..7a65af77 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -583,7 +583,7 @@ impl NodePlan { /// the `OwnedNode`. This is useful for trie iterators. #[cfg_attr(feature = "std", derive(Debug))] #[derive(PartialEq, Eq)] -pub struct OwnedNode> { +pub struct OwnedNode { data: D, plan: NodePlan, } diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index c456c4d8..1169084a 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -29,6 +29,7 @@ use crate::{ proof::VerifyError, rstd::{ borrow::{Borrow, Cow}, + boxed::Box, cmp::*, result::Result, }, @@ -89,6 +90,7 @@ impl<'a> QueryPlanItem<'a> { /// Query plan in memory. pub struct InMemQueryPlan { pub items: Vec, + pub kind: ProofKind, pub ignore_unordered: bool, } @@ -112,6 +114,7 @@ impl InMemQueryPlan { pub fn as_ref(&self) -> QueryPlan { QueryPlan { items: QueryPlanItemIter(&self.items, 0), + kind: self.kind, ignore_unordered: self.ignore_unordered, _ph: PhantomData, } @@ -122,6 +125,7 @@ impl InMemQueryPlan { pub struct QueryPlan<'a, I> { items: I, ignore_unordered: bool, + kind: ProofKind, _ph: PhantomData<&'a ()>, } @@ -516,17 +520,33 @@ impl HaltedStateRecord { /// When process is halted keep execution state /// to restore later. -pub struct HaltedStateCheck<'a, C> { - stack: (), - stack_content: (), - currently_query_item: Option, +pub struct HaltedStateCheck<'a, L, C, D> { query_plan: QueryPlan<'a, C>, - // _ph: PhantomData, + current: Option>, + stack: ReadStack, + state: ReadProofState, } -impl<'a, C> From> for HaltedStateCheck<'a, C> { +impl<'a, L, C, D> From> for HaltedStateCheck<'a, L, C, D> { fn from(query_plan: QueryPlan<'a, C>) -> Self { - HaltedStateCheck { stack: (), stack_content: (), currently_query_item: None, query_plan } + let is_compact = match query_plan.kind { + ProofKind::FullNodes => false, + ProofKind::CompactNodesStream => true, + _ => false, + }; + + HaltedStateCheck { + stack: ReadStack { + items: Default::default(), + prefix: Default::default(), + is_compact, + iter_prefix: None, + _ph: PhantomData, + }, + state: ReadProofState::NotStarted, + current: None, + query_plan, + } } } @@ -962,7 +982,9 @@ where P: Iterator, D: Borrow<[u8]>, { - query_plan: QueryPlan<'a, C>, + // always needed, this is option only + // to avoid unsafe code when halting. + query_plan: Option>, proof: P, is_compact: bool, expected_root: Option>, @@ -987,13 +1009,13 @@ enum ReadProofState { Finished, } -struct ItemStack> { +struct ItemStack { node: ItemStackNode, depth: usize, next_descended_child: u8, } -enum ItemStackNode> { +enum ItemStackNode { Inline(OwnedNode>), Node(OwnedNode), } @@ -1020,7 +1042,7 @@ impl> ItemStack { } } -struct ReadStack> { +struct ReadStack { items: Vec>, prefix: NibbleVec, iter_prefix: Option<(usize, bool)>, // limit and wether we return value @@ -1257,9 +1279,9 @@ impl> ReadStack { } /// Content return on success when reading proof. -pub enum ReadProofItem<'a, C> { +pub enum ReadProofItem<'a, L, C, D> { /// Successfull read of proof, not all content read. - Halted(HaltedStateCheck<'a, C>), + Halted(Box>), /// Seen value and key in proof. /// When we set the query plan, we only return content /// matching the query plan. @@ -1281,12 +1303,15 @@ where P: Iterator, D: Borrow<[u8]>, { - type Item = Result, VerifyError, CError>>; + type Item = Result, VerifyError, CError>>; fn next(&mut self) -> Option { if self.state == ReadProofState::Finished { return None } + if self.state == ReadProofState::Halted { + todo!("restore needed??"); + } let check_hash = self.expected_root.is_some(); let mut to_check_slice = self.current.as_ref().map(|n| NibbleSlice::new(n.key)); @@ -1295,14 +1320,15 @@ where if self.state == ReadProofState::SwitchQueryPlan || self.state == ReadProofState::NotStarted { - if let Some(next) = self.query_plan.items.next() { + let query_plan = self.query_plan.as_mut().expect("Removed with state"); + if let Some(next) = query_plan.items.next() { let (ordered, common_nibbles) = if let Some(old) = self.current.as_ref() { old.before(&next) } else { (true, 0) }; if !ordered { - if self.query_plan.ignore_unordered { + if query_plan.ignore_unordered { continue } else { self.state = ReadProofState::Finished; @@ -1469,12 +1495,34 @@ where self.state = ReadProofState::SwitchQueryPlan; return Some(Ok(ReadProofItem::NoValue(to_check.key))) }, - TryStackChildResult::Halted => break, + TryStackChildResult::Halted => { + self.state = ReadProofState::Halted; + break + }, } } if self.state == ReadProofState::Halted { - unimplemented!() + self.state = ReadProofState::Finished; + let query_plan = crate::rstd::mem::replace(&mut self.query_plan, None); + let query_plan = query_plan.expect("Init with state"); + let current = crate::rstd::mem::take(&mut self.current); + let stack = crate::rstd::mem::replace( + &mut self.stack, + ReadStack { + items: Default::default(), + prefix: Default::default(), + is_compact: self.is_compact, + iter_prefix: None, + _ph: PhantomData, + }, + ); + return Some(Ok(ReadProofItem::Halted(Box::new(HaltedStateCheck { + query_plan, + current, + stack, + state: ReadProofState::Halted, + })))) } else { debug_assert!(self.state == ReadProofState::PlanConsumed); if self.is_compact { @@ -1491,28 +1539,12 @@ where } } -/* -impl<'a, L, C, D, P> ReadProofIterator<'a, L, C, D, P> -where - L: TrieLayout, - C: Iterator>, - P: Iterator, - D: Borrow<[u8]>, -{ - /// On suspended check extract compenents to restart later. - pub fn halted(self) -> QueryPlan<'a, C> { - self.query_plan - } -} -*/ - /// Read the proof. /// /// If expected root is None, then we do not check hashes at all. pub fn verify_query_plan_iter<'a, L, C, D, P>( - state: HaltedStateCheck<'a, C>, + state: HaltedStateCheck<'a, L, C, D>, proof: P, - kind: ProofKind, expected_root: Option>, ) -> Result, VerifyError, CError>> where @@ -1521,29 +1553,23 @@ where P: Iterator, D: Borrow<[u8]>, { - let HaltedStateCheck { mut query_plan, .. } = state; - let is_compact = match kind { + let HaltedStateCheck { query_plan, current, stack, state } = state; + + match query_plan.kind { ProofKind::CompactNodes | ProofKind::CompactContent => { return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent }, - ProofKind::FullNodes => false, - ProofKind::CompactNodesStream => true, + _ => (), }; Ok(ReadProofIterator { - query_plan, + query_plan: Some(query_plan), proof, - is_compact, + is_compact: stack.is_compact, expected_root, - current: None, - state: ReadProofState::NotStarted, - stack: ReadStack { - items: Default::default(), - prefix: Default::default(), - is_compact, - iter_prefix: None, - _ph: PhantomData, - }, + current, + state, + stack, }) } diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index a2b1faa7..af4e24c6 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -264,6 +264,7 @@ fn test_query_plan_internal() { // TODO add a cache let db = >::new(&db, &root).build(); + let kind = ProofKind::FullNodes; let query_plans = [ InMemQueryPlan { items: vec![ @@ -272,10 +273,12 @@ fn test_query_plan_internal() { InMemQueryPlanItem::new(b"horsey".to_vec(), false), ], ignore_unordered: false, + kind, }, InMemQueryPlan { items: vec![InMemQueryPlanItem::new(b"".to_vec(), true)], ignore_unordered: false, + kind, }, InMemQueryPlan { items: vec![ @@ -283,10 +286,10 @@ fn test_query_plan_internal() { InMemQueryPlanItem::new(b"do".to_vec(), true), ], ignore_unordered: false, + kind, }, ]; for query_plan in query_plans { - let kind = ProofKind::FullNodes; for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] { let limit = limit_conf.0; let limit = (limit != 0).then(|| limit); @@ -317,12 +320,11 @@ fn test_query_plan_internal() { let mut full_proof: Vec> = Default::default(); let mut query_plan_iter: QueryPlan<_> = query_plan.as_ref(); - let mut state: HaltedStateCheck<_> = query_plan_iter.into(); + let mut state: HaltedStateCheck<_, _, _> = query_plan_iter.into(); loop { let proof = if let Some(proof) = proofs.pop() { full_proof.extend_from_slice(&proof); - continue - // proof + proof } else { if proofs.len() == 0 { break @@ -333,7 +335,6 @@ fn test_query_plan_internal() { let verify_iter = verify_query_plan_iter::( state, proof.into_iter(), - kind, Some(root.clone()), ) .unwrap(); @@ -355,7 +356,7 @@ fn test_query_plan_internal() { in_prefix = false; }, ReadProofItem::Halted(resume) => { - state = resume; + state = *resume; continue }, } From e92a75a831468cc2f3b75e3017737b43e08b99c0 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 3 Apr 2023 12:11:39 +0200 Subject: [PATCH 026/154] multiple on value iter check incremental --- trie-db/src/nibble/nibbleslice.rs | 4 ++++ trie-db/src/query_plan.rs | 19 ++++++++++++++----- trie-db/test/src/proof.rs | 23 ++++++++++++++++------- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index f91fad7f..8e0c0e86 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -42,6 +42,10 @@ impl<'a> NibbleSlice<'a> { Self::new_slice(data, offset) } + pub(crate) fn offset(&self) -> usize { + self.offset + } + fn new_slice(data: &'a [u8], offset: usize) -> Self { NibbleSlice { data, offset } } diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 1169084a..049c4175 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -525,6 +525,7 @@ pub struct HaltedStateCheck<'a, L, C, D> { current: Option>, stack: ReadStack, state: ReadProofState, + restore_offset: usize, } impl<'a, L, C, D> From> for HaltedStateCheck<'a, L, C, D> { @@ -545,6 +546,7 @@ impl<'a, L, C, D> From> for HaltedStateCheck<'a, L, C, D> { }, state: ReadProofState::NotStarted, current: None, + restore_offset: 0, query_plan, } } @@ -991,6 +993,7 @@ where current: Option>, state: ReadProofState, stack: ReadStack, + restore_offset: usize, } #[derive(Eq, PartialEq)] @@ -1309,12 +1312,16 @@ where if self.state == ReadProofState::Finished { return None } - if self.state == ReadProofState::Halted { - todo!("restore needed??"); - } let check_hash = self.expected_root.is_some(); + let mut to_check_slice = if self.state == ReadProofState::Halted { + self.state = ReadProofState::Running; + self.current + .as_ref() + .map(|n| NibbleSlice::new_offset(n.key, self.restore_offset)) + } else { + self.current.as_ref().map(|n| NibbleSlice::new(n.key)) + }; - let mut to_check_slice = self.current.as_ref().map(|n| NibbleSlice::new(n.key)); // read proof loop { if self.state == ReadProofState::SwitchQueryPlan || @@ -1522,6 +1529,7 @@ where current, stack, state: ReadProofState::Halted, + restore_offset: to_check_slice.map(|s| s.offset()).unwrap_or(0), })))) } else { debug_assert!(self.state == ReadProofState::PlanConsumed); @@ -1553,7 +1561,7 @@ where P: Iterator, D: Borrow<[u8]>, { - let HaltedStateCheck { query_plan, current, stack, state } = state; + let HaltedStateCheck { query_plan, current, stack, state, restore_offset } = state; match query_plan.kind { ProofKind::CompactNodes | ProofKind::CompactContent => { @@ -1570,6 +1578,7 @@ where current, state, stack, + restore_offset, }) } diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index af4e24c6..b4de1d5e 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -290,7 +290,10 @@ fn test_query_plan_internal() { }, ]; for query_plan in query_plans { - for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] { + for limit_conf in [ + /* (0, false), */ + (1, false), /* (1, true), (2, false), (2, true), (3, true) */ + ] { let limit = limit_conf.0; let limit = (limit != 0).then(|| limit); let recorder = Recorder::new(kind, InMemoryRecorder::default(), limit, None); @@ -318,10 +321,11 @@ fn test_query_plan_internal() { } let mut full_proof: Vec> = Default::default(); - + proofs.reverse(); let mut query_plan_iter: QueryPlan<_> = query_plan.as_ref(); - let mut state: HaltedStateCheck<_, _, _> = query_plan_iter.into(); - loop { + let mut run_state: Option> = Some(query_plan_iter.into()); + let mut has_run_full = false; + while let Some(state) = run_state.take() { let proof = if let Some(proof) = proofs.pop() { full_proof.extend_from_slice(&proof); proof @@ -340,6 +344,7 @@ fn test_query_plan_internal() { .unwrap(); let content: BTreeMap<_, _> = set.iter().cloned().collect(); let mut in_prefix = false; + let mut halted = false; for item in verify_iter { match item.unwrap() { ReadProofItem::Value(key, value) => { @@ -356,12 +361,16 @@ fn test_query_plan_internal() { in_prefix = false; }, ReadProofItem::Halted(resume) => { - state = *resume; - continue + run_state = Some(*resume); + break }, } } - break + if run_state.is_none() && !has_run_full { + has_run_full = true; + query_plan_iter = query_plan.as_ref(); + run_state = Some(query_plan_iter.into()); + } } } From adb970b2d0f1c97ce1e76a7e2d3428926c84b6a0 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 3 Apr 2023 15:00:45 +0200 Subject: [PATCH 027/154] impl iteration iterative check. --- trie-db/src/query_plan.rs | 88 +++++++++++++++++++++++---------------- trie-db/test/src/proof.rs | 19 ++++----- 2 files changed, 59 insertions(+), 48 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 049c4175..f0d753d1 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -1416,7 +1416,10 @@ where TryStackChildResult::NotStacked => break, TryStackChildResult::NotStackedBranch => (), TryStackChildResult::Halted => { - unimplemented!() + if let Some(last) = self.stack.items.last_mut() { + last.next_descended_child -= 1; + } + return self.halt(None) }, } } @@ -1502,48 +1505,59 @@ where self.state = ReadProofState::SwitchQueryPlan; return Some(Ok(ReadProofItem::NoValue(to_check.key))) }, - TryStackChildResult::Halted => { - self.state = ReadProofState::Halted; - break - }, + TryStackChildResult::Halted => return self.halt(Some(to_check_slice)), } } - if self.state == ReadProofState::Halted { - self.state = ReadProofState::Finished; - let query_plan = crate::rstd::mem::replace(&mut self.query_plan, None); - let query_plan = query_plan.expect("Init with state"); - let current = crate::rstd::mem::take(&mut self.current); - let stack = crate::rstd::mem::replace( - &mut self.stack, - ReadStack { - items: Default::default(), - prefix: Default::default(), - is_compact: self.is_compact, - iter_prefix: None, - _ph: PhantomData, - }, - ); - return Some(Ok(ReadProofItem::Halted(Box::new(HaltedStateCheck { - query_plan, - current, - stack, - state: ReadProofState::Halted, - restore_offset: to_check_slice.map(|s| s.offset()).unwrap_or(0), - })))) + debug_assert!(self.state == ReadProofState::PlanConsumed); + if self.is_compact { + unimplemented!("check hash from stack"); } else { - debug_assert!(self.state == ReadProofState::PlanConsumed); - if self.is_compact { - unimplemented!("check hash from stack"); - } else { - if self.proof.next().is_some() { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) - } + if self.proof.next().is_some() { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) } - self.state = ReadProofState::Finished; - return None } + self.state = ReadProofState::Finished; + return None + } +} + +impl<'a, L, C, D, P> ReadProofIterator<'a, L, C, D, P> +where + L: TrieLayout, + C: Iterator>, + P: Iterator, + D: Borrow<[u8]>, +{ + fn halt( + &mut self, + to_check_slice: Option<&mut NibbleSlice>, + ) -> Option, VerifyError, CError>>> { + if self.is_compact { + unimplemented!("check hash from stack"); + } + self.state = ReadProofState::Finished; + let query_plan = crate::rstd::mem::replace(&mut self.query_plan, None); + let query_plan = query_plan.expect("Init with state"); + let current = crate::rstd::mem::take(&mut self.current); + let stack = crate::rstd::mem::replace( + &mut self.stack, + ReadStack { + items: Default::default(), + prefix: Default::default(), + is_compact: self.is_compact, + iter_prefix: None, + _ph: PhantomData, + }, + ); + Some(Ok(ReadProofItem::Halted(Box::new(HaltedStateCheck { + query_plan, + current, + stack, + state: ReadProofState::Halted, + restore_offset: to_check_slice.map(|s| s.offset()).unwrap_or(0), + })))) } } diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index b4de1d5e..d9b0a4af 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -267,33 +267,30 @@ fn test_query_plan_internal() { let kind = ProofKind::FullNodes; let query_plans = [ InMemQueryPlan { - items: vec![ - InMemQueryPlanItem::new(b"bravo".to_vec(), false), - InMemQueryPlanItem::new(b"doge".to_vec(), false), - InMemQueryPlanItem::new(b"horsey".to_vec(), false), - ], + items: vec![InMemQueryPlanItem::new(b"".to_vec(), true)], ignore_unordered: false, kind, }, InMemQueryPlan { - items: vec![InMemQueryPlanItem::new(b"".to_vec(), true)], + items: vec![ + InMemQueryPlanItem::new(b"bravo".to_vec(), false), + InMemQueryPlanItem::new(b"do".to_vec(), true), + ], ignore_unordered: false, kind, }, InMemQueryPlan { items: vec![ InMemQueryPlanItem::new(b"bravo".to_vec(), false), - InMemQueryPlanItem::new(b"do".to_vec(), true), + InMemQueryPlanItem::new(b"doge".to_vec(), false), + InMemQueryPlanItem::new(b"horsey".to_vec(), false), ], ignore_unordered: false, kind, }, ]; for query_plan in query_plans { - for limit_conf in [ - /* (0, false), */ - (1, false), /* (1, true), (2, false), (2, true), (3, true) */ - ] { + for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] { let limit = limit_conf.0; let limit = (limit != 0).then(|| limit); let recorder = Recorder::new(kind, InMemoryRecorder::default(), limit, None); From 2cc716c219bbd65c1e4ed7d5ca57ada1fe5aeb13 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 11 Apr 2023 11:56:39 +0200 Subject: [PATCH 028/154] limited cache support --- trie-db/src/query_plan.rs | 17 +++++------ trie-db/src/triedb.rs | 62 +++++++++++++++++++++++++++++++++------ trie-db/test/src/proof.rs | 8 +++-- 3 files changed, 65 insertions(+), 22 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index f0d753d1..3e60312a 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -578,26 +578,17 @@ pub fn record_query_plan< // TODO //) resto // let restore_buf; - let mut restore_buf2 = Vec::new(); let dummy_parent_hash = TrieHash::::default(); let mut stateless = false; let mut statefull = None; if let Some(lower_bound) = from.from.take() { if from.currently_query_item.is_none() { stateless = true; - restore_buf2 = lower_bound.0.clone(); let mut bound = NibbleVec::new(); bound.append_optional_slice_and_nibble(Some(&NibbleSlice::new(&lower_bound.0)), None); if lower_bound.1 { bound.pop(); } - /* - bound = LeftNibbleSlice::new(&restore_buf.0[..]); - if lower_bound.1 { - bound.truncate(bound.len() - 1); - //restore_buf2.pop(); - } - */ from.stack.recorder.start_at = Some(bound.len()); from.stack.seek = Some(bound); } else { @@ -845,7 +836,13 @@ impl RecordStack { } // TODO handle cache first let child_node = db - .get_raw_or_lookup(parent_hash, child_handle, prefix.as_prefix(), false) + .get_raw_or_lookup_with_cache( + parent_hash, + child_handle, + prefix.as_prefix(), + false, + true, + ) .map_err(|_| VerifyError::IncompleteProof)?; // actually incomplete db: TODO consider switching error // TODO put in proof (only if Hash or inline for content one) diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index ad6166eb..1bbee670 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -153,20 +153,64 @@ where node_handle: NodeHandle, partial_key: Prefix, record_access: bool, + ) -> Result<(OwnedNode, Option>), TrieHash, CError> { + self.get_raw_or_lookup_with_cache( + parent_hash, + node_handle, + partial_key, + record_access, + false, + ) + } + + /// Same as get_raw_or_lookup but with optionally use of the node cache. + /// Warning cache usage double encode decode here so only to use for + /// avoiding a costy db access, generally db cache would be better. + /// A small switch in cache api could lift this. + pub(crate) fn get_raw_or_lookup_with_cache( + &self, + parent_hash: TrieHash, + node_handle: NodeHandle, + partial_key: Prefix, + record_access: bool, + with_cache: bool, ) -> Result<(OwnedNode, Option>), TrieHash, CError> { let (node_hash, node_data) = match node_handle { NodeHandle::Hash(data) => { let node_hash = decode_hash::(data) .ok_or_else(|| Box::new(TrieError::InvalidHash(parent_hash, data.to_vec())))?; - let node_data = self.db.get(&node_hash, partial_key).ok_or_else(|| { - if partial_key == EMPTY_PREFIX { - Box::new(TrieError::InvalidStateRoot(node_hash)) - } else { - Box::new(TrieError::IncompleteDatabase(node_hash)) - } - })?; - - (Some(node_hash), node_data) + if let Some(c) = self.cache.as_ref() { + let mut cache = c.borrow_mut(); + let node = cache.get_or_insert_node(node_hash, &mut || { + let node_data = self.db.get(&node_hash, partial_key).ok_or_else(|| { + if partial_key == EMPTY_PREFIX { + Box::new(TrieError::InvalidStateRoot(node_hash)) + } else { + Box::new(TrieError::IncompleteDatabase(node_hash)) + } + })?; + use crate::node_codec::NodeCodec; + let decoded = match L::Codec::decode(&node_data[..]) { + Ok(node) => node, + Err(e) => return Err(Box::new(TrieError::DecoderError(node_hash, e))), + }; + + decoded.to_owned_node::() + })?; + let encoded = node.to_encoded::(); + + (Some(node_hash), encoded) + } else { + let node_data = self.db.get(&node_hash, partial_key).ok_or_else(|| { + if partial_key == EMPTY_PREFIX { + Box::new(TrieError::InvalidStateRoot(node_hash)) + } else { + Box::new(TrieError::IncompleteDatabase(node_hash)) + } + })?; + + (Some(node_hash), node_data) + } }, NodeHandle::Inline(data) => (None, data.to_vec()), }; diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index d9b0a4af..1a2352e9 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -13,7 +13,7 @@ // limitations under the License. use hash_db::Hasher; -use reference_trie::{test_layouts, NoExtensionLayout}; +use reference_trie::{test_layouts, NoExtensionLayout, TestTrieCache}; use std::collections::BTreeMap; use trie_db::{ @@ -250,6 +250,9 @@ fn test_query_plan_internal() { InMemQueryPlanItem, InMemoryRecorder, ProofKind, QueryPlan, ReadProofItem, Recorder, }; let set = test_entries(); + + let mut cache = TestTrieCache::::default(); + let (db, root) = { let mut db = >::default(); let mut root = Default::default(); @@ -261,8 +264,7 @@ fn test_query_plan_internal() { } (db, root) }; - // TODO add a cache - let db = >::new(&db, &root).build(); + let db = >::new(&db, &root).with_cache(&mut cache).build(); let kind = ProofKind::FullNodes; let query_plans = [ From 1e7b790b92ad54680c3a4c05988bddedf6caaa5e Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 12 Apr 2023 12:06:34 +0200 Subject: [PATCH 029/154] compact proof record, no halting --- trie-db/src/query_plan.rs | 125 ++++++++++++++++++---- trie-db/src/trie_codec.rs | 155 ++++++++++++++------------- trie-db/test/src/proof.rs | 213 ++++++++++++++++++++------------------ 3 files changed, 296 insertions(+), 197 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 3e60312a..5b2fb794 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -165,11 +165,23 @@ pub enum ProofKind { #[derive(Default, Clone, Copy)] struct Bitmap(u16); -impl Bitmap { +pub(crate) trait BitmapAccess: Copy { + fn at(&self, i: usize) -> bool; +} + +impl BitmapAccess for Bitmap { fn at(&self, i: usize) -> bool { self.0 & (1u16 << i) != 0 } +} +impl<'a> BitmapAccess for &'a [bool] { + fn at(&self, i: usize) -> bool { + self[i] + } +} + +impl Bitmap { fn set(&mut self, i: usize, v: bool) { if v { self.0 |= 1u16 << i @@ -295,9 +307,12 @@ impl Recorder { res = self.limits.add_node(item.node.data().len()); output.write_entry(item.node.data().into()); }, - RecorderStateInner::Compact { output, proof, stacked_pos } => { - unimplemented!() - }, + RecorderStateInner::Compact { output, proof, stacked_pos } => + if !item.is_inline { + res = self.limits.add_node(item.node.data().len()); + stacked_pos.push(proof.len()); + proof.push(Vec::new()); + }, RecorderStateInner::CompactStream(output) => { unimplemented!() }, @@ -308,16 +323,23 @@ impl Recorder { res } - fn record_popped_node(&mut self, item: &CompactEncodingInfos, stack_pos: usize) { + fn record_popped_node(&mut self, item: &CompactEncodingInfos, stack_pos: usize) { if !self.check_start_at(item.depth) { return } match &mut self.output { RecorderStateInner::Stream(_) => (), - RecorderStateInner::Compact { output, proof, stacked_pos } => { - unimplemented!() - }, + RecorderStateInner::Compact { output, proof, stacked_pos } => + if !item.is_inline { + let at = stacked_pos.pop().expect("always stacked"); + proof[at] = crate::trie_codec::encode_node_internal::( + &item.node, + item.accessed_value, + item.accessed_children, + ) + .expect("TODO error handling, can it actually fail?"); + }, RecorderStateInner::CompactStream(output) => { unimplemented!() }, @@ -340,7 +362,8 @@ impl Recorder { output.write_entry(value.into()); }, RecorderStateInner::Compact { output, proof, stacked_pos } => { - unimplemented!() + res = self.limits.add_value(value.len()); + proof.push(value.into()); }, RecorderStateInner::CompactStream(output) => { unimplemented!() @@ -358,13 +381,34 @@ impl Recorder { } match &mut self.output { - RecorderStateInner::Stream(output) => { + RecorderStateInner::Compact { .. } | RecorderStateInner::Stream(_) => { // not writing inline value (already // in parent node). }, - RecorderStateInner::Compact { output, proof, stacked_pos } => { + RecorderStateInner::CompactStream(output) => { unimplemented!() }, + RecorderStateInner::Content(output) => { + unimplemented!() + }, + } + } + + fn finalize(&mut self) { + match &mut self.output { + RecorderStateInner::Compact { output, proof, stacked_pos } => { + if stacked_pos.len() > 0 { + // halted: complete up to 0 and write all nodes keeping stack. + unimplemented!() + } else { + for entry in core::mem::take(proof) { + output.write_entry(entry.into()); + } + } + }, + RecorderStateInner::Stream(output) => { + // all written + }, RecorderStateInner::CompactStream(output) => { unimplemented!() }, @@ -380,7 +424,7 @@ impl Limits { fn add_node(&mut self, size: usize) -> bool { let mut res = false; match self.kind { - ProofKind::FullNodes => { + ProofKind::CompactNodes | ProofKind::FullNodes => { if let Some(rem_size) = self.remaining_size.as_mut() { if *rem_size >= size { *rem_size -= size; @@ -398,9 +442,6 @@ impl Limits { } } }, - ProofKind::CompactNodes => { - unimplemented!() - }, ProofKind::CompactNodesStream => { unimplemented!() }, @@ -415,7 +456,7 @@ impl Limits { fn add_value(&mut self, size: usize) -> bool { let mut res = false; match self.kind { - ProofKind::FullNodes => { + ProofKind::CompactNodes | ProofKind::FullNodes => { if let Some(rem_size) = self.remaining_size.as_mut() { if *rem_size >= size { *rem_size -= size; @@ -433,9 +474,43 @@ impl Limits { } } }, + ProofKind::CompactNodesStream => { + unimplemented!() + }, + ProofKind::CompactContent => { + unimplemented!() + }, + } + res + } + + fn reclaim_child_hash(&mut self, size: usize) { + match self.kind { + ProofKind::FullNodes => unreachable!(), ProofKind::CompactNodes => { + // replaced by a 0 len inline hash so same size encoding + if let Some(rem_size) = self.remaining_size.as_mut() { + *rem_size += size; + } + }, + ProofKind::CompactNodesStream => { unimplemented!() }, + ProofKind::CompactContent => { + unimplemented!() + }, + } + } + + fn reclaim_value_hash(&mut self, size: usize) { + match self.kind { + ProofKind::FullNodes => unreachable!(), + ProofKind::CompactNodes => { + if let Some(rem_size) = self.remaining_size.as_mut() { + // escape byte added + *rem_size += size - 1; + } + }, ProofKind::CompactNodesStream => { unimplemented!() }, @@ -443,7 +518,6 @@ impl Limits { unimplemented!() }, } - res } } @@ -650,7 +724,8 @@ pub fn record_query_plan< match stack.prefix.len().cmp(&common_nibbles) { Ordering::Equal | Ordering::Less => break, Ordering::Greater => - if !stack.pop() { + if !stack.pop::() { + stack.recorder.finalize(); return Ok(from) }, } @@ -699,6 +774,7 @@ pub fn record_query_plan< )); stack.prefix.pop(); from.currently_query_item = Some(query.to_owned()); + stack.recorder.finalize(); return Ok(from) }, } @@ -756,13 +832,14 @@ pub fn record_query_plan< )); stack.prefix.pop(); from.currently_query_item = prev_query.map(|q| q.to_owned()); + stack.recorder.finalize(); return Ok(from) }, } } // pop - if !stack.pop() { + if !stack.pop::() { break } } @@ -770,6 +847,8 @@ pub fn record_query_plan< } } + while !stack.pop::() {} + stack.recorder.finalize(); Ok(from) } @@ -946,17 +1025,17 @@ impl RecordStack { Ok(true) } - fn pop(&mut self) -> bool { + fn pop(&mut self) -> bool { if self.iter_prefix == Some(self.items.len()) { return false } if let Some(item) = self.items.pop() { - self.recorder.record_popped_node(&item, self.items.len()); + self.recorder.record_popped_node::(&item, self.items.len()); let depth = self.items.last().map(|i| i.depth).unwrap_or(0); self.prefix.drop_lasts(self.prefix.len() - depth); if depth == item.depth { // Two consecutive identical depth is an extension - self.pop(); + self.pop::(); } true } else { @@ -1575,7 +1654,7 @@ where let HaltedStateCheck { query_plan, current, stack, state, restore_offset } = state; match query_plan.kind { - ProofKind::CompactNodes | ProofKind::CompactContent => { + ProofKind::CompactNodesStream | ProofKind::CompactContent => { return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent }, _ => (), diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 9a1f51b3..8e627e82 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -28,6 +28,7 @@ use crate::{ nibble_ops::NIBBLE_LENGTH, node::{Node, NodeHandle, NodeHandlePlan, NodePlan, OwnedNode, ValuePlan}, + query_plan::BitmapAccess, rstd::{boxed::Box, convert::TryInto, marker::PhantomData, result, sync::Arc, vec, vec::Vec}, CError, ChildReference, DBValue, NibbleVec, NodeCodec, Result, TrieDB, TrieDBRawIterator, TrieError, TrieHash, TrieLayout, @@ -100,88 +101,94 @@ impl EncoderStackEntry { /// Generates the encoding of the subtrie rooted at this entry. fn encode_node(&mut self) -> Result, C::HashOut, C::Error> { - let node_data = self.node.data(); - let node_plan = self.node.node_plan(); - let mut encoded = match node_plan { - NodePlan::Empty => node_data.to_vec(), - NodePlan::Leaf { partial, value: _ } => - if self.omit_value { - let partial = partial.build(node_data); - C::leaf_node(partial.right_iter(), partial.len(), OMIT_VALUE_HASH) - } else { - node_data.to_vec() - }, - NodePlan::Extension { partial, child: _ } => - if !self.omit_children[0] { - node_data.to_vec() - } else { - let partial = partial.build(node_data); - let empty_child = ChildReference::Inline(C::HashOut::default(), 0); - C::extension_node(partial.right_iter(), partial.len(), empty_child) - }, - NodePlan::Branch { value, children } => { - let value = if self.omit_value { - value.is_some().then_some(OMIT_VALUE_HASH) - } else { - value.as_ref().map(|v| v.build(node_data)) - }; - C::branch_node( - Self::branch_children(node_data, &children, &self.omit_children)?.iter(), - value, - ) + encode_node_internal::(&*self.node, self.omit_value, self.omit_children.as_slice()) + } +} + +/// Generate the list of child references for a branch node with certain children omitted. +/// +/// Preconditions: +/// - omit_children has size NIBBLE_LENGTH. +/// - omit_children[i] is only true if child_handles[i] is Some +fn branch_children( + node_data: &[u8], + child_handles: &[Option; NIBBLE_LENGTH], + omit_children: impl BitmapAccess, +) -> Result<[Option>; NIBBLE_LENGTH], C::HashOut, C::Error> { + let empty_child = ChildReference::Inline(C::HashOut::default(), 0); + let mut children = [None; NIBBLE_LENGTH]; + for i in 0..NIBBLE_LENGTH { + children[i] = if omit_children.at(i) { + Some(empty_child) + } else if let Some(child_plan) = &child_handles[i] { + let child_ref = child_plan + .build(node_data) + .try_into() + .map_err(|hash| Box::new(TrieError::InvalidHash(C::HashOut::default(), hash)))?; + Some(child_ref) + } else { + None + }; + } + Ok(children) +} + +pub(crate) fn encode_node_internal( + node: &OwnedNode, + omit_value: bool, + omit_children: impl BitmapAccess, +) -> Result, C::HashOut, C::Error> { + let node_data = node.data(); + let node_plan = node.node_plan(); + let mut encoded = match node_plan { + NodePlan::Empty => node_data.to_vec(), + NodePlan::Leaf { partial, value: _ } => + if omit_value { + let partial = partial.build(node_data); + C::leaf_node(partial.right_iter(), partial.len(), OMIT_VALUE_HASH) + } else { + node_data.to_vec() }, - NodePlan::NibbledBranch { partial, value, children } => { + NodePlan::Extension { partial, child: _ } => + if !omit_children.at(0) { + node_data.to_vec() + } else { let partial = partial.build(node_data); - let value = if self.omit_value { - value.is_some().then_some(OMIT_VALUE_HASH) - } else { - value.as_ref().map(|v| v.build(node_data)) - }; - C::branch_node_nibbled( - partial.right_iter(), - partial.len(), - Self::branch_children(node_data, &children, &self.omit_children)?.iter(), - value, - ) + let empty_child = ChildReference::Inline(C::HashOut::default(), 0); + C::extension_node(partial.right_iter(), partial.len(), empty_child) }, - }; - - if self.omit_value { - if let Some(header) = C::ESCAPE_HEADER { - encoded.insert(0, header); + NodePlan::Branch { value, children } => { + let value = if omit_value { + value.is_some().then_some(OMIT_VALUE_HASH) } else { - return Err(Box::new(TrieError::InvalidStateRoot(Default::default()))) - } - } - Ok(encoded) - } - - /// Generate the list of child references for a branch node with certain children omitted. - /// - /// Preconditions: - /// - omit_children has size NIBBLE_LENGTH. - /// - omit_children[i] is only true if child_handles[i] is Some - fn branch_children( - node_data: &[u8], - child_handles: &[Option; NIBBLE_LENGTH], - omit_children: &[bool], - ) -> Result<[Option>; NIBBLE_LENGTH], C::HashOut, C::Error> { - let empty_child = ChildReference::Inline(C::HashOut::default(), 0); - let mut children = [None; NIBBLE_LENGTH]; - for i in 0..NIBBLE_LENGTH { - children[i] = if omit_children[i] { - Some(empty_child) - } else if let Some(child_plan) = &child_handles[i] { - let child_ref = child_plan.build(node_data).try_into().map_err(|hash| { - Box::new(TrieError::InvalidHash(C::HashOut::default(), hash)) - })?; - Some(child_ref) + value.as_ref().map(|v| v.build(node_data)) + }; + C::branch_node(branch_children::(node_data, &children, omit_children)?.iter(), value) + }, + NodePlan::NibbledBranch { partial, value, children } => { + let partial = partial.build(node_data); + let value = if omit_value { + value.is_some().then_some(OMIT_VALUE_HASH) } else { - None + value.as_ref().map(|v| v.build(node_data)) }; + C::branch_node_nibbled( + partial.right_iter(), + partial.len(), + branch_children::(node_data, &children, omit_children)?.iter(), + value, + ) + }, + }; + + if omit_value { + if let Some(header) = C::ESCAPE_HEADER { + encoded.insert(0, header); + } else { + return Err(Box::new(TrieError::InvalidStateRoot(Default::default()))) } - Ok(children) } + Ok(encoded) } /// Detached value if included does write a reserved header, diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 1a2352e9..35c7bd47 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -266,113 +266,126 @@ fn test_query_plan_internal() { }; let db = >::new(&db, &root).with_cache(&mut cache).build(); - let kind = ProofKind::FullNodes; - let query_plans = [ - InMemQueryPlan { - items: vec![InMemQueryPlanItem::new(b"".to_vec(), true)], - ignore_unordered: false, - kind, - }, - InMemQueryPlan { - items: vec![ - InMemQueryPlanItem::new(b"bravo".to_vec(), false), - InMemQueryPlanItem::new(b"do".to_vec(), true), - ], - ignore_unordered: false, - kind, - }, - InMemQueryPlan { - items: vec![ - InMemQueryPlanItem::new(b"bravo".to_vec(), false), - InMemQueryPlanItem::new(b"doge".to_vec(), false), - InMemQueryPlanItem::new(b"horsey".to_vec(), false), - ], - ignore_unordered: false, - kind, - }, - ]; - for query_plan in query_plans { - for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] { - let limit = limit_conf.0; - let limit = (limit != 0).then(|| limit); - let recorder = Recorder::new(kind, InMemoryRecorder::default(), limit, None); - let mut from = HaltedStateRecord::from_start(recorder); - // no limit - let mut proofs: Vec>> = Default::default(); - let mut query_plan_iter = query_plan.as_ref(); - loop { - from = record_query_plan::(&db, &mut query_plan_iter, from).unwrap(); - - if limit.is_none() { - assert!(from.is_finished()); - } - if from.is_finished() { - proofs.push(from.finish().output().nodes); - break - } - let rec = if limit_conf.1 { - query_plan_iter = query_plan.as_ref(); - from.stateless(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) - } else { - from.statefull(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) - }; - proofs.push(rec.output().nodes); - } - - let mut full_proof: Vec> = Default::default(); - proofs.reverse(); - let mut query_plan_iter: QueryPlan<_> = query_plan.as_ref(); - let mut run_state: Option> = Some(query_plan_iter.into()); - let mut has_run_full = false; - while let Some(state) = run_state.take() { - let proof = if let Some(proof) = proofs.pop() { - full_proof.extend_from_slice(&proof); - proof - } else { - if proofs.len() == 0 { + for kind in [ProofKind::CompactNodes, ProofKind::FullNodes] { + let query_plans = [ + InMemQueryPlan { + items: vec![InMemQueryPlanItem::new(b"".to_vec(), true)], + ignore_unordered: false, + kind, + }, + InMemQueryPlan { + items: vec![ + InMemQueryPlanItem::new(b"bravo".to_vec(), false), + InMemQueryPlanItem::new(b"do".to_vec(), true), + ], + ignore_unordered: false, + kind, + }, + InMemQueryPlan { + items: vec![ + InMemQueryPlanItem::new(b"bravo".to_vec(), false), + InMemQueryPlanItem::new(b"doge".to_vec(), false), + InMemQueryPlanItem::new(b"horsey".to_vec(), false), + ], + ignore_unordered: false, + kind, + }, + ]; + for query_plan in query_plans { + for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] + { + let limit = limit_conf.0; + let limit = (limit != 0).then(|| limit); + let recorder = Recorder::new(kind, InMemoryRecorder::default(), limit, None); + let mut from = HaltedStateRecord::from_start(recorder); + // no limit + let mut proofs: Vec>> = Default::default(); + let mut query_plan_iter = query_plan.as_ref(); + loop { + from = record_query_plan::(&db, &mut query_plan_iter, from).unwrap(); + + if limit.is_none() { + assert!(from.is_finished()); + } + if from.is_finished() { + proofs.push(from.finish().output().nodes); break } - proofs.clear(); - std::mem::take(&mut full_proof) - }; - let verify_iter = verify_query_plan_iter::( - state, - proof.into_iter(), - Some(root.clone()), - ) - .unwrap(); - let content: BTreeMap<_, _> = set.iter().cloned().collect(); - let mut in_prefix = false; - let mut halted = false; - for item in verify_iter { - match item.unwrap() { - ReadProofItem::Value(key, value) => { - assert_eq!(content.get(&*key), Some(&value.as_ref())); - }, - ReadProofItem::NoValue(key) => { - assert_eq!(content.get(key), None); - }, - ReadProofItem::StartPrefix(_prefix) => { - in_prefix = true; - }, - ReadProofItem::EndPrefix => { - assert!(in_prefix); - in_prefix = false; - }, - ReadProofItem::Halted(resume) => { - run_state = Some(*resume); + let rec = if limit_conf.1 { + query_plan_iter = query_plan.as_ref(); + from.stateless(Recorder::new( + kind, + InMemoryRecorder::default(), + limit, + None, + )) + } else { + from.statefull(Recorder::new( + kind, + InMemoryRecorder::default(), + limit, + None, + )) + }; + proofs.push(rec.output().nodes); + } + + let mut full_proof: Vec> = Default::default(); + proofs.reverse(); + let mut query_plan_iter: QueryPlan<_> = query_plan.as_ref(); + let mut run_state: Option> = Some(query_plan_iter.into()); + let mut has_run_full = false; + while let Some(state) = run_state.take() { + let proof = if let Some(proof) = proofs.pop() { + full_proof.extend_from_slice(&proof); + proof + } else { + if full_proof.is_empty() { break - }, + } + proofs.clear(); + std::mem::take(&mut full_proof) + }; + let verify_iter = verify_query_plan_iter::( + state, + proof.into_iter(), + Some(root.clone()), + ) + .unwrap(); + let content: BTreeMap<_, _> = set.iter().cloned().collect(); + let mut in_prefix = false; + let mut halted = false; + for item in verify_iter { + match item.unwrap() { + ReadProofItem::Value(key, value) => { + assert_eq!(content.get(&*key), Some(&value.as_ref())); + }, + ReadProofItem::NoValue(key) => { + assert_eq!(content.get(key), None); + }, + ReadProofItem::StartPrefix(_prefix) => { + in_prefix = true; + }, + ReadProofItem::EndPrefix => { + assert!(in_prefix); + in_prefix = false; + }, + ReadProofItem::Halted(resume) => { + run_state = Some(*resume); + break + }, + } + } + if run_state.is_none() && !has_run_full { + has_run_full = true; + query_plan_iter = query_plan.as_ref(); + run_state = Some(query_plan_iter.into()); } } - if run_state.is_none() && !has_run_full { - has_run_full = true; - query_plan_iter = query_plan.as_ref(); - run_state = Some(query_plan_iter.into()); + if !has_run_full { + panic!("did not run full proof") } } } - - // TODO limit 1, 2, 3 and restarts } } From 3119bf2b6ab6c402741f1a84e979e9e48a041b9e Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 12 Apr 2023 18:58:43 +0200 Subject: [PATCH 030/154] compact no restore with all threshold --- trie-db/src/query_plan.rs | 283 ++++++++++++++++++++++++++++++++------ trie-db/src/trie_codec.rs | 50 ++++--- trie-db/test/src/proof.rs | 5 +- 3 files changed, 274 insertions(+), 64 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 5b2fb794..9a5b5815 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -24,7 +24,7 @@ use core::marker::PhantomData; use crate::{ - nibble::{nibble_ops, LeftNibbleSlice, NibbleSlice}, + nibble::{nibble_ops, nibble_ops::NIBBLE_LENGTH, LeftNibbleSlice, NibbleSlice}, node::{NodeHandle, NodePlan, OwnedNode, Value}, proof::VerifyError, rstd::{ @@ -33,7 +33,7 @@ use crate::{ cmp::*, result::Result, }, - CError, DBValue, NibbleVec, Trie, TrieDB, TrieError, TrieHash, TrieLayout, + CError, ChildReference, DBValue, NibbleVec, Trie, TrieDB, TrieError, TrieHash, TrieLayout, }; use hash_db::Hasher; @@ -594,7 +594,7 @@ impl HaltedStateRecord { /// When process is halted keep execution state /// to restore later. -pub struct HaltedStateCheck<'a, L, C, D> { +pub struct HaltedStateCheck<'a, L: TrieLayout, C, D> { query_plan: QueryPlan<'a, C>, current: Option>, stack: ReadStack, @@ -602,11 +602,12 @@ pub struct HaltedStateCheck<'a, L, C, D> { restore_offset: usize, } -impl<'a, L, C, D> From> for HaltedStateCheck<'a, L, C, D> { +impl<'a, L: TrieLayout, C, D> From> for HaltedStateCheck<'a, L, C, D> { fn from(query_plan: QueryPlan<'a, C>) -> Self { let is_compact = match query_plan.kind { ProofKind::FullNodes => false, ProofKind::CompactNodesStream => true, + ProofKind::CompactNodes => true, _ => false, }; @@ -615,6 +616,7 @@ impl<'a, L, C, D> From> for HaltedStateCheck<'a, L, C, D> { items: Default::default(), prefix: Default::default(), is_compact, + expect_value: false, iter_prefix: None, _ph: PhantomData, }, @@ -802,7 +804,7 @@ pub fn record_query_plan< } let child_index = if let Some(mut item) = stack.items.last_mut() { - if item.next_descended_child as usize >= crate::nibble_ops::NIBBLE_LENGTH { + if item.next_descended_child as usize >= NIBBLE_LENGTH { break } item.next_descended_child += 1; @@ -847,7 +849,7 @@ pub fn record_query_plan< } } - while !stack.pop::() {} + while stack.pop::() {} stack.recorder.finalize(); Ok(from) } @@ -927,6 +929,7 @@ impl RecordStack { // TODO put in proof (only if Hash or inline for content one) let node_data = child_node.0.data(); + //println!("r: {:?}", &node_data); match child_node.0.node_plan() { NodePlan::Branch { .. } => (), @@ -1088,8 +1091,10 @@ enum ReadProofState { Finished, } -struct ItemStack { +struct ItemStack { node: ItemStackNode, + children: Vec>>>, + attached_value_hash: Option>, depth: usize, next_descended_child: u8, } @@ -1099,13 +1104,62 @@ enum ItemStackNode { Node(OwnedNode), } -impl> From> for ItemStack { - fn from(node: ItemStackNode) -> Self { - ItemStack { node, depth: 0, next_descended_child: 0 } +impl> From<(ItemStackNode, bool)> for ItemStack { + fn from((node, is_compact): (ItemStackNode, bool)) -> Self { + let children = if !is_compact { + Vec::new() + } else { + match &node { + ItemStackNode::Inline(_) => Vec::new(), + ItemStackNode::Node(node) => match node.node_plan() { + NodePlan::Empty | NodePlan::Leaf { .. } => Vec::new(), + NodePlan::Extension { child, .. } => { + let mut result: Vec>>> = vec![None; 1]; + let node_data = node.data(); + match child.build(node_data) { + NodeHandle::Inline(data) if data.is_empty() => (), + child => { + use std::convert::TryInto; + let child_ref = + child.try_into().expect("TODO proper error and not using From"); + + result[0] = Some(child_ref); + }, + } + result + }, + NodePlan::Branch { children, .. } | + NodePlan::NibbledBranch { children, .. } => { + let mut i = 0; + let mut result: Vec>>> = + vec![None; NIBBLE_LENGTH]; + let node_data = node.data(); + while i < NIBBLE_LENGTH { + match children[i].as_ref().map(|c| c.build(node_data)) { + Some(NodeHandle::Inline(data)) if data.is_empty() => (), + Some(child) => { + use std::convert::TryInto; + let child_ref = child + .try_into() + .expect("TODO proper error and not using From"); + + result[i] = Some(child_ref); + }, + None => {}, + } + i += 1; + } + result + }, + }, + } + }; + + ItemStack { node, depth: 0, next_descended_child: 0, children, attached_value_hash: None } } } -impl> ItemStack { +impl> ItemStack { fn data(&self) -> &[u8] { match &self.node { ItemStackNode::Inline(n) => n.data(), @@ -1121,11 +1175,12 @@ impl> ItemStack { } } -struct ReadStack { - items: Vec>, +struct ReadStack { + items: Vec>, prefix: NibbleVec, iter_prefix: Option<(usize, bool)>, // limit and wether we return value is_compact: bool, + expect_value: bool, _ph: PhantomData, } @@ -1143,7 +1198,27 @@ fn verify_hash( } } -impl> ReadStack { +/// Byte array where we can remove first item. +/// This is only needed for the ESCAPE_HEADER of COMPACT which +/// itself is not strictly needed (we can know if escaped from +/// query plan). +pub trait SplitFirst: Borrow<[u8]> { + fn split_first(&mut self); +} + +impl SplitFirst for Vec { + fn split_first(&mut self) { + *self = self.split_off(1); + } +} + +impl<'a> SplitFirst for &'a [u8] { + fn split_first(&mut self) { + *self = &self[1..]; + } +} + +impl ReadStack { fn try_stack_child( &mut self, child_index: u8, @@ -1170,17 +1245,49 @@ impl> ReadStack { }, } } else { - NodeHandle::Hash(expected_root.as_ref().map(AsRef::as_ref).unwrap_or(&[])) + if self.is_compact { + NodeHandle::Inline(&[]) + } else { + NodeHandle::Hash(expected_root.as_ref().map(AsRef::as_ref).unwrap_or(&[])) + } }; - let mut node: ItemStack<_> = match child_handle { + let mut node: ItemStack<_, _> = match child_handle { NodeHandle::Inline(data) => - // try access in inline then return - ItemStackNode::Inline(match OwnedNode::new::(data.to_vec()) { - Ok(node) => node, - Err(e) => return Err(VerifyError::DecodeError(e)), - }) - .into(), + if self.is_compact && data.len() == 0 { + // ommitted hash + let Some(mut encoded_node) = proof.next() else { + // halt happens with a hash, this is not. + return Err(VerifyError::IncompleteProof); + }; + if self.is_compact && + encoded_node.borrow().len() > 0 && + Some(encoded_node.borrow()[0]) == + ::ESCAPE_HEADER + { + self.expect_value = true; + // no value to visit TODO set a boolean to ensure we got a hash and don + // t expect reanding a node value + encoded_node.split_first(); + } + let node = match OwnedNode::new::(encoded_node) { + Ok(node) => node, + Err(e) => return Err(VerifyError::DecodeError(e)), + }; + (ItemStackNode::Node(node), self.is_compact).into() + } else { + // try access in inline then return + ( + ItemStackNode::Inline(match OwnedNode::new::(data.to_vec()) { + Ok(node) => node, + Err(e) => return Err(VerifyError::DecodeError(e)), + }), + self.is_compact, + ) + .into() + }, NodeHandle::Hash(hash) => { + // TODO if is_compact allow only if restart bellow depth (otherwhise should be + // inline(0)) or means halted (if something in proof it is extraneous node) let Some(encoded_node) = proof.next() else { return Ok(TryStackChildResult::Halted); }; @@ -1188,10 +1295,10 @@ impl> ReadStack { Ok(node) => node, Err(e) => return Err(VerifyError::DecodeError(e)), }; - if check_hash { + if !self.is_compact && check_hash { verify_hash::(node.data(), hash)?; } - ItemStackNode::Node(node).into() + (ItemStackNode::Node(node), self.is_compact).into() }, }; let node_data = node.data(); @@ -1251,13 +1358,13 @@ impl> ReadStack { verify_hash::(encoded_branch.borrow(), hash)?; } node = match OwnedNode::new::(encoded_branch) { - Ok(node) => ItemStackNode::Node(node).into(), + Ok(node) => (ItemStackNode::Node(node), self.is_compact).into(), Err(e) => return Err(VerifyError::DecodeError(e)), }; }, NodeHandle::Inline(encoded_branch) => { node = match OwnedNode::new::(encoded_branch.to_vec()) { - Ok(node) => ItemStackNode::Inline(node).into(), + Ok(node) => (ItemStackNode::Inline(node), self.is_compact).into(), Err(e) => return Err(VerifyError::DecodeError(e)), }; }, @@ -1267,6 +1374,10 @@ impl> ReadStack { }; } node.depth = self.prefix.len(); + // needed for compact + self.items.last_mut().map(|parent| { + parent.next_descended_child = child_index + 1; + }); self.items.push(node); if prefix_incomplete { Ok(TryStackChildResult::StackedDescendIncomplete) @@ -1291,8 +1402,27 @@ impl> ReadStack { }; if let Some(value) = value { match value { - Value::Inline(value) => return Ok(Some(value.to_vec())), + Value::Inline(value) => + if self.expect_value { + assert!(self.is_compact); + self.expect_value = false; + let Some(value) = proof.next() else { + return Err(VerifyError::IncompleteProof); + }; + if check_hash { + let hash = L::Hash::hash(value.borrow()); + self.items.last_mut().map(|i| i.attached_value_hash = Some(hash)); + } + return Ok(Some(value.borrow().to_vec())) + } else { + return Ok(Some(value.to_vec())) + }, Value::Node(hash) => { + if self.expect_value { + let mut error_hash = TrieHash::::default(); + error_hash.as_mut().copy_from_slice(hash); + return Err(VerifyError::ExtraneousHashReference(error_hash)) + } let Some(value) = proof.next() else { return Err(VerifyError::IncompleteProof); }; @@ -1310,20 +1440,78 @@ impl> ReadStack { Ok(None) } - fn pop(&mut self) -> bool { + fn pop( + &mut self, + expected_root: &Option>, + ) -> Result, CError>> { if self.iter_prefix.as_ref().map(|p| p.0 == self.items.len()).unwrap_or(false) { - return false + return Ok(false) } - if let Some(_last) = self.items.pop() { + if let Some(last) = self.items.pop() { let depth = self.items.last().map(|i| i.depth).unwrap_or(0); self.prefix.drop_lasts(self.prefix.len() - depth); - true + if self.is_compact && expected_root.is_some() { + match last.node { + ItemStackNode::Inline(_) => (), + ItemStackNode::Node(node) => { + let origin = 0; // TODO restart depth in stack + let node_data = node.data(); + let node = node.node_plan().build(node_data); + let encoded_node = crate::trie_codec::encode_read_node_internal::( + node, + &last.children, + last.attached_value_hash.as_ref().map(|h| h.as_ref()), + ); + + //println!("{:?}", encoded_node); + if self.items.len() == origin { + if let Some(parent) = self.items.last() { + unimplemented!("check agains right child"); + } else { + let expected = expected_root.as_ref().expect("checked above"); + verify_hash::(&encoded_node, expected.as_ref())?; + } + } else { + let hash = L::Hash::hash(&encoded_node); + if let Some(parent) = self.items.last_mut() { + let at = parent.next_descended_child - 1; + parent.children[at as usize] = Some(ChildReference::Hash(hash)); + } else { + if &Some(hash) != expected_root { + return Err(VerifyError::RootMismatch(hash)) + } + } + } + }, + } + } + Ok(true) } else { - false + Ok(false) } } - fn pop_until(&mut self, target: usize) -> Result<(), VerifyError, CError>> { + fn pop_until( + &mut self, + target: usize, + expected_root: &Option>, + ) -> Result<(), VerifyError, CError>> { + if self.is_compact && expected_root.is_some() { + // one by one + while let Some(last) = self.items.last() { + match last.depth.cmp(&target) { + Ordering::Greater => (), + // depth should match. + Ordering::Less => { + // TODO other error + return Err(VerifyError::ExtraneousNode) + }, + Ordering::Equal => return Ok(()), + } + // one by one + let _ = self.pop(expected_root)?; + } + } loop { if let Some(last) = self.items.last() { match last.depth.cmp(&target) { @@ -1358,7 +1546,7 @@ impl> ReadStack { } /// Content return on success when reading proof. -pub enum ReadProofItem<'a, L, C, D> { +pub enum ReadProofItem<'a, L: TrieLayout, C, D> { /// Successfull read of proof, not all content read. Halted(Box>), /// Seen value and key in proof. @@ -1380,7 +1568,7 @@ where L: TrieLayout, C: Iterator>, P: Iterator, - D: Borrow<[u8]>, + D: SplitFirst, { type Item = Result, VerifyError, CError>>; @@ -1419,7 +1607,7 @@ where } } - let r = self.stack.pop_until(common_nibbles); + let r = self.stack.pop_until(common_nibbles, &self.expected_root); if let Err(e) = r { self.state = ReadProofState::Finished; return Some(Err(e)) @@ -1458,7 +1646,7 @@ where }; } while let Some(child_index) = self.stack.items.last_mut().and_then(|last| { - if last.next_descended_child as usize >= crate::nibble_ops::NIBBLE_LENGTH { + if last.next_descended_child as usize >= NIBBLE_LENGTH { None } else { let child_index = last.next_descended_child; @@ -1500,7 +1688,13 @@ where } } if self.stack.iter_prefix.as_ref().map(|p| p.1).unwrap_or_default() { - if !self.stack.pop() { + if !match self.stack.pop(&self.expected_root) { + Ok(r) => r, + Err(e) => { + self.state = ReadProofState::Finished; + return Some(Err(e)) + }, + } { // end iter self.stack.exit_prefix_iter(); } @@ -1587,7 +1781,13 @@ where debug_assert!(self.state == ReadProofState::PlanConsumed); if self.is_compact { - unimplemented!("check hash from stack"); + let stack_to = 0; // TODO restart is different + // let r = self.stack.pop_until(common_nibbles, &self.expected_root); + let r = self.stack.pop_until(stack_to, &self.expected_root); + if let Err(e) = r { + self.state = ReadProofState::Finished; + return Some(Err(e)) + } } else { if self.proof.next().is_some() { self.state = ReadProofState::Finished; @@ -1604,7 +1804,7 @@ where L: TrieLayout, C: Iterator>, P: Iterator, - D: Borrow<[u8]>, + D: SplitFirst, { fn halt( &mut self, @@ -1623,6 +1823,7 @@ where items: Default::default(), prefix: Default::default(), is_compact: self.is_compact, + expect_value: false, iter_prefix: None, _ph: PhantomData, }, @@ -1649,7 +1850,7 @@ where L: TrieLayout, C: Iterator>, P: Iterator, - D: Borrow<[u8]>, + D: SplitFirst, { let HaltedStateCheck { query_plan, current, stack, state, restore_offset } = state; diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 8e627e82..fb80e34f 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -414,27 +414,35 @@ impl<'a, C: NodeCodec> DecoderStackEntry<'a, C> { /// Preconditions: /// - if node is an extension node, then `children[0]` is Some. fn encode_node(self, attached_hash: Option<&[u8]>) -> Vec { - let attached_hash = attached_hash.map(|h| crate::node::Value::Node(h)); - match self.node { - Node::Empty => C::empty_node().to_vec(), - Node::Leaf(partial, value) => - C::leaf_node(partial.right_iter(), partial.len(), attached_hash.unwrap_or(value)), - Node::Extension(partial, _) => C::extension_node( - partial.right_iter(), - partial.len(), - self.children[0].expect("required by method precondition; qed"), - ), - Node::Branch(_, value) => C::branch_node( - self.children.into_iter(), - if attached_hash.is_some() { attached_hash } else { value }, - ), - Node::NibbledBranch(partial, _, value) => C::branch_node_nibbled( - partial.right_iter(), - partial.len(), - self.children.iter(), - if attached_hash.is_some() { attached_hash } else { value }, - ), - } + encode_read_node_internal::(self.node, &self.children, attached_hash) + } +} + +pub(crate) fn encode_read_node_internal( + node: Node, + children: &Vec>>, + attached_hash: Option<&[u8]>, +) -> Vec { + let attached_hash = attached_hash.map(|h| crate::node::Value::Node(h)); + match node { + Node::Empty => C::empty_node().to_vec(), + Node::Leaf(partial, value) => + C::leaf_node(partial.right_iter(), partial.len(), attached_hash.unwrap_or(value)), + Node::Extension(partial, _) => C::extension_node( + partial.right_iter(), + partial.len(), + children[0].expect("required by method precondition; qed"), + ), + Node::Branch(_, value) => C::branch_node( + children.into_iter(), + if attached_hash.is_some() { attached_hash } else { value }, + ), + Node::NibbledBranch(partial, _, value) => C::branch_node_nibbled( + partial.right_iter(), + partial.len(), + children.iter(), + if attached_hash.is_some() { attached_hash } else { value }, + ), } } diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 35c7bd47..cfb996d8 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -266,7 +266,7 @@ fn test_query_plan_internal() { }; let db = >::new(&db, &root).with_cache(&mut cache).build(); - for kind in [ProofKind::CompactNodes, ProofKind::FullNodes] { + for kind in [ProofKind::CompactNodes /* ProofKind::FullNodes */] { let query_plans = [ InMemQueryPlan { items: vec![InMemQueryPlanItem::new(b"".to_vec(), true)], @@ -292,7 +292,8 @@ fn test_query_plan_internal() { }, ]; for query_plan in query_plans { - for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] + for limit_conf in + [(0, false) /* (1, false), (1, true), (2, false), (2, true), (3, true) */] { let limit = limit_conf.0; let limit = (limit != 0).then(|| limit); From 818e67b279c94bb68ae8bf7733c2c6e588dc693a Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 13 Apr 2023 09:35:05 +0200 Subject: [PATCH 031/154] fix --- test-support/reference-trie/src/lib.rs | 1 + trie-db/src/query_plan.rs | 33 +++++++++++++++----------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index be626801..2cbe4c1a 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -51,6 +51,7 @@ macro_rules! test_layouts { ($test:ident, $test_internal:ident) => { #[test] fn $test() { + $test_internal::<$crate::ExtensionLayout>(); // TODO rem eprintln!("Running with layout `HashedValueNoExtThreshold`"); $test_internal::<$crate::HashedValueNoExtThreshold<1>>(); eprintln!("Running with layout `HashedValueNoExt`"); diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 9a5b5815..d3ea5405 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -196,9 +196,9 @@ struct CompactEncodingInfos { /// Node in memory content. node: OwnedNode, /// Flags indicating whether each child is omitted in the encoded node. - accessed_children: Bitmap, + accessed_children_node: Bitmap, /// Skip value if value node is after. - accessed_value: bool, + accessed_value_node: bool, /// Depth of node in nible. depth: usize, /// Next descended child, this is only really needed when iterating on @@ -335,8 +335,8 @@ impl Recorder { let at = stacked_pos.pop().expect("always stacked"); proof[at] = crate::trie_codec::encode_node_internal::( &item.node, - item.accessed_value, - item.accessed_children, + item.accessed_value_node, + item.accessed_children_node, ) .expect("TODO error handling, can it actually fail?"); }, @@ -884,14 +884,17 @@ impl RecordStack { return Ok(TryStackChildResult::NotStacked), NodePlan::Extension { child, .. } => if child_index == 0 { - item.accessed_children.set(child_index as usize, true); - child.build(node_data) + let child_handle = child.build(node_data); + if let &NodeHandle::Hash(_) = &child_handle { + item.accessed_children_node.set(child_index as usize, true); + } + child_handle } else { return Ok(TryStackChildResult::NotStacked) }, NodePlan::NibbledBranch { children, .. } | NodePlan::Branch { children, .. } => if let Some(child) = &children[child_index as usize] { - from_branch = Some(&mut item.accessed_children); + from_branch = Some(&mut item.accessed_children_node); child.build(node_data) } else { return Ok(TryStackChildResult::NotStackedBranch) @@ -909,8 +912,10 @@ impl RecordStack { return Ok(TryStackChildResult::Halted) } } - if let Some(accessed_children) = from_branch { - accessed_children.set(child_index as usize, true); + if let Some(accessed_children_node) = from_branch { + if !is_inline { + accessed_children_node.set(child_index as usize, true); + } slice_query.as_mut().map(|s| s.advance(1)); prefix.push(child_index); @@ -963,8 +968,8 @@ impl RecordStack { }; let infos = CompactEncodingInfos { node: child_node.0, - accessed_children: Default::default(), - accessed_value: false, + accessed_children_node: Default::default(), + accessed_value_node: false, depth: prefix.len(), next_descended_child, is_inline, @@ -1009,9 +1014,9 @@ impl RecordStack { }, _ => return Ok(false), }; - item.accessed_value = true; match value { Value::Node(hash_slice) => { + item.accessed_value_node = true; let mut hash = TrieHash::::default(); hash.as_mut().copy_from_slice(hash_slice); let Some(value) = db.db().get(&hash, self.prefix.as_prefix()) else { @@ -1626,9 +1631,9 @@ where } }; let did_prefix = self.stack.iter_prefix.is_some(); - while let Some((_, accessed_value)) = self.stack.iter_prefix.clone() { + while let Some((_, accessed_value_node)) = self.stack.iter_prefix.clone() { // prefix iteration - if !accessed_value { + if !accessed_value_node { self.stack.iter_prefix.as_mut().map(|s| { s.1 = true; }); From 8bffb85f7c7a39da218adb70e5ebe81ce4a493a0 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 13 Apr 2023 10:23:44 +0200 Subject: [PATCH 032/154] extension requires refact, TODO later --- test-support/reference-trie/src/lib.rs | 3 +-- trie-db/src/query_plan.rs | 36 ++++++++++++++++++++------ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 2cbe4c1a..d8e025a6 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -51,7 +51,6 @@ macro_rules! test_layouts { ($test:ident, $test_internal:ident) => { #[test] fn $test() { - $test_internal::<$crate::ExtensionLayout>(); // TODO rem eprintln!("Running with layout `HashedValueNoExtThreshold`"); $test_internal::<$crate::HashedValueNoExtThreshold<1>>(); eprintln!("Running with layout `HashedValueNoExt`"); @@ -59,7 +58,7 @@ macro_rules! test_layouts { eprintln!("Running with layout `NoExtensionLayout`"); $test_internal::<$crate::NoExtensionLayout>(); eprintln!("Running with layout `ExtensionLayout`"); - $test_internal::<$crate::ExtensionLayout>(); +// $test_internal::<$crate::ExtensionLayout>(); TODO restore when extension in stack } }; } diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index d3ea5405..59302634 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -1356,9 +1356,14 @@ impl ReadStack { match child { NodeHandle::Hash(hash) => { let Some(encoded_branch) = proof.next() else { - // No halt on extension node (restart over a child index). - return Err(VerifyError::IncompleteProof); - }; + // No halt on extension node (restart over a child index). + return Err(VerifyError::IncompleteProof); + }; + if self.is_compact { + let mut error_hash = TrieHash::::default(); + error_hash.as_mut().copy_from_slice(hash); + return Err(VerifyError::ExtraneousHashReference(error_hash)); + } if check_hash { verify_hash::(encoded_branch.borrow(), hash)?; } @@ -1367,11 +1372,26 @@ impl ReadStack { Err(e) => return Err(VerifyError::DecodeError(e)), }; }, - NodeHandle::Inline(encoded_branch) => { - node = match OwnedNode::new::(encoded_branch.to_vec()) { - Ok(node) => (ItemStackNode::Inline(node), self.is_compact).into(), - Err(e) => return Err(VerifyError::DecodeError(e)), - }; + NodeHandle::Inline(data) => { + if self.is_compact && data.len() == 0 { + unimplemented!("This requires to put extension in stack"); + /* + // ommitted hash + let Some(encoded_node) = proof.next() else { + // halt happens with a hash, this is not. + return Err(VerifyError::IncompleteProof); + }; + node = match OwnedNode::new::(encoded_node) { + Ok(node) => (ItemStackNode::Node(node), self.is_compact).into(), + Err(e) => return Err(VerifyError::DecodeError(e)), + }; + */ + } else { + node = match OwnedNode::new::(data.to_vec()) { + Ok(node) => (ItemStackNode::Inline(node), self.is_compact).into(), + Err(e) => return Err(VerifyError::DecodeError(e)), + }; + } }, } let NodePlan::Branch { .. } = node.node_plan() else { From 7f8342d16ea53b12d54ee4c29ea344f8c556badb Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 14 Apr 2023 14:09:49 +0200 Subject: [PATCH 033/154] iterative --- test-support/reference-trie/src/lib.rs | 2 +- trie-db/src/node.rs | 6 + trie-db/src/query_plan.rs | 189 ++++++++++++++++++------- trie-db/test/src/proof.rs | 7 +- 4 files changed, 150 insertions(+), 54 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index d8e025a6..940fb630 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -58,7 +58,7 @@ macro_rules! test_layouts { eprintln!("Running with layout `NoExtensionLayout`"); $test_internal::<$crate::NoExtensionLayout>(); eprintln!("Running with layout `ExtensionLayout`"); -// $test_internal::<$crate::ExtensionLayout>(); TODO restore when extension in stack + // $test_internal::<$crate::ExtensionLayout>(); TODO restore when extension in stack } }; } diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 7a65af77..25dbe9d3 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -588,6 +588,12 @@ pub struct OwnedNode { plan: NodePlan, } +impl Clone for OwnedNode { + fn clone(&self) -> Self { + OwnedNode { data: self.data.clone(), plan: self.plan.clone() } + } +} + impl> OwnedNode { /// Construct an `OwnedNode` by decoding an owned data source according to some codec. pub fn new(data: D) -> core::result::Result { diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 59302634..ad22d761 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -332,14 +332,16 @@ impl Recorder { RecorderStateInner::Stream(_) => (), RecorderStateInner::Compact { output, proof, stacked_pos } => if !item.is_inline { - let at = stacked_pos.pop().expect("always stacked"); - proof[at] = crate::trie_codec::encode_node_internal::( - &item.node, - item.accessed_value_node, - item.accessed_children_node, - ) - .expect("TODO error handling, can it actually fail?"); + if let Some(at) = stacked_pos.pop() { + proof[at] = crate::trie_codec::encode_node_internal::( + &item.node, + item.accessed_value_node, + item.accessed_children_node, + ) + .expect("TODO error handling, can it actually fail?"); + } // else when restarting record, this is not to be recorded }, + RecorderStateInner::CompactStream(output) => { unimplemented!() }, @@ -394,17 +396,32 @@ impl Recorder { } } - fn finalize(&mut self) { + fn finalize(&mut self, items: &Vec) { match &mut self.output { RecorderStateInner::Compact { output, proof, stacked_pos } => { - if stacked_pos.len() > 0 { + let restarted_from = 0; + if stacked_pos.len() > restarted_from { // halted: complete up to 0 and write all nodes keeping stack. - unimplemented!() - } else { - for entry in core::mem::take(proof) { - output.write_entry(entry.into()); + let mut at = stacked_pos.len(); + let mut items = items.iter().rev(); + while let Some(pos) = stacked_pos.pop() { + loop { + let item = items.next().expect("pos stacked with an item"); + if !item.is_inline { + proof[pos] = crate::trie_codec::encode_node_internal::( + &item.node, + item.accessed_value_node, + item.accessed_children_node, + ) + .expect("TODO error handling, can it actually fail?"); + break + } + } } } + for entry in core::mem::take(proof) { + output.write_entry(entry.into()); + } }, RecorderStateInner::Stream(output) => { // all written @@ -594,7 +611,7 @@ impl HaltedStateRecord { /// When process is halted keep execution state /// to restore later. -pub struct HaltedStateCheck<'a, L: TrieLayout, C, D> { +pub struct HaltedStateCheck<'a, L: TrieLayout, C, D: SplitFirst> { query_plan: QueryPlan<'a, C>, current: Option>, stack: ReadStack, @@ -602,7 +619,7 @@ pub struct HaltedStateCheck<'a, L: TrieLayout, C, D> { restore_offset: usize, } -impl<'a, L: TrieLayout, C, D> From> for HaltedStateCheck<'a, L, C, D> { +impl<'a, L: TrieLayout, C, D: SplitFirst> From> for HaltedStateCheck<'a, L, C, D> { fn from(query_plan: QueryPlan<'a, C>) -> Self { let is_compact = match query_plan.kind { ProofKind::FullNodes => false, @@ -614,6 +631,7 @@ impl<'a, L: TrieLayout, C, D> From> for HaltedStateCheck<'a, L, HaltedStateCheck { stack: ReadStack { items: Default::default(), + start_items: 0, prefix: Default::default(), is_compact, expect_value: false, @@ -657,6 +675,7 @@ pub fn record_query_plan< let dummy_parent_hash = TrieHash::::default(); let mut stateless = false; let mut statefull = None; + let mut depth_started_from = 0; if let Some(lower_bound) = from.from.take() { if from.currently_query_item.is_none() { stateless = true; @@ -666,12 +685,13 @@ pub fn record_query_plan< bound.pop(); } from.stack.recorder.start_at = Some(bound.len()); + depth_started_from = bound.len(); from.stack.seek = Some(bound); } else { - statefull = Some( - lower_bound.0.len() * nibble_ops::NIBBLE_PER_BYTE - - if lower_bound.1 { 2 } else { 1 }, - ); + let bound_len = lower_bound.0.len() * nibble_ops::NIBBLE_PER_BYTE - + if lower_bound.1 { 2 } else { 1 }; + // from.stack.recorder.start_at = Some(bound_len); + statefull = Some(bound_len); } } @@ -710,6 +730,7 @@ pub fn record_query_plan< } } let common_nibbles = if let Some(slice_at) = statefull.take() { + depth_started_from = slice_at + 1; slice_at } else { let (ordered, common_nibbles) = @@ -727,7 +748,7 @@ pub fn record_query_plan< Ordering::Equal | Ordering::Less => break, Ordering::Greater => if !stack.pop::() { - stack.recorder.finalize(); + stack.recorder.finalize::(&stack.items); return Ok(from) }, } @@ -776,7 +797,7 @@ pub fn record_query_plan< )); stack.prefix.pop(); from.currently_query_item = Some(query.to_owned()); - stack.recorder.finalize(); + stack.recorder.finalize::(&stack.items); return Ok(from) }, } @@ -834,7 +855,7 @@ pub fn record_query_plan< )); stack.prefix.pop(); from.currently_query_item = prev_query.map(|q| q.to_owned()); - stack.recorder.finalize(); + stack.recorder.finalize::(&stack.items); return Ok(from) }, } @@ -850,7 +871,7 @@ pub fn record_query_plan< } while stack.pop::() {} - stack.recorder.finalize(); + stack.recorder.finalize::(&stack.items); Ok(from) } @@ -1066,7 +1087,7 @@ where L: TrieLayout, C: Iterator>, P: Iterator, - D: Borrow<[u8]>, + D: SplitFirst, { // always needed, this is option only // to avoid unsafe code when halting. @@ -1096,7 +1117,7 @@ enum ReadProofState { Finished, } -struct ItemStack { +struct ItemStack { node: ItemStackNode, children: Vec>>>, attached_value_hash: Option>, @@ -1104,12 +1125,25 @@ struct ItemStack { next_descended_child: u8, } -enum ItemStackNode { +impl Clone for ItemStack { + fn clone(&self) -> Self { + ItemStack { + node: self.node.clone(), + children: self.children.clone(), + attached_value_hash: self.attached_value_hash, + depth: self.depth, + next_descended_child: self.next_descended_child, + } + } +} + +#[derive(Clone)] +enum ItemStackNode { Inline(OwnedNode>), Node(OwnedNode), } -impl> From<(ItemStackNode, bool)> for ItemStack { +impl From<(ItemStackNode, bool)> for ItemStack { fn from((node, is_compact): (ItemStackNode, bool)) -> Self { let children = if !is_compact { Vec::new() @@ -1164,7 +1198,7 @@ impl> From<(ItemStackNode, bool)> for ItemStac } } -impl> ItemStack { +impl ItemStack { fn data(&self) -> &[u8] { match &self.node { ItemStackNode::Inline(n) => n.data(), @@ -1180,15 +1214,30 @@ impl> ItemStack { } } -struct ReadStack { +struct ReadStack { items: Vec>, prefix: NibbleVec, iter_prefix: Option<(usize, bool)>, // limit and wether we return value + start_items: usize, is_compact: bool, expect_value: bool, _ph: PhantomData, } +impl Clone for ReadStack { + fn clone(&self) -> Self { + ReadStack { + items: self.items.clone(), + start_items: self.start_items.clone(), + prefix: self.prefix.clone(), + iter_prefix: self.iter_prefix, + is_compact: self.is_compact, + expect_value: self.expect_value, + _ph: PhantomData, + } + } +} + fn verify_hash( data: &[u8], expected: &[u8], @@ -1207,7 +1256,7 @@ fn verify_hash( /// This is only needed for the ESCAPE_HEADER of COMPACT which /// itself is not strictly needed (we can know if escaped from /// query plan). -pub trait SplitFirst: Borrow<[u8]> { +pub trait SplitFirst: Borrow<[u8]> + Clone { fn split_first(&mut self); } @@ -1293,9 +1342,19 @@ impl ReadStack { NodeHandle::Hash(hash) => { // TODO if is_compact allow only if restart bellow depth (otherwhise should be // inline(0)) or means halted (if something in proof it is extraneous node) - let Some(encoded_node) = proof.next() else { + let Some(mut encoded_node) = proof.next() else { return Ok(TryStackChildResult::Halted); }; + if self.is_compact && + encoded_node.borrow().len() > 0 && + Some(encoded_node.borrow()[0]) == + ::ESCAPE_HEADER + { + self.expect_value = true; + // no value to visit TODO set a boolean to ensure we got a hash and don + // t expect reanding a node value + encoded_node.split_first(); + } let node = match OwnedNode::new::(encoded_node) { Ok(node) => node, Err(e) => return Err(VerifyError::DecodeError(e)), @@ -1362,7 +1421,7 @@ impl ReadStack { if self.is_compact { let mut error_hash = TrieHash::::default(); error_hash.as_mut().copy_from_slice(hash); - return Err(VerifyError::ExtraneousHashReference(error_hash)); + return Err(VerifyError::ExtraneousHashReference(error_hash)) } if check_hash { verify_hash::(encoded_branch.borrow(), hash)?; @@ -1375,17 +1434,17 @@ impl ReadStack { NodeHandle::Inline(data) => { if self.is_compact && data.len() == 0 { unimplemented!("This requires to put extension in stack"); - /* - // ommitted hash - let Some(encoded_node) = proof.next() else { - // halt happens with a hash, this is not. - return Err(VerifyError::IncompleteProof); - }; - node = match OwnedNode::new::(encoded_node) { - Ok(node) => (ItemStackNode::Node(node), self.is_compact).into(), - Err(e) => return Err(VerifyError::DecodeError(e)), - }; - */ + /* + // ommitted hash + let Some(encoded_node) = proof.next() else { + // halt happens with a hash, this is not. + return Err(VerifyError::IncompleteProof); + }; + node = match OwnedNode::new::(encoded_node) { + Ok(node) => (ItemStackNode::Node(node), self.is_compact).into(), + Err(e) => return Err(VerifyError::DecodeError(e)), + }; + */ } else { node = match OwnedNode::new::(data.to_vec()) { Ok(node) => (ItemStackNode::Inline(node), self.is_compact).into(), @@ -1479,7 +1538,7 @@ impl ReadStack { match last.node { ItemStackNode::Inline(_) => (), ItemStackNode::Node(node) => { - let origin = 0; // TODO restart depth in stack + let origin = self.start_items; let node_data = node.data(); let node = node.node_plan().build(node_data); let encoded_node = crate::trie_codec::encode_read_node_internal::( @@ -1491,11 +1550,21 @@ impl ReadStack { //println!("{:?}", encoded_node); if self.items.len() == origin { if let Some(parent) = self.items.last() { - unimplemented!("check agains right child"); + let at = parent.next_descended_child - 1; + if let Some(Some(ChildReference::Hash(expected))) = + parent.children.get(at as usize) + { + verify_hash::(&encoded_node, expected.as_ref())?; + } else { + return Err(VerifyError::RootMismatch(Default::default())) + } } else { let expected = expected_root.as_ref().expect("checked above"); verify_hash::(&encoded_node, expected.as_ref())?; } + } else if self.items.len() < origin { + // popped origin, need to check against new origin + self.start_items = self.items.len(); } else { let hash = L::Hash::hash(&encoded_node); if let Some(parent) = self.items.last_mut() { @@ -1520,8 +1589,16 @@ impl ReadStack { &mut self, target: usize, expected_root: &Option>, + check_only: bool, ) -> Result<(), VerifyError, CError>> { if self.is_compact && expected_root.is_some() { + // TODO pop with check only, here unefficient implementation where we just restore + + let mut restore = None; + if check_only { + restore = Some(self.clone()); + self.iter_prefix = None; + } // one by one while let Some(last) = self.items.last() { match last.depth.cmp(&target) { @@ -1536,6 +1613,11 @@ impl ReadStack { // one by one let _ = self.pop(expected_root)?; } + + if let Some(old) = restore.take() { + *self = old; + return Ok(()) + } } loop { if let Some(last) = self.items.last() { @@ -1571,7 +1653,7 @@ impl ReadStack { } /// Content return on success when reading proof. -pub enum ReadProofItem<'a, L: TrieLayout, C, D> { +pub enum ReadProofItem<'a, L: TrieLayout, C, D: SplitFirst> { /// Successfull read of proof, not all content read. Halted(Box>), /// Seen value and key in proof. @@ -1632,7 +1714,7 @@ where } } - let r = self.stack.pop_until(common_nibbles, &self.expected_root); + let r = self.stack.pop_until(common_nibbles, &self.expected_root, false); if let Err(e) = r { self.state = ReadProofState::Finished; return Some(Err(e)) @@ -1808,7 +1890,7 @@ where if self.is_compact { let stack_to = 0; // TODO restart is different // let r = self.stack.pop_until(common_nibbles, &self.expected_root); - let r = self.stack.pop_until(stack_to, &self.expected_root); + let r = self.stack.pop_until(stack_to, &self.expected_root, false); if let Err(e) = r { self.state = ReadProofState::Finished; return Some(Err(e)) @@ -1836,16 +1918,22 @@ where to_check_slice: Option<&mut NibbleSlice>, ) -> Option, VerifyError, CError>>> { if self.is_compact { - unimplemented!("check hash from stack"); + let stack_to = 0; // TODO restart is different + let r = self.stack.pop_until(stack_to, &self.expected_root, true); + if let Err(e) = r { + self.state = ReadProofState::Finished; + return Some(Err(e)) + } } self.state = ReadProofState::Finished; let query_plan = crate::rstd::mem::replace(&mut self.query_plan, None); let query_plan = query_plan.expect("Init with state"); let current = crate::rstd::mem::take(&mut self.current); - let stack = crate::rstd::mem::replace( + let mut stack = crate::rstd::mem::replace( &mut self.stack, ReadStack { items: Default::default(), + start_items: 0, prefix: Default::default(), is_compact: self.is_compact, expect_value: false, @@ -1853,6 +1941,7 @@ where _ph: PhantomData, }, ); + stack.start_items = stack.items.len(); Some(Ok(ReadProofItem::Halted(Box::new(HaltedStateCheck { query_plan, current, diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index cfb996d8..d2de2f6c 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -292,9 +292,10 @@ fn test_query_plan_internal() { }, ]; for query_plan in query_plans { - for limit_conf in - [(0, false) /* (1, false), (1, true), (2, false), (2, true), (3, true) */] - { + for limit_conf in [ + /* (0, false) */ + (1, false), /* (1, true), (2, false), (2, true), (3, true) */ + ] { let limit = limit_conf.0; let limit = (limit != 0).then(|| limit); let recorder = Recorder::new(kind, InMemoryRecorder::default(), limit, None); From a3847958cdd64201e13a9e85581958d2ea2a728f Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 14 Apr 2023 14:26:07 +0200 Subject: [PATCH 034/154] check for concate of partial proofs --- test-support/reference-trie/src/lib.rs | 2 +- trie-db/src/query_plan.rs | 21 ++++++++++++++++++++- trie-db/test/src/proof.rs | 10 ++++++++-- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 940fb630..be626801 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -58,7 +58,7 @@ macro_rules! test_layouts { eprintln!("Running with layout `NoExtensionLayout`"); $test_internal::<$crate::NoExtensionLayout>(); eprintln!("Running with layout `ExtensionLayout`"); - // $test_internal::<$crate::ExtensionLayout>(); TODO restore when extension in stack + $test_internal::<$crate::ExtensionLayout>(); } }; } diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index ad22d761..d720a6c1 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -1569,7 +1569,26 @@ impl ReadStack { let hash = L::Hash::hash(&encoded_node); if let Some(parent) = self.items.last_mut() { let at = parent.next_descended_child - 1; - parent.children[at as usize] = Some(ChildReference::Hash(hash)); + match parent.children[at as usize] { + None => + return Err(VerifyError::RootMismatch(Default::default())), + Some(ChildReference::Hash(expected)) => { + // can append if chunks are concatenated (not progressively + // checked) + verify_hash::(&encoded_node, expected.as_ref())?; + }, + Some(ChildReference::Inline(_h, size)) => { + if size > 0 { + // only non inline are stacked + return Err( + VerifyError::RootMismatch(Default::default()), + ) + } + // Complete + parent.children[at as usize] = + Some(ChildReference::Hash(hash)); + }, + } } else { if &Some(hash) != expected_root { return Err(VerifyError::RootMismatch(hash)) diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index d2de2f6c..4571103a 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -267,6 +267,12 @@ fn test_query_plan_internal() { let db = >::new(&db, &root).with_cache(&mut cache).build(); for kind in [ProofKind::CompactNodes /* ProofKind::FullNodes */] { + if kind == ProofKind::CompactNodes && L::USE_EXTENSION { + // Compact proofs are not supported with extensions. + // Requires changing the way extension are handled + // when decoding (putting on stack). + continue + } let query_plans = [ InMemQueryPlan { items: vec![InMemQueryPlanItem::new(b"".to_vec(), true)], @@ -293,8 +299,8 @@ fn test_query_plan_internal() { ]; for query_plan in query_plans { for limit_conf in [ - /* (0, false) */ - (1, false), /* (1, true), (2, false), (2, true), (3, true) */ + (1, true), /* (0, false), (1, false), (1, true), (2, false), (2, true), (3, + * true) */ ] { let limit = limit_conf.0; let limit = (limit != 0).then(|| limit); From 44ef72c58e15205b172a5e9e845a12e4446095b6 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 14 Apr 2023 14:46:07 +0200 Subject: [PATCH 035/154] compact fine --- trie-db/src/query_plan.rs | 18 +++++++++--------- trie-db/test/src/proof.rs | 6 ++---- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index d720a6c1..45118359 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -1570,24 +1570,24 @@ impl ReadStack { if let Some(parent) = self.items.last_mut() { let at = parent.next_descended_child - 1; match parent.children[at as usize] { - None => - return Err(VerifyError::RootMismatch(Default::default())), Some(ChildReference::Hash(expected)) => { // can append if chunks are concatenated (not progressively // checked) verify_hash::(&encoded_node, expected.as_ref())?; }, - Some(ChildReference::Inline(_h, size)) => { - if size > 0 { - // only non inline are stacked - return Err( - VerifyError::RootMismatch(Default::default()), - ) - } + None => { + // Complete + parent.children[at as usize] = + Some(ChildReference::Hash(hash)); + }, + Some(ChildReference::Inline(_h, size)) if size == 0 => { // Complete parent.children[at as usize] = Some(ChildReference::Hash(hash)); }, + _ => + // only non inline are stacked + return Err(VerifyError::RootMismatch(Default::default())), } } else { if &Some(hash) != expected_root { diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 4571103a..c91f9a30 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -298,10 +298,8 @@ fn test_query_plan_internal() { }, ]; for query_plan in query_plans { - for limit_conf in [ - (1, true), /* (0, false), (1, false), (1, true), (2, false), (2, true), (3, - * true) */ - ] { + for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] + { let limit = limit_conf.0; let limit = (limit != 0).then(|| limit); let recorder = Recorder::new(kind, InMemoryRecorder::default(), limit, None); From f9e9971f1a5081cad42cbb3b621a7fde891e57c5 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 18 Apr 2023 16:11:35 +0200 Subject: [PATCH 036/154] restore test --- trie-db/src/query_plan.rs | 58 ++++++++++----------------------------- trie-db/test/src/proof.rs | 2 +- 2 files changed, 16 insertions(+), 44 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 45118359..5f51034f 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -147,13 +147,14 @@ pub enum ProofKind { /// proof at once. CompactNodes, + /* TODO does not seem usefull, CompactContent is strictly better /// Same encoding as CompactNodes, but with an alternate ordering that allows streaming /// node and avoid unbound memory when building proof. /// /// Ordering is starting at first met proof and parent up to intersection with next /// sibling access in a branch, then next leaf, and repeating, finishing with root node. CompactNodesStream, - + */ /// Content oriented proof, no nodes are written, just a /// sequence of accessed by lexicographical order as described /// in compact_content_proof::Op. @@ -272,8 +273,7 @@ impl Recorder { match self.output { RecorderStateInner::Stream(output) | RecorderStateInner::Compact { output, .. } | - RecorderStateInner::CompactStream(output) | - RecorderStateInner::Content(output) => output, + RecorderStateInner::Content { output, .. } => output, } } @@ -288,8 +288,7 @@ impl Recorder { ProofKind::FullNodes => RecorderStateInner::Stream(output), ProofKind::CompactNodes => RecorderStateInner::Compact { output, proof: Vec::new(), stacked_pos: Vec::new() }, - ProofKind::CompactNodesStream => RecorderStateInner::CompactStream(output), - ProofKind::CompactContent => RecorderStateInner::Content(output), + ProofKind::CompactContent => RecorderStateInner::Content { output, stacked_push: None }, }; let limits = Limits { remaining_node: limit_node, remaining_size: limit_size, kind }; Self { output, limits, start_at: None } @@ -313,10 +312,7 @@ impl Recorder { stacked_pos.push(proof.len()); proof.push(Vec::new()); }, - RecorderStateInner::CompactStream(output) => { - unimplemented!() - }, - RecorderStateInner::Content(output) => { + RecorderStateInner::Content { output, stacked_push } => { unimplemented!() }, } @@ -341,11 +337,7 @@ impl Recorder { .expect("TODO error handling, can it actually fail?"); } // else when restarting record, this is not to be recorded }, - - RecorderStateInner::CompactStream(output) => { - unimplemented!() - }, - RecorderStateInner::Content(output) => { + RecorderStateInner::Content { output, stacked_push } => { unimplemented!() }, } @@ -367,10 +359,7 @@ impl Recorder { res = self.limits.add_value(value.len()); proof.push(value.into()); }, - RecorderStateInner::CompactStream(output) => { - unimplemented!() - }, - RecorderStateInner::Content(output) => { + RecorderStateInner::Content { output, stacked_push } => { unimplemented!() }, } @@ -387,10 +376,7 @@ impl Recorder { // not writing inline value (already // in parent node). }, - RecorderStateInner::CompactStream(output) => { - unimplemented!() - }, - RecorderStateInner::Content(output) => { + RecorderStateInner::Content { output, stacked_push } => { unimplemented!() }, } @@ -426,10 +412,7 @@ impl Recorder { RecorderStateInner::Stream(output) => { // all written }, - RecorderStateInner::CompactStream(output) => { - unimplemented!() - }, - RecorderStateInner::Content(output) => { + RecorderStateInner::Content { output, stacked_push } => { unimplemented!() }, } @@ -459,9 +442,6 @@ impl Limits { } } }, - ProofKind::CompactNodesStream => { - unimplemented!() - }, ProofKind::CompactContent => { unimplemented!() }, @@ -491,9 +471,6 @@ impl Limits { } } }, - ProofKind::CompactNodesStream => { - unimplemented!() - }, ProofKind::CompactContent => { unimplemented!() }, @@ -510,9 +487,6 @@ impl Limits { *rem_size += size; } }, - ProofKind::CompactNodesStream => { - unimplemented!() - }, ProofKind::CompactContent => { unimplemented!() }, @@ -528,9 +502,6 @@ impl Limits { *rem_size += size - 1; } }, - ProofKind::CompactNodesStream => { - unimplemented!() - }, ProofKind::CompactContent => { unimplemented!() }, @@ -551,9 +522,11 @@ enum RecorderStateInner { stacked_pos: Vec, }, /// For FullNodes proofs, just send node to this stream. - CompactStream(O), - /// For FullNodes proofs, just send node to this stream. - Content(O), + Content { + output: O, + // push from depth + stacked_push: Option<(Vec, u8)>, + }, } /// When process is halted keep execution state @@ -623,7 +596,6 @@ impl<'a, L: TrieLayout, C, D: SplitFirst> From> for HaltedState fn from(query_plan: QueryPlan<'a, C>) -> Self { let is_compact = match query_plan.kind { ProofKind::FullNodes => false, - ProofKind::CompactNodesStream => true, ProofKind::CompactNodes => true, _ => false, }; @@ -1988,7 +1960,7 @@ where let HaltedStateCheck { query_plan, current, stack, state, restore_offset } = state; match query_plan.kind { - ProofKind::CompactNodesStream | ProofKind::CompactContent => { + ProofKind::CompactContent => { return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent }, _ => (), diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index c91f9a30..4cf31f1a 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -266,7 +266,7 @@ fn test_query_plan_internal() { }; let db = >::new(&db, &root).with_cache(&mut cache).build(); - for kind in [ProofKind::CompactNodes /* ProofKind::FullNodes */] { + for kind in [ProofKind::CompactNodes, ProofKind::FullNodes] { if kind == ProofKind::CompactNodes && L::USE_EXTENSION { // Compact proofs are not supported with extensions. // Requires changing the way extension are handled From b09b50d2c9d2439b8d32f031ffcd5a8af28e4cd1 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 18 Apr 2023 17:38:31 +0200 Subject: [PATCH 037/154] change trait --- trie-db/src/query_plan.rs | 134 ++++++++++++++++++++++++++------------ trie-db/test/src/proof.rs | 14 +++- 2 files changed, 104 insertions(+), 44 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 5f51034f..e601200a 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -33,7 +33,7 @@ use crate::{ cmp::*, result::Result, }, - CError, ChildReference, DBValue, NibbleVec, Trie, TrieDB, TrieError, TrieHash, TrieLayout, + CError, ChildReference, DBValue, NibbleVec, Trie, TrieDB, TrieHash, TrieLayout, }; use hash_db::Hasher; @@ -202,8 +202,8 @@ struct CompactEncodingInfos { accessed_value_node: bool, /// Depth of node in nible. depth: usize, - /// Next descended child, this is only really needed when iterating on - /// prefix. + /// Next descended child, can also be use to get node position in parent + /// (this minus one). next_descended_child: u8, /// Is the node inline. is_inline: bool, @@ -243,11 +243,12 @@ impl RecorderOutput for InMemoryRecorder { } /// Simplified recorder. -pub struct Recorder { +pub struct Recorder { output: RecorderStateInner, limits: Limits, // on restore only record content AFTER this position. start_at: Option, + _ph: PhantomData, } /// Limits to size proof to record. @@ -257,7 +258,7 @@ struct Limits { kind: ProofKind, } -impl Recorder { +impl Recorder { /// Check and update start at record. /// When return true, do record. fn check_start_at(&mut self, depth: usize) -> bool { @@ -291,11 +292,16 @@ impl Recorder { ProofKind::CompactContent => RecorderStateInner::Content { output, stacked_push: None }, }; let limits = Limits { remaining_node: limit_node, remaining_size: limit_size, kind }; - Self { output, limits, start_at: None } + Self { output, limits, start_at: None, _ph: PhantomData } } #[must_use] - fn record_stacked_node(&mut self, item: &CompactEncodingInfos, _stack_pos: usize) -> bool { + fn record_stacked_node( + &mut self, + item: &CompactEncodingInfos, + stack_pos: usize, + parent_index: u8, + ) -> bool { if !self.check_start_at(item.depth) { return false } @@ -313,13 +319,47 @@ impl Recorder { proof.push(Vec::new()); }, RecorderStateInner::Content { output, stacked_push } => { - unimplemented!() + if stacked_push.is_none() { + *stacked_push = Some(NibbleVec::new()); + } + if let Some(buff) = stacked_push.as_mut() { + if stack_pos > 0 { + buff.push(parent_index); + } + let node_data = item.node.data(); + + match item.node.node_plan() { + NodePlan::Branch { .. } => (), + | NodePlan::Empty => (), + NodePlan::Leaf { partial, .. } | + NodePlan::NibbledBranch { partial, .. } | + NodePlan::Extension { partial, .. } => { + let partial = partial.build(node_data); + buff.append_optional_slice_and_nibble(Some(&partial), None); + }, + } + } }, } res } - fn record_popped_node(&mut self, item: &CompactEncodingInfos, stack_pos: usize) { + fn flush_compact_content_pushes(&mut self, depth: usize) { + if !self.check_start_at(depth) { + // TODO actually should be unreachable + return + } + if let RecorderStateInner::Content { output, stacked_push } = &mut self.output { + if let Some(buff) = stacked_push.take() { + let mask = if buff.len() % 2 == 0 { 0xff } else { 0xf0 }; + let op = compact_content_proof::Op::, Vec>::KeyPush(buff.inner().to_vec(), mask); + use codec::Encode; + output.write_bytes(&op.encode()); + } + } + } + + fn record_popped_node(&mut self, item: &CompactEncodingInfos, stack_pos: usize) { if !self.check_start_at(item.depth) { return } @@ -350,6 +390,9 @@ impl Recorder { } let mut res = false; + if let RecorderStateInner::Content { .. } = &self.output { + self.flush_compact_content_pushes(depth); + } match &mut self.output { RecorderStateInner::Stream(output) => { res = self.limits.add_value(value.len()); @@ -360,7 +403,9 @@ impl Recorder { proof.push(value.into()); }, RecorderStateInner::Content { output, stacked_push } => { - unimplemented!() + let op = compact_content_proof::Op::, Vec>::Value(value); + use codec::Encode; + output.write_bytes(&op.encode()); }, } res @@ -370,6 +415,9 @@ impl Recorder { if !self.check_start_at(depth) { return } + if let RecorderStateInner::Content { .. } = &self.output { + self.flush_compact_content_pushes(depth); + } match &mut self.output { RecorderStateInner::Compact { .. } | RecorderStateInner::Stream(_) => { @@ -377,18 +425,20 @@ impl Recorder { // in parent node). }, RecorderStateInner::Content { output, stacked_push } => { - unimplemented!() + let op = compact_content_proof::Op::, &[u8]>::Value(value); + use codec::Encode; + output.write_bytes(&op.encode()); }, } } - fn finalize(&mut self, items: &Vec) { + fn finalize(&mut self, items: &Vec) { match &mut self.output { RecorderStateInner::Compact { output, proof, stacked_pos } => { let restarted_from = 0; if stacked_pos.len() > restarted_from { // halted: complete up to 0 and write all nodes keeping stack. - let mut at = stacked_pos.len(); + let at = stacked_pos.len(); let mut items = items.iter().rev(); while let Some(pos) = stacked_pos.pop() { loop { @@ -524,32 +574,32 @@ enum RecorderStateInner { /// For FullNodes proofs, just send node to this stream. Content { output: O, - // push from depth - stacked_push: Option<(Vec, u8)>, + // push from depth. + stacked_push: Option, }, } /// When process is halted keep execution state /// to restore later. -pub struct HaltedStateRecord { +pub struct HaltedStateRecord { currently_query_item: Option, - stack: RecordStack, + stack: RecordStack, // This indicate a restore point, it takes precedence over // stack and currently_query_item. from: Option<(Vec, bool)>, } -impl HaltedStateRecord { +impl HaltedStateRecord { /// Indicate we reuse the query plan iterator /// and stack. - pub fn statefull(&mut self, recorder: Recorder) -> Recorder { + pub fn statefull(&mut self, recorder: Recorder) -> Recorder { let result = core::mem::replace(&mut self.stack.recorder, recorder); result } /// Indicate to use stateless (on a fresh proof /// and a fresh query plan iterator). - pub fn stateless(&mut self, recorder: Recorder) -> Recorder { + pub fn stateless(&mut self, recorder: Recorder) -> Recorder { let new_start = Self::from_start(recorder); let old = core::mem::replace(self, new_start); self.from = old.from; @@ -558,7 +608,7 @@ impl HaltedStateRecord { } /// Init from start. - pub fn from_start(recorder: Recorder) -> Self { + pub fn from_start(recorder: Recorder) -> Self { HaltedStateRecord { currently_query_item: None, stack: RecordStack { @@ -577,7 +627,7 @@ impl HaltedStateRecord { self.from == None } - pub fn finish(self) -> Recorder { + pub fn finish(self) -> Recorder { self.stack.recorder } } @@ -618,8 +668,8 @@ impl<'a, L: TrieLayout, C, D: SplitFirst> From> for HaltedState } } -struct RecordStack { - recorder: Recorder, +struct RecordStack { + recorder: Recorder, items: Vec, prefix: NibbleVec, iter_prefix: Option, @@ -639,8 +689,8 @@ pub fn record_query_plan< >( db: &TrieDB, query_plan: &mut QueryPlan<'a, I>, - mut from: HaltedStateRecord, -) -> Result, VerifyError, CError>> { + mut from: HaltedStateRecord, +) -> Result, VerifyError, CError>> { // TODO //) resto // let restore_buf; @@ -680,7 +730,7 @@ pub fn record_query_plan< None }; */ - let mut from_query = from.currently_query_item.take(); + let from_query = from.currently_query_item.take(); let mut from_query_ref = from_query.as_ref().map(|f| f.as_ref()); while let Some(query) = from_query_ref.clone().or_else(|| query_plan.items.next()) { if stateless { @@ -719,8 +769,8 @@ pub fn record_query_plan< match stack.prefix.len().cmp(&common_nibbles) { Ordering::Equal | Ordering::Less => break, Ordering::Greater => - if !stack.pop::() { - stack.recorder.finalize::(&stack.items); + if !stack.pop() { + stack.recorder.finalize(&stack.items); return Ok(from) }, } @@ -769,7 +819,7 @@ pub fn record_query_plan< )); stack.prefix.pop(); from.currently_query_item = Some(query.to_owned()); - stack.recorder.finalize::(&stack.items); + stack.recorder.finalize(&stack.items); return Ok(from) }, } @@ -827,14 +877,14 @@ pub fn record_query_plan< )); stack.prefix.pop(); from.currently_query_item = prev_query.map(|q| q.to_owned()); - stack.recorder.finalize::(&stack.items); + stack.recorder.finalize(&stack.items); return Ok(from) }, } } // pop - if !stack.pop::() { + if !stack.pop() { break } } @@ -842,8 +892,8 @@ pub fn record_query_plan< } } - while stack.pop::() {} - stack.recorder.finalize::(&stack.items); + while stack.pop() {} + stack.recorder.finalize(&stack.items); Ok(from) } @@ -855,8 +905,8 @@ enum TryStackChildResult { Halted, } -impl RecordStack { - fn try_stack_child<'a, L: TrieLayout>( +impl RecordStack { + fn try_stack_child<'a>( &mut self, child_index: u8, db: &TrieDB, @@ -967,7 +1017,7 @@ impl RecordStack { next_descended_child, is_inline, }; - if self.recorder.record_stacked_node(&infos, stack.len()) { + if self.recorder.record_stacked_node(&infos, stack.len(), child_index) { self.halt = true; } stack.push(infos); @@ -985,7 +1035,7 @@ impl RecordStack { } } - fn access_value<'a, L: TrieLayout>( + fn access_value<'a>( &mut self, db: &TrieDB, ) -> Result, CError>> { @@ -1026,17 +1076,17 @@ impl RecordStack { Ok(true) } - fn pop(&mut self) -> bool { + fn pop(&mut self) -> bool { if self.iter_prefix == Some(self.items.len()) { return false } if let Some(item) = self.items.pop() { - self.recorder.record_popped_node::(&item, self.items.len()); + self.recorder.record_popped_node(&item, self.items.len()); let depth = self.items.last().map(|i| i.depth).unwrap_or(0); self.prefix.drop_lasts(self.prefix.len() - depth); if depth == item.depth { // Two consecutive identical depth is an extension - self.pop::(); + self.pop(); } true } else { @@ -1995,7 +2045,7 @@ mod compact_content_proof { * encode) */ // Last call to pop is implicit (up to root), defining // one will result in an error. - // Two consecutive `KeyPush` are invalid. + // Two consecutive `KeyPop` are invalid. // TODO should be compact encoding of number. KeyPop(u16), // u8 is child index, shorthand for key push one nibble followed by key pop. diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 4cf31f1a..d5674445 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -266,11 +266,15 @@ fn test_query_plan_internal() { }; let db = >::new(&db, &root).with_cache(&mut cache).build(); - for kind in [ProofKind::CompactNodes, ProofKind::FullNodes] { - if kind == ProofKind::CompactNodes && L::USE_EXTENSION { + for kind in [ProofKind::CompactContent, ProofKind::CompactNodes, ProofKind::FullNodes] { + if (kind == ProofKind::CompactContent || kind == ProofKind::CompactNodes) && + L::USE_EXTENSION + { // Compact proofs are not supported with extensions. // Requires changing the way extension are handled // when decoding (putting on stack). + // Not implemented for `CompactContent`, would need + // to not append 0 after pushing an extension node. continue } let query_plans = [ @@ -338,6 +342,12 @@ fn test_query_plan_internal() { let mut full_proof: Vec> = Default::default(); proofs.reverse(); + + if kind == ProofKind::CompactContent { + // Decode not written + continue + } + let mut query_plan_iter: QueryPlan<_> = query_plan.as_ref(); let mut run_state: Option> = Some(query_plan_iter.into()); let mut has_run_full = false; From 3ff93b060e172cb88d488c5d6898fe248c396ba5 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 19 Apr 2023 12:35:26 +0200 Subject: [PATCH 038/154] write proof (need to access inline next) --- trie-db/src/query_plan.rs | 188 +++++++++++++++++++++++++++++++++----- 1 file changed, 165 insertions(+), 23 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index e601200a..b3986d95 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -41,6 +41,7 @@ use hash_db::Hasher; #[derive(Default)] pub struct InMemQueryPlanItem { key: Vec, + // hash_only: bool, TODO implement as_prefix: bool, } @@ -59,6 +60,7 @@ impl InMemQueryPlanItem { #[derive(Clone)] pub struct QueryPlanItem<'a> { key: &'a [u8], + // hash_only: bool, TODO implement as_prefix: bool, } @@ -193,6 +195,7 @@ impl Bitmap { } // TODO rename +#[derive(Clone)] struct CompactEncodingInfos { /// Node in memory content. node: OwnedNode, @@ -269,6 +272,7 @@ impl Recorder { true } } + /// Get back output handle from a recorder. pub fn output(self) -> O { match self.output { @@ -289,7 +293,8 @@ impl Recorder { ProofKind::FullNodes => RecorderStateInner::Stream(output), ProofKind::CompactNodes => RecorderStateInner::Compact { output, proof: Vec::new(), stacked_pos: Vec::new() }, - ProofKind::CompactContent => RecorderStateInner::Content { output, stacked_push: None }, + ProofKind::CompactContent => + RecorderStateInner::Content { output, stacked_push: None, stacked_pop: None }, }; let limits = Limits { remaining_node: limit_node, remaining_size: limit_size, kind }; Self { output, limits, start_at: None, _ph: PhantomData } @@ -301,6 +306,7 @@ impl Recorder { item: &CompactEncodingInfos, stack_pos: usize, parent_index: u8, + items: &Vec, ) -> bool { if !self.check_start_at(item.depth) { return false @@ -318,7 +324,8 @@ impl Recorder { stacked_pos.push(proof.len()); proof.push(Vec::new()); }, - RecorderStateInner::Content { output, stacked_push } => { + RecorderStateInner::Content { output, stacked_push, stacked_pop } => { + Self::flush_compact_content_pop(output, item.depth, items); if stacked_push.is_none() { *stacked_push = Some(NibbleVec::new()); } @@ -349,20 +356,44 @@ impl Recorder { // TODO actually should be unreachable return } - if let RecorderStateInner::Content { output, stacked_push } = &mut self.output { + if let RecorderStateInner::Content { output, stacked_push, .. } = &mut self.output { if let Some(buff) = stacked_push.take() { let mask = if buff.len() % 2 == 0 { 0xff } else { 0xf0 }; - let op = compact_content_proof::Op::, Vec>::KeyPush(buff.inner().to_vec(), mask); + let op = compact_content_proof::Op::, Vec>::KeyPush( + buff.inner().to_vec(), + mask, + ); use codec::Encode; output.write_bytes(&op.encode()); } } } - fn record_popped_node(&mut self, item: &CompactEncodingInfos, stack_pos: usize) { + fn flush_compact_content_pop(out: &mut O, from: usize, items: &Vec) { + let pop_to = items.last().map(|i| i.depth).unwrap_or(0); + assert!(from >= pop_to); + if from > pop_to && pop_to > 0 { + // Warning this implies key size limit of u16::max + let op = + compact_content_proof::Op::, Vec>::KeyPop((from - pop_to) as u16); + use codec::Encode; + out.write_bytes(&op.encode()); + } + } + + fn record_popped_node( + &mut self, + item: &CompactEncodingInfos, + items: &Vec, + ) { if !self.check_start_at(item.depth) { return } + let stack_pos = items.len(); + if let RecorderStateInner::Content { .. } = &self.output { + // if no value accessed, then we can have push then stack pop. + self.flush_compact_content_pushes(item.depth); + } match &mut self.output { RecorderStateInner::Stream(_) => (), @@ -377,8 +408,62 @@ impl Recorder { .expect("TODO error handling, can it actually fail?"); } // else when restarting record, this is not to be recorded }, - RecorderStateInner::Content { output, stacked_push } => { - unimplemented!() + RecorderStateInner::Content { output, stacked_pop, .. } => { + if stacked_pop.is_none() { + *stacked_pop = Some(item.depth); + } + // two case: children to register or all children accessed. + let mut has_hash_to_write = false; + let node_data = item.node.data(); + match item.node.node_plan() { + NodePlan::Branch { children, .. } | + NodePlan::NibbledBranch { children, .. } => + for i in 0..children.len() { + if children[i].is_some() && !item.accessed_children_node.at(i) { + has_hash_to_write = true; + break + } + }, + _ => (), + } + + if has_hash_to_write || stack_pos == 0 { + Self::flush_compact_content_pop(output, item.depth, items); + } + if !has_hash_to_write { + return + } + match item.node.node_plan() { + NodePlan::Branch { children, .. } | + NodePlan::NibbledBranch { children, .. } => { + for i in 0..children.len() { + if let Some(child) = &children[i] { + if !item.accessed_children_node.at(i) { + match child.build(node_data) { + NodeHandle::Hash(hash_slice) => { + let mut hash = TrieHash::::default(); + hash.as_mut().copy_from_slice(hash_slice); + let op = compact_content_proof::Op::< + TrieHash, + Vec, + >::HashChild( + compact_content_proof::Enc(hash), i as u8 + ); + use codec::Encode; + output.write_bytes(&op.encode()); + }, + NodeHandle::Inline(_) => { + unreachable!("Should be accessed"); // TODO all inline content with this proof + // requires to be part of the proof, even + // if unaccessed. + }, + } + } + } + } + }, + _ => (), + } }, } } @@ -391,7 +476,7 @@ impl Recorder { let mut res = false; if let RecorderStateInner::Content { .. } = &self.output { - self.flush_compact_content_pushes(depth); + self.flush_compact_content_pushes(depth); } match &mut self.output { RecorderStateInner::Stream(output) => { @@ -402,7 +487,7 @@ impl Recorder { res = self.limits.add_value(value.len()); proof.push(value.into()); }, - RecorderStateInner::Content { output, stacked_push } => { + RecorderStateInner::Content { output, .. } => { let op = compact_content_proof::Op::, Vec>::Value(value); use codec::Encode; output.write_bytes(&op.encode()); @@ -416,7 +501,7 @@ impl Recorder { return } if let RecorderStateInner::Content { .. } = &self.output { - self.flush_compact_content_pushes(depth); + self.flush_compact_content_pushes(depth); } match &mut self.output { @@ -424,7 +509,7 @@ impl Recorder { // not writing inline value (already // in parent node). }, - RecorderStateInner::Content { output, stacked_push } => { + RecorderStateInner::Content { output, .. } => { let op = compact_content_proof::Op::, &[u8]>::Value(value); use codec::Encode; output.write_bytes(&op.encode()); @@ -432,13 +517,58 @@ impl Recorder { } } + fn record_skip_value(&mut self, items: &Vec) { + let mut op = None; + if let RecorderStateInner::Content { .. } = &self.output { + if let Some(item) = items.last() { + assert!(!item.accessed_value_node); + if !self.check_start_at(item.depth) { + return + } + let node_data = item.node.data(); + + match item.node.node_plan() { + NodePlan::Leaf { value, .. } | + NodePlan::Branch { value: Some(value), .. } | + NodePlan::NibbledBranch { value: Some(value), .. } => { + op = Some(match value.build(node_data) { + Value::Node(hash_slice) => { + let mut hash = TrieHash::::default(); + hash.as_mut().copy_from_slice(hash_slice); + compact_content_proof::Op::<_, Vec>::HashValue( + compact_content_proof::Enc(hash), + ) + }, + Value::Inline(value) => + compact_content_proof::Op::, Vec>::Value( + value.to_vec(), + ), + }); + }, + _ => return, + } + + self.flush_compact_content_pushes(item.depth); + } + } + + if let Some(op) = op { + match &mut self.output { + RecorderStateInner::Content { output, .. } => { + use codec::Encode; + output.write_bytes(&op.encode()); + }, + _ => (), + } + } + } + fn finalize(&mut self, items: &Vec) { match &mut self.output { RecorderStateInner::Compact { output, proof, stacked_pos } => { let restarted_from = 0; if stacked_pos.len() > restarted_from { // halted: complete up to 0 and write all nodes keeping stack. - let at = stacked_pos.len(); let mut items = items.iter().rev(); while let Some(pos) = stacked_pos.pop() { loop { @@ -462,8 +592,14 @@ impl Recorder { RecorderStateInner::Stream(output) => { // all written }, - RecorderStateInner::Content { output, stacked_push } => { - unimplemented!() + RecorderStateInner::Content { output, stacked_push, stacked_pop } => { + assert!(stacked_push.is_none()); + // TODO could use function with &item and &[item] as param + // to skip this clone. + let mut items = items.clone(); + while let Some(item) = items.pop() { + self.record_popped_node(&item, &items); + } }, } } @@ -574,8 +710,10 @@ enum RecorderStateInner { /// For FullNodes proofs, just send node to this stream. Content { output: O, - // push from depth. + // push current todos. stacked_push: Option, + // pop from depth. + stacked_pop: Option, }, } @@ -783,14 +921,18 @@ pub fn record_query_plan< let mut slice_query = NibbleSlice::new_offset(&query.key, common_nibbles); let mut touched = false; loop { - if slice_query.is_empty() && !stack.items.is_empty() { - if query.as_prefix { - first_iter = true; - stack.enter_prefix_iter(); + if !stack.items.is_empty() { + if slice_query.is_empty() { + if query.as_prefix { + first_iter = true; + stack.enter_prefix_iter(); + } else { + touched = true; + } + break } else { - touched = true; + stack.recorder.record_skip_value(&stack.items); } - break } let child_index = if stack.items.is_empty() { 0 } else { slice_query.at(0) }; @@ -1017,7 +1159,7 @@ impl RecordStack { next_descended_child, is_inline, }; - if self.recorder.record_stacked_node(&infos, stack.len(), child_index) { + if self.recorder.record_stacked_node(&infos, stack.len(), child_index, &*stack) { self.halt = true; } stack.push(infos); @@ -1081,7 +1223,7 @@ impl RecordStack { return false } if let Some(item) = self.items.pop() { - self.recorder.record_popped_node(&item, self.items.len()); + self.recorder.record_popped_node(&item, &self.items); let depth = self.items.last().map(|i| i.depth).unwrap_or(0); self.prefix.drop_lasts(self.prefix.len() - depth); if depth == item.depth { From 8a28b5cc28634edc99f6a4be6a6d6fc9816a3365 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 19 Apr 2023 19:04:47 +0200 Subject: [PATCH 039/154] put iter out of main function --- trie-db/src/query_plan.rs | 290 +++++++++++++++++++++++++------------- trie-db/test/src/proof.rs | 4 +- 2 files changed, 192 insertions(+), 102 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index b3986d95..f9ad9ee5 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -165,6 +165,15 @@ pub enum ProofKind { CompactContent, } +impl ProofKind { + fn record_inline(&self) -> bool { + match self { + ProofKind::FullNodes | ProofKind::CompactNodes => false, + ProofKind::CompactContent => true, + } + } +} + #[derive(Default, Clone, Copy)] struct Bitmap(u16); @@ -836,6 +845,8 @@ pub fn record_query_plan< let mut stateless = false; let mut statefull = None; let mut depth_started_from = 0; + // When define we iter prefix in a node but really want the next non inline. + let mut inline_reg = None; if let Some(lower_bound) = from.from.take() { if from.currently_query_item.is_none() { stateless = true; @@ -855,7 +866,7 @@ pub fn record_query_plan< } } - let stack = &mut from.stack; + let mut stack = &mut from.stack; let mut prev_query: Option = None; /* @@ -916,127 +927,203 @@ pub fn record_query_plan< common_nibbles }; let mut first_iter = false; - if stack.iter_prefix.is_none() { - // descend - let mut slice_query = NibbleSlice::new_offset(&query.key, common_nibbles); - let mut touched = false; - loop { - if !stack.items.is_empty() { - if slice_query.is_empty() { - if query.as_prefix { - first_iter = true; - stack.enter_prefix_iter(); + if stack.iter_prefix.is_some() { + // statefull halted during iteration. + let (f, halt) = iter_prefix::(from, Some(&query), db, false, false)?; + if halt { + return Ok(f) + } else { + from = f; + stack = &mut from.stack; + from_query_ref = None; + } + from_query_ref = None; + prev_query = Some(query); + continue + } + // descend + let (mut slice_query, mut replay) = if let Some(restart) = inline_reg.take() { + restart + } else { + (NibbleSlice::new_offset(&query.key, common_nibbles), None) + }; + let mut touched = false; + loop { + if !stack.items.is_empty() { + if slice_query.is_empty() { + if query.as_prefix { + let (f, halt) = iter_prefix::(from, Some(&query), db, true, false)?; + if halt { + return Ok(f) } else { - touched = true; + from = f; + stack = &mut from.stack; + from_query_ref = None; } - break } else { - stack.recorder.record_skip_value(&stack.items); + touched = true; } + break + } else { + stack.recorder.record_skip_value(&stack.items); } + } - let child_index = if stack.items.is_empty() { 0 } else { slice_query.at(0) }; - match stack.try_stack_child( - child_index, - db, - dummy_parent_hash, - Some(&mut slice_query), - )? { - TryStackChildResult::Stacked => {}, - TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked => - break, - TryStackChildResult::StackedDescendIncomplete => { - if query.as_prefix { - first_iter = true; // TODO reorder to avoid this bool? - stack.enter_prefix_iter(); - } + let (pre, child_index) = if let Some(r) = replay.take() { + let child_index = if let Some(mut item) = stack.items.last_mut() { + if item.next_descended_child as usize >= NIBBLE_LENGTH - 1 { break - }, - TryStackChildResult::Halted => { - stack.halt = false; - stack.prefix.push(child_index); - from.from = Some(( - stack.prefix.inner().to_vec(), - (stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, - )); - stack.prefix.pop(); - from.currently_query_item = Some(query.to_owned()); - stack.recorder.finalize(&stack.items); - return Ok(from) - }, + } + item.next_descended_child += 1; + item.next_descended_child + } else { + break + }; + r + } else if stack.items.is_empty() { + (0, 0) + } else { + (0, slice_query.at(0)) + }; + if query_plan.kind.record_inline() { + for i in pre..child_index { + match stack.try_stack_child(i, db, dummy_parent_hash, None, true)? { + // only expect a stacked prefix here + TryStackChildResult::Stacked => { + let (f, halt) = + iter_prefix::(from, Some(&query), db, true, true)?; + if halt { + // no halt on inline. + unreachable!() + } else { + from = f; + stack = &mut from.stack; + from_query_ref = None; + } + }, + _ => (), + } } } - - if touched { - // try access value - stack.access_value(db)?; - touched = false; + match stack.try_stack_child( + child_index, + db, + dummy_parent_hash, + Some(&mut slice_query), + false, + )? { + TryStackChildResult::Stacked => {}, + TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked => break, + TryStackChildResult::StackedDescendIncomplete => { + if query.as_prefix { + let (f, halt) = iter_prefix::(from, Some(&query), db, true, false)?; + if halt { + return Ok(f) + } else { + from = f; + stack = &mut from.stack; + from_query_ref = None; + } + } + break + }, + TryStackChildResult::Halted => { + stack.halt = false; + stack.prefix.push(child_index); + from.from = Some(( + stack.prefix.inner().to_vec(), + (stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, + )); + stack.prefix.pop(); + from.currently_query_item = Some(query.to_owned()); + stack.recorder.finalize(&stack.items); + return Ok(from) + }, } } + + if touched { + // try access value + stack.access_value(db)?; + touched = false; + } from_query_ref = None; prev_query = Some(query); + } - if let Some(prefix_stack_depth) = stack.iter_prefix.clone() { - // run prefix iteration - let mut stacked = first_iter; - loop { - // descend - loop { - if stacked { - // try access value in next node - stack.access_value(db)?; - stacked = false; - } + while stack.pop() {} + stack.recorder.finalize(&stack.items); + Ok(from) +} - let child_index = if let Some(mut item) = stack.items.last_mut() { - if item.next_descended_child as usize >= NIBBLE_LENGTH { - break - } - item.next_descended_child += 1; - item.next_descended_child - 1 - } else { - break - }; +fn iter_prefix( + mut from: HaltedStateRecord, + prev_query: Option<&QueryPlanItem>, + db: &TrieDB, + first_iter: bool, + inline_iter: bool, +) -> Result<(HaltedStateRecord, bool), VerifyError, CError>> { + let stack = &mut from.stack; + let dummy_parent_hash = TrieHash::::default(); + if first_iter { + stack.enter_prefix_iter(); + } - match stack.try_stack_child(child_index, db, dummy_parent_hash, None)? { - TryStackChildResult::Stacked => { - stacked = true; - }, - TryStackChildResult::NotStackedBranch => (), - TryStackChildResult::NotStacked => break, - TryStackChildResult::StackedDescendIncomplete => { - unreachable!("no slice query") - }, - TryStackChildResult::Halted => { - if let Some(mut item) = stack.items.last_mut() { - item.next_descended_child -= 1; - } - stack.halt = false; - stack.prefix.push(child_index); - from.from = Some(( - stack.prefix.inner().to_vec(), - (stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, - )); - stack.prefix.pop(); - from.currently_query_item = prev_query.map(|q| q.to_owned()); - stack.recorder.finalize(&stack.items); - return Ok(from) - }, - } - } + // run prefix iteration + let mut stacked = first_iter; + loop { + // descend + loop { + if stacked { + // try access value in next node + stack.access_value(db)?; + stacked = false; + } - // pop - if !stack.pop() { + let child_index = if let Some(mut item) = stack.items.last_mut() { + if item.next_descended_child as usize >= NIBBLE_LENGTH { break } + item.next_descended_child += 1; + item.next_descended_child - 1 + } else { + break + }; + + match stack.try_stack_child(child_index, db, dummy_parent_hash, None, inline_iter)? { + TryStackChildResult::Stacked => { + stacked = true; + }, + TryStackChildResult::NotStackedBranch => (), + TryStackChildResult::NotStacked => break, + TryStackChildResult::StackedDescendIncomplete => { + unreachable!("no slice query") + }, + TryStackChildResult::Halted => { + if let Some(mut item) = stack.items.last_mut() { + item.next_descended_child -= 1; + } + stack.halt = false; + stack.prefix.push(child_index); + from.from = Some(( + stack.prefix.inner().to_vec(), + (stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, + )); + stack.prefix.pop(); + from.currently_query_item = prev_query.map(|q| q.to_owned()); + stack.recorder.finalize(&stack.items); + return Ok((from, true)) + }, } - stack.exit_prefix_iter(); } - } - while stack.pop() {} - stack.recorder.finalize(&stack.items); - Ok(from) + // pop + if !stack.pop() { + break + } + } + stack.exit_prefix_iter(); + Ok((from, false)) } enum TryStackChildResult { @@ -1054,6 +1141,7 @@ impl RecordStack { db: &TrieDB, parent_hash: TrieHash, mut slice_query: Option<&mut NibbleSlice>, + inline_only: bool, ) -> Result, CError>> { let mut is_inline = false; let prefix = &mut self.prefix; @@ -1164,7 +1252,7 @@ impl RecordStack { } stack.push(infos); if stack_extension { - let sbranch = self.try_stack_child(0, db, parent_hash, slice_query)?; + let sbranch = self.try_stack_child(0, db, parent_hash, slice_query, inline_only)?; let TryStackChildResult::Stacked = sbranch else { return Err(VerifyError::InvalidChildReference(b"branch in db should follow extension".to_vec())); }; diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index d5674445..a90cabc3 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -266,7 +266,9 @@ fn test_query_plan_internal() { }; let db = >::new(&db, &root).with_cache(&mut cache).build(); - for kind in [ProofKind::CompactContent, ProofKind::CompactNodes, ProofKind::FullNodes] { + for kind in + [/* ProofKind::CompactContent, */ ProofKind::CompactNodes, ProofKind::FullNodes] + { if (kind == ProofKind::CompactContent || kind == ProofKind::CompactNodes) && L::USE_EXTENSION { From d6bf22e2f4397124647b17a88f24619abe1c45e5 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 19 Apr 2023 19:46:56 +0200 Subject: [PATCH 040/154] access inline (broken) --- trie-db/src/query_plan.rs | 93 +++++++++++++++++++++++++++------------ trie-db/test/src/proof.rs | 4 +- 2 files changed, 67 insertions(+), 30 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index f9ad9ee5..4885ce6f 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -846,7 +846,6 @@ pub fn record_query_plan< let mut statefull = None; let mut depth_started_from = 0; // When define we iter prefix in a node but really want the next non inline. - let mut inline_reg = None; if let Some(lower_bound) = from.from.take() { if from.currently_query_item.is_none() { stateless = true; @@ -917,11 +916,42 @@ pub fn record_query_plan< loop { match stack.prefix.len().cmp(&common_nibbles) { Ordering::Equal | Ordering::Less => break, - Ordering::Greater => + Ordering::Greater => { + if query_plan.kind.record_inline() { + if let Some(item) = stack.items.last() { + let pre = item.next_descended_child + 1; + for i in pre..NIBBLE_LENGTH as u8 { + match stack.try_stack_child( + i, + db, + dummy_parent_hash, + None, + true, + )? { + // only expect a stacked prefix here + TryStackChildResult::Stacked => { + let (f, halt) = + iter_prefix::(from, None, db, true, true)?; + if halt { + // no halt on inline. + unreachable!() + } else { + from = f; + stack = &mut from.stack; + from_query_ref = None; + stack.pop(); + } + }, + _ => (), + } + } + } + } if !stack.pop() { stack.recorder.finalize(&stack.items); return Ok(from) - }, + } + }, } } common_nibbles @@ -942,11 +972,7 @@ pub fn record_query_plan< continue } // descend - let (mut slice_query, mut replay) = if let Some(restart) = inline_reg.take() { - restart - } else { - (NibbleSlice::new_offset(&query.key, common_nibbles), None) - }; + let mut slice_query = NibbleSlice::new_offset(&query.key, common_nibbles); let mut touched = false; loop { if !stack.items.is_empty() { @@ -969,29 +995,14 @@ pub fn record_query_plan< } } - let (pre, child_index) = if let Some(r) = replay.take() { - let child_index = if let Some(mut item) = stack.items.last_mut() { - if item.next_descended_child as usize >= NIBBLE_LENGTH - 1 { - break - } - item.next_descended_child += 1; - item.next_descended_child - } else { - break - }; - r - } else if stack.items.is_empty() { - (0, 0) - } else { - (0, slice_query.at(0)) - }; + let child_index = if stack.items.is_empty() { 0 } else { slice_query.at(0) }; if query_plan.kind.record_inline() { + let pre = stack.items.last().map(|i| i.next_descended_child + 1).unwrap_or(0); for i in pre..child_index { match stack.try_stack_child(i, db, dummy_parent_hash, None, true)? { // only expect a stacked prefix here TryStackChildResult::Stacked => { - let (f, halt) = - iter_prefix::(from, Some(&query), db, true, true)?; + let (f, halt) = iter_prefix::(from, None, db, true, true)?; if halt { // no halt on inline. unreachable!() @@ -999,6 +1010,7 @@ pub fn record_query_plan< from = f; stack = &mut from.stack; from_query_ref = None; + stack.pop(); } }, _ => (), @@ -1050,8 +1062,35 @@ pub fn record_query_plan< from_query_ref = None; prev_query = Some(query); } + loop { + if query_plan.kind.record_inline() { + if let Some(item) = stack.items.last() { + let pre = item.next_descended_child + 1; + for i in pre..NIBBLE_LENGTH as u8 { + match stack.try_stack_child(i, db, dummy_parent_hash, None, true)? { + // only expect a stacked prefix here + TryStackChildResult::Stacked => { + let (f, halt) = iter_prefix::(from, None, db, true, true)?; + if halt { + // no halt on inline. + unreachable!() + } else { + from = f; + stack = &mut from.stack; + from_query_ref = None; + stack.pop(); + } + }, + _ => (), + } + } + } + } - while stack.pop() {} + if !stack.pop() { + break + } + } stack.recorder.finalize(&stack.items); Ok(from) } diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index a90cabc3..d5674445 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -266,9 +266,7 @@ fn test_query_plan_internal() { }; let db = >::new(&db, &root).with_cache(&mut cache).build(); - for kind in - [/* ProofKind::CompactContent, */ ProofKind::CompactNodes, ProofKind::FullNodes] - { + for kind in [ProofKind::CompactContent, ProofKind::CompactNodes, ProofKind::FullNodes] { if (kind == ProofKind::CompactContent || kind == ProofKind::CompactNodes) && L::USE_EXTENSION { From f5bb14677770606a4c26cabd561fa6f9df11eca7 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 19 Apr 2023 20:05:48 +0200 Subject: [PATCH 041/154] initial code --- test-support/reference-trie/src/lib.rs | 1 + trie-db/src/query_plan.rs | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index be626801..0bd37417 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -51,6 +51,7 @@ macro_rules! test_layouts { ($test:ident, $test_internal:ident) => { #[test] fn $test() { + $test_internal::<$crate::HashedValueNoExt>(); // TODO rem eprintln!("Running with layout `HashedValueNoExtThreshold`"); $test_internal::<$crate::HashedValueNoExtThreshold<1>>(); eprintln!("Running with layout `HashedValueNoExt`"); diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 4885ce6f..fd5b3289 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -462,9 +462,7 @@ impl Recorder { output.write_bytes(&op.encode()); }, NodeHandle::Inline(_) => { - unreachable!("Should be accessed"); // TODO all inline content with this proof - // requires to be part of the proof, even - // if unaccessed. + // As been accessed if needed (inline are not mark). }, } } From 6b3244c8df51f494b0d3144ce102bf1ccefe1511 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 28 Apr 2023 18:13:51 +0200 Subject: [PATCH 042/154] TODO hash only access to implement and test --- trie-db/src/query_plan.rs | 15 ++++++++++----- trie-db/test/src/proof.rs | 18 +++++++++++------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index fd5b3289..4093e35f 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -41,18 +41,19 @@ use hash_db::Hasher; #[derive(Default)] pub struct InMemQueryPlanItem { key: Vec, + hash_only: bool, // hash_only: bool, TODO implement as_prefix: bool, } impl InMemQueryPlanItem { /// Create new item. - pub fn new(key: Vec, as_prefix: bool) -> Self { - Self { key, as_prefix } + pub fn new(key: Vec, hash_only: bool, as_prefix: bool) -> Self { + Self { key, hash_only, as_prefix } } /// Get ref. pub fn as_ref(&self) -> QueryPlanItem { - QueryPlanItem { key: &self.key, as_prefix: self.as_prefix } + QueryPlanItem { key: &self.key, hash_only: self.hash_only, as_prefix: self.as_prefix } } } @@ -60,7 +61,7 @@ impl InMemQueryPlanItem { #[derive(Clone)] pub struct QueryPlanItem<'a> { key: &'a [u8], - // hash_only: bool, TODO implement + hash_only: bool, as_prefix: bool, } @@ -85,7 +86,11 @@ impl<'a> QueryPlanItem<'a> { } fn to_owned(&self) -> InMemQueryPlanItem { - InMemQueryPlanItem { key: self.key.to_vec(), as_prefix: self.as_prefix } + InMemQueryPlanItem { + key: self.key.to_vec(), + hash_only: self.hash_only, + as_prefix: self.as_prefix, + } } } diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index d5674445..a5ffb3b6 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -266,7 +266,11 @@ fn test_query_plan_internal() { }; let db = >::new(&db, &root).with_cache(&mut cache).build(); - for kind in [ProofKind::CompactContent, ProofKind::CompactNodes, ProofKind::FullNodes] { + for (hash_only, kind) in [ + (false, ProofKind::CompactContent), + (false, ProofKind::CompactNodes), + (false, ProofKind::FullNodes), + ] { if (kind == ProofKind::CompactContent || kind == ProofKind::CompactNodes) && L::USE_EXTENSION { @@ -279,23 +283,23 @@ fn test_query_plan_internal() { } let query_plans = [ InMemQueryPlan { - items: vec![InMemQueryPlanItem::new(b"".to_vec(), true)], + items: vec![InMemQueryPlanItem::new(b"".to_vec(), hash_only, true)], ignore_unordered: false, kind, }, InMemQueryPlan { items: vec![ - InMemQueryPlanItem::new(b"bravo".to_vec(), false), - InMemQueryPlanItem::new(b"do".to_vec(), true), + InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), + InMemQueryPlanItem::new(b"do".to_vec(), hash_only, true), ], ignore_unordered: false, kind, }, InMemQueryPlan { items: vec![ - InMemQueryPlanItem::new(b"bravo".to_vec(), false), - InMemQueryPlanItem::new(b"doge".to_vec(), false), - InMemQueryPlanItem::new(b"horsey".to_vec(), false), + InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), + InMemQueryPlanItem::new(b"doge".to_vec(), hash_only, false), + InMemQueryPlanItem::new(b"horsey".to_vec(), hash_only, false), ], ignore_unordered: false, kind, From 2b8403af5a7e92bd6bc370027f9264b958f9a45c Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 2 May 2023 11:28:45 +0200 Subject: [PATCH 043/154] pass hash only to access value --- trie-db/src/query_plan.rs | 98 +++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 51 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 4093e35f..4721168e 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -332,13 +332,13 @@ impl Recorder { res = self.limits.add_node(item.node.data().len()); output.write_entry(item.node.data().into()); }, - RecorderStateInner::Compact { output, proof, stacked_pos } => + RecorderStateInner::Compact { output: _, proof, stacked_pos } => if !item.is_inline { res = self.limits.add_node(item.node.data().len()); stacked_pos.push(proof.len()); proof.push(Vec::new()); }, - RecorderStateInner::Content { output, stacked_push, stacked_pop } => { + RecorderStateInner::Content { output, stacked_push, stacked_pop: _ } => { Self::flush_compact_content_pop(output, item.depth, items); if stacked_push.is_none() { *stacked_push = Some(NibbleVec::new()); @@ -495,7 +495,7 @@ impl Recorder { res = self.limits.add_value(value.len()); output.write_entry(value.into()); }, - RecorderStateInner::Compact { output, proof, stacked_pos } => { + RecorderStateInner::Compact { output: _, proof, stacked_pos: _ } => { res = self.limits.add_value(value.len()); proof.push(value.into()); }, @@ -529,6 +529,8 @@ impl Recorder { } } + // TODO this should be call also in all node (not only when not finding value): + // then could just be part of enter node? fn record_skip_value(&mut self, items: &Vec) { let mut op = None; if let RecorderStateInner::Content { .. } = &self.output { @@ -601,10 +603,10 @@ impl Recorder { output.write_entry(entry.into()); } }, - RecorderStateInner::Stream(output) => { + RecorderStateInner::Stream(_output) => { // all written }, - RecorderStateInner::Content { output, stacked_push, stacked_pop } => { + RecorderStateInner::Content { output: _, stacked_push, stacked_pop: _ } => { assert!(stacked_push.is_none()); // TODO could use function with &item and &[item] as param // to skip this clone. @@ -707,7 +709,6 @@ impl Limits { } } -// TODO may be useless enum RecorderStateInner { /// For FullNodes proofs, just send node to this stream. Stream(O), @@ -822,7 +823,7 @@ struct RecordStack { recorder: Recorder, items: Vec, prefix: NibbleVec, - iter_prefix: Option, + iter_prefix: Option<(usize, bool)>, seek: Option, halt: bool, } @@ -847,7 +848,6 @@ pub fn record_query_plan< let dummy_parent_hash = TrieHash::::default(); let mut stateless = false; let mut statefull = None; - let mut depth_started_from = 0; // When define we iter prefix in a node but really want the next non inline. if let Some(lower_bound) = from.from.take() { if from.currently_query_item.is_none() { @@ -858,7 +858,6 @@ pub fn record_query_plan< bound.pop(); } from.stack.recorder.start_at = Some(bound.len()); - depth_started_from = bound.len(); from.stack.seek = Some(bound); } else { let bound_len = lower_bound.0.len() * nibble_ops::NIBBLE_PER_BYTE - @@ -871,16 +870,6 @@ pub fn record_query_plan< let mut stack = &mut from.stack; let mut prev_query: Option = None; - /* - let mut prev_query: Option = if stateless { - Some(QueryPlanItem { - key: restore_buf2.as_slice(), - as_prefix: false, // not checked only to advance. - }) - } else { - None - }; - */ let from_query = from.currently_query_item.take(); let mut from_query_ref = from_query.as_ref().map(|f| f.as_ref()); while let Some(query) = from_query_ref.clone().or_else(|| query_plan.items.next()) { @@ -903,7 +892,6 @@ pub fn record_query_plan< } } let common_nibbles = if let Some(slice_at) = statefull.take() { - depth_started_from = slice_at + 1; slice_at } else { let (ordered, common_nibbles) = @@ -912,8 +900,7 @@ pub fn record_query_plan< if query_plan.ignore_unordered { continue } else { - return Err(VerifyError::UnorderedKey(query.key.to_vec())) // TODO not kind as param if keeping - // CompactContent + return Err(VerifyError::UnorderedKey(query.key.to_vec())) } } loop { @@ -933,15 +920,15 @@ pub fn record_query_plan< )? { // only expect a stacked prefix here TryStackChildResult::Stacked => { - let (f, halt) = - iter_prefix::(from, None, db, true, true)?; + let (f, halt) = iter_prefix::( + from, None, db, false, true, true, + )?; if halt { // no halt on inline. unreachable!() } else { from = f; stack = &mut from.stack; - from_query_ref = None; stack.pop(); } }, @@ -959,16 +946,14 @@ pub fn record_query_plan< } common_nibbles }; - let mut first_iter = false; - if stack.iter_prefix.is_some() { + if let Some((_, hash_only)) = stack.iter_prefix.clone() { // statefull halted during iteration. - let (f, halt) = iter_prefix::(from, Some(&query), db, false, false)?; + let (f, halt) = iter_prefix::(from, Some(&query), db, hash_only, false, false)?; if halt { return Ok(f) } else { from = f; stack = &mut from.stack; - from_query_ref = None; } from_query_ref = None; prev_query = Some(query); @@ -976,23 +961,28 @@ pub fn record_query_plan< } // descend let mut slice_query = NibbleSlice::new_offset(&query.key, common_nibbles); - let mut touched = false; - loop { + let touched = loop { if !stack.items.is_empty() { if slice_query.is_empty() { if query.as_prefix { - let (f, halt) = iter_prefix::(from, Some(&query), db, true, false)?; + let (f, halt) = iter_prefix::( + from, + Some(&query), + db, + query.hash_only, + true, + false, + )?; if halt { return Ok(f) } else { from = f; stack = &mut from.stack; - from_query_ref = None; } + break false } else { - touched = true; + break true } - break } else { stack.recorder.record_skip_value(&stack.items); } @@ -1005,14 +995,13 @@ pub fn record_query_plan< match stack.try_stack_child(i, db, dummy_parent_hash, None, true)? { // only expect a stacked prefix here TryStackChildResult::Stacked => { - let (f, halt) = iter_prefix::(from, None, db, true, true)?; + let (f, halt) = iter_prefix::(from, None, db, false, true, true)?; if halt { // no halt on inline. unreachable!() } else { from = f; stack = &mut from.stack; - from_query_ref = None; stack.pop(); } }, @@ -1028,19 +1017,26 @@ pub fn record_query_plan< false, )? { TryStackChildResult::Stacked => {}, - TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked => break, + TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked => + break false, TryStackChildResult::StackedDescendIncomplete => { if query.as_prefix { - let (f, halt) = iter_prefix::(from, Some(&query), db, true, false)?; + let (f, halt) = iter_prefix::( + from, + Some(&query), + db, + query.hash_only, + true, + false, + )?; if halt { return Ok(f) } else { from = f; stack = &mut from.stack; - from_query_ref = None; } } - break + break false }, TryStackChildResult::Halted => { stack.halt = false; @@ -1055,12 +1051,11 @@ pub fn record_query_plan< return Ok(from) }, } - } + }; if touched { // try access value - stack.access_value(db)?; - touched = false; + stack.access_value(db, query.hash_only)?; } from_query_ref = None; prev_query = Some(query); @@ -1073,14 +1068,13 @@ pub fn record_query_plan< match stack.try_stack_child(i, db, dummy_parent_hash, None, true)? { // only expect a stacked prefix here TryStackChildResult::Stacked => { - let (f, halt) = iter_prefix::(from, None, db, true, true)?; + let (f, halt) = iter_prefix::(from, None, db, false, true, true)?; if halt { // no halt on inline. unreachable!() } else { from = f; stack = &mut from.stack; - from_query_ref = None; stack.pop(); } }, @@ -1102,13 +1096,14 @@ fn iter_prefix( mut from: HaltedStateRecord, prev_query: Option<&QueryPlanItem>, db: &TrieDB, + hash_only: bool, first_iter: bool, inline_iter: bool, ) -> Result<(HaltedStateRecord, bool), VerifyError, CError>> { let stack = &mut from.stack; let dummy_parent_hash = TrieHash::::default(); if first_iter { - stack.enter_prefix_iter(); + stack.enter_prefix_iter(hash_only); } // run prefix iteration @@ -1118,7 +1113,7 @@ fn iter_prefix( loop { if stacked { // try access value in next node - stack.access_value(db)?; + stack.access_value(db, hash_only)?; stacked = false; } @@ -1310,6 +1305,7 @@ impl RecordStack { fn access_value<'a>( &mut self, db: &TrieDB, + hash_only: bool, ) -> Result, CError>> { let Some(item)= self.items.last_mut() else { return Ok(false) @@ -1349,7 +1345,7 @@ impl RecordStack { } fn pop(&mut self) -> bool { - if self.iter_prefix == Some(self.items.len()) { + if self.iter_prefix.map(|(l, _)| l == self.items.len()).unwrap_or(false) { return false } if let Some(item) = self.items.pop() { @@ -1366,8 +1362,8 @@ impl RecordStack { } } - fn enter_prefix_iter(&mut self) { - self.iter_prefix = Some(self.items.len()); + fn enter_prefix_iter(&mut self, hash_only: bool) { + self.iter_prefix = Some((self.items.len(), hash_only)); } fn exit_prefix_iter(&mut self) { From 60d0268dfcdd072639e3fc204a4baebff9a69904 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 2 May 2023 11:40:57 +0200 Subject: [PATCH 044/154] skip record in favor of hash --- trie-db/src/query_plan.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 4721168e..92b58c91 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -1326,17 +1326,20 @@ impl RecordStack { _ => return Ok(false), }; match value { - Value::Node(hash_slice) => { - item.accessed_value_node = true; - let mut hash = TrieHash::::default(); - hash.as_mut().copy_from_slice(hash_slice); - let Some(value) = db.db().get(&hash, self.prefix.as_prefix()) else { - return Err(VerifyError::IncompleteProof); - }; - if self.recorder.record_value_node(value, self.prefix.len()) { - self.halt = true; - } - }, + Value::Node(hash_slice) => + if !hash_only { + item.accessed_value_node = true; + let mut hash = TrieHash::::default(); + hash.as_mut().copy_from_slice(hash_slice); + let Some(value) = db.db().get(&hash, self.prefix.as_prefix()) else { + return Err(VerifyError::IncompleteProof); + }; + if self.recorder.record_value_node(value, self.prefix.len()) { + self.halt = true; + } + } else { + self.recorder.record_skip_value(&self.items); + }, Value::Inline(value) => { self.recorder.record_value_inline(value, self.prefix.len()); }, From e1277be8c0703e3803de5bdd4cff9ffb235466b2 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 2 May 2023 16:35:50 +0200 Subject: [PATCH 045/154] hash --- test-support/reference-trie/src/lib.rs | 1 - trie-db/src/query_plan.rs | 76 ++++++++++++++++++-------- trie-db/test/src/proof.rs | 8 +++ 3 files changed, 62 insertions(+), 23 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 0bd37417..be626801 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -51,7 +51,6 @@ macro_rules! test_layouts { ($test:ident, $test_internal:ident) => { #[test] fn $test() { - $test_internal::<$crate::HashedValueNoExt>(); // TODO rem eprintln!("Running with layout `HashedValueNoExtThreshold`"); $test_internal::<$crate::HashedValueNoExtThreshold<1>>(); eprintln!("Running with layout `HashedValueNoExt`"); diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 92b58c91..77dfbe3b 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -98,6 +98,7 @@ impl<'a> QueryPlanItem<'a> { pub struct InMemQueryPlan { pub items: Vec, pub kind: ProofKind, + // TODO rem pub ignore_unordered: bool, } @@ -1510,7 +1511,8 @@ impl ItemStack { struct ReadStack { items: Vec>, prefix: NibbleVec, - iter_prefix: Option<(usize, bool)>, // limit and wether we return value + // limit and wether we return value and if hash only iteration. + iter_prefix: Option<(usize, bool, bool)>, start_items: usize, is_compact: bool, expect_value: bool, @@ -1521,8 +1523,8 @@ impl Clone for ReadStack { fn clone(&self) -> Self { ReadStack { items: self.items.clone(), - start_items: self.start_items.clone(), prefix: self.prefix.clone(), + start_items: self.start_items.clone(), iter_prefix: self.iter_prefix, is_compact: self.is_compact, expect_value: self.expect_value, @@ -1767,7 +1769,8 @@ impl ReadStack { &mut self, proof: &mut impl Iterator, check_hash: bool, - ) -> Result>, VerifyError, CError>> { + hash_only: bool, + ) -> Result<(Option>, Option>), VerifyError, CError>> { if let Some(node) = self.items.last() { let node_data = node.data(); @@ -1775,7 +1778,7 @@ impl ReadStack { NodePlan::Leaf { value, .. } => Some(value.build(node_data)), NodePlan::Branch { value, .. } | NodePlan::NibbledBranch { value, .. } => value.as_ref().map(|v| v.build(node_data)), - _ => return Ok(None), + _ => return Ok((None, None)), }; if let Some(value) = value { match value { @@ -1783,6 +1786,10 @@ impl ReadStack { if self.expect_value { assert!(self.is_compact); self.expect_value = false; + if hash_only { + return Err(VerifyError::ExtraneousValue(Default::default())) + } + let Some(value) = proof.next() else { return Err(VerifyError::IncompleteProof); }; @@ -1790,23 +1797,36 @@ impl ReadStack { let hash = L::Hash::hash(value.borrow()); self.items.last_mut().map(|i| i.attached_value_hash = Some(hash)); } - return Ok(Some(value.borrow().to_vec())) + return Ok((Some(value.borrow().to_vec()), None)) } else { - return Ok(Some(value.to_vec())) + if hash_only { + let hash = L::Hash::hash(value.borrow()); + return Ok((None, Some(hash))) + } + return Ok((Some(value.to_vec()), None)) }, Value::Node(hash) => { if self.expect_value { + if hash_only { + return Err(VerifyError::ExtraneousValue(Default::default())) + } + self.expect_value = false; let mut error_hash = TrieHash::::default(); error_hash.as_mut().copy_from_slice(hash); return Err(VerifyError::ExtraneousHashReference(error_hash)) } + if hash_only { + let mut result_hash = TrieHash::::default(); + result_hash.as_mut().copy_from_slice(hash); + return Ok((None, Some(result_hash))) + } let Some(value) = proof.next() else { return Err(VerifyError::IncompleteProof); }; if check_hash { verify_hash::(value.borrow(), hash)?; } - return Ok(Some(value.borrow().to_vec())) + return Ok((Some(value.borrow().to_vec()), None)) }, } } @@ -1814,7 +1834,7 @@ impl ReadStack { return Err(VerifyError::IncompleteProof) } - Ok(None) + Ok((None, None)) } fn pop( @@ -1955,8 +1975,8 @@ impl ReadStack { Err(VerifyError::ExtraneousNode) } - fn enter_prefix_iter(&mut self) { - self.iter_prefix = Some((self.items.len(), false)); + fn enter_prefix_iter(&mut self, hash_only: bool) { + self.iter_prefix = Some((self.items.len(), false, hash_only)); } fn exit_prefix_iter(&mut self) { @@ -1969,9 +1989,11 @@ pub enum ReadProofItem<'a, L: TrieLayout, C, D: SplitFirst> { /// Successfull read of proof, not all content read. Halted(Box>), /// Seen value and key in proof. - /// When we set the query plan, we only return content - /// matching the query plan. + /// We only return content matching the query plan. Value(Cow<'a, [u8]>, Vec), + /// Seen hash of value and key in proof. + /// We only return content matching the query plan. + Hash(Cow<'a, [u8]>, TrieHash), /// No value seen for a key in the input query plan. NoValue(&'a [u8]), /// Seen fully covered prefix in proof, this is only @@ -2045,19 +2067,25 @@ where } }; let did_prefix = self.stack.iter_prefix.is_some(); - while let Some((_, accessed_value_node)) = self.stack.iter_prefix.clone() { + while let Some((_, accessed_value_node, hash_only)) = self.stack.iter_prefix.clone() { // prefix iteration if !accessed_value_node { self.stack.iter_prefix.as_mut().map(|s| { s.1 = true; }); - match self.stack.access_value(&mut self.proof, check_hash) { - Ok(Some(value)) => + match self.stack.access_value(&mut self.proof, check_hash, hash_only) { + Ok((Some(value), None)) => return Some(Ok(ReadProofItem::Value( self.stack.prefix.inner().to_vec().into(), value, ))), - Ok(None) => (), + Ok((None, Some(hash))) => + return Some(Ok(ReadProofItem::Hash( + self.stack.prefix.inner().to_vec().into(), + hash, + ))), + Ok((None, None)) => (), + Ok(_) => unreachable!(), Err(e) => { self.state = ReadProofState::Finished; return Some(Err(e)) @@ -2127,7 +2155,8 @@ where let to_check = self.current.as_ref().expect("Init above"); let to_check_len = to_check.key.len() * nibble_ops::NIBBLE_PER_BYTE; let mut to_check_slice = to_check_slice.as_mut().expect("Init above"); - let as_prefix = to_check.as_prefix; + let as_prefix = to_check.as_prefix; // TODO useless? + let hash_only = to_check.hash_only; // TODO useless? let mut at_value = false; match self.stack.prefix.len().cmp(&to_check_len) { Ordering::Equal => @@ -2142,14 +2171,17 @@ where if at_value { if as_prefix { - self.stack.enter_prefix_iter(); + self.stack.enter_prefix_iter(hash_only); continue } self.state = ReadProofState::SwitchQueryPlan; - match self.stack.access_value(&mut self.proof, check_hash) { - Ok(Some(value)) => + match self.stack.access_value(&mut self.proof, check_hash, hash_only) { + Ok((Some(value), None)) => return Some(Ok(ReadProofItem::Value(to_check.key.into(), value))), - Ok(None) => return Some(Ok(ReadProofItem::NoValue(to_check.key))), + Ok((None, Some(hash))) => + return Some(Ok(ReadProofItem::Hash(to_check.key.into(), hash))), + Ok((None, None)) => return Some(Ok(ReadProofItem::NoValue(to_check.key))), + Ok(_) => unreachable!(), Err(e) => { self.state = ReadProofState::Finished; return Some(Err(e)) @@ -2180,7 +2212,7 @@ where TryStackChildResult::Stacked => (), TryStackChildResult::StackedDescendIncomplete => { if as_prefix { - self.stack.enter_prefix_iter(); + self.stack.enter_prefix_iter(hash_only); continue } self.state = ReadProofState::SwitchQueryPlan; diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index a5ffb3b6..4fbee87e 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -267,6 +267,7 @@ fn test_query_plan_internal() { let db = >::new(&db, &root).with_cache(&mut cache).build(); for (hash_only, kind) in [ + (true, ProofKind::FullNodes), (false, ProofKind::CompactContent), (false, ProofKind::CompactNodes), (false, ProofKind::FullNodes), @@ -377,6 +378,13 @@ fn test_query_plan_internal() { let mut halted = false; for item in verify_iter { match item.unwrap() { + ReadProofItem::Hash(key, hash) => { + assert!(hash_only); + assert_eq!( + content.get(&*key).map(|v| L::Hash::hash(&v.as_ref())), + Some(hash) + ); + }, ReadProofItem::Value(key, value) => { assert_eq!(content.get(&*key), Some(&value.as_ref())); }, From b35169ba6acbd4e7e0e1f222fe38f353e54b6499 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 2 May 2023 19:24:52 +0200 Subject: [PATCH 046/154] test full iter batch --- trie-db/src/query_plan.rs | 32 ++++++++++++------- trie-db/test/src/proof.rs | 66 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 82 insertions(+), 16 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 77dfbe3b..6fb839ad 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -249,13 +249,13 @@ pub struct InMemoryRecorder { impl RecorderOutput for InMemoryRecorder { fn write_bytes(&mut self, bytes: &[u8]) { - if !self.buffer.is_empty() { - self.nodes.push(core::mem::take(&mut self.buffer)); - } self.buffer.extend_from_slice(bytes) } fn write_entry(&mut self, bytes: Cow<[u8]>) { + if !self.buffer.is_empty() { + self.nodes.push(core::mem::take(&mut self.buffer)); + } self.nodes.push(bytes.into_owned()); } } @@ -339,8 +339,8 @@ impl Recorder { stacked_pos.push(proof.len()); proof.push(Vec::new()); }, - RecorderStateInner::Content { output, stacked_push, stacked_pop: _ } => { - Self::flush_compact_content_pop(output, item.depth, items); + RecorderStateInner::Content { output, stacked_push, stacked_pop } => { + Self::flush_compact_content_pop2(output, stacked_pop, items); if stacked_push.is_none() { *stacked_push = Some(NibbleVec::new()); } @@ -384,10 +384,18 @@ impl Recorder { } } - fn flush_compact_content_pop(out: &mut O, from: usize, items: &Vec) { + fn flush_compact_content_pop2( + out: &mut O, + stacked_from: &mut Option, + items: &Vec, + ) { + let Some(from) = stacked_from.take() else { + return + }; let pop_to = items.last().map(|i| i.depth).unwrap_or(0); - assert!(from >= pop_to); - if from > pop_to && pop_to > 0 { + debug_assert!(from > pop_to); + if pop_to > 0 { + debug_assert!(from - pop_to <= u16::max_value() as usize); // Warning this implies key size limit of u16::max let op = compact_content_proof::Op::, Vec>::KeyPop((from - pop_to) as u16); @@ -443,7 +451,7 @@ impl Recorder { } if has_hash_to_write || stack_pos == 0 { - Self::flush_compact_content_pop(output, item.depth, items); + Self::flush_compact_content_pop2(output, stacked_pop, items); } if !has_hash_to_write { return @@ -2331,15 +2339,15 @@ where }) } -mod compact_content_proof { +pub mod compact_content_proof { - use codec::{Decode, Encode}; + pub use codec::{Decode, Encode}; /// Representation of each encoded action /// for building the proof. /// TODO ref variant for encoding ?? #[derive(Encode, Decode, Debug)] - pub(crate) enum Op { + pub enum Op { // key content followed by a mask for last byte. // If mask erase some content the content need to // be set at 0 (or error). diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 4fbee87e..d89b0e4e 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -267,9 +267,10 @@ fn test_query_plan_internal() { let db = >::new(&db, &root).with_cache(&mut cache).build(); for (hash_only, kind) in [ - (true, ProofKind::FullNodes), (false, ProofKind::CompactContent), + (true, ProofKind::FullNodes), (false, ProofKind::CompactNodes), + (true, ProofKind::CompactNodes), (false, ProofKind::FullNodes), ] { if (kind == ProofKind::CompactContent || kind == ProofKind::CompactNodes) && @@ -306,7 +307,7 @@ fn test_query_plan_internal() { kind, }, ]; - for query_plan in query_plans { + for (nb_plan, query_plan) in query_plans.iter().enumerate() { for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] { let limit = limit_conf.0; @@ -323,7 +324,11 @@ fn test_query_plan_internal() { assert!(from.is_finished()); } if from.is_finished() { - proofs.push(from.finish().output().nodes); + if kind == ProofKind::CompactContent { + proofs.push(vec![from.finish().output().buffer]); + } else { + proofs.push(from.finish().output().nodes); + } break } let rec = if limit_conf.1 { @@ -342,13 +347,66 @@ fn test_query_plan_internal() { None, )) }; - proofs.push(rec.output().nodes); + if kind == ProofKind::CompactContent { + proofs.push(vec![rec.output().buffer]); + } else { + proofs.push(rec.output().nodes); + } } let mut full_proof: Vec> = Default::default(); proofs.reverse(); + fn shifted(bytes: &[u8], aligned: bool) -> Vec { + let mut shifted: Vec = vec![]; + let last = bytes.len(); + bytes.iter().enumerate().for_each(|(i, b)| { + shifted.last_mut().map(|l| { + *l |= *b >> 4; + }); + if !(i == last - 1 && aligned) { + shifted.push(*b << 4); + } + }); + shifted + } + if kind == ProofKind::CompactContent { + use trie_db::query_plan::compact_content_proof::{Encode, Op}; + // hard coded check + if nb_plan == 0 && L::MAX_INLINE_VALUE.is_some() { + if limit == None { + // full on iter all + assert_eq!(proofs.len(), 1); + assert_eq!(proofs[0].len(), 1); + + let refs: &[Op, Vec>] = &[ + Op::KeyPush(b"alfa".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"bravo", false), 0xf0), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + ]; + let mut encoded = Vec::new(); + for r in refs { + r.encode_to(&mut encoded); + } + assert_eq!(proofs[0][0], encoded); + } + } // Decode not written continue } From b0438155aa2463aac0d6b23280bd4f10adbdf921 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 2 May 2023 21:06:51 +0200 Subject: [PATCH 047/154] fix a bit ops --- trie-db/src/query_plan.rs | 41 ++++++++++++++++++-------- trie-db/test/src/proof.rs | 60 ++++++++++++++++++++++++++++++--------- 2 files changed, 76 insertions(+), 25 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 6fb839ad..e8bbb726 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -340,7 +340,7 @@ impl Recorder { proof.push(Vec::new()); }, RecorderStateInner::Content { output, stacked_push, stacked_pop } => { - Self::flush_compact_content_pop2(output, stacked_pop, items); + Self::flush_compact_content_pop2(output, stacked_pop, items, None); if stacked_push.is_none() { *stacked_push = Some(NibbleVec::new()); } @@ -388,20 +388,19 @@ impl Recorder { out: &mut O, stacked_from: &mut Option, items: &Vec, + add_depth: Option, ) { let Some(from) = stacked_from.take() else { return }; - let pop_to = items.last().map(|i| i.depth).unwrap_or(0); + let pop_to = items.last().map(|i| i.depth).unwrap_or(0) + add_depth.unwrap_or(0); debug_assert!(from > pop_to); - if pop_to > 0 { - debug_assert!(from - pop_to <= u16::max_value() as usize); - // Warning this implies key size limit of u16::max - let op = - compact_content_proof::Op::, Vec>::KeyPop((from - pop_to) as u16); - use codec::Encode; - out.write_bytes(&op.encode()); - } + + debug_assert!(from - pop_to <= u16::max_value() as usize); + // Warning this implies key size limit of u16::max + let op = compact_content_proof::Op::, Vec>::KeyPop((from - pop_to) as u16); + use codec::Encode; + out.write_bytes(&op.encode()); } fn record_popped_node( @@ -432,7 +431,9 @@ impl Recorder { } // else when restarting record, this is not to be recorded }, RecorderStateInner::Content { output, stacked_pop, .. } => { + let mut had_stack = true; if stacked_pop.is_none() { + had_stack = false; *stacked_pop = Some(item.depth); } // two case: children to register or all children accessed. @@ -450,8 +451,13 @@ impl Recorder { _ => (), } - if has_hash_to_write || stack_pos == 0 { - Self::flush_compact_content_pop2(output, stacked_pop, items); + if has_hash_to_write { + Self::flush_compact_content_pop2( + output, + stacked_pop, + items, + had_stack.then(|| item.depth), + ); } if !has_hash_to_write { return @@ -1227,6 +1233,9 @@ impl RecordStack { // Returning NotStacked here sounds safe, then the is_inline field is not needed. is_inline = true; } else { + if inline_only { + return Ok(TryStackChildResult::NotStacked) + } if self.halt && from_branch.is_some() { return Ok(TryStackChildResult::Halted) } @@ -2387,6 +2396,14 @@ pub mod compact_content_proof { } } + impl + Default> From<&[u8]> for Enc { + fn from(v: &[u8]) -> Self { + let mut hash = H::default(); + hash.as_mut().copy_from_slice(v); + Enc(hash) + } + } + impl + Default> Decode for Enc { fn decode(input: &mut I) -> core::result::Result { let mut dest = H::default(); diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index d89b0e4e..d7604b62 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -268,10 +268,10 @@ fn test_query_plan_internal() { for (hash_only, kind) in [ (false, ProofKind::CompactContent), + (false, ProofKind::FullNodes), (true, ProofKind::FullNodes), (false, ProofKind::CompactNodes), (true, ProofKind::CompactNodes), - (false, ProofKind::FullNodes), ] { if (kind == ProofKind::CompactContent || kind == ProofKind::CompactNodes) && L::USE_EXTENSION @@ -372,15 +372,18 @@ fn test_query_plan_internal() { } if kind == ProofKind::CompactContent { + if L::MAX_INLINE_VALUE.is_none() { + continue + } use trie_db::query_plan::compact_content_proof::{Encode, Op}; // hard coded check - if nb_plan == 0 && L::MAX_INLINE_VALUE.is_some() { - if limit == None { - // full on iter all - assert_eq!(proofs.len(), 1); - assert_eq!(proofs[0].len(), 1); + if limit == None { + // full on iter all + assert_eq!(proofs.len(), 1); + assert_eq!(proofs[0].len(), 1); - let refs: &[Op, Vec>] = &[ + let refs: Vec, Vec>> = match nb_plan { + 0 => vec![ Op::KeyPush(b"alfa".to_vec(), 0xff), Op::Value([0; 32].to_vec()), Op::KeyPop(7), @@ -399,13 +402,44 @@ fn test_query_plan_internal() { Op::KeyPop(5), Op::KeyPush(shifted(b"use", false), 0xf0), Op::Value(b"building".to_vec()), - ]; - let mut encoded = Vec::new(); - for r in refs { - r.encode_to(&mut encoded); - } - assert_eq!(proofs[0][0], encoded); + ], + 1 => vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::HashChild( + (&[ + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, 20, 32, + 247, 110, 189, 213, 140, 86, 162, 229, 70, 86, 163, 223, + 26, 52, 253, 176, 201, 65, 248, + ][..]) + .into(), + 1, + ), + Op::HashChild( + (&[ + 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, 14, 161, 91, + 109, 136, 84, 6, 128, 190, 201, 49, 142, 21, 154, 250, 246, + 133, 0, 199, 138, 49, + ][..]) + .into(), + 8, + ), + ], + _ => continue, + }; + let mut encoded = Vec::new(); + for r in refs { + r.encode_to(&mut encoded); } + assert_eq!(proofs[0][0], encoded); } // Decode not written continue From 786295de1891d879028328fd32e73894daf986b8 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 3 May 2023 09:39:41 +0200 Subject: [PATCH 048/154] add substrate v1 --- test-support/reference-trie/src/lib.rs | 2 ++ trie-db/test/src/triedbmut.rs | 14 ++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index be626801..5a8150e0 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -53,6 +53,8 @@ macro_rules! test_layouts { fn $test() { eprintln!("Running with layout `HashedValueNoExtThreshold`"); $test_internal::<$crate::HashedValueNoExtThreshold<1>>(); + eprintln!("Running with layout `SubstrateV1`"); + $test_internal::<$crate::SubstrateV1<$crate::RefHasher>>(); eprintln!("Running with layout `HashedValueNoExt`"); $test_internal::<$crate::HashedValueNoExt>(); eprintln!("Running with layout `NoExtensionLayout`"); diff --git a/trie-db/test/src/triedbmut.rs b/trie-db/test/src/triedbmut.rs index 02316892..baa5c105 100644 --- a/trie-db/test/src/triedbmut.rs +++ b/trie-db/test/src/triedbmut.rs @@ -477,13 +477,15 @@ fn insert_empty_internal() { assert_eq!(*t.root(), reference_trie_root::(x.clone())); - for &(ref key, _) in &x { - t.insert(key, &[]).unwrap(); - } + if T::ALLOW_EMPTY == false { + for &(ref key, _) in &x { + t.insert(key, &[]).unwrap(); + } - assert!(t.is_empty()); - let hashed_null_node = reference_hashed_null_node::(); - assert_eq!(*t.root(), hashed_null_node); + assert!(t.is_empty()); + let hashed_null_node = reference_hashed_null_node::(); + assert_eq!(*t.root(), hashed_null_node); + } } test_layouts!(return_old_values, return_old_values_internal); From 0ce65c4ab6d6debc4dbc4d6fcedc2fbf972db33c Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 3 May 2023 09:50:24 +0200 Subject: [PATCH 049/154] mark inline for content --- test-support/reference-trie/src/lib.rs | 1 + trie-db/src/query_plan.rs | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 5a8150e0..c5233f21 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -51,6 +51,7 @@ macro_rules! test_layouts { ($test:ident, $test_internal:ident) => { #[test] fn $test() { + $test_internal::<$crate::SubstrateV1<$crate::RefHasher>>(); // TODO rem eprintln!("Running with layout `HashedValueNoExtThreshold`"); $test_internal::<$crate::HashedValueNoExtThreshold<1>>(); eprintln!("Running with layout `SubstrateV1`"); diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index e8bbb726..9afff26c 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -277,6 +277,13 @@ struct Limits { } impl Recorder { + fn mark_inline_access(&self) -> bool { + match &self.output { + RecorderStateInner::Content { .. } => true, + _ => false, + } + } + /// Check and update start at record. /// When return true, do record. fn check_start_at(&mut self, depth: usize) -> bool { @@ -1241,7 +1248,7 @@ impl RecordStack { } } if let Some(accessed_children_node) = from_branch { - if !is_inline { + if !is_inline || self.recorder.mark_inline_access() { accessed_children_node.set(child_index as usize, true); } From 209b854225f037dab2bf9797bf9e99520af603cc Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 3 May 2023 09:54:49 +0200 Subject: [PATCH 050/154] switch --- trie-db/test/src/proof.rs | 73 +++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index d7604b62..df0b71d1 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -372,9 +372,11 @@ fn test_query_plan_internal() { } if kind == ProofKind::CompactContent { - if L::MAX_INLINE_VALUE.is_none() { - continue - } + let all = match L::MAX_INLINE_VALUE { + Some(1) => true, + Some(33) => false, + _ => continue, + }; use trie_db::query_plan::compact_content_proof::{Encode, Op}; // hard coded check if limit == None { @@ -403,36 +405,41 @@ fn test_query_plan_internal() { Op::KeyPush(shifted(b"use", false), 0xf0), Op::Value(b"building".to_vec()), ], - 1 => vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::HashChild( - (&[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, 20, 32, - 247, 110, 189, 213, 140, 86, 162, 229, 70, 86, 163, 223, - 26, 52, 253, 176, 201, 65, 248, - ][..]) - .into(), - 1, - ), - Op::HashChild( - (&[ - 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, 14, 161, 91, - 109, 136, 84, 6, 128, 190, 201, 49, 142, 21, 154, 250, 246, - 133, 0, 199, 138, 49, - ][..]) - .into(), - 8, - ), - ], + 1 => + if all { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::HashChild( + (&[ + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, 20, + 32, 247, 110, 189, 213, 140, 86, 162, 229, 70, 86, + 163, 223, 26, 52, 253, 176, 201, 65, 248, + ][..]) + .into(), + 1, + ), + Op::HashChild( + (&[ + 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, 14, + 161, 91, 109, 136, 84, 6, 128, 190, 201, 49, 142, + 21, 154, 250, 246, 133, 0, 199, 138, 49, + ][..]) + .into(), + 8, + ), + ] + } else { + continue + }, _ => continue, }; let mut encoded = Vec::new(); From 5971330ab7ad4a52c07d0c83fb6be19059402827 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 3 May 2023 11:16:04 +0200 Subject: [PATCH 051/154] factor and fix --- trie-db/src/query_plan.rs | 110 +++++++++++++++----------------------- trie-db/test/src/proof.rs | 35 +++++++++++- 2 files changed, 77 insertions(+), 68 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 9afff26c..67413770 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -930,34 +930,8 @@ pub fn record_query_plan< Ordering::Equal | Ordering::Less => break, Ordering::Greater => { if query_plan.kind.record_inline() { - if let Some(item) = stack.items.last() { - let pre = item.next_descended_child + 1; - for i in pre..NIBBLE_LENGTH as u8 { - match stack.try_stack_child( - i, - db, - dummy_parent_hash, - None, - true, - )? { - // only expect a stacked prefix here - TryStackChildResult::Stacked => { - let (f, halt) = iter_prefix::( - from, None, db, false, true, true, - )?; - if halt { - // no halt on inline. - unreachable!() - } else { - from = f; - stack = &mut from.stack; - stack.pop(); - } - }, - _ => (), - } - } - } + from = try_stack_inline_child(from, db, NIBBLE_LENGTH as u8)?; + stack = &mut from.stack; } if !stack.pop() { stack.recorder.finalize(&stack.items); @@ -1012,25 +986,13 @@ pub fn record_query_plan< let child_index = if stack.items.is_empty() { 0 } else { slice_query.at(0) }; if query_plan.kind.record_inline() { - let pre = stack.items.last().map(|i| i.next_descended_child + 1).unwrap_or(0); - for i in pre..child_index { - match stack.try_stack_child(i, db, dummy_parent_hash, None, true)? { - // only expect a stacked prefix here - TryStackChildResult::Stacked => { - let (f, halt) = iter_prefix::(from, None, db, false, true, true)?; - if halt { - // no halt on inline. - unreachable!() - } else { - from = f; - stack = &mut from.stack; - stack.pop(); - } - }, - _ => (), - } - } + from = try_stack_inline_child(from, db, child_index)?; + stack = &mut from.stack; } + stack.items.last_mut().map(|i| { + // TODO only needed for content but could be better to be always aligned + i.next_descended_child = child_index + 1; + }); match stack.try_stack_child( child_index, db, @@ -1084,26 +1046,8 @@ pub fn record_query_plan< } loop { if query_plan.kind.record_inline() { - if let Some(item) = stack.items.last() { - let pre = item.next_descended_child + 1; - for i in pre..NIBBLE_LENGTH as u8 { - match stack.try_stack_child(i, db, dummy_parent_hash, None, true)? { - // only expect a stacked prefix here - TryStackChildResult::Stacked => { - let (f, halt) = iter_prefix::(from, None, db, false, true, true)?; - if halt { - // no halt on inline. - unreachable!() - } else { - from = f; - stack = &mut from.stack; - stack.pop(); - } - }, - _ => (), - } - } - } + from = try_stack_inline_child(from, db, NIBBLE_LENGTH as u8)?; + stack = &mut from.stack; } if !stack.pop() { @@ -1114,6 +1058,38 @@ pub fn record_query_plan< Ok(from) } +fn try_stack_inline_child<'a, L: TrieLayout, O: RecorderOutput>( + mut from: HaltedStateRecord, + db: &TrieDB, + upper: u8, +) -> Result, VerifyError, CError>> { + let dummy_parent_hash = TrieHash::::default(); + let mut stack = &mut from.stack; + if let Some(item) = stack.items.last() { + let pre = item.next_descended_child; // TODO put next_descended child to 16 for leaf (skip some noop iter) + for i in pre..upper as u8 { + match stack.try_stack_child(i, db, dummy_parent_hash, None, true)? { + // only expect a stacked prefix here + TryStackChildResult::Stacked => { + let (f, halt) = iter_prefix::(from, None, db, false, true, true)?; + if halt { + // no halt on inline. + unreachable!() + } else { + from = f; + stack = &mut from.stack; + stack.pop(); + } + }, + TryStackChildResult::NotStackedBranch => (), + _ => break, + } + } + } + stack.items.last_mut().map(|i| i.next_descended_child = upper); + Ok(from) +} + fn iter_prefix( mut from: HaltedStateRecord, prev_query: Option<&QueryPlanItem>, @@ -1241,7 +1217,7 @@ impl RecordStack { is_inline = true; } else { if inline_only { - return Ok(TryStackChildResult::NotStacked) + return Ok(TryStackChildResult::NotStackedBranch) } if self.halt && from_branch.is_some() { return Ok(TryStackChildResult::Halted) diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index df0b71d1..8a8bbfc5 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -308,6 +308,12 @@ fn test_query_plan_internal() { }, ]; for (nb_plan, query_plan) in query_plans.iter().enumerate() { + /* + // TODO rem + if nb_plan < 1 { + continue + } + */ for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] { let limit = limit_conf.0; @@ -438,7 +444,34 @@ fn test_query_plan_internal() { ), ] } else { - continue + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + // inline ix 8 + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + Op::KeyPop(9), + Op::HashChild( + (&[ + 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, + 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, 211, + 231, 109, 244, 137, 208, 244, 12, 65, 196, 119, + ][..]) + .into(), + 1, + ), + ] }, _ => continue, }; From b08708a8c480a1b257528ba6f820ad9090342bab Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 3 May 2023 14:48:40 +0200 Subject: [PATCH 052/154] ok full content (TODO partial ones) --- test-support/reference-trie/src/lib.rs | 1 - trie-db/src/query_plan.rs | 45 ++++++------- trie-db/test/src/proof.rs | 91 +++++++++++++++++++++++++- 3 files changed, 113 insertions(+), 24 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index c5233f21..5a8150e0 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -51,7 +51,6 @@ macro_rules! test_layouts { ($test:ident, $test_internal:ident) => { #[test] fn $test() { - $test_internal::<$crate::SubstrateV1<$crate::RefHasher>>(); // TODO rem eprintln!("Running with layout `HashedValueNoExtThreshold`"); $test_internal::<$crate::HashedValueNoExtThreshold<1>>(); eprintln!("Running with layout `SubstrateV1`"); diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 67413770..ee15275d 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -400,7 +400,7 @@ impl Recorder { let Some(from) = stacked_from.take() else { return }; - let pop_to = items.last().map(|i| i.depth).unwrap_or(0) + add_depth.unwrap_or(0); + let pop_to = add_depth.unwrap_or_else(|| items.last().map(|i| i.depth).unwrap_or(0)); debug_assert!(from > pop_to); debug_assert!(from - pop_to <= u16::max_value() as usize); @@ -446,29 +446,21 @@ impl Recorder { // two case: children to register or all children accessed. let mut has_hash_to_write = false; let node_data = item.node.data(); - match item.node.node_plan() { - NodePlan::Branch { children, .. } | - NodePlan::NibbledBranch { children, .. } => - for i in 0..children.len() { - if children[i].is_some() && !item.accessed_children_node.at(i) { - has_hash_to_write = true; - break - } - }, - _ => (), + if let Some(last_item) = items.last() { + match last_item.node.node_plan() { + NodePlan::Branch { children, .. } | + NodePlan::NibbledBranch { children, .. } => + for i in 0..children.len() { + if children[i].is_some() && !last_item.accessed_children_node.at(i) + { + has_hash_to_write = true; + break + } + }, + _ => (), + } } - if has_hash_to_write { - Self::flush_compact_content_pop2( - output, - stacked_pop, - items, - had_stack.then(|| item.depth), - ); - } - if !has_hash_to_write { - return - } match item.node.node_plan() { NodePlan::Branch { children, .. } | NodePlan::NibbledBranch { children, .. } => { @@ -498,6 +490,15 @@ impl Recorder { }, _ => (), } + if has_hash_to_write { + Self::flush_compact_content_pop2( + output, + stacked_pop, + items, + None, + //had_stack.then(|| item.depth), + ); + } }, } } diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 8a8bbfc5..40dd00aa 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -310,7 +310,7 @@ fn test_query_plan_internal() { for (nb_plan, query_plan) in query_plans.iter().enumerate() { /* // TODO rem - if nb_plan < 1 { + if nb_plan < 2 { continue } */ @@ -473,6 +473,95 @@ fn test_query_plan_internal() { ), ] }, + 2 => + // bravo, doge, horsey + if all { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + // hash value here is not really good (could only be with + // child hashes when no hash query). + Op::HashValue( + (&[ + 48, 51, 75, 77, 6, 75, 210, 124, 205, 63, 59, 165, + 81, 140, 222, 237, 196, 168, 203, 206, 105, 245, + 15, 154, 233, 147, 189, 123, 194, 243, 179, 137, + ][..]) + .into(), + ), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::HashValue( + (&[ + 104, 225, 103, 23, 160, 148, 143, 214, 98, 64, 250, + 245, 134, 99, 233, 36, 28, 150, 26, 205, 25, 165, + 122, 211, 170, 180, 45, 82, 143, 71, 191, 19, + ][..]) + .into(), + ), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::HashValue( + (&[ + 170, 195, 61, 227, 244, 86, 86, 205, 233, 84, 40, + 116, 166, 25, 158, 33, 18, 236, 208, 172, 115, 246, + 158, 34, 158, 170, 197, 139, 219, 254, 124, 136, + ][..]) + .into(), + ), + Op::KeyPop(5), + Op::HashChild( + (&[ + 115, 96, 173, 184, 157, 30, 165, 173, 98, 91, 45, + 97, 173, 249, 2, 240, 133, 247, 131, 7, 128, 195, + 235, 114, 210, 152, 24, 22, 105, 232, 147, 171, + ][..]) + .into(), + 5, + ), + Op::KeyPop(4), + Op::HashChild( + (&[ + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, 20, + 32, 247, 110, 189, 213, 140, 86, 162, 229, 70, 86, + 163, 223, 26, 52, 253, 176, 201, 65, 248, + ][..]) + .into(), + 1, + ), + ] + } else { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + Op::KeyPop(9), + Op::HashChild( + (&[ + 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, + 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, 211, + 231, 109, 244, 137, 208, 244, 12, 65, 196, 119, + ][..]) + .into(), + 1, + ), + ] + }, _ => continue, }; let mut encoded = Vec::new(); From b78b836e119aa0788588664e143da57cbe75ec8a Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 3 May 2023 16:35:21 +0200 Subject: [PATCH 053/154] public --- trie-db/src/query_plan.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index ee15275d..4739f79a 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -131,10 +131,10 @@ impl InMemQueryPlan { /// Query plan. pub struct QueryPlan<'a, I> { - items: I, - ignore_unordered: bool, - kind: ProofKind, - _ph: PhantomData<&'a ()>, + pub items: I, + pub ignore_unordered: bool, + pub kind: ProofKind, + pub _ph: PhantomData<&'a ()>, } /// Different proof support. From 5140a38fc6ddc7a6ea6acb497c50ccbf647e6a08 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 3 May 2023 16:36:35 +0200 Subject: [PATCH 054/154] more pub --- trie-db/src/query_plan.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 4739f79a..c84c63ba 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -60,9 +60,9 @@ impl InMemQueryPlanItem { /// Item to query. #[derive(Clone)] pub struct QueryPlanItem<'a> { - key: &'a [u8], - hash_only: bool, - as_prefix: bool, + pub key: &'a [u8], + pub hash_only: bool, + pub as_prefix: bool, } impl<'a> QueryPlanItem<'a> { From 24ec3f9436764d9b75458d1bc881bc23b205ad56 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 3 May 2023 17:31:55 +0200 Subject: [PATCH 055/154] fix --- trie-db/src/query_plan.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index c84c63ba..ae24669c 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -1671,7 +1671,7 @@ impl ReadStack { NodePlan::NibbledBranch { partial, .. } | NodePlan::Extension { partial, .. } => { let partial = partial.build(node_data); - if self.prefix.len() > 0 { + if self.items.len() > 0 { if let Some(slice) = slice_query.as_mut() { slice.advance(1); } From 492862326f5131a8d15b7340d226c0c33a2a2a0f Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 3 May 2023 17:36:01 +0200 Subject: [PATCH 056/154] fix fix --- trie-db/src/query_plan.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index ae24669c..3ab8fd0e 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -1693,7 +1693,7 @@ impl ReadStack { slice_query = None; } if ok { - if self.prefix.len() > 0 { + if self.items.len() > 0 { self.prefix.push(child_index); } if let Some(slice) = slice_query.as_mut() { From dc693031cdf9f0cb46eeb4a57f156894fa958861 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 3 May 2023 18:07:02 +0200 Subject: [PATCH 057/154] some api to restart --- trie-db/src/query_plan.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 3ab8fd0e..b9eca39d 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -783,6 +783,11 @@ impl HaltedStateRecord { /// Init from start. pub fn from_start(recorder: Recorder) -> Self { + Self::from_at(recorder, None) + } + + /// Init from position or start. + pub fn from_at(recorder: Recorder, at: Option<(Vec, bool)>) -> Self { HaltedStateRecord { currently_query_item: None, stack: RecordStack { @@ -793,10 +798,14 @@ impl HaltedStateRecord { halt: false, seek: None, }, - from: None, + from: at, } } + pub fn stopped_at(&self) -> Option<(Vec, bool)> { + self.from.clone() + } + pub fn is_finished(&self) -> bool { self.from == None } From e40823a659e0928408120b3478b500c8d5dda647 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 5 May 2023 16:04:53 +0200 Subject: [PATCH 058/154] test case for multi --- trie-db/src/query_plan.rs | 144 +++++++++--- trie-db/test/src/proof.rs | 474 +++++++++++++++++++++++--------------- 2 files changed, 401 insertions(+), 217 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index b9eca39d..333079ed 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -347,7 +347,15 @@ impl Recorder { proof.push(Vec::new()); }, RecorderStateInner::Content { output, stacked_push, stacked_pop } => { - Self::flush_compact_content_pop2(output, stacked_pop, items, None); + if Self::flush_compact_content_pop2( + output, + stacked_pop, + items, + None, + &mut self.limits, + ) { + res = true + } if stacked_push.is_none() { *stacked_push = Some(NibbleVec::new()); } @@ -373,10 +381,12 @@ impl Recorder { res } - fn flush_compact_content_pushes(&mut self, depth: usize) { + #[must_use] + fn flush_compact_content_pushes(&mut self, depth: usize) -> bool { + let mut res = false; if !self.check_start_at(depth) { // TODO actually should be unreachable - return + return res } if let RecorderStateInner::Content { output, stacked_push, .. } = &mut self.output { if let Some(buff) = stacked_push.take() { @@ -386,19 +396,25 @@ impl Recorder { mask, ); use codec::Encode; - output.write_bytes(&op.encode()); + let encoded = op.encode(); + res = self.limits.add_node(encoded.len()); + output.write_bytes(&encoded); } } + res } + #[must_use] fn flush_compact_content_pop2( out: &mut O, stacked_from: &mut Option, items: &Vec, add_depth: Option, - ) { + limits: &mut Limits, + ) -> bool { + let mut res = false; let Some(from) = stacked_from.take() else { - return + return res }; let pop_to = add_depth.unwrap_or_else(|| items.last().map(|i| i.depth).unwrap_or(0)); debug_assert!(from > pop_to); @@ -407,21 +423,28 @@ impl Recorder { // Warning this implies key size limit of u16::max let op = compact_content_proof::Op::, Vec>::KeyPop((from - pop_to) as u16); use codec::Encode; - out.write_bytes(&op.encode()); + let encoded = op.encode(); + res = limits.add_node(encoded.len()); + out.write_bytes(&encoded); + res } + #[must_use] fn record_popped_node( &mut self, item: &CompactEncodingInfos, items: &Vec, - ) { + ) -> bool { + let mut res = false; if !self.check_start_at(item.depth) { - return + return res } let stack_pos = items.len(); if let RecorderStateInner::Content { .. } = &self.output { // if no value accessed, then we can have push then stack pop. - self.flush_compact_content_pushes(item.depth); + if self.flush_compact_content_pushes(item.depth) { + res = true; + } } match &mut self.output { @@ -478,7 +501,9 @@ impl Recorder { compact_content_proof::Enc(hash), i as u8 ); use codec::Encode; - output.write_bytes(&op.encode()); + let encoded = op.encode(); + res = self.limits.add_node(encoded.len()); + output.write_bytes(&encoded); }, NodeHandle::Inline(_) => { // As been accessed if needed (inline are not mark). @@ -491,16 +516,20 @@ impl Recorder { _ => (), } if has_hash_to_write { - Self::flush_compact_content_pop2( + if Self::flush_compact_content_pop2( output, stacked_pop, items, None, + &mut self.limits, //had_stack.then(|| item.depth), - ); + ) { + res = true; + } } }, } + res } #[must_use] @@ -511,7 +540,9 @@ impl Recorder { let mut res = false; if let RecorderStateInner::Content { .. } = &self.output { - self.flush_compact_content_pushes(depth); + if self.flush_compact_content_pushes(depth) { + res = true; + } } match &mut self.output { RecorderStateInner::Stream(output) => { @@ -525,18 +556,24 @@ impl Recorder { RecorderStateInner::Content { output, .. } => { let op = compact_content_proof::Op::, Vec>::Value(value); use codec::Encode; - output.write_bytes(&op.encode()); + let mut encoded = op.encode(); + res = self.limits.add_node(encoded.len()); + output.write_bytes(&encoded); }, } res } - fn record_value_inline(&mut self, value: &[u8], depth: usize) { + #[must_use] + fn record_value_inline(&mut self, value: &[u8], depth: usize) -> bool { + let mut res = false; if !self.check_start_at(depth) { - return + return res } if let RecorderStateInner::Content { .. } = &self.output { - self.flush_compact_content_pushes(depth); + if self.flush_compact_content_pushes(depth) { + res = true; + } } match &mut self.output { @@ -547,20 +584,28 @@ impl Recorder { RecorderStateInner::Content { output, .. } => { let op = compact_content_proof::Op::, &[u8]>::Value(value); use codec::Encode; - output.write_bytes(&op.encode()); + let mut encoded = op.encode(); + res = self.limits.add_node(encoded.len()); + output.write_bytes(&encoded); }, } + res } // TODO this should be call also in all node (not only when not finding value): // then could just be part of enter node? - fn record_skip_value(&mut self, items: &Vec) { + #[must_use] + fn record_skip_value(&mut self, items: &mut Vec) -> bool { + let mut res = false; let mut op = None; if let RecorderStateInner::Content { .. } = &self.output { - if let Some(item) = items.last() { - assert!(!item.accessed_value_node); + if let Some(item) = items.last_mut() { + if item.accessed_value_node { + return res + } + item.accessed_value_node = true; if !self.check_start_at(item.depth) { - return + return res } let node_data = item.node.data(); @@ -582,10 +627,12 @@ impl Recorder { ), }); }, - _ => return, + _ => return res, } - self.flush_compact_content_pushes(item.depth); + if self.flush_compact_content_pushes(item.depth) { + res = true; + } } } @@ -593,11 +640,14 @@ impl Recorder { match &mut self.output { RecorderStateInner::Content { output, .. } => { use codec::Encode; - output.write_bytes(&op.encode()); + let mut encoded = op.encode(); + res = self.limits.add_node(encoded.len()); + output.write_bytes(&encoded); }, _ => (), } } + res } fn finalize(&mut self, items: &Vec) { @@ -630,12 +680,13 @@ impl Recorder { // all written }, RecorderStateInner::Content { output: _, stacked_push, stacked_pop: _ } => { + // TODO protect existing stack as for compact assert!(stacked_push.is_none()); // TODO could use function with &item and &[item] as param // to skip this clone. let mut items = items.clone(); while let Some(item) = items.pop() { - self.record_popped_node(&item, &items); + let _ = self.record_popped_node(&item, &items); } }, } @@ -666,7 +717,23 @@ impl Limits { } }, ProofKind::CompactContent => { - unimplemented!() + // everything is counted as a node. + if let Some(rem_size) = self.remaining_size.as_mut() { + if *rem_size >= size { + *rem_size -= size; + } else { + *rem_size = 0; + res = true; + } + } + if let Some(rem_node) = self.remaining_node.as_mut() { + if *rem_node > 1 { + *rem_node -= 1; + } else { + *rem_node = 0; + res = true; + } + } }, } res @@ -695,7 +762,7 @@ impl Limits { } }, ProofKind::CompactContent => { - unimplemented!() + unreachable!() }, } res @@ -990,7 +1057,9 @@ pub fn record_query_plan< break true } } else { - stack.recorder.record_skip_value(&stack.items); + if stack.recorder.record_skip_value(&mut stack.items) { + stack.halt = true; + } } } @@ -1349,11 +1418,14 @@ impl RecordStack { self.halt = true; } } else { - self.recorder.record_skip_value(&self.items); + if self.recorder.record_skip_value(&mut self.items) { + self.halt = true; + } + }, + Value::Inline(value) => + if self.recorder.record_value_inline(value, self.prefix.len()) { + self.halt = true; }, - Value::Inline(value) => { - self.recorder.record_value_inline(value, self.prefix.len()); - }, } Ok(true) } @@ -1363,7 +1435,9 @@ impl RecordStack { return false } if let Some(item) = self.items.pop() { - self.recorder.record_popped_node(&item, &self.items); + if self.recorder.record_popped_node(&item, &self.items) { + self.halt = true; + } let depth = self.items.last().map(|i| i.depth).unwrap_or(0); self.prefix.drop_lasts(self.prefix.len() - depth); if depth == item.depth { diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 40dd00aa..30c675c6 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -355,6 +355,7 @@ fn test_query_plan_internal() { }; if kind == ProofKind::CompactContent { proofs.push(vec![rec.output().buffer]); + break // TODO remove } else { proofs.push(rec.output().nodes); } @@ -383,194 +384,303 @@ fn test_query_plan_internal() { Some(33) => false, _ => continue, }; - use trie_db::query_plan::compact_content_proof::{Encode, Op}; - // hard coded check - if limit == None { + let mut nb = 0; + while let Some(proof) = proofs.pop() { + use trie_db::query_plan::compact_content_proof::{Encode, Op}; // full on iter all - assert_eq!(proofs.len(), 1); - assert_eq!(proofs[0].len(), 1); - - let refs: Vec, Vec>> = match nb_plan { - 0 => vec![ - Op::KeyPush(b"alfa".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"bravo", false), 0xf0), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - ], - 1 => - if all { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::HashChild( - (&[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, 20, - 32, 247, 110, 189, 213, 140, 86, 162, 229, 70, 86, - 163, 223, 26, 52, 253, 176, 201, 65, 248, - ][..]) - .into(), - 1, - ), - Op::HashChild( - (&[ - 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, 14, - 161, 91, 109, 136, 84, 6, 128, 190, 201, 49, 142, - 21, 154, 250, 246, 133, 0, 199, 138, 49, - ][..]) - .into(), - 8, - ), - ] - } else { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - // inline ix 8 - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - Op::KeyPop(9), - Op::HashChild( - (&[ - 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, - 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, 211, - 231, 109, 244, 137, 208, 244, 12, 65, 196, 119, - ][..]) - .into(), - 1, - ), - ] - }, - 2 => - // bravo, doge, horsey - if all { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - // hash value here is not really good (could only be with - // child hashes when no hash query). - Op::HashValue( - (&[ - 48, 51, 75, 77, 6, 75, 210, 124, 205, 63, 59, 165, - 81, 140, 222, 237, 196, 168, 203, 206, 105, 245, - 15, 154, 233, 147, 189, 123, 194, 243, 179, 137, - ][..]) - .into(), - ), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::HashValue( - (&[ - 104, 225, 103, 23, 160, 148, 143, 214, 98, 64, 250, - 245, 134, 99, 233, 36, 28, 150, 26, 205, 25, 165, - 122, 211, 170, 180, 45, 82, 143, 71, 191, 19, - ][..]) - .into(), - ), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::HashValue( - (&[ - 170, 195, 61, 227, 244, 86, 86, 205, 233, 84, 40, - 116, 166, 25, 158, 33, 18, 236, 208, 172, 115, 246, - 158, 34, 158, 170, 197, 139, 219, 254, 124, 136, - ][..]) - .into(), - ), - Op::KeyPop(5), - Op::HashChild( - (&[ - 115, 96, 173, 184, 157, 30, 165, 173, 98, 91, 45, - 97, 173, 249, 2, 240, 133, 247, 131, 7, 128, 195, - 235, 114, 210, 152, 24, 22, 105, 232, 147, 171, - ][..]) - .into(), - 5, - ), - Op::KeyPop(4), - Op::HashChild( - (&[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, 20, - 32, 247, 110, 189, 213, 140, 86, 162, 229, 70, 86, - 163, 223, 26, 52, 253, 176, 201, 65, 248, - ][..]) - .into(), - 1, - ), - ] - } else { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - Op::KeyPop(9), - Op::HashChild( - (&[ - 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, - 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, 211, - 231, 109, 244, 137, 208, 244, 12, 65, 196, 119, - ][..]) - .into(), - 1, - ), - ] - }, - _ => continue, - }; + // assert_eq!(proofs.len(), 1); + assert_eq!(proof.len(), 1); + + let refs: Vec, Vec>> = + match (limit.unwrap_or(0), nb_plan, nb) { + (0, 0, 0) => vec![ + Op::KeyPush(b"alfa".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"bravo", false), 0xf0), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + ], + (0, 1, 0) => + if all { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::HashChild( + (&[ + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, + 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, + 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, + 248, + ][..]) + .into(), + 1, + ), + Op::HashChild( + (&[ + 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, + 14, 161, 91, 109, 136, 84, 6, 128, 190, 201, + 49, 142, 21, 154, 250, 246, 133, 0, 199, 138, + 49, + ][..]) + .into(), + 8, + ), + ] + } else { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + // inline ix 8 + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + Op::KeyPop(9), + Op::HashChild( + (&[ + 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, + 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, + 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, + 119, + ][..]) + .into(), + 1, + ), + ] + }, + (0, 2, 0) => + // bravo, doge, horsey + if all { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + // hash value here is not really good (could only be + // with child hashes when no hash query). + Op::HashValue( + (&[ + 48, 51, 75, 77, 6, 75, 210, 124, 205, 63, 59, + 165, 81, 140, 222, 237, 196, 168, 203, 206, + 105, 245, 15, 154, 233, 147, 189, 123, 194, + 243, 179, 137, + ][..]) + .into(), + ), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::HashValue( + (&[ + 104, 225, 103, 23, 160, 148, 143, 214, 98, 64, + 250, 245, 134, 99, 233, 36, 28, 150, 26, 205, + 25, 165, 122, 211, 170, 180, 45, 82, 143, 71, + 191, 19, + ][..]) + .into(), + ), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::HashValue( + (&[ + 170, 195, 61, 227, 244, 86, 86, 205, 233, 84, + 40, 116, 166, 25, 158, 33, 18, 236, 208, 172, + 115, 246, 158, 34, 158, 170, 197, 139, 219, + 254, 124, 136, + ][..]) + .into(), + ), + Op::KeyPop(5), + Op::HashChild( + (&[ + 115, 96, 173, 184, 157, 30, 165, 173, 98, 91, + 45, 97, 173, 249, 2, 240, 133, 247, 131, 7, + 128, 195, 235, 114, 210, 152, 24, 22, 105, 232, + 147, 171, + ][..]) + .into(), + 5, + ), + Op::KeyPop(4), + Op::HashChild( + (&[ + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, + 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, + 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, + 248, + ][..]) + .into(), + 1, + ), + ] + } else { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + Op::KeyPop(9), + Op::HashChild( + (&[ + 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, + 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, + 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, + 119, + ][..]) + .into(), + 1, + ), + ] + }, + (1, 2, 0) => + // bravo, doge, horsey + if all { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + // hash value here is not really good (could only be + // with child hashes when no hash query). + Op::HashValue( + (&[ + 48, 51, 75, 77, 6, 75, 210, 124, 205, 63, 59, + 165, 81, 140, 222, 237, 196, 168, 203, 206, + 105, 245, 15, 154, 233, 147, 189, 123, 194, + 243, 179, 137, + ][..]) + .into(), + ), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::HashValue( + (&[ + 104, 225, 103, 23, 160, 148, 143, 214, 98, 64, + 250, 245, 134, 99, 233, 36, 28, 150, 26, 205, + 25, 165, 122, 211, 170, 180, 45, 82, 143, 71, + 191, 19, + ][..]) + .into(), + ), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::HashValue( + (&[ + 170, 195, 61, 227, 244, 86, 86, 205, 233, 84, + 40, 116, 166, 25, 158, 33, 18, 236, 208, 172, + 115, 246, 158, 34, 158, 170, 197, 139, 219, + 254, 124, 136, + ][..]) + .into(), + ), + Op::KeyPop(5), + Op::HashChild( + (&[ + 115, 96, 173, 184, 157, 30, 165, 173, 98, 91, + 45, 97, 173, 249, 2, 240, 133, 247, 131, 7, + 128, 195, 235, 114, 210, 152, 24, 22, 105, 232, + 147, 171, + ][..]) + .into(), + 5, + ), + Op::KeyPop(4), + Op::HashChild( + (&[ + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, + 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, + 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, + 248, + ][..]) + .into(), + 1, + ), + ] + } else { + break + /* + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + Op::KeyPop(9), + Op::HashChild( + (&[ + 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, + 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, + 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, + 119, + ][..]) + .into(), + 1, + ), + ] + */ + }, + + _ => break, + }; let mut encoded = Vec::new(); for r in refs { r.encode_to(&mut encoded); } - assert_eq!(proofs[0][0], encoded); + assert_eq!(proof[0], encoded); + nb += 1; } - // Decode not written continue } From 5d5603af117dc6f44cbfefc7edb17d33f0fae616 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 5 May 2023 16:24:50 +0200 Subject: [PATCH 059/154] ok first --- trie-db/src/query_plan.rs | 11 ++++---- trie-db/test/src/proof.rs | 59 +++++++++++---------------------------- 2 files changed, 22 insertions(+), 48 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 333079ed..3e394254 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -408,7 +408,7 @@ impl Recorder { fn flush_compact_content_pop2( out: &mut O, stacked_from: &mut Option, - items: &Vec, + items: &[CompactEncodingInfos], add_depth: Option, limits: &mut Limits, ) -> bool { @@ -433,7 +433,7 @@ impl Recorder { fn record_popped_node( &mut self, item: &CompactEncodingInfos, - items: &Vec, + items: &[CompactEncodingInfos], ) -> bool { let mut res = false; if !self.check_start_at(item.depth) { @@ -684,9 +684,10 @@ impl Recorder { assert!(stacked_push.is_none()); // TODO could use function with &item and &[item] as param // to skip this clone. - let mut items = items.clone(); - while let Some(item) = items.pop() { - let _ = self.record_popped_node(&item, &items); + for i in (0..items.len()).rev() { + let item = items.get(i).expect("bounded iter"); + let items = &items[..i]; + let _ = self.record_popped_node(item, &items); } }, } diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 30c675c6..1e8cee15 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -580,62 +580,35 @@ fn test_query_plan_internal() { Op::KeyPush(b"bravo".to_vec(), 0xff), Op::Value(b"bravo".to_vec()), Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - // hash value here is not really good (could only be - // with child hashes when no hash query). - Op::HashValue( - (&[ - 48, 51, 75, 77, 6, 75, 210, 124, 205, 63, 59, - 165, 81, 140, 222, 237, 196, 168, 203, 206, - 105, 245, 15, 154, 233, 147, 189, 123, 194, - 243, 179, 137, - ][..]) - .into(), - ), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::HashValue( - (&[ - 104, 225, 103, 23, 160, 148, 143, 214, 98, 64, - 250, 245, 134, 99, 233, 36, 28, 150, 26, 205, - 25, 165, 122, 211, 170, 180, 45, 82, 143, 71, - 191, 19, - ][..]) - .into(), - ), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::HashValue( + Op::HashChild( (&[ - 170, 195, 61, 227, 244, 86, 86, 205, 233, 84, - 40, 116, 166, 25, 158, 33, 18, 236, 208, 172, - 115, 246, 158, 34, 158, 170, 197, 139, 219, - 254, 124, 136, + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, + 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, + 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, + 248, ][..]) .into(), + 1, ), - Op::KeyPop(5), Op::HashChild( (&[ - 115, 96, 173, 184, 157, 30, 165, 173, 98, 91, - 45, 97, 173, 249, 2, 240, 133, 247, 131, 7, - 128, 195, 235, 114, 210, 152, 24, 22, 105, 232, - 147, 171, + 223, 91, 16, 28, 134, 71, 144, 93, 127, 153, + 131, 180, 101, 103, 252, 121, 200, 66, 33, 188, + 58, 187, 247, 197, 65, 169, 112, 46, 241, 22, + 96, 196, ][..]) .into(), - 5, + 4, ), - Op::KeyPop(4), Op::HashChild( (&[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, - 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, - 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, - 248, + 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, + 14, 161, 91, 109, 136, 84, 6, 128, 190, 201, + 49, 142, 21, 154, 250, 246, 133, 0, 199, 138, + 49, ][..]) .into(), - 1, + 8, ), ] } else { From 3bbf2d2b57aaa72cfd1c3ce682bb4514f49b19ce Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 13 Jun 2023 09:10:21 +0200 Subject: [PATCH 060/154] unused param --- trie-db/src/query_plan.rs | 8 +------- trie-db/src/triedb.rs | 9 +-------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 3e394254..18bcbf68 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -1313,13 +1313,7 @@ impl RecordStack { } // TODO handle cache first let child_node = db - .get_raw_or_lookup_with_cache( - parent_hash, - child_handle, - prefix.as_prefix(), - false, - true, - ) + .get_raw_or_lookup_with_cache(parent_hash, child_handle, prefix.as_prefix(), false) .map_err(|_| VerifyError::IncompleteProof)?; // actually incomplete db: TODO consider switching error // TODO put in proof (only if Hash or inline for content one) diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 1bbee670..b7073571 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -154,13 +154,7 @@ where partial_key: Prefix, record_access: bool, ) -> Result<(OwnedNode, Option>), TrieHash, CError> { - self.get_raw_or_lookup_with_cache( - parent_hash, - node_handle, - partial_key, - record_access, - false, - ) + self.get_raw_or_lookup_with_cache(parent_hash, node_handle, partial_key, record_access) } /// Same as get_raw_or_lookup but with optionally use of the node cache. @@ -173,7 +167,6 @@ where node_handle: NodeHandle, partial_key: Prefix, record_access: bool, - with_cache: bool, ) -> Result<(OwnedNode, Option>), TrieHash, CError> { let (node_hash, node_data) = match node_handle { NodeHandle::Hash(data) => { From 38a4622640d25e2e9e0cf1204540858a8cff0002 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 13 Jun 2023 10:42:16 +0200 Subject: [PATCH 061/154] fix some warnings --- test-support/reference-trie/src/lib.rs | 5 + test-support/reference-trie/src/substrate.rs | 2 + .../reference-trie/src/substrate_like.rs | 2 + trie-db/src/node_codec.rs | 6 + trie-db/src/query_plan.rs | 122 +++++++++--------- trie-db/test/src/proof.rs | 13 +- 6 files changed, 81 insertions(+), 69 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 5a8150e0..ac8d7c54 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -601,6 +601,9 @@ impl<'a> Input for ByteSliceInput<'a> { // `const HASHED_NULL_NODE: ::Out = ::Out( … … )`. // Perhaps one day soon? impl NodeCodec for ReferenceNodeCodec { + const DELTA_COMPACT_OMITTED_NODE: usize = 32; + const DELTA_COMPACT_OMITTED_VALUE: usize = 30; + type Error = CodecError; type HashOut = H::Out; @@ -755,6 +758,8 @@ impl NodeCodec for ReferenceNodeCodec { } impl NodeCodec for ReferenceNodeCodecNoExt { + const DELTA_COMPACT_OMITTED_NODE: usize = 32; + const DELTA_COMPACT_OMITTED_VALUE: usize = 30; type Error = CodecError; type HashOut = ::Out; diff --git a/test-support/reference-trie/src/substrate.rs b/test-support/reference-trie/src/substrate.rs index 9b1573f1..5eff71d7 100644 --- a/test-support/reference-trie/src/substrate.rs +++ b/test-support/reference-trie/src/substrate.rs @@ -209,6 +209,8 @@ impl NodeCodecT for NodeCodec where H: Hasher, { + const DELTA_COMPACT_OMITTED_NODE: usize = 32; + const DELTA_COMPACT_OMITTED_VALUE: usize = 30; const ESCAPE_HEADER: Option = Some(trie_constants::ESCAPE_COMPACT_HEADER); type Error = Error; type HashOut = H::Out; diff --git a/test-support/reference-trie/src/substrate_like.rs b/test-support/reference-trie/src/substrate_like.rs index 37cccefc..a2cab9d5 100644 --- a/test-support/reference-trie/src/substrate_like.rs +++ b/test-support/reference-trie/src/substrate_like.rs @@ -149,6 +149,8 @@ impl NodeCodecT for NodeCodec where H: Hasher, { + const DELTA_COMPACT_OMITTED_NODE: usize = 32; + const DELTA_COMPACT_OMITTED_VALUE: usize = 30; const ESCAPE_HEADER: Option = Some(trie_constants::ESCAPE_COMPACT_HEADER); type Error = Error; type HashOut = H::Out; diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index eb9b1f67..032f05e8 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -36,6 +36,12 @@ pub trait NodeCodec: Sized { /// branch or leaf with hash of value, followed by the value node. const ESCAPE_HEADER: Option = None; + /// Size delta for compact encoding of omitted nodes. + const DELTA_COMPACT_OMITTED_NODE: usize; + + /// Size delta for compact encoding of omitted value nodes. + const DELTA_COMPACT_OMITTED_VALUE: usize; + /// Codec error type. type Error: Error; diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 18bcbf68..8cafdb78 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -26,6 +26,7 @@ use core::marker::PhantomData; use crate::{ nibble::{nibble_ops, nibble_ops::NIBBLE_LENGTH, LeftNibbleSlice, NibbleSlice}, node::{NodeHandle, NodePlan, OwnedNode, Value}, + node_codec::NodeCodec, proof::VerifyError, rstd::{ borrow::{Borrow, Cow}, @@ -326,7 +327,7 @@ impl Recorder { fn record_stacked_node( &mut self, item: &CompactEncodingInfos, - stack_pos: usize, + is_root: bool, parent_index: u8, items: &Vec, ) -> bool { @@ -337,17 +338,25 @@ impl Recorder { match &mut self.output { RecorderStateInner::Stream(output) => if !item.is_inline { - res = self.limits.add_node(item.node.data().len()); + res = self.limits.add_node( + item.node.data().len(), + L::Codec::DELTA_COMPACT_OMITTED_NODE, + is_root, + ); output.write_entry(item.node.data().into()); }, RecorderStateInner::Compact { output: _, proof, stacked_pos } => if !item.is_inline { - res = self.limits.add_node(item.node.data().len()); + res = self.limits.add_node( + item.node.data().len(), + L::Codec::DELTA_COMPACT_OMITTED_NODE, + is_root, + ); stacked_pos.push(proof.len()); proof.push(Vec::new()); }, RecorderStateInner::Content { output, stacked_push, stacked_pop } => { - if Self::flush_compact_content_pop2( + if Self::flush_compact_content_pop( output, stacked_pop, items, @@ -360,7 +369,7 @@ impl Recorder { *stacked_push = Some(NibbleVec::new()); } if let Some(buff) = stacked_push.as_mut() { - if stack_pos > 0 { + if !is_root { buff.push(parent_index); } let node_data = item.node.data(); @@ -397,7 +406,7 @@ impl Recorder { ); use codec::Encode; let encoded = op.encode(); - res = self.limits.add_node(encoded.len()); + res = self.limits.add_node(encoded.len(), 0, false); output.write_bytes(&encoded); } } @@ -405,7 +414,7 @@ impl Recorder { } #[must_use] - fn flush_compact_content_pop2( + fn flush_compact_content_pop( out: &mut O, stacked_from: &mut Option, items: &[CompactEncodingInfos], @@ -424,7 +433,7 @@ impl Recorder { let op = compact_content_proof::Op::, Vec>::KeyPop((from - pop_to) as u16); use codec::Encode; let encoded = op.encode(); - res = limits.add_node(encoded.len()); + res = limits.add_node(encoded.len(), 0, false); out.write_bytes(&encoded); res } @@ -439,7 +448,6 @@ impl Recorder { if !self.check_start_at(item.depth) { return res } - let stack_pos = items.len(); if let RecorderStateInner::Content { .. } = &self.output { // if no value accessed, then we can have push then stack pop. if self.flush_compact_content_pushes(item.depth) { @@ -449,7 +457,7 @@ impl Recorder { match &mut self.output { RecorderStateInner::Stream(_) => (), - RecorderStateInner::Compact { output, proof, stacked_pos } => + RecorderStateInner::Compact { proof, stacked_pos, .. } => if !item.is_inline { if let Some(at) = stacked_pos.pop() { proof[at] = crate::trie_codec::encode_node_internal::( @@ -461,9 +469,7 @@ impl Recorder { } // else when restarting record, this is not to be recorded }, RecorderStateInner::Content { output, stacked_pop, .. } => { - let mut had_stack = true; if stacked_pop.is_none() { - had_stack = false; *stacked_pop = Some(item.depth); } // two case: children to register or all children accessed. @@ -502,7 +508,7 @@ impl Recorder { ); use codec::Encode; let encoded = op.encode(); - res = self.limits.add_node(encoded.len()); + res = self.limits.add_node(encoded.len(), 0, false); output.write_bytes(&encoded); }, NodeHandle::Inline(_) => { @@ -516,13 +522,12 @@ impl Recorder { _ => (), } if has_hash_to_write { - if Self::flush_compact_content_pop2( + if Self::flush_compact_content_pop( output, stacked_pop, items, None, &mut self.limits, - //had_stack.then(|| item.depth), ) { res = true; } @@ -540,24 +545,22 @@ impl Recorder { let mut res = false; if let RecorderStateInner::Content { .. } = &self.output { - if self.flush_compact_content_pushes(depth) { - res = true; - } + res = self.flush_compact_content_pushes(depth); } match &mut self.output { RecorderStateInner::Stream(output) => { - res = self.limits.add_value(value.len()); + res = self.limits.add_value(value.len(), L::Codec::DELTA_COMPACT_OMITTED_VALUE); output.write_entry(value.into()); }, RecorderStateInner::Compact { output: _, proof, stacked_pos: _ } => { - res = self.limits.add_value(value.len()); + res = self.limits.add_value(value.len(), L::Codec::DELTA_COMPACT_OMITTED_VALUE); proof.push(value.into()); }, RecorderStateInner::Content { output, .. } => { let op = compact_content_proof::Op::, Vec>::Value(value); use codec::Encode; - let mut encoded = op.encode(); - res = self.limits.add_node(encoded.len()); + let encoded = op.encode(); + res |= self.limits.add_node(encoded.len(), 0, false); output.write_bytes(&encoded); }, } @@ -585,7 +588,7 @@ impl Recorder { let op = compact_content_proof::Op::, &[u8]>::Value(value); use codec::Encode; let mut encoded = op.encode(); - res = self.limits.add_node(encoded.len()); + res = self.limits.add_node(encoded.len(), 0, false); output.write_bytes(&encoded); }, } @@ -640,8 +643,8 @@ impl Recorder { match &mut self.output { RecorderStateInner::Content { output, .. } => { use codec::Encode; - let mut encoded = op.encode(); - res = self.limits.add_node(encoded.len()); + let encoded = op.encode(); + res = self.limits.add_node(encoded.len(), 0, false); output.write_bytes(&encoded); }, _ => (), @@ -696,11 +699,17 @@ impl Recorder { impl Limits { #[must_use] - fn add_node(&mut self, size: usize) -> bool { + fn add_node(&mut self, size: usize, hash_size: usize, is_root: bool) -> bool { let mut res = false; match self.kind { ProofKind::CompactNodes | ProofKind::FullNodes => { if let Some(rem_size) = self.remaining_size.as_mut() { + if let ProofKind::CompactNodes = self.kind { + if !is_root { + // remove a parent hash + *rem_size += hash_size; + } + } if *rem_size >= size { *rem_size -= size; } else { @@ -741,11 +750,15 @@ impl Limits { } #[must_use] - fn add_value(&mut self, size: usize) -> bool { + fn add_value(&mut self, size: usize, hash_size: usize) -> bool { let mut res = false; match self.kind { ProofKind::CompactNodes | ProofKind::FullNodes => { if let Some(rem_size) = self.remaining_size.as_mut() { + if let ProofKind::CompactNodes = self.kind { + // remove a parent value hash + *rem_size += hash_size; + } if *rem_size >= size { *rem_size -= size; } else { @@ -768,36 +781,6 @@ impl Limits { } res } - - fn reclaim_child_hash(&mut self, size: usize) { - match self.kind { - ProofKind::FullNodes => unreachable!(), - ProofKind::CompactNodes => { - // replaced by a 0 len inline hash so same size encoding - if let Some(rem_size) = self.remaining_size.as_mut() { - *rem_size += size; - } - }, - ProofKind::CompactContent => { - unimplemented!() - }, - } - } - - fn reclaim_value_hash(&mut self, size: usize) { - match self.kind { - ProofKind::FullNodes => unreachable!(), - ProofKind::CompactNodes => { - if let Some(rem_size) = self.remaining_size.as_mut() { - // escape byte added - *rem_size += size - 1; - } - }, - ProofKind::CompactContent => { - unimplemented!() - }, - } - } } enum RecorderStateInner { @@ -1359,7 +1342,10 @@ impl RecordStack { next_descended_child, is_inline, }; - if self.recorder.record_stacked_node(&infos, stack.len(), child_index, &*stack) { + if self + .recorder + .record_stacked_node(&infos, stack.is_empty(), child_index, &*stack) + { self.halt = true; } stack.push(infos); @@ -2141,7 +2127,6 @@ where } else { self.state = ReadProofState::PlanConsumed; self.current = None; - to_check_slice = None; break } }; @@ -2414,6 +2399,8 @@ pub mod compact_content_proof { pub use codec::{Decode, Encode}; + use super::RecorderOutput; + /// Representation of each encoded action /// for building the proof. /// TODO ref variant for encoding ?? @@ -2440,6 +2427,23 @@ pub mod compact_content_proof { EndProof, } + impl, V> Op { + /// Calculate encoded len. + pub fn encoded_len(&self) -> usize { + todo!() + } + + /// Write op. + pub fn encode_into(&self, out: &mut impl RecorderOutput) { + todo!() + } + + /// Read an op, return op and number byte read. Or error if invalid encoded. + pub fn decode(encoded: &[u8]) -> Result<(Self, usize), ()> { + todo!() + } + } + #[derive(Debug)] #[repr(transparent)] pub struct Enc(pub H); diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 1e8cee15..f9bd693d 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -18,7 +18,6 @@ use reference_trie::{test_layouts, NoExtensionLayout, TestTrieCache}; use std::collections::BTreeMap; use trie_db::{ proof::{generate_proof, verify_proof, VerifyError}, - query_plan::{HaltedStateCheck, HaltedStateRecord}, DBValue, Trie, TrieDBBuilder, TrieDBMutBuilder, TrieLayout, TrieMut, }; @@ -246,8 +245,9 @@ fn test_verify_decode_error_internal() { test_layouts!(test_query_plan, test_query_plan_internal); fn test_query_plan_internal() { use trie_db::query_plan::{ - record_query_plan, verify_query_plan_iter, HaltedStateCheck, InMemQueryPlan, - InMemQueryPlanItem, InMemoryRecorder, ProofKind, QueryPlan, ReadProofItem, Recorder, + record_query_plan, verify_query_plan_iter, HaltedStateCheck, HaltedStateRecord, + InMemQueryPlan, InMemQueryPlanItem, InMemoryRecorder, ProofKind, QueryPlan, ReadProofItem, + Recorder, }; let set = test_entries(); @@ -308,12 +308,6 @@ fn test_query_plan_internal() { }, ]; for (nb_plan, query_plan) in query_plans.iter().enumerate() { - /* - // TODO rem - if nb_plan < 2 { - continue - } - */ for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] { let limit = limit_conf.0; @@ -679,7 +673,6 @@ fn test_query_plan_internal() { .unwrap(); let content: BTreeMap<_, _> = set.iter().cloned().collect(); let mut in_prefix = false; - let mut halted = false; for item in verify_iter { match item.unwrap() { ReadProofItem::Hash(key, hash) => { From 9ea09ac16c74a4a3d5954aa41c8b081ca23e9cae Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 13 Jun 2023 18:03:18 +0200 Subject: [PATCH 062/154] rem scale codec dependency --- trie-db/Cargo.toml | 1 - trie-db/src/query_plan.rs | 279 ++++++++++++++++++++++++++++---------- trie-db/test/src/proof.rs | 176 +++++++++++++----------- 3 files changed, 299 insertions(+), 157 deletions(-) diff --git a/trie-db/Cargo.toml b/trie-db/Cargo.toml index f39c3ceb..f8e6b00c 100644 --- a/trie-db/Cargo.toml +++ b/trie-db/Cargo.toml @@ -13,7 +13,6 @@ smallvec = { version = "1.0.0", features = ["union", "const_new"] } hash-db = { path = "../hash-db", default-features = false, version = "0.16.0"} hashbrown = { version = "0.13.2", default-features = false, features = ["ahash"] } rustc-hex = { version = "2.1.0", default-features = false, optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } # TODO remove (only to proto encoding but not appropriate). [features] default = ["std"] diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 8cafdb78..43f2812b 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -233,6 +233,9 @@ pub trait RecorderOutput { /// Append bytes. fn write_bytes(&mut self, bytes: &[u8]); + /// Bytes buf len. + fn buf_len(&self) -> usize; + /// Append a delimited sequence of bytes (usually a node). fn write_entry(&mut self, bytes: Cow<[u8]>); } @@ -253,6 +256,10 @@ impl RecorderOutput for InMemoryRecorder { self.buffer.extend_from_slice(bytes) } + fn buf_len(&self) -> usize { + self.buffer.len() + } + fn write_entry(&mut self, bytes: Cow<[u8]>) { if !self.buffer.is_empty() { self.nodes.push(core::mem::take(&mut self.buffer)); @@ -404,10 +411,10 @@ impl Recorder { buff.inner().to_vec(), mask, ); - use codec::Encode; - let encoded = op.encode(); - res = self.limits.add_node(encoded.len(), 0, false); - output.write_bytes(&encoded); + let init_len = output.buf_len(); + op.encode_into(output); + let written = output.buf_len() - init_len; + res = self.limits.add_node(written, 0, false); } } res @@ -421,9 +428,8 @@ impl Recorder { add_depth: Option, limits: &mut Limits, ) -> bool { - let mut res = false; let Some(from) = stacked_from.take() else { - return res + return false }; let pop_to = add_depth.unwrap_or_else(|| items.last().map(|i| i.depth).unwrap_or(0)); debug_assert!(from > pop_to); @@ -431,11 +437,10 @@ impl Recorder { debug_assert!(from - pop_to <= u16::max_value() as usize); // Warning this implies key size limit of u16::max let op = compact_content_proof::Op::, Vec>::KeyPop((from - pop_to) as u16); - use codec::Encode; - let encoded = op.encode(); - res = limits.add_node(encoded.len(), 0, false); - out.write_bytes(&encoded); - res + let init_len = out.buf_len(); + op.encode_into(out); + let written = out.buf_len() - init_len; + limits.add_node(written, 0, false) } #[must_use] @@ -503,13 +508,11 @@ impl Recorder { let op = compact_content_proof::Op::< TrieHash, Vec, - >::HashChild( - compact_content_proof::Enc(hash), i as u8 - ); - use codec::Encode; - let encoded = op.encode(); - res = self.limits.add_node(encoded.len(), 0, false); - output.write_bytes(&encoded); + >::HashChild(hash, i as u8); + let init_len = output.buf_len(); + op.encode_into(output); + let written = output.buf_len() - init_len; + res = self.limits.add_node(written, 0, false) }, NodeHandle::Inline(_) => { // As been accessed if needed (inline are not mark). @@ -558,10 +561,10 @@ impl Recorder { }, RecorderStateInner::Content { output, .. } => { let op = compact_content_proof::Op::, Vec>::Value(value); - use codec::Encode; - let encoded = op.encode(); - res |= self.limits.add_node(encoded.len(), 0, false); - output.write_bytes(&encoded); + let init_len = output.buf_len(); + op.encode_into(output); + let written = output.buf_len() - init_len; + res |= self.limits.add_node(written, 0, false) }, } res @@ -586,10 +589,10 @@ impl Recorder { }, RecorderStateInner::Content { output, .. } => { let op = compact_content_proof::Op::, &[u8]>::Value(value); - use codec::Encode; - let mut encoded = op.encode(); - res = self.limits.add_node(encoded.len(), 0, false); - output.write_bytes(&encoded); + let init_len = output.buf_len(); + op.encode_into(output); + let written = output.buf_len() - init_len; + res = self.limits.add_node(written, 0, false); }, } res @@ -620,9 +623,7 @@ impl Recorder { Value::Node(hash_slice) => { let mut hash = TrieHash::::default(); hash.as_mut().copy_from_slice(hash_slice); - compact_content_proof::Op::<_, Vec>::HashValue( - compact_content_proof::Enc(hash), - ) + compact_content_proof::Op::<_, Vec>::HashValue(hash) }, Value::Inline(value) => compact_content_proof::Op::, Vec>::Value( @@ -642,10 +643,10 @@ impl Recorder { if let Some(op) = op { match &mut self.output { RecorderStateInner::Content { output, .. } => { - use codec::Encode; - let encoded = op.encode(); - res = self.limits.add_node(encoded.len(), 0, false); - output.write_bytes(&encoded); + let init_len = output.buf_len(); + op.encode_into(output); + let written = output.buf_len() - init_len; + res = self.limits.add_node(written, 0, false); }, _ => (), } @@ -2396,15 +2397,12 @@ where } pub mod compact_content_proof { - - pub use codec::{Decode, Encode}; - use super::RecorderOutput; /// Representation of each encoded action /// for building the proof. - /// TODO ref variant for encoding ?? - #[derive(Encode, Decode, Debug)] + /// TODO ref variant for encoding ?? or key using V and use Op<&H, &[u8]>. + #[derive(Debug)] pub enum Op { // key content followed by a mask for last byte. // If mask erase some content the content need to @@ -2418,63 +2416,196 @@ pub mod compact_content_proof { // TODO should be compact encoding of number. KeyPop(u16), // u8 is child index, shorthand for key push one nibble followed by key pop. - HashChild(Enc, u8), + HashChild(H, u8), // All value variant are only after a `KeyPush` or at first position. - HashValue(Enc), + HashValue(H), Value(V), // This is not strictly necessary, only if the proof is not sized, otherwhise if we know // the stream will end it can be skipped. EndProof, } - impl, V> Op { - /// Calculate encoded len. - pub fn encoded_len(&self) -> usize { - todo!() - } + // Limiting size to u32 (could also just use a terminal character). + #[derive(Debug, PartialEq, Eq)] + #[repr(transparent)] + struct VarInt(u32); - /// Write op. - pub fn encode_into(&self, out: &mut impl RecorderOutput) { - todo!() + impl VarInt { + fn encoded_len(&self) -> usize { + if self.0 == 0 { + return 1 + } + let len = 32 - self.0.leading_zeros() as usize; + if len % 7 == 0 { + len / 7 + } else { + len / 7 + 1 + } + /* + match self.0 { + l if l < 2 ^ 7 => 1, // leading 0: 25 + l if l < 2 ^ 14 => 2, // leading 0: 18 + + l if l < 2 ^ 21 => 3, // 11 + l if l < 2 ^ 28 => 4, // 4 + _ => 5, + } + */ } - /// Read an op, return op and number byte read. Or error if invalid encoded. - pub fn decode(encoded: &[u8]) -> Result<(Self, usize), ()> { - todo!() + fn encode_into(&self, out: &mut impl RecorderOutput) { + let mut to_encode = self.0; + for _ in 0..self.encoded_len() - 1 { + out.write_bytes(&[0b1000_0000 | to_encode as u8]); + to_encode >>= 7; + } + out.write_bytes(&[to_encode as u8]); } - } - - #[derive(Debug)] - #[repr(transparent)] - pub struct Enc(pub H); - impl> Encode for Enc { - fn size_hint(&self) -> usize { - self.0.as_ref().len() + fn decode(encoded: &[u8]) -> Result<(Self, usize), ()> { + let mut value = 0u32; + for (i, byte) in encoded.iter().enumerate() { + let last = byte & 0b1000_0000 == 0; + value |= ((byte & 0b0111_1111) as u32) << (i * 7); + if last { + return Ok((VarInt(value), i + 1)) + } + } + Err(()) } + } - fn encoded_size(&self) -> usize { - self.0.as_ref().len() + #[test] + fn varint_encode_decode() { + let mut buf = super::InMemoryRecorder::default(); + for i in 0..u16::MAX as u32 + 1 { + VarInt(i).encode_into(&mut buf); + assert_eq!(buf.buffer.len(), VarInt(i).encoded_len()); + assert_eq!(Ok((VarInt(i), buf.buffer.len())), VarInt::decode(&buf.buffer)); + buf.buffer.clear(); } + } - fn encode_to(&self, dest: &mut T) { - dest.write(self.0.as_ref()) + impl, V: AsRef<[u8]>> Op { + /// Calculate encoded len. + pub fn encoded_len(&self) -> usize { + let mut len = 1; + match self { + Op::KeyPush(key, _mask) => { + len += VarInt(key.len() as u32).encoded_len(); + len += key.len(); + len += 1; + }, + Op::KeyPop(nb) => { + len += VarInt(*nb as u32).encoded_len(); + }, + Op::HashChild(hash, _at) => { + len += hash.as_ref().len(); + len += 1; + }, + Op::HashValue(hash) => { + len += hash.as_ref().len(); + }, + Op::Value(value) => { + len += VarInt(value.as_ref().len() as u32).encoded_len(); + len += value.as_ref().len(); + }, + Op::EndProof => (), + } + len } - } - impl + Default> From<&[u8]> for Enc { - fn from(v: &[u8]) -> Self { - let mut hash = H::default(); - hash.as_mut().copy_from_slice(v); - Enc(hash) + /// Write op. + pub fn encode_into(&self, out: &mut impl RecorderOutput) { + match self { + Op::KeyPush(key, mask) => { + out.write_bytes(&[0]); + VarInt(key.len() as u32).encode_into(out); + out.write_bytes(&key); + out.write_bytes(&[*mask]); + }, + Op::KeyPop(nb) => { + out.write_bytes(&[1]); + VarInt(*nb as u32).encode_into(out); + }, + Op::HashChild(hash, at) => { + out.write_bytes(&[2]); + out.write_bytes(hash.as_ref()); + out.write_bytes(&[*at]); + }, + Op::HashValue(hash) => { + out.write_bytes(&[3]); + out.write_bytes(hash.as_ref()); + }, + Op::Value(value) => { + out.write_bytes(&[4]); + let value = value.as_ref(); + VarInt(value.len() as u32).encode_into(out); + out.write_bytes(&value); + }, + Op::EndProof => { + out.write_bytes(&[5]); + }, + } } } - impl + Default> Decode for Enc { - fn decode(input: &mut I) -> core::result::Result { - let mut dest = H::default(); - input.read(dest.as_mut())?; - Ok(Enc(dest)) + impl + AsMut<[u8]> + Default> Op> { + /// Read an op, return op and number byte read. Or error if invalid encoded. + pub fn decode(encoded: &[u8]) -> Result<(Self, usize), ()> { + let mut i = 0; + if i >= encoded.len() { + return Err(()) + } + Ok(match encoded[i] { + 0 => { + let (len, offset) = VarInt::decode(&encoded[i + 1..])?; + i += 1 + offset; + if i + len.0 as usize >= encoded.len() { + return Err(()) + } + let key = &encoded[i..i + len.0 as usize]; + let mask = encoded[i + len.0 as usize]; + (Op::KeyPush(key.to_vec(), mask), i + len.0 as usize + 1) + }, + 1 => { + let (len, offset) = VarInt::decode(&encoded[i + 1..])?; + if len.0 > u16::MAX as u32 { + return Err(()) + } + (Op::KeyPop(len.0 as u16), i + 1 + offset) + }, + 2 => { + let mut hash = H::default(); + let end = i + 1 + hash.as_ref().len(); + if end >= encoded.len() { + return Err(()) + } + hash.as_mut().copy_from_slice(&encoded[i + 1..end]); + let mask = encoded[end]; + (Op::HashChild(hash, mask), end + 1) + }, + 3 => { + let mut hash = H::default(); + let end = i + 1 + hash.as_ref().len(); + if end >= encoded.len() { + return Err(()) + } + hash.as_mut().copy_from_slice(&encoded[i + 1..end]); + (Op::HashValue(hash), end) + }, + 4 => { + let (len, offset) = VarInt::decode(&encoded[i + 1..])?; + i += 1 + offset; + if i + len.0 as usize > encoded.len() { + return Err(()) + } + let value = &encoded[i..i + len.0 as usize]; + (Op::Value(value.to_vec()), i + len.0 as usize) + }, + 5 => (Op::EndProof, 1), + _ => return Err(()), + }) } } } diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index f9bd693d..be7c694b 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -372,6 +372,12 @@ fn test_query_plan_internal() { shifted } + fn hash + Default>(b: &[u8]) -> H { + let mut hash = H::default(); + hash.as_mut().copy_from_slice(&b[..]); + hash + } + if kind == ProofKind::CompactContent { let all = match L::MAX_INLINE_VALUE { Some(1) => true, @@ -380,7 +386,7 @@ fn test_query_plan_internal() { }; let mut nb = 0; while let Some(proof) = proofs.pop() { - use trie_db::query_plan::compact_content_proof::{Encode, Op}; + use trie_db::query_plan::compact_content_proof::Op; // full on iter all // assert_eq!(proofs.len(), 1); assert_eq!(proof.len(), 1); @@ -421,23 +427,25 @@ fn test_query_plan_internal() { Op::Value([0; 32].to_vec()), Op::KeyPop(7), Op::HashChild( - (&[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, - 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, - 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, - 248, - ][..]) - .into(), + hash( + &[ + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, + 236, 20, 32, 247, 110, 189, 213, 140, 86, + 162, 229, 70, 86, 163, 223, 26, 52, 253, + 176, 201, 65, 248, + ][..], + ), 1, ), Op::HashChild( - (&[ - 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, - 14, 161, 91, 109, 136, 84, 6, 128, 190, 201, - 49, 142, 21, 154, 250, 246, 133, 0, 199, 138, - 49, - ][..]) - .into(), + hash( + &[ + 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, + 78, 14, 161, 91, 109, 136, 84, 6, 128, 190, + 201, 49, 142, 21, 154, 250, 246, 133, 0, + 199, 138, 49, + ][..], + ), 8, ), ] @@ -461,13 +469,14 @@ fn test_query_plan_internal() { Op::Value(b"building".to_vec()), Op::KeyPop(9), Op::HashChild( - (&[ - 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, - 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, - 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, - 119, - ][..]) - .into(), + hash( + &[ + 225, 211, 100, 128, 231, 82, 240, 112, 33, + 165, 225, 30, 244, 128, 56, 45, 17, 21, + 138, 87, 3, 211, 231, 109, 244, 137, 208, + 244, 12, 65, 196, 119, + ][..], + ), 1, ), ] @@ -482,58 +491,57 @@ fn test_query_plan_internal() { Op::KeyPush(shifted(b"do", false), 0xf0), // hash value here is not really good (could only be // with child hashes when no hash query). - Op::HashValue( - (&[ + Op::HashValue(hash( + &[ 48, 51, 75, 77, 6, 75, 210, 124, 205, 63, 59, 165, 81, 140, 222, 237, 196, 168, 203, 206, 105, 245, 15, 154, 233, 147, 189, 123, 194, 243, 179, 137, - ][..]) - .into(), - ), + ][..], + )), Op::KeyPush(b"g".to_vec(), 0xff), - Op::HashValue( - (&[ + Op::HashValue(hash( + &[ 104, 225, 103, 23, 160, 148, 143, 214, 98, 64, 250, 245, 134, 99, 233, 36, 28, 150, 26, 205, 25, 165, 122, 211, 170, 180, 45, 82, 143, 71, 191, 19, - ][..]) - .into(), - ), + ][..], + )), Op::KeyPush(b"e".to_vec(), 0xff), Op::Value([0; 32].to_vec()), Op::KeyPop(7), Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::HashValue( - (&[ + Op::HashValue(hash( + &[ 170, 195, 61, 227, 244, 86, 86, 205, 233, 84, 40, 116, 166, 25, 158, 33, 18, 236, 208, 172, 115, 246, 158, 34, 158, 170, 197, 139, 219, 254, 124, 136, - ][..]) - .into(), - ), + ][..], + )), Op::KeyPop(5), Op::HashChild( - (&[ - 115, 96, 173, 184, 157, 30, 165, 173, 98, 91, - 45, 97, 173, 249, 2, 240, 133, 247, 131, 7, - 128, 195, 235, 114, 210, 152, 24, 22, 105, 232, - 147, 171, - ][..]) - .into(), + hash( + &[ + 115, 96, 173, 184, 157, 30, 165, 173, 98, + 91, 45, 97, 173, 249, 2, 240, 133, 247, + 131, 7, 128, 195, 235, 114, 210, 152, 24, + 22, 105, 232, 147, 171, + ][..], + ), 5, ), Op::KeyPop(4), Op::HashChild( - (&[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, - 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, - 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, - 248, - ][..]) - .into(), + hash( + &[ + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, + 236, 20, 32, 247, 110, 189, 213, 140, 86, + 162, 229, 70, 86, 163, 223, 26, 52, 253, + 176, 201, 65, 248, + ][..], + ), 1, ), ] @@ -556,13 +564,14 @@ fn test_query_plan_internal() { Op::Value(b"building".to_vec()), Op::KeyPop(9), Op::HashChild( - (&[ - 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, - 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, - 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, - 119, - ][..]) - .into(), + hash( + &[ + 225, 211, 100, 128, 231, 82, 240, 112, 33, + 165, 225, 30, 244, 128, 56, 45, 17, 21, + 138, 87, 3, 211, 231, 109, 244, 137, 208, + 244, 12, 65, 196, 119, + ][..], + ), 1, ), ] @@ -575,33 +584,36 @@ fn test_query_plan_internal() { Op::Value(b"bravo".to_vec()), Op::KeyPop(9), Op::HashChild( - (&[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, - 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, - 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, - 248, - ][..]) - .into(), + hash( + &[ + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, + 236, 20, 32, 247, 110, 189, 213, 140, 86, + 162, 229, 70, 86, 163, 223, 26, 52, 253, + 176, 201, 65, 248, + ][..], + ), 1, ), Op::HashChild( - (&[ - 223, 91, 16, 28, 134, 71, 144, 93, 127, 153, - 131, 180, 101, 103, 252, 121, 200, 66, 33, 188, - 58, 187, 247, 197, 65, 169, 112, 46, 241, 22, - 96, 196, - ][..]) - .into(), + hash( + &[ + 223, 91, 16, 28, 134, 71, 144, 93, 127, + 153, 131, 180, 101, 103, 252, 121, 200, 66, + 33, 188, 58, 187, 247, 197, 65, 169, 112, + 46, 241, 22, 96, 196, + ][..], + ), 4, ), Op::HashChild( - (&[ - 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, - 14, 161, 91, 109, 136, 84, 6, 128, 190, 201, - 49, 142, 21, 154, 250, 246, 133, 0, 199, 138, - 49, - ][..]) - .into(), + hash( + &[ + 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, + 78, 14, 161, 91, 109, 136, 84, 6, 128, 190, + 201, 49, 142, 21, 154, 250, 246, 133, 0, + 199, 138, 49, + ][..], + ), 8, ), ] @@ -641,11 +653,11 @@ fn test_query_plan_internal() { _ => break, }; - let mut encoded = Vec::new(); + let mut encoded = InMemoryRecorder::default(); for r in refs { - r.encode_to(&mut encoded); + r.encode_into(&mut encoded); } - assert_eq!(proof[0], encoded); + assert_eq!(proof[0], encoded.buffer); nb += 1; } continue From da82a7d468bcf0edd8e1c1f532837123296d54ad Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 14 Jun 2023 09:22:42 +0200 Subject: [PATCH 063/154] plug content read iter (unimpl) --- trie-db/src/query_plan.rs | 401 +++++++++++++++++++++++++++++++++++++- trie-db/test/src/proof.rs | 66 +++++-- 2 files changed, 448 insertions(+), 19 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 43f2812b..3bfdab49 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -869,7 +869,14 @@ impl HaltedStateRecord { /// When process is halted keep execution state /// to restore later. -pub struct HaltedStateCheck<'a, L: TrieLayout, C, D: SplitFirst> { +pub enum HaltedStateCheck<'a, L: TrieLayout, C, D: SplitFirst> { + Node(HaltedStateCheckNode<'a, L, C, D>), + Content(HaltedStateCheckContent<'a, L, C>), +} + +/// When process is halted keep execution state +/// to restore later. +pub struct HaltedStateCheckNode<'a, L: TrieLayout, C, D: SplitFirst> { query_plan: QueryPlan<'a, C>, current: Option>, stack: ReadStack, @@ -877,7 +884,9 @@ pub struct HaltedStateCheck<'a, L: TrieLayout, C, D: SplitFirst> { restore_offset: usize, } -impl<'a, L: TrieLayout, C, D: SplitFirst> From> for HaltedStateCheck<'a, L, C, D> { +impl<'a, L: TrieLayout, C, D: SplitFirst> From> + for HaltedStateCheckNode<'a, L, C, D> +{ fn from(query_plan: QueryPlan<'a, C>) -> Self { let is_compact = match query_plan.kind { ProofKind::FullNodes => false, @@ -885,7 +894,7 @@ impl<'a, L: TrieLayout, C, D: SplitFirst> From> for HaltedState _ => false, }; - HaltedStateCheck { + HaltedStateCheckNode { stack: ReadStack { items: Default::default(), start_items: 0, @@ -903,6 +912,35 @@ impl<'a, L: TrieLayout, C, D: SplitFirst> From> for HaltedState } } +/// When process is halted keep execution state +/// to restore later. +pub struct HaltedStateCheckContent<'a, L: TrieLayout, C> { + query_plan: QueryPlan<'a, C>, + current: Option>, + stack: ReadContentStack, + state: ReadProofState, + restore_offset: usize, +} + +impl<'a, L: TrieLayout, C> From> for HaltedStateCheckContent<'a, L, C> { + fn from(query_plan: QueryPlan<'a, C>) -> Self { + HaltedStateCheckContent { + stack: ReadContentStack { + items: Default::default(), + start_items: 0, + prefix: Default::default(), + expect_value: false, + iter_prefix: None, + _ph: PhantomData, + }, + state: ReadProofState::NotStarted, + current: None, + restore_offset: 0, + query_plan, + } + } +} + struct RecordStack { recorder: Recorder, items: Vec, @@ -1461,6 +1499,24 @@ where restore_offset: usize, } +/// Proof reading iterator. +pub struct ReadProofContentIterator<'a, L, C, P> +where + L: TrieLayout, + C: Iterator>, + P: Iterator, Vec>>>, +{ + // always needed, this is option only + // to avoid unsafe code when halting. + query_plan: Option>, + proof: P, + expected_root: Option>, + current: Option>, + state: ReadProofState, + stack: ReadContentStack, + restore_offset: usize, +} + #[derive(Eq, PartialEq)] enum ReadProofState { /// Iteration not started. @@ -1485,6 +1541,14 @@ struct ItemStack { next_descended_child: u8, } +struct ItemContentStack { + children: Vec>>, + inline_value: Option>, + attached_value_hash: Option>, + depth: usize, + next_descended_child: u8, +} + impl Clone for ItemStack { fn clone(&self) -> Self { ItemStack { @@ -1585,6 +1649,16 @@ struct ReadStack { _ph: PhantomData, } +struct ReadContentStack { + items: Vec>, + prefix: NibbleVec, + // limit and wether we return value and if hash only iteration. + iter_prefix: Option<(usize, bool, bool)>, + start_items: usize, + expect_value: bool, + _ph: PhantomData, +} + impl Clone for ReadStack { fn clone(&self) -> Self { ReadStack { @@ -2315,6 +2389,253 @@ where } } +impl<'a, L, C, P> Iterator for ReadProofContentIterator<'a, L, C, P> +where + L: TrieLayout, + C: Iterator>, + P: Iterator, Vec>>>, +{ + type Item = Result>, VerifyError, CError>>; + + fn next(&mut self) -> Option { + todo!() + /* + if self.state == ReadProofState::Finished { + return None + } + let check_hash = self.expected_root.is_some(); + let mut to_check_slice = if self.state == ReadProofState::Halted { + self.state = ReadProofState::Running; + self.current + .as_ref() + .map(|n| NibbleSlice::new_offset(n.key, self.restore_offset)) + } else { + self.current.as_ref().map(|n| NibbleSlice::new(n.key)) + }; + + // read proof + loop { + if self.state == ReadProofState::SwitchQueryPlan || + self.state == ReadProofState::NotStarted + { + let query_plan = self.query_plan.as_mut().expect("Removed with state"); + if let Some(next) = query_plan.items.next() { + let (ordered, common_nibbles) = if let Some(old) = self.current.as_ref() { + old.before(&next) + } else { + (true, 0) + }; + if !ordered { + if query_plan.ignore_unordered { + continue + } else { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::UnorderedKey(next.key.to_vec()))) + } + } + + let r = self.stack.pop_until(common_nibbles, &self.expected_root, false); + if let Err(e) = r { + self.state = ReadProofState::Finished; + return Some(Err(e)) + } + self.state = ReadProofState::Running; + self.current = Some(next); + to_check_slice = self + .current + .as_ref() + .map(|n| NibbleSlice::new_offset(n.key, common_nibbles)); + } else { + self.state = ReadProofState::PlanConsumed; + self.current = None; + break + } + }; + let did_prefix = self.stack.iter_prefix.is_some(); + while let Some((_, accessed_value_node, hash_only)) = self.stack.iter_prefix.clone() { + // prefix iteration + if !accessed_value_node { + self.stack.iter_prefix.as_mut().map(|s| { + s.1 = true; + }); + match self.stack.access_value(&mut self.proof, check_hash, hash_only) { + Ok((Some(value), None)) => + return Some(Ok(ReadProofItem::Value( + self.stack.prefix.inner().to_vec().into(), + value, + ))), + Ok((None, Some(hash))) => + return Some(Ok(ReadProofItem::Hash( + self.stack.prefix.inner().to_vec().into(), + hash, + ))), + Ok((None, None)) => (), + Ok(_) => unreachable!(), + Err(e) => { + self.state = ReadProofState::Finished; + return Some(Err(e)) + }, + }; + } + while let Some(child_index) = self.stack.items.last_mut().and_then(|last| { + if last.next_descended_child as usize >= NIBBLE_LENGTH { + None + } else { + let child_index = last.next_descended_child; + last.next_descended_child += 1; + Some(child_index) + } + }) { + let r = match self.stack.try_stack_child( + child_index, + &mut self.proof, + &self.expected_root, + None, + false, + ) { + Ok(r) => r, + Err(e) => { + self.state = ReadProofState::Finished; + return Some(Err(e)) + }, + }; + match r { + TryStackChildResult::Stacked => { + self.stack.iter_prefix.as_mut().map(|p| { + p.1 = false; + }); + break + }, + TryStackChildResult::StackedDescendIncomplete => { + unreachable!("slice query none"); + }, + TryStackChildResult::NotStacked => break, + TryStackChildResult::NotStackedBranch => (), + TryStackChildResult::Halted => { + if let Some(last) = self.stack.items.last_mut() { + last.next_descended_child -= 1; + } + return self.halt(None) + }, + } + } + if self.stack.iter_prefix.as_ref().map(|p| p.1).unwrap_or_default() { + if !match self.stack.pop(&self.expected_root) { + Ok(r) => r, + Err(e) => { + self.state = ReadProofState::Finished; + return Some(Err(e)) + }, + } { + // end iter + self.stack.exit_prefix_iter(); + } + } + } + if did_prefix { + // exit a prefix iter, next content looping + self.state = ReadProofState::SwitchQueryPlan; + continue + } + let to_check = self.current.as_ref().expect("Init above"); + let to_check_len = to_check.key.len() * nibble_ops::NIBBLE_PER_BYTE; + let mut to_check_slice = to_check_slice.as_mut().expect("Init above"); + let as_prefix = to_check.as_prefix; // TODO useless? + let hash_only = to_check.hash_only; // TODO useless? + let mut at_value = false; + match self.stack.prefix.len().cmp(&to_check_len) { + Ordering::Equal => + if !self.stack.items.is_empty() { + at_value = true; + }, + Ordering::Less => (), + Ordering::Greater => { + unreachable!(); + }, + } + + if at_value { + if as_prefix { + self.stack.enter_prefix_iter(hash_only); + continue + } + self.state = ReadProofState::SwitchQueryPlan; + match self.stack.access_value(&mut self.proof, check_hash, hash_only) { + Ok((Some(value), None)) => + return Some(Ok(ReadProofItem::Value(to_check.key.into(), value))), + Ok((None, Some(hash))) => + return Some(Ok(ReadProofItem::Hash(to_check.key.into(), hash))), + Ok((None, None)) => return Some(Ok(ReadProofItem::NoValue(to_check.key))), + Ok(_) => unreachable!(), + Err(e) => { + self.state = ReadProofState::Finished; + return Some(Err(e)) + }, + } + } + + let child_index = if self.stack.items.len() == 0 { + // dummy + 0 + } else { + to_check_slice.at(0) + }; + let r = match self.stack.try_stack_child( + child_index, + &mut self.proof, + &self.expected_root, + Some(&mut to_check_slice), + to_check.as_prefix, + ) { + Ok(r) => r, + Err(e) => { + self.state = ReadProofState::Finished; + return Some(Err(e)) + }, + }; + match r { + TryStackChildResult::Stacked => (), + TryStackChildResult::StackedDescendIncomplete => { + if as_prefix { + self.stack.enter_prefix_iter(hash_only); + continue + } + self.state = ReadProofState::SwitchQueryPlan; + return Some(Ok(ReadProofItem::NoValue(to_check.key))) + }, + TryStackChildResult::NotStacked => { + self.state = ReadProofState::SwitchQueryPlan; + return Some(Ok(ReadProofItem::NoValue(to_check.key))) + }, + TryStackChildResult::NotStackedBranch => { + self.state = ReadProofState::SwitchQueryPlan; + return Some(Ok(ReadProofItem::NoValue(to_check.key))) + }, + TryStackChildResult::Halted => return self.halt(Some(to_check_slice)), + } + } + + debug_assert!(self.state == ReadProofState::PlanConsumed); + if self.is_compact { + let stack_to = 0; // TODO restart is different + // let r = self.stack.pop_until(common_nibbles, &self.expected_root); + let r = self.stack.pop_until(stack_to, &self.expected_root, false); + if let Err(e) = r { + self.state = ReadProofState::Finished; + return Some(Err(e)) + } + } else { + if self.proof.next().is_some() { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) + } + } + self.state = ReadProofState::Finished; + return None + */ + } +} + impl<'a, L, C, D, P> ReadProofIterator<'a, L, C, D, P> where L: TrieLayout, @@ -2351,13 +2672,13 @@ where }, ); stack.start_items = stack.items.len(); - Some(Ok(ReadProofItem::Halted(Box::new(HaltedStateCheck { + Some(Ok(ReadProofItem::Halted(Box::new(HaltedStateCheck::Node(HaltedStateCheckNode { query_plan, current, stack, state: ReadProofState::Halted, restore_offset: to_check_slice.map(|s| s.offset()).unwrap_or(0), - })))) + }))))) } } @@ -2375,7 +2696,10 @@ where P: Iterator, D: SplitFirst, { - let HaltedStateCheck { query_plan, current, stack, state, restore_offset } = state; + let HaltedStateCheck::Node(state) = state else { + return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent + }; + let HaltedStateCheckNode { query_plan, current, stack, state, restore_offset } = state; match query_plan.kind { ProofKind::CompactContent => { @@ -2396,8 +2720,46 @@ where }) } +/// Read the proof. +/// +/// If expected root is None, then we do not check hashes at all. +pub fn verify_query_plan_iter_content<'a, L, C, P>( + state: HaltedStateCheck<'a, L, C, Vec>, + proof: P, + expected_root: Option>, +) -> Result, VerifyError, CError>> +where + L: TrieLayout, + C: Iterator>, + P: Iterator, Vec>>>, +{ + let HaltedStateCheck::Content(state) = state else { + return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent + }; + + let HaltedStateCheckContent { query_plan, current, stack, state, restore_offset } = state; + + match query_plan.kind { + ProofKind::CompactContent => (), + _ => { + return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent + }, + }; + + Ok(ReadProofContentIterator { + query_plan: Some(query_plan), + proof, + expected_root, + current, + state, + stack, + restore_offset, + }) +} + pub mod compact_content_proof { use super::RecorderOutput; + use core::marker::PhantomData; /// Representation of each encoded action /// for building the proof. @@ -2608,4 +2970,31 @@ pub mod compact_content_proof { }) } } + + /// Iterator on op from a in memory encoded proof. + pub struct IterOpProof + AsMut<[u8]> + Default, B: AsRef<[u8]>>( + B, + usize, + PhantomData, + ); + + impl + AsMut<[u8]> + Default, B: AsRef<[u8]>> From for IterOpProof { + fn from(b: B) -> Self { + Self(b, 0, PhantomData) + } + } + + impl + AsMut<[u8]> + Default, B: AsRef<[u8]>> Iterator for IterOpProof { + type Item = Option>>; + + fn next(&mut self) -> Option { + match Op::decode(self.0.as_ref()) { + Ok((op, len)) => { + self.1 += len; + Some(Some(op)) + }, + Err(_) => Some(None), + } + } + } } diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index be7c694b..bff48250 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -245,9 +245,10 @@ fn test_verify_decode_error_internal() { test_layouts!(test_query_plan, test_query_plan_internal); fn test_query_plan_internal() { use trie_db::query_plan::{ - record_query_plan, verify_query_plan_iter, HaltedStateCheck, HaltedStateRecord, - InMemQueryPlan, InMemQueryPlanItem, InMemoryRecorder, ProofKind, QueryPlan, ReadProofItem, - Recorder, + compact_content_proof::IterOpProof, record_query_plan, verify_query_plan_iter, + verify_query_plan_iter_content, HaltedStateCheck, HaltedStateCheckContent, + HaltedStateCheckNode, HaltedStateRecord, InMemQueryPlan, InMemQueryPlanItem, + InMemoryRecorder, ProofKind, QueryPlan, ReadProofItem, Recorder, }; let set = test_entries(); @@ -385,6 +386,7 @@ fn test_query_plan_internal() { _ => continue, }; let mut nb = 0; + let mut proofs = proofs.clone(); while let Some(proof) = proofs.pop() { use trie_db::query_plan::compact_content_proof::Op; // full on iter all @@ -660,11 +662,16 @@ fn test_query_plan_internal() { assert_eq!(proof[0], encoded.buffer); nb += 1; } - continue + // continue } let mut query_plan_iter: QueryPlan<_> = query_plan.as_ref(); - let mut run_state: Option> = Some(query_plan_iter.into()); + let is_content_proof = kind == ProofKind::CompactContent; + let mut run_state: Option> = Some(if is_content_proof { + HaltedStateCheck::Content(query_plan_iter.into()) + } else { + HaltedStateCheck::Node(query_plan_iter.into()) + }); let mut has_run_full = false; while let Some(state) = run_state.take() { let proof = if let Some(proof) = proofs.pop() { @@ -677,15 +684,44 @@ fn test_query_plan_internal() { proofs.clear(); std::mem::take(&mut full_proof) }; - let verify_iter = verify_query_plan_iter::( - state, - proof.into_iter(), - Some(root.clone()), - ) - .unwrap(); + let (mut verify_iter, mut verify_iter_content) = if is_content_proof { + ( + None, + Some( + verify_query_plan_iter_content::>( + state, + (&proof[0]).into(), + Some(root.clone()), + ) + .unwrap(), + ), + ) + } else { + ( + Some( + verify_query_plan_iter::( + state, + proof.into_iter(), + Some(root.clone()), + ) + .unwrap(), + ), + None, + ) + }; + let mut next_item = || { + if let Some(verify_iter) = verify_iter.as_mut() { + verify_iter.next() + } else if let Some(verify_iter_content) = verify_iter_content.as_mut() { + verify_iter_content.next() + } else { + None + } + }; + let content: BTreeMap<_, _> = set.iter().cloned().collect(); let mut in_prefix = false; - for item in verify_iter { + while let Some(item) = next_item() { match item.unwrap() { ReadProofItem::Hash(key, hash) => { assert!(hash_only); @@ -716,7 +752,11 @@ fn test_query_plan_internal() { if run_state.is_none() && !has_run_full { has_run_full = true; query_plan_iter = query_plan.as_ref(); - run_state = Some(query_plan_iter.into()); + run_state = Some(if is_content_proof { + HaltedStateCheck::Content(query_plan_iter.into()) + } else { + HaltedStateCheck::Node(query_plan_iter.into()) + }); } } if !has_run_full { From de946ecde3b438468a65bf86cbc1ff5605db3e7b Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 14 Jun 2023 10:50:48 +0200 Subject: [PATCH 064/154] pop on next --- trie-db/src/query_plan.rs | 126 ++++++++++++++++++++++++++++++++------ 1 file changed, 106 insertions(+), 20 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 3bfdab49..586c3244 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -931,6 +931,9 @@ impl<'a, L: TrieLayout, C> From> for HaltedStateCheckContent<'a prefix: Default::default(), expect_value: false, iter_prefix: None, + is_prev_push_key: false, + is_prev_pop_key: false, + first: true, _ph: PhantomData, }, state: ReadProofState::NotStarted, @@ -1541,12 +1544,21 @@ struct ItemStack { next_descended_child: u8, } +#[derive(Clone)] +enum ValueSet { + None, + Standard(V), + HashOnly(H), + // ForceInline(V), + // ForceHashed(V), + // BranchHash(H, u8), +} + struct ItemContentStack { children: Vec>>, - inline_value: Option>, + value: ValueSet, Vec>, attached_value_hash: Option>, depth: usize, - next_descended_child: u8, } impl Clone for ItemStack { @@ -1561,6 +1573,17 @@ impl Clone for ItemStack { } } +impl Clone for ItemContentStack { + fn clone(&self) -> Self { + ItemContentStack { + children: self.children.clone(), + value: self.value.clone(), + attached_value_hash: self.attached_value_hash, + depth: self.depth, + } + } +} + #[derive(Clone)] enum ItemStackNode { Inline(OwnedNode>), @@ -1656,6 +1679,9 @@ struct ReadContentStack { iter_prefix: Option<(usize, bool, bool)>, start_items: usize, expect_value: bool, + is_prev_push_key: bool, + is_prev_pop_key: bool, + first: bool, _ph: PhantomData, } @@ -1673,6 +1699,22 @@ impl Clone for ReadStack { } } +impl Clone for ReadContentStack { + fn clone(&self) -> Self { + ReadContentStack { + items: self.items.clone(), + prefix: self.prefix.clone(), + start_items: self.start_items.clone(), + iter_prefix: self.iter_prefix, + expect_value: self.expect_value, + is_prev_push_key: self.is_prev_push_key, + is_prev_pop_key: self.is_prev_pop_key, + first: self.first, + _ph: PhantomData, + } + } +} + fn verify_hash( data: &[u8], expected: &[u8], @@ -2124,6 +2166,52 @@ impl ReadStack { } } +impl ReadContentStack { + fn pop_until( + &mut self, + target: usize, + check_only: bool, // TODO used? + ) -> Result<(), VerifyError, CError>> { + // TODO pop with check only, here unefficient implementation where we just restore + + let mut restore = None; + if check_only { + restore = Some(self.clone()); + self.iter_prefix = None; + } + // one by one + while let Some(last) = self.items.last() { + // depth should match. + match last.depth.cmp(&target) { + Ordering::Greater => { + // TODO could implicit pop here with a variant + // that do not write redundant pop. + return Err(VerifyError::ExtraneousNode) // TODO more precise error + }, + Ordering::Less => { + if self.first { + // allowed to have next at a upper level + } else { + return Err(VerifyError::ExtraneousNode) + } + }, + Ordering::Equal => return Ok(()), + } + + // start_items update. + self.start_items = core::cmp::min(self.start_items, self.items.len()); + // one by one + let _ = self.items.pop(); + } + + if let Some(old) = restore.take() { + *self = old; + return Ok(()) + } + Err(VerifyError::ExtraneousNode) + } +} + /// Content return on success when reading proof. pub enum ReadProofItem<'a, L: TrieLayout, C, D: SplitFirst> { /// Successfull read of proof, not all content read. @@ -2158,14 +2246,13 @@ where return None } let check_hash = self.expected_root.is_some(); - let mut to_check_slice = if self.state == ReadProofState::Halted { + if self.state == ReadProofState::Halted { self.state = ReadProofState::Running; - self.current - .as_ref() - .map(|n| NibbleSlice::new_offset(n.key, self.restore_offset)) - } else { - self.current.as_ref().map(|n| NibbleSlice::new(n.key)) - }; + } + let mut to_check_slice = self + .current + .as_ref() + .map(|n| NibbleSlice::new_offset(n.key, self.restore_offset)); // read proof loop { @@ -2398,21 +2485,17 @@ where type Item = Result>, VerifyError, CError>>; fn next(&mut self) -> Option { - todo!() - /* if self.state == ReadProofState::Finished { return None } let check_hash = self.expected_root.is_some(); - let mut to_check_slice = if self.state == ReadProofState::Halted { + if self.state == ReadProofState::Halted { self.state = ReadProofState::Running; - self.current - .as_ref() - .map(|n| NibbleSlice::new_offset(n.key, self.restore_offset)) - } else { - self.current.as_ref().map(|n| NibbleSlice::new(n.key)) - }; - + } + let mut to_check_slice = self + .current + .as_ref() + .map(|n| NibbleSlice::new_offset(n.key, self.restore_offset)); // read proof loop { if self.state == ReadProofState::SwitchQueryPlan || @@ -2434,7 +2517,7 @@ where } } - let r = self.stack.pop_until(common_nibbles, &self.expected_root, false); + let r = self.stack.pop_until(common_nibbles, false); if let Err(e) = r { self.state = ReadProofState::Finished; return Some(Err(e)) @@ -2451,6 +2534,9 @@ where break } }; + } + todo!() + /* let did_prefix = self.stack.iter_prefix.is_some(); while let Some((_, accessed_value_node, hash_only)) = self.stack.iter_prefix.clone() { // prefix iteration From 2bf158abae496014ccf732781f9df7fd7b9dd117 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 14 Jun 2023 12:55:51 +0200 Subject: [PATCH 065/154] pending --- trie-db/src/nibble/leftnibbleslice.rs | 20 +- trie-db/src/nibble/nibblevec.rs | 21 +- trie-db/src/query_plan.rs | 317 +++++++++++++++++++++++++- 3 files changed, 343 insertions(+), 15 deletions(-) diff --git a/trie-db/src/nibble/leftnibbleslice.rs b/trie-db/src/nibble/leftnibbleslice.rs index f3ba2527..0424205f 100644 --- a/trie-db/src/nibble/leftnibbleslice.rs +++ b/trie-db/src/nibble/leftnibbleslice.rs @@ -16,7 +16,7 @@ use crate::rstd::cmp::{self, Ordering}; use crate::nibble::{ nibble_ops::{self, NIBBLE_PER_BYTE}, - NibbleSlice, + NibbleSlice, NibbleVec, }; /// A representation of a nibble slice which is left-aligned. The regular `NibbleSlice` is @@ -24,16 +24,32 @@ use crate::nibble::{ /// /// This is an immutable struct. No operations actually change it. pub struct LeftNibbleSlice<'a> { - bytes: &'a [u8], + pub(crate) bytes: &'a [u8], len: usize, } +impl<'a> From<&'a NibbleVec> for LeftNibbleSlice<'a> { + fn from(v: &'a NibbleVec) -> Self { + LeftNibbleSlice { bytes: v.inner.as_slice(), len: v.len } + } +} + impl<'a> LeftNibbleSlice<'a> { /// Constructs a byte-aligned nibble slice from a byte slice. pub fn new(bytes: &'a [u8]) -> Self { LeftNibbleSlice { bytes, len: bytes.len() * NIBBLE_PER_BYTE } } + /// Constructs a byte-aligned nibble slice from a byte slice. + pub fn new_with_mask(bytes: &'a [u8], mask: u8) -> Self { + let mut len = bytes.len() * NIBBLE_PER_BYTE; + // warn this is only working with hex trie + if mask != 255 { + len = len.saturating_sub(1); + } + LeftNibbleSlice { bytes, len } + } + /// Returns the length of the slice in nibbles. pub fn len(&self) -> usize { self.len diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index 26bf8574..31bb5816 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -119,27 +119,32 @@ impl NibbleVec { /// Append another `NibbleVec`. Can be slow (alignement of second vec). pub fn append(&mut self, v: &NibbleVec) { - if v.len == 0 { + self.append_slice(v.into()); + } + + /// Append a `LeftNibbleSlice`. Can be slow (alignement of second vec). + pub fn append_slice(&mut self, v: crate::nibble::LeftNibbleSlice) { + if v.len() == 0 { return } - let final_len = self.len + v.len; + let final_len = self.len + v.len(); let offset = self.len % nibble_ops::NIBBLE_PER_BYTE; let final_offset = final_len % nibble_ops::NIBBLE_PER_BYTE; let last_index = self.len / nibble_ops::NIBBLE_PER_BYTE; if offset > 0 { let (s1, s2) = nibble_ops::SPLIT_SHIFTS; self.inner[last_index] = - nibble_ops::pad_left(self.inner[last_index]) | (v.inner[0] >> s2); - (0..v.inner.len() - 1) - .for_each(|i| self.inner.push(v.inner[i] << s1 | v.inner[i + 1] >> s2)); + nibble_ops::pad_left(self.inner[last_index]) | (v.bytes[0] >> s2); + (0..v.bytes.len() - 1) + .for_each(|i| self.inner.push(v.bytes[i] << s1 | v.bytes[i + 1] >> s2)); if final_offset > 0 { - self.inner.push(v.inner[v.inner.len() - 1] << s1); + self.inner.push(v.bytes[v.bytes.len() - 1] << s1); } } else { - (0..v.inner.len()).for_each(|i| self.inner.push(v.inner[i])); + (0..v.bytes.len()).for_each(|i| self.inner.push(v.bytes[i])); } - self.len += v.len; + self.len += v.len(); } /// Append a `Partial`. Can be slow (alignement of partial). diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 586c3244..6bcd8f19 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -59,7 +59,7 @@ impl InMemQueryPlanItem { } /// Item to query. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct QueryPlanItem<'a> { pub key: &'a [u8], pub hash_only: bool, @@ -933,6 +933,7 @@ impl<'a, L: TrieLayout, C> From> for HaltedStateCheckContent<'a iter_prefix: None, is_prev_push_key: false, is_prev_pop_key: false, + is_prev_hash_child: None, first: true, _ph: PhantomData, }, @@ -1554,10 +1555,20 @@ enum ValueSet { // BranchHash(H, u8), } +impl ValueSet { + fn as_ref(&self) -> Option<&V> { + match self { + ValueSet::Standard(v) => Some(v), + //ValueSet::ForceInline(v) | ValueSet::ForceHashed(v) => Some(v), + ValueSet::HashOnly(..) | ValueSet::None => None, + // ValueSet::BranchHash(..) => None, + } + } +} + struct ItemContentStack { - children: Vec>>, + children: Vec>>>, value: ValueSet, Vec>, - attached_value_hash: Option>, depth: usize, } @@ -1578,7 +1589,6 @@ impl Clone for ItemContentStack { ItemContentStack { children: self.children.clone(), value: self.value.clone(), - attached_value_hash: self.attached_value_hash, depth: self.depth, } } @@ -1678,6 +1688,7 @@ struct ReadContentStack { // limit and wether we return value and if hash only iteration. iter_prefix: Option<(usize, bool, bool)>, start_items: usize, + is_prev_hash_child: Option, expect_value: bool, is_prev_push_key: bool, is_prev_pop_key: bool, @@ -1709,6 +1720,7 @@ impl Clone for ReadContentStack { expect_value: self.expect_value, is_prev_push_key: self.is_prev_push_key, is_prev_pop_key: self.is_prev_pop_key, + is_prev_hash_child: self.is_prev_hash_child, first: self.first, _ph: PhantomData, } @@ -2210,6 +2222,187 @@ impl ReadContentStack { } Err(VerifyError::ExtraneousNode) } + + #[inline(always)] + fn stack_empty(&mut self, depth: usize) { + /* + items: Vec>, + prefix: NibbleVec, + // limit and wether we return value and if hash only iteration. + iter_prefix: Option<(usize, bool, bool)>, + start_items: usize, + */ + + self.items.push(ItemContentStack { + children: vec![None; NIBBLE_LENGTH], + value: ValueSet::None, + depth, + }) + } + + #[inline(always)] + fn stack_pop( + &mut self, + full_key: &NibbleVec, + nb_nibble: Option, + expected_root: &Option>, + ) -> Result<(), VerifyError, CError>> { + let target_depth = nb_nibble.map(|n| full_key.len() - n); + let mut first = true; + while self + .items + .last() + .map(|item| target_depth.map(|target| item.depth > target).unwrap_or(true)) + .unwrap_or(false) + { + let item = self.items.pop().expect("Checked"); + let mut from_depth = + self.items.last().map(|item| item.depth).unwrap_or(target_depth.unwrap_or(0)); + if let Some(from) = target_depth { + if from > from_depth { + self.stack_empty(from); + from_depth = from; + } + } + let depth = item.depth; + let is_root = target_depth.is_none() && self.items.is_empty(); + let inc = if is_root { 0 } else { 1 }; + + let child_reference = if item.children.iter().any(|child| child.is_some()) { + let nkey = (depth > (from_depth + inc)) + .then(|| (from_depth + inc, depth - from_depth - inc)); + if L::USE_EXTENSION { + let extension_only = first && + matches!(&item.value, &ValueSet::None) && + item.children.iter().filter(|child| child.is_some()).count() == 1; + self.items.push(item); // TODO this looks bad (pop then push, branch or leaf function should or should + // not pop instead) + // encode branch + self.standard_extension( + &full_key.inner().as_ref()[..], + depth, + is_root, + nkey, + extension_only, + ) + } else { + self.items.push(item); // TODO this looks bad (pop then push, branch or leaf function should or should + // not pop instead) + // encode branch + self.no_extension( + &full_key.inner().as_ref()[..], + callback, + depth, + is_root, + nkey, + ) + } + } else { + // leaf with value + self.flush_value_change( + callback, + &full_key.inner().as_ref()[..], + from_depth + inc, + item.2, + &item.1, + is_root, + ) + }; + + if self.items.is_empty() && !is_root { + self.stack_empty(from_depth); + } + + if let Some(item) = self.items.last_mut() { + let child_ix = full_key.at(item.depth); + if let Some(hash) = item.children[child_ix as usize].as_ref() { + if self.items.len() == self.start_items + 1 { + if expected_root.is_some() && hash != child_reference { + return Some(Err(VerifyError::HashMismatch(*child_reference))) + } + } else { + return Some(Err(VerifyError::ExtraneousHashReference(*hash))) + // return Err(CompactDecoderError::HashChildNotOmitted.into()) + } + } + item.children[child_ix as usize] = Some(child_reference); + } else { + if let Some(root) = expected_root.as_ref() { + if nb_nibble.is_none() { + if root != child_reference { + return Some(Err(VerifyError::RootMismatch(*child_reference))) + } + } + } + } + first = false; + self.start_items = core::cmp::min(self.start_items, self.items.len()); + } + Ok(()) + } + + fn process(encoded_node: Vec, is_root: bool) -> ChildReference> { + let len = encoded_node.len(); + if !is_root && len < ::LENGTH { + let mut h = <::Out as Default>::default(); + h.as_mut()[..len].copy_from_slice(&encoded_node[..len]); + return ChildReference::Inline(h, len) + } + let hash = ::hash(encoded_node.as_slice()); + ChildReference::Hash(hash) + } + + // TODO factor with iter_build (reuse cacheaccum here). + #[inline(always)] + fn standard_extension( + &mut self, + key_branch: &[u8], + branch_d: usize, + is_root: bool, + nkey: Option<(usize, usize)>, + extension_only: bool, + ) -> ChildReference> { + let last = self.items.len() - 1; + assert_eq!(self.items[last].depth, branch_d); + + let ItemContentStack { children, value: v, depth, .. } = self.items.pop().expect("checked"); + + debug_assert!(branch_d == depth); + let pr = NibbleSlice::new_offset(&key_branch, branch_d); + + let hashed; + let value = if let Some(v) = v.as_ref() { + Some(if let Some(value) = Value::new_inline(v.as_ref(), L::MAX_INLINE_VALUE) { + value + } else { + let mut prefix = NibbleSlice::new_offset(&key_branch, 0); + prefix.advance(branch_d); + + hashed = ::hash(v.as_ref()); + Value::Node(hashed.as_ref()) + }) + } else { + None + }; + + // encode branch + let branch_hash = if !extension_only { + let encoded = L::Codec::branch_node(children.iter(), value); + Self::process(encoded, is_root && nkey.is_none()) + } else { + // This is hacky but extension only store as first children + children[0].unwrap() + }; + + if let Some(nkeyix) = nkey { + let pr = NibbleSlice::new_offset(&key_branch, nkeyix.0); + let nib = pr.right_range_iter(nkeyix.1); + let encoded = L::Codec::extension_node(nib, nkeyix.1, branch_hash); + Self::process(encoded, is_root) + } else { + branch_hash + } + } } /// Content return on success when reading proof. @@ -2534,10 +2727,123 @@ where break } }; + let did_prefix = self.stack.iter_prefix.is_some(); + } + use compact_content_proof::Op; + // op sequence check + + while let Some(op) = self.proof.next() { + let Some(op) = op else { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)); // TODO a decode op error + }; + + // check ordering logic + // TODO wrap in an error and put bools in a struct + match &op { + Op::KeyPush(..) => { + if self.stack.is_prev_push_key { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error + // TODO return + // Err(CompactDecoderError::ConsecutivePushKeys. + // into()) + } + self.stack.is_prev_push_key = true; + self.stack.is_prev_pop_key = false; + self.stack.is_prev_hash_child = None; + self.stack.first = false; + }, + Op::KeyPop(..) => { + if self.stack.is_prev_pop_key { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error + // return Err(CompactDecoderError::ConsecutivePopKeys. + // into()) + } + self.stack.is_prev_push_key = false; + self.stack.is_prev_pop_key = true; + self.stack.is_prev_hash_child = None; + self.stack.first = false; + }, + Op::HashChild(_, ix) => { + if let Some(prev_ix) = self.stack.is_prev_hash_child.as_ref() { + if prev_ix >= ix { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error + // return Err(CompactDecoderError::NotConsecutiveHash. + // into()) + } + } + // child ix on an existing content would be handle by iter_build. + self.stack.is_prev_push_key = false; + self.stack.is_prev_pop_key = false; + self.stack.is_prev_hash_child = Some(*ix); + }, + Op::Value(_) => { + // | Op::ValueForceInline(_) | Op::ValueForceHashed(_) => { + if !(self.stack.is_prev_push_key || self.stack.first) { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error + // return Err(CompactDecoderError::ValueNotAfterPush. + // into()) + } + self.stack.is_prev_push_key = false; + self.stack.is_prev_pop_key = false; + self.stack.is_prev_hash_child = None; + self.stack.first = false; + }, + _ => { + self.stack.is_prev_push_key = false; + self.stack.is_prev_pop_key = false; + self.stack.is_prev_hash_child = None; + self.stack.first = false; + }, + } + + // debug TODO make it log and external function + match &op { + Op::HashChild(hash, child_ix) => { + println!("ChildHash {:?}, {:?}, {:?}", self.stack.prefix, child_ix, hash); + }, + Op::HashValue(hash) => { + println!("ValueHash {:?}, {:?}", self.stack.prefix, hash); + }, + Op::Value(value) => { + println!("Value {:?}, {:?}", self.stack.prefix, value); + }, + _ => (), + } + + // act + let r = match op { + Op::KeyPush(partial, mask) => { + self.stack + .prefix + .append_slice(LeftNibbleSlice::new_with_mask(partial.as_slice(), mask)); + self.stack.stack_empty(self.stack.prefix.len()); + Ok(()) + }, + Op::KeyPop(nb_nibble) => { + let r = self.stack.stack_pop( + &self.stack.prefix, + Some(nb_nibble as usize), + &self.expected_root, + ); + self.stack.prefix.drop_lasts(nb_nibble.into()); + r + }, + Op::EndProof => break, + op => self.stack.set_cache_change(op.into()), + }; + if let Err(e) = r { + self.state = ReadProofState::Finished; + return Some(Err(e)) + } } todo!() /* - let did_prefix = self.stack.iter_prefix.is_some(); + while let Some((_, accessed_value_node, hash_only)) = self.stack.iter_prefix.clone() { // prefix iteration if !accessed_value_node { @@ -2563,6 +2869,7 @@ where }, }; } + while let Some(child_index) = self.stack.items.last_mut().and_then(|last| { if last.next_descended_child as usize >= NIBBLE_LENGTH { None From b9c4172d203b421ebcc8a26609418ca32050e4d8 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 14 Jun 2023 17:47:19 +0200 Subject: [PATCH 066/154] some impl --- trie-db/src/query_plan.rs | 764 +++++++++++++++++++------------------- trie-db/src/triedbmut.rs | 12 +- 2 files changed, 391 insertions(+), 385 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 6bcd8f19..f55c840b 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -36,6 +36,7 @@ use crate::{ }, CError, ChildReference, DBValue, NibbleVec, Trie, TrieDB, TrieHash, TrieLayout, }; +use compact_content_proof::Op; use hash_db::Hasher; /// Item to query, in memory. @@ -407,10 +408,7 @@ impl Recorder { if let RecorderStateInner::Content { output, stacked_push, .. } = &mut self.output { if let Some(buff) = stacked_push.take() { let mask = if buff.len() % 2 == 0 { 0xff } else { 0xf0 }; - let op = compact_content_proof::Op::, Vec>::KeyPush( - buff.inner().to_vec(), - mask, - ); + let op = Op::, Vec>::KeyPush(buff.inner().to_vec(), mask); let init_len = output.buf_len(); op.encode_into(output); let written = output.buf_len() - init_len; @@ -436,7 +434,7 @@ impl Recorder { debug_assert!(from - pop_to <= u16::max_value() as usize); // Warning this implies key size limit of u16::max - let op = compact_content_proof::Op::, Vec>::KeyPop((from - pop_to) as u16); + let op = Op::, Vec>::KeyPop((from - pop_to) as u16); let init_len = out.buf_len(); op.encode_into(out); let written = out.buf_len() - init_len; @@ -505,10 +503,9 @@ impl Recorder { NodeHandle::Hash(hash_slice) => { let mut hash = TrieHash::::default(); hash.as_mut().copy_from_slice(hash_slice); - let op = compact_content_proof::Op::< - TrieHash, - Vec, - >::HashChild(hash, i as u8); + let op = Op::, Vec>::HashChild( + hash, i as u8, + ); let init_len = output.buf_len(); op.encode_into(output); let written = output.buf_len() - init_len; @@ -560,7 +557,7 @@ impl Recorder { proof.push(value.into()); }, RecorderStateInner::Content { output, .. } => { - let op = compact_content_proof::Op::, Vec>::Value(value); + let op = Op::, Vec>::Value(value); let init_len = output.buf_len(); op.encode_into(output); let written = output.buf_len() - init_len; @@ -588,7 +585,7 @@ impl Recorder { // in parent node). }, RecorderStateInner::Content { output, .. } => { - let op = compact_content_proof::Op::, &[u8]>::Value(value); + let op = Op::, &[u8]>::Value(value); let init_len = output.buf_len(); op.encode_into(output); let written = output.buf_len() - init_len; @@ -623,12 +620,10 @@ impl Recorder { Value::Node(hash_slice) => { let mut hash = TrieHash::::default(); hash.as_mut().copy_from_slice(hash_slice); - compact_content_proof::Op::<_, Vec>::HashValue(hash) + Op::<_, Vec>::HashValue(hash) }, Value::Inline(value) => - compact_content_proof::Op::, Vec>::Value( - value.to_vec(), - ), + Op::, Vec>::Value(value.to_vec()), }); }, _ => return res, @@ -1508,7 +1503,7 @@ pub struct ReadProofContentIterator<'a, L, C, P> where L: TrieLayout, C: Iterator>, - P: Iterator, Vec>>>, + P: Iterator, Vec>>>, { // always needed, this is option only // to avoid unsafe code when halting. @@ -1519,6 +1514,7 @@ where state: ReadProofState, stack: ReadContentStack, restore_offset: usize, + buf_op: Option, Vec>>, } #[derive(Eq, PartialEq)] @@ -1552,7 +1548,7 @@ enum ValueSet { HashOnly(H), // ForceInline(V), // ForceHashed(V), - // BranchHash(H, u8), + BranchHash(H, u8), } impl ValueSet { @@ -1560,8 +1556,20 @@ impl ValueSet { match self { ValueSet::Standard(v) => Some(v), //ValueSet::ForceInline(v) | ValueSet::ForceHashed(v) => Some(v), - ValueSet::HashOnly(..) | ValueSet::None => None, - // ValueSet::BranchHash(..) => None, + ValueSet::HashOnly(..) | ValueSet::BranchHash(..) | ValueSet::None => None, + } + } +} + +impl From> for ValueSet { + fn from(op: Op) -> Self { + match op { + Op::HashChild(hash, child_ix) => ValueSet::BranchHash(hash, child_ix), + Op::HashValue(hash) => ValueSet::HashOnly(hash), + Op::Value(value) => ValueSet::Standard(value), + //Op::ValueForceInline(value) => ValueSet::ForceInline(value), + //Op::ValueForceHashed(value) => ValueSet::ForceHashed(value), + _ => ValueSet::None, } } } @@ -2243,101 +2251,83 @@ impl ReadContentStack { #[inline(always)] fn stack_pop( &mut self, - full_key: &NibbleVec, nb_nibble: Option, expected_root: &Option>, ) -> Result<(), VerifyError, CError>> { - let target_depth = nb_nibble.map(|n| full_key.len() - n); + let target_depth = nb_nibble.map(|n| self.prefix.len() - n); let mut first = true; while self .items - .last() - .map(|item| target_depth.map(|target| item.depth > target).unwrap_or(true)) - .unwrap_or(false) - { - let item = self.items.pop().expect("Checked"); - let mut from_depth = - self.items.last().map(|item| item.depth).unwrap_or(target_depth.unwrap_or(0)); - if let Some(from) = target_depth { - if from > from_depth { - self.stack_empty(from); - from_depth = from; - } - } - let depth = item.depth; - let is_root = target_depth.is_none() && self.items.is_empty(); - let inc = if is_root { 0 } else { 1 }; - - let child_reference = if item.children.iter().any(|child| child.is_some()) { - let nkey = (depth > (from_depth + inc)) - .then(|| (from_depth + inc, depth - from_depth - inc)); - if L::USE_EXTENSION { - let extension_only = first && - matches!(&item.value, &ValueSet::None) && - item.children.iter().filter(|child| child.is_some()).count() == 1; - self.items.push(item); // TODO this looks bad (pop then push, branch or leaf function should or should - // not pop instead) - // encode branch - self.standard_extension( - &full_key.inner().as_ref()[..], - depth, - is_root, - nkey, - extension_only, - ) - } else { - self.items.push(item); // TODO this looks bad (pop then push, branch or leaf function should or should - // not pop instead) - // encode branch - self.no_extension( - &full_key.inner().as_ref()[..], - callback, - depth, - is_root, - nkey, - ) - } - } else { - // leaf with value - self.flush_value_change( - callback, - &full_key.inner().as_ref()[..], - from_depth + inc, - item.2, - &item.1, - is_root, - ) - }; + .last() + .map(|item| target_depth.map(|target| item.depth > target).unwrap_or(true)) + .unwrap_or(false) + { + let item = self.items.pop().expect("Checked"); + let mut from_depth = + self.items.last().map(|item| item.depth).unwrap_or(target_depth.unwrap_or(0)); + if let Some(from) = target_depth { + if from > from_depth { + self.stack_empty(from); + from_depth = from; + } + } + let depth = item.depth; + let is_root = target_depth.is_none() && self.items.is_empty(); + let inc = if is_root { 0 } else { 1 }; + + let child_reference = if item.children.iter().any(|child| child.is_some()) { + let nkey = (depth > (from_depth + inc)) + .then(|| (from_depth + inc, depth - from_depth - inc)); + if L::USE_EXTENSION { + let extension_only = first && + matches!(&item.value, &ValueSet::None) && + item.children.iter().filter(|child| child.is_some()).count() == 1; + self.items.push(item); // TODO this looks bad (pop then push, branch or leaf function should or should + // not pop instead) + // encode branch + self.standard_extension(depth, is_root, nkey, extension_only) + } else { + self.items.push(item); // TODO this looks bad (pop then push, branch or leaf function should or should + // not pop instead) + // encode branch + self.no_extension(depth, is_root, nkey) + } + } else { + // leaf with value + self.flush_value_change(from_depth + inc, item.depth, &item.value, is_root) + }; - if self.items.is_empty() && !is_root { - self.stack_empty(from_depth); - } + if self.items.is_empty() && !is_root { + self.stack_empty(from_depth); + } - if let Some(item) = self.items.last_mut() { - let child_ix = full_key.at(item.depth); - if let Some(hash) = item.children[child_ix as usize].as_ref() { - if self.items.len() == self.start_items + 1 { - if expected_root.is_some() && hash != child_reference { - return Some(Err(VerifyError::HashMismatch(*child_reference))) + let items_len = self.items.len(); + if let Some(item) = self.items.last_mut() { + let child_ix = self.prefix.at(item.depth); + if let Some(hash) = item.children[child_ix as usize].as_ref() { + if items_len == self.start_items + 1 { + if expected_root.is_some() && hash != &child_reference { + return Err(VerifyError::HashMismatch(*child_reference.disp_hash())) + } + } else { + return Err(VerifyError::ExtraneousHashReference(*hash.disp_hash())) + // return Err(CompactDecoderError::HashChildNotOmitted.into()) + } } + item.children[child_ix as usize] = Some(child_reference); } else { - return Some(Err(VerifyError::ExtraneousHashReference(*hash))) - // return Err(CompactDecoderError::HashChildNotOmitted.into()) - } - } - item.children[child_ix as usize] = Some(child_reference); - } else { - if let Some(root) = expected_root.as_ref() { - if nb_nibble.is_none() { - if root != child_reference { - return Some(Err(VerifyError::RootMismatch(*child_reference))) + if let Some(root) = expected_root.as_ref() { + if nb_nibble.is_none() { + if root != child_reference.disp_hash() { + return Err(VerifyError::RootMismatch(*child_reference.disp_hash())) + } + } } } + first = false; + // TODO can skip hash checks when above start_items. + self.start_items = core::cmp::min(self.start_items, self.items.len()); } - } - first = false; - self.start_items = core::cmp::min(self.start_items, self.items.len()); - } Ok(()) } @@ -2356,12 +2346,12 @@ impl ReadContentStack { #[inline(always)] fn standard_extension( &mut self, - key_branch: &[u8], branch_d: usize, is_root: bool, nkey: Option<(usize, usize)>, extension_only: bool, ) -> ChildReference> { + let key_branch = &self.prefix.inner().as_ref()[..]; let last = self.items.len() - 1; assert_eq!(self.items[last].depth, branch_d); @@ -2403,6 +2393,99 @@ impl ReadContentStack { branch_hash } } + + #[inline(always)] + fn no_extension( + &mut self, + branch_d: usize, + is_root: bool, + nkey: Option<(usize, usize)>, + ) -> ChildReference> { + let key_branch = &self.prefix.inner().as_ref()[..]; + let ItemContentStack { children, value: v, depth, .. } = self.items.pop().expect("checked"); + + debug_assert!(branch_d == depth); + // encode branch + let nkeyix = nkey.unwrap_or((branch_d, 0)); + let pr = NibbleSlice::new_offset(&key_branch, nkeyix.0); + let hashed; + let value = if let Some(v) = v.as_ref() { + Some(if let Some(value) = Value::new_inline(v.as_ref(), L::MAX_INLINE_VALUE) { + value + } else { + let mut prefix = NibbleSlice::new_offset(&key_branch, 0); + prefix.advance(branch_d); + hashed = ::hash(v.as_ref()); + Value::Node(hashed.as_ref()) + }) + } else { + if let ValueSet::HashOnly(h) = &v { + Some(Value::Node(h.as_ref())) + } else { + None + } + }; + + let encoded = L::Codec::branch_node_nibbled( + pr.right_range_iter(nkeyix.1), + nkeyix.1, + children.iter(), + value, + ); + Self::process(encoded, is_root) + } + + fn flush_value_change<'a>( + &mut self, + from_depth: usize, + to_depth: usize, + value: &ValueSet, Vec>, + is_root: bool, + ) -> ChildReference> { + let key_content = &self.prefix.inner().as_ref()[..]; + let k2 = &key_content[..to_depth / nibble_ops::NIBBLE_PER_BYTE]; + let pr = NibbleSlice::new_offset(k2, from_depth); + + let hashed; + let value = match value { + ValueSet::Standard(v) => + if let Some(value) = Value::new_inline(v.as_ref(), L::MAX_INLINE_VALUE) { + value + } else { + hashed = ::hash(v.as_ref()); + Value::Node(hashed.as_ref()) + }, + ValueSet::HashOnly(h) => { + Value::Node(h.as_ref()) // TODO may have following hash and fail? ont if leaf + }, + ValueSet::BranchHash(..) | ValueSet::None => unreachable!("Not in cache accum"), + }; + let encoded = L::Codec::leaf_node(pr.right_iter(), pr.len(), value); + Self::process(encoded, is_root) + } + + #[inline(always)] + fn set_cache_change( + &mut self, + change: ValueSet, Vec>, + ) -> Result<(), VerifyError, CError>> { + if self.items.is_empty() { + self.stack_empty(0); + } + let last = self.items.len() - 1; + let mut item = &mut self.items[last]; + match change { + ValueSet::BranchHash(h, i) => { + if let Some(hash) = item.children[i as usize].as_ref() { + return Err(VerifyError::ExtraneousHashReference(*hash.disp_hash())) + //return Err(CompactDecoderError::HashChildNotOmitted.into()) TODO + } + item.children[i as usize] = Some(ChildReference::Hash(h)); + }, + value => item.value = value, + } + Ok(()) + } } /// Content return on success when reading proof. @@ -2411,6 +2494,7 @@ pub enum ReadProofItem<'a, L: TrieLayout, C, D: SplitFirst> { Halted(Box>), /// Seen value and key in proof. /// We only return content matching the query plan. + /// TODO should be possible to return &Vec Value(Cow<'a, [u8]>, Vec), /// Seen hash of value and key in proof. /// We only return content matching the query plan. @@ -2420,8 +2504,10 @@ pub enum ReadProofItem<'a, L: TrieLayout, C, D: SplitFirst> { /// Seen fully covered prefix in proof, this is only /// return when we read the proof with the query input (otherwhise /// we would need to indicate every child without a hash as a prefix). + /// TODO unused implement StartPrefix(&'a [u8]), /// End of a previously start prefix. + /// TODO unused implement EndPrefix, } @@ -2673,7 +2759,7 @@ impl<'a, L, C, P> Iterator for ReadProofContentIterator<'a, L, C, P> where L: TrieLayout, C: Iterator>, - P: Iterator, Vec>>>, + P: Iterator, Vec>>>, { type Item = Result>, VerifyError, CError>>; @@ -2724,308 +2810,217 @@ where } else { self.state = ReadProofState::PlanConsumed; self.current = None; - break } }; let did_prefix = self.stack.iter_prefix.is_some(); - } - use compact_content_proof::Op; - // op sequence check - - while let Some(op) = self.proof.next() { - let Some(op) = op else { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)); // TODO a decode op error - }; - - // check ordering logic - // TODO wrap in an error and put bools in a struct - match &op { - Op::KeyPush(..) => { - if self.stack.is_prev_push_key { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error - // TODO return - // Err(CompactDecoderError::ConsecutivePushKeys. - // into()) - } - self.stack.is_prev_push_key = true; - self.stack.is_prev_pop_key = false; - self.stack.is_prev_hash_child = None; - self.stack.first = false; - }, - Op::KeyPop(..) => { - if self.stack.is_prev_pop_key { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error - // return Err(CompactDecoderError::ConsecutivePopKeys. - // into()) - } - self.stack.is_prev_push_key = false; - self.stack.is_prev_pop_key = true; - self.stack.is_prev_hash_child = None; - self.stack.first = false; - }, - Op::HashChild(_, ix) => { - if let Some(prev_ix) = self.stack.is_prev_hash_child.as_ref() { - if prev_ix >= ix { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error - // return Err(CompactDecoderError::NotConsecutiveHash. - // into()) - } - } - // child ix on an existing content would be handle by iter_build. - self.stack.is_prev_push_key = false; - self.stack.is_prev_pop_key = false; - self.stack.is_prev_hash_child = Some(*ix); - }, - Op::Value(_) => { - // | Op::ValueForceInline(_) | Op::ValueForceHashed(_) => { - if !(self.stack.is_prev_push_key || self.stack.first) { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error - // return Err(CompactDecoderError::ValueNotAfterPush. - // into()) - } - self.stack.is_prev_push_key = false; - self.stack.is_prev_pop_key = false; - self.stack.is_prev_hash_child = None; - self.stack.first = false; - }, - _ => { - self.stack.is_prev_push_key = false; - self.stack.is_prev_pop_key = false; - self.stack.is_prev_hash_child = None; - self.stack.first = false; - }, - } - - // debug TODO make it log and external function - match &op { - Op::HashChild(hash, child_ix) => { - println!("ChildHash {:?}, {:?}, {:?}", self.stack.prefix, child_ix, hash); - }, - Op::HashValue(hash) => { - println!("ValueHash {:?}, {:?}", self.stack.prefix, hash); - }, - Op::Value(value) => { - println!("Value {:?}, {:?}", self.stack.prefix, value); - }, - _ => (), - } - - // act - let r = match op { - Op::KeyPush(partial, mask) => { - self.stack - .prefix - .append_slice(LeftNibbleSlice::new_with_mask(partial.as_slice(), mask)); - self.stack.stack_empty(self.stack.prefix.len()); - Ok(()) - }, - Op::KeyPop(nb_nibble) => { - let r = self.stack.stack_pop( - &self.stack.prefix, - Some(nb_nibble as usize), - &self.expected_root, - ); - self.stack.prefix.drop_lasts(nb_nibble.into()); - r - }, - Op::EndProof => break, - op => self.stack.set_cache_change(op.into()), - }; - if let Err(e) = r { - self.state = ReadProofState::Finished; - return Some(Err(e)) - } - } - todo!() - /* - - while let Some((_, accessed_value_node, hash_only)) = self.stack.iter_prefix.clone() { - // prefix iteration - if !accessed_value_node { - self.stack.iter_prefix.as_mut().map(|s| { - s.1 = true; - }); - match self.stack.access_value(&mut self.proof, check_hash, hash_only) { - Ok((Some(value), None)) => - return Some(Ok(ReadProofItem::Value( - self.stack.prefix.inner().to_vec().into(), - value, - ))), - Ok((None, Some(hash))) => - return Some(Ok(ReadProofItem::Hash( - self.stack.prefix.inner().to_vec().into(), - hash, - ))), - Ok((None, None)) => (), - Ok(_) => unreachable!(), - Err(e) => { - self.state = ReadProofState::Finished; - return Some(Err(e)) - }, - }; - } - - while let Some(child_index) = self.stack.items.last_mut().and_then(|last| { - if last.next_descended_child as usize >= NIBBLE_LENGTH { - None - } else { - let child_index = last.next_descended_child; - last.next_descended_child += 1; - Some(child_index) - } - }) { - let r = match self.stack.try_stack_child( - child_index, - &mut self.proof, - &self.expected_root, - None, - false, - ) { - Ok(r) => r, - Err(e) => { - self.state = ReadProofState::Finished; - return Some(Err(e)) + // op sequence check + + let mut from_iter = false; + while let Some(op) = self.buf_op.take().map(Option::Some).or_else(|| { + from_iter = true; + self.proof.next() + }) { + let Some(op) = op else { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)); // TODO a decode op error + }; + if from_iter { + // check ordering logic + // TODO wrap in an error and put bools in a struct + match &op { + Op::KeyPush(..) => { + if self.stack.is_prev_push_key { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error + // TODO return + // Err(CompactDecoderError::ConsecutivePushKeys. + // into()) + } + self.stack.is_prev_push_key = true; + self.stack.is_prev_pop_key = false; + self.stack.is_prev_hash_child = None; + self.stack.first = false; }, - }; - match r { - TryStackChildResult::Stacked => { - self.stack.iter_prefix.as_mut().map(|p| { - p.1 = false; - }); - break + Op::KeyPop(..) => { + if self.stack.is_prev_pop_key { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error + // return Err(CompactDecoderError::ConsecutivePopKeys. + // into()) + } + self.stack.is_prev_push_key = false; + self.stack.is_prev_pop_key = true; + self.stack.is_prev_hash_child = None; + self.stack.first = false; }, - TryStackChildResult::StackedDescendIncomplete => { - unreachable!("slice query none"); + Op::HashChild(_, ix) => { + if let Some(prev_ix) = self.stack.is_prev_hash_child.as_ref() { + if prev_ix >= ix { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error + // return Err(CompactDecoderError::NotConsecutiveHash. + // into()) + } + } + // child ix on an existing content would be handle by iter_build. + self.stack.is_prev_push_key = false; + self.stack.is_prev_pop_key = false; + self.stack.is_prev_hash_child = Some(*ix); }, - TryStackChildResult::NotStacked => break, - TryStackChildResult::NotStackedBranch => (), - TryStackChildResult::Halted => { - if let Some(last) = self.stack.items.last_mut() { - last.next_descended_child -= 1; + Op::Value(_) => { + // | Op::ValueForceInline(_) | Op::ValueForceHashed(_) => { + if !(self.stack.is_prev_push_key || self.stack.first) { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error + // return Err(CompactDecoderError::ValueNotAfterPush. + // into()) } - return self.halt(None) + self.stack.is_prev_push_key = false; + self.stack.is_prev_pop_key = false; + self.stack.is_prev_hash_child = None; + self.stack.first = false; + }, + _ => { + self.stack.is_prev_push_key = false; + self.stack.is_prev_pop_key = false; + self.stack.is_prev_hash_child = None; + self.stack.first = false; }, } + + // debug TODO make it log and external function + match &op { + Op::HashChild(hash, child_ix) => { + println!( + "ChildHash {:?}, {:?}, {:?}", + self.stack.prefix, child_ix, hash + ); + }, + Op::HashValue(hash) => { + println!("ValueHash {:?}, {:?}", self.stack.prefix, hash); + }, + Op::Value(value) => { + println!("Value {:?}, {:?}", self.stack.prefix, value); + }, + _ => (), + } } - if self.stack.iter_prefix.as_ref().map(|p| p.1).unwrap_or_default() { - if !match self.stack.pop(&self.expected_root) { - Ok(r) => r, - Err(e) => { + from_iter = false; + + // netx + let item = if match &op { + Op::Value(..) | Op::HashValue(..) => true, + _ => false, + } { + let Some(current) = self.current.as_ref() else { + let r = self.stack.stack_pop(None, &self.expected_root); + self.state = ReadProofState::Finished; + if let Err(e) = r { self.state = ReadProofState::Finished; return Some(Err(e)) - }, - } { - // end iter - self.stack.exit_prefix_iter(); + } + return None; // finished + }; + let mut to_check_slice = to_check_slice.as_mut().expect("Init above"); + + let mut at_value = false; + let mut next_query = false; + let query_slice = LeftNibbleSlice::new(¤t.key); + match self.stack.prefix.as_leftnibbleslice().cmp(&query_slice) { + Ordering::Equal => + if !self.stack.items.is_empty() { + at_value = true; + }, + Ordering::Less => (), + Ordering::Greater => + if current.as_prefix { + let query_slice = LeftNibbleSlice::new(¤t.key); + if self.stack.prefix.as_leftnibbleslice().starts_with(&query_slice) + { + at_value = true; + } else { + next_query = true; + } + } else { + next_query = true; + }, + } + if next_query { + self.buf_op = Some(op); + if current.as_prefix { + break + } else { + return Some(Ok(ReadProofItem::NoValue(¤t.key))) + } } - } - } - if did_prefix { - // exit a prefix iter, next content looping - self.state = ReadProofState::SwitchQueryPlan; - continue - } - let to_check = self.current.as_ref().expect("Init above"); - let to_check_len = to_check.key.len() * nibble_ops::NIBBLE_PER_BYTE; - let mut to_check_slice = to_check_slice.as_mut().expect("Init above"); - let as_prefix = to_check.as_prefix; // TODO useless? - let hash_only = to_check.hash_only; // TODO useless? - let mut at_value = false; - match self.stack.prefix.len().cmp(&to_check_len) { - Ordering::Equal => - if !self.stack.items.is_empty() { - at_value = true; - }, - Ordering::Less => (), - Ordering::Greater => { - unreachable!(); - }, - } - if at_value { - if as_prefix { - self.stack.enter_prefix_iter(hash_only); - continue - } - self.state = ReadProofState::SwitchQueryPlan; - match self.stack.access_value(&mut self.proof, check_hash, hash_only) { - Ok((Some(value), None)) => - return Some(Ok(ReadProofItem::Value(to_check.key.into(), value))), - Ok((None, Some(hash))) => - return Some(Ok(ReadProofItem::Hash(to_check.key.into(), hash))), - Ok((None, None)) => return Some(Ok(ReadProofItem::NoValue(to_check.key))), - Ok(_) => unreachable!(), - Err(e) => { - self.state = ReadProofState::Finished; - return Some(Err(e)) - }, - } - } + if at_value { + match &op { + Op::Value(value) => { + // TODO could get content from op with no clone. + Some(ReadProofItem::Value(self.stack.prefix.inner().to_vec().into(), value.clone())) + }, + Op::HashValue(hash) => { + // TODO could get content from op with no clone. + Some(ReadProofItem::Hash(self.stack.prefix.inner().to_vec().into(), hash.clone())) + }, + _ => unreachable!(), + } + } else { + match &op { + Op::Value(value) => { + // hash value here not value + if L::MAX_INLINE_VALUE.map(|max| max as usize <= value.len()).unwrap_or(false) { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousValue(value.clone()))); + } + }, + _ => (), + } + None + } + } else { + None + }; - let child_index = if self.stack.items.len() == 0 { - // dummy - 0 - } else { - to_check_slice.at(0) - }; - let r = match self.stack.try_stack_child( - child_index, - &mut self.proof, - &self.expected_root, - Some(&mut to_check_slice), - to_check.as_prefix, - ) { - Ok(r) => r, - Err(e) => { + // act + let r = match op { + Op::KeyPush(partial, mask) => { + self.stack + .prefix + .append_slice(LeftNibbleSlice::new_with_mask(partial.as_slice(), mask)); + self.stack.stack_empty(self.stack.prefix.len()); + Ok(()) + }, + Op::KeyPop(nb_nibble) => { + let r = self.stack.stack_pop(Some(nb_nibble as usize), &self.expected_root); + self.stack.prefix.drop_lasts(nb_nibble.into()); + r + }, + Op::EndProof => break, + op => self.stack.set_cache_change(op.into()), + }; + if let Err(e) = r { self.state = ReadProofState::Finished; return Some(Err(e)) - }, - }; - match r { - TryStackChildResult::Stacked => (), - TryStackChildResult::StackedDescendIncomplete => { - if as_prefix { - self.stack.enter_prefix_iter(hash_only); - continue - } - self.state = ReadProofState::SwitchQueryPlan; - return Some(Ok(ReadProofItem::NoValue(to_check.key))) - }, - TryStackChildResult::NotStacked => { - self.state = ReadProofState::SwitchQueryPlan; - return Some(Ok(ReadProofItem::NoValue(to_check.key))) - }, - TryStackChildResult::NotStackedBranch => { - self.state = ReadProofState::SwitchQueryPlan; - return Some(Ok(ReadProofItem::NoValue(to_check.key))) - }, - TryStackChildResult::Halted => return self.halt(Some(to_check_slice)), + } + if let Some(r) = item { + return Some(Ok(r)) + } + } + if self.current.is_some() { + // TODO return halt instead + return Some(Err(VerifyError::IncompleteProof)) } } - debug_assert!(self.state == ReadProofState::PlanConsumed); - if self.is_compact { - let stack_to = 0; // TODO restart is different - // let r = self.stack.pop_until(common_nibbles, &self.expected_root); - let r = self.stack.pop_until(stack_to, &self.expected_root, false); - if let Err(e) = r { - self.state = ReadProofState::Finished; - return Some(Err(e)) - } + + self.state = ReadProofState::Finished; + if self.proof.next().is_some() { + return Some(Err(VerifyError::ExtraneousNode)) } else { - if self.proof.next().is_some() { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) - } + return None } - self.state = ReadProofState::Finished; - return None - */ } } @@ -3124,7 +3119,7 @@ pub fn verify_query_plan_iter_content<'a, L, C, P>( where L: TrieLayout, C: Iterator>, - P: Iterator, Vec>>>, + P: Iterator, Vec>>>, { let HaltedStateCheck::Content(state) = state else { return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent @@ -3147,6 +3142,7 @@ where state, stack, restore_offset, + buf_op: None, }) } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index bf3edbcb..43559677 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -555,7 +555,7 @@ enum Stored { } /// Used to build a collection of child nodes from a collection of `NodeHandle`s -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "std", derive(Debug))] pub enum ChildReference { // `HO` is e.g. `H256`, i.e. the output of a `Hasher` @@ -563,6 +563,16 @@ pub enum ChildReference { Inline(HO, usize), // usize is the length of the node data we store in the `H::Out` } +impl ChildReference { + // Representation of hash, may contain filler bytes. + pub fn disp_hash(&self) -> &HO { + match self { + ChildReference::Hash(h) => h, + ChildReference::Inline(h, _) => h, + } + } +} + impl<'a, HO> TryFrom> for ChildReference where HO: AsRef<[u8]> + AsMut<[u8]> + Default + Clone + Copy, From c6a8e1d111fe92deb66758fcf49228630fc5f54b Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 14 Jun 2023 19:23:32 +0200 Subject: [PATCH 067/154] iter no resume ok --- trie-db/src/query_plan.rs | 184 ++++++++++++++++++++++---------------- 1 file changed, 109 insertions(+), 75 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index f55c840b..353a98a9 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -1694,6 +1694,7 @@ struct ReadContentStack { items: Vec>, prefix: NibbleVec, // limit and wether we return value and if hash only iteration. + // TODO should be removable (just check current). iter_prefix: Option<(usize, bool, bool)>, start_items: usize, is_prev_hash_child: Option, @@ -2228,7 +2229,11 @@ impl ReadContentStack { *self = old; return Ok(()) } - Err(VerifyError::ExtraneousNode) + if self.items.is_empty() && target == 0 { + Ok(()) + } else { + Err(VerifyError::ExtraneousNode) + } } #[inline(always)] @@ -2258,76 +2263,76 @@ impl ReadContentStack { let mut first = true; while self .items - .last() - .map(|item| target_depth.map(|target| item.depth > target).unwrap_or(true)) - .unwrap_or(false) - { - let item = self.items.pop().expect("Checked"); - let mut from_depth = - self.items.last().map(|item| item.depth).unwrap_or(target_depth.unwrap_or(0)); - if let Some(from) = target_depth { - if from > from_depth { - self.stack_empty(from); - from_depth = from; - } - } - let depth = item.depth; - let is_root = target_depth.is_none() && self.items.is_empty(); - let inc = if is_root { 0 } else { 1 }; - - let child_reference = if item.children.iter().any(|child| child.is_some()) { - let nkey = (depth > (from_depth + inc)) - .then(|| (from_depth + inc, depth - from_depth - inc)); - if L::USE_EXTENSION { - let extension_only = first && - matches!(&item.value, &ValueSet::None) && - item.children.iter().filter(|child| child.is_some()).count() == 1; - self.items.push(item); // TODO this looks bad (pop then push, branch or leaf function should or should - // not pop instead) - // encode branch - self.standard_extension(depth, is_root, nkey, extension_only) - } else { - self.items.push(item); // TODO this looks bad (pop then push, branch or leaf function should or should - // not pop instead) - // encode branch - self.no_extension(depth, is_root, nkey) - } - } else { - // leaf with value - self.flush_value_change(from_depth + inc, item.depth, &item.value, is_root) - }; + .last() + .map(|item| target_depth.map(|target| item.depth > target).unwrap_or(true)) + .unwrap_or(false) + { + let item = self.items.pop().expect("Checked"); + let mut from_depth = + self.items.last().map(|item| item.depth).unwrap_or(target_depth.unwrap_or(0)); + if let Some(from) = target_depth { + if from > from_depth { + self.stack_empty(from); + from_depth = from; + } + } + let depth = item.depth; + let is_root = target_depth.is_none() && self.items.is_empty(); + let inc = if is_root { 0 } else { 1 }; + + let child_reference = if item.children.iter().any(|child| child.is_some()) { + let nkey = (depth > (from_depth + inc)) + .then(|| (from_depth + inc, depth - from_depth - inc)); + if L::USE_EXTENSION { + let extension_only = first && + matches!(&item.value, &ValueSet::None) && + item.children.iter().filter(|child| child.is_some()).count() == 1; + self.items.push(item); // TODO this looks bad (pop then push, branch or leaf function should or should + // not pop instead) + // encode branch + self.standard_extension(depth, is_root, nkey, extension_only) + } else { + self.items.push(item); // TODO this looks bad (pop then push, branch or leaf function should or should + // not pop instead) + // encode branch + self.no_extension(depth, is_root, nkey) + } + } else { + // leaf with value + self.flush_value_change(from_depth + inc, item.depth, &item.value, is_root) + }; - if self.items.is_empty() && !is_root { - self.stack_empty(from_depth); - } + if self.items.is_empty() && !is_root { + self.stack_empty(from_depth); + } - let items_len = self.items.len(); - if let Some(item) = self.items.last_mut() { - let child_ix = self.prefix.at(item.depth); - if let Some(hash) = item.children[child_ix as usize].as_ref() { - if items_len == self.start_items + 1 { - if expected_root.is_some() && hash != &child_reference { - return Err(VerifyError::HashMismatch(*child_reference.disp_hash())) - } - } else { - return Err(VerifyError::ExtraneousHashReference(*hash.disp_hash())) - // return Err(CompactDecoderError::HashChildNotOmitted.into()) - } + let items_len = self.items.len(); + if let Some(item) = self.items.last_mut() { + let child_ix = self.prefix.at(item.depth); + if let Some(hash) = item.children[child_ix as usize].as_ref() { + if items_len == self.start_items + 1 { + if expected_root.is_some() && hash != &child_reference { + return Err(VerifyError::HashMismatch(*child_reference.disp_hash())) } - item.children[child_ix as usize] = Some(child_reference); } else { - if let Some(root) = expected_root.as_ref() { - if nb_nibble.is_none() { - if root != child_reference.disp_hash() { - return Err(VerifyError::RootMismatch(*child_reference.disp_hash())) - } - } + return Err(VerifyError::ExtraneousHashReference(*hash.disp_hash())) + // return Err(CompactDecoderError::HashChildNotOmitted.into()) + } + } + item.children[child_ix as usize] = Some(child_reference); + } else { + if let Some(root) = expected_root.as_ref() { + if nb_nibble.is_none() { + if root != child_reference.disp_hash() { + return Err(VerifyError::RootMismatch(*child_reference.disp_hash())) } } - first = false; - // TODO can skip hash checks when above start_items. - self.start_items = core::cmp::min(self.start_items, self.items.len()); } + } + first = false; + // TODO can skip hash checks when above start_items. + self.start_items = core::cmp::min(self.start_items, self.items.len()); + } Ok(()) } @@ -2812,17 +2817,33 @@ where self.current = None; } }; - let did_prefix = self.stack.iter_prefix.is_some(); - // op sequence check + // op sequence check let mut from_iter = false; while let Some(op) = self.buf_op.take().map(Option::Some).or_else(|| { from_iter = true; self.proof.next() }) { let Some(op) = op else { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)); // TODO a decode op error + let r = self.stack.stack_pop(None, &self.expected_root); + self.state = ReadProofState::Finished; + if let Err(e) = r { + self.state = ReadProofState::Finished; + return Some(Err(e)) + } + if let Some(c) = self.current.as_ref() { + if c.as_prefix { + // end prefix switch to next + self.state = ReadProofState::SwitchQueryPlan; + break; + } else { + // missing value + self.state = ReadProofState::SwitchQueryPlan; + return Some(Ok(ReadProofItem::NoValue(&c.key))); + } + } else { + return None; // finished + } }; if from_iter { // check ordering logic @@ -2947,6 +2968,7 @@ where } if next_query { self.buf_op = Some(op); + self.state = ReadProofState::SwitchQueryPlan; if current.as_prefix { break } else { @@ -2958,11 +2980,17 @@ where match &op { Op::Value(value) => { // TODO could get content from op with no clone. - Some(ReadProofItem::Value(self.stack.prefix.inner().to_vec().into(), value.clone())) + Some(ReadProofItem::Value( + self.stack.prefix.inner().to_vec().into(), + value.clone(), + )) }, Op::HashValue(hash) => { // TODO could get content from op with no clone. - Some(ReadProofItem::Hash(self.stack.prefix.inner().to_vec().into(), hash.clone())) + Some(ReadProofItem::Hash( + self.stack.prefix.inner().to_vec().into(), + hash.clone(), + )) }, _ => unreachable!(), } @@ -2970,9 +2998,12 @@ where match &op { Op::Value(value) => { // hash value here not value - if L::MAX_INLINE_VALUE.map(|max| max as usize <= value.len()).unwrap_or(false) { + if L::MAX_INLINE_VALUE + .map(|max| max as usize <= value.len()) + .unwrap_or(false) + { self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousValue(value.clone()))); + return Some(Err(VerifyError::ExtraneousValue(value.clone()))) } }, _ => (), @@ -3005,16 +3036,19 @@ where return Some(Err(e)) } if let Some(r) = item { + if self.current.as_ref().map(|c| !c.as_prefix).unwrap_or(true) { + self.state = ReadProofState::SwitchQueryPlan; // TODO this is same as content NOne? + } return Some(Ok(r)) } } - if self.current.is_some() { + if self.state != ReadProofState::SwitchQueryPlan && self.current.is_some() { // TODO return halt instead return Some(Err(VerifyError::IncompleteProof)) } + self.state = ReadProofState::SwitchQueryPlan; } - self.state = ReadProofState::Finished; if self.proof.next().is_some() { return Some(Err(VerifyError::ExtraneousNode)) @@ -3377,7 +3411,7 @@ pub mod compact_content_proof { type Item = Option>>; fn next(&mut self) -> Option { - match Op::decode(self.0.as_ref()) { + match Op::decode(&self.0.as_ref()[self.1..]) { Ok((op, len)) => { self.1 += len; Some(Some(op)) From f18a7d17d2b451a7cb2118a2390aabeffdd37737 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 15 Jun 2023 11:02:51 +0200 Subject: [PATCH 068/154] switch from recorder to stack --- test-support/reference-trie/src/lib.rs | 2 +- trie-db/src/query_plan.rs | 341 +++++++++++++------------ trie-db/test/src/proof.rs | 12 +- 3 files changed, 181 insertions(+), 174 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index ac8d7c54..16728b1a 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -52,7 +52,7 @@ macro_rules! test_layouts { #[test] fn $test() { eprintln!("Running with layout `HashedValueNoExtThreshold`"); - $test_internal::<$crate::HashedValueNoExtThreshold<1>>(); + // TODO $test_internal::<$crate::HashedValueNoExtThreshold<1>>(); eprintln!("Running with layout `SubstrateV1`"); $test_internal::<$crate::SubstrateV1<$crate::RefHasher>>(); eprintln!("Running with layout `HashedValueNoExt`"); diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 353a98a9..8b1c0a14 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -364,7 +364,7 @@ impl Recorder { proof.push(Vec::new()); }, RecorderStateInner::Content { output, stacked_push, stacked_pop } => { - if Self::flush_compact_content_pop( + if flush_compact_content_pop::( output, stacked_pop, items, @@ -418,125 +418,6 @@ impl Recorder { res } - #[must_use] - fn flush_compact_content_pop( - out: &mut O, - stacked_from: &mut Option, - items: &[CompactEncodingInfos], - add_depth: Option, - limits: &mut Limits, - ) -> bool { - let Some(from) = stacked_from.take() else { - return false - }; - let pop_to = add_depth.unwrap_or_else(|| items.last().map(|i| i.depth).unwrap_or(0)); - debug_assert!(from > pop_to); - - debug_assert!(from - pop_to <= u16::max_value() as usize); - // Warning this implies key size limit of u16::max - let op = Op::, Vec>::KeyPop((from - pop_to) as u16); - let init_len = out.buf_len(); - op.encode_into(out); - let written = out.buf_len() - init_len; - limits.add_node(written, 0, false) - } - - #[must_use] - fn record_popped_node( - &mut self, - item: &CompactEncodingInfos, - items: &[CompactEncodingInfos], - ) -> bool { - let mut res = false; - if !self.check_start_at(item.depth) { - return res - } - if let RecorderStateInner::Content { .. } = &self.output { - // if no value accessed, then we can have push then stack pop. - if self.flush_compact_content_pushes(item.depth) { - res = true; - } - } - - match &mut self.output { - RecorderStateInner::Stream(_) => (), - RecorderStateInner::Compact { proof, stacked_pos, .. } => - if !item.is_inline { - if let Some(at) = stacked_pos.pop() { - proof[at] = crate::trie_codec::encode_node_internal::( - &item.node, - item.accessed_value_node, - item.accessed_children_node, - ) - .expect("TODO error handling, can it actually fail?"); - } // else when restarting record, this is not to be recorded - }, - RecorderStateInner::Content { output, stacked_pop, .. } => { - if stacked_pop.is_none() { - *stacked_pop = Some(item.depth); - } - // two case: children to register or all children accessed. - let mut has_hash_to_write = false; - let node_data = item.node.data(); - if let Some(last_item) = items.last() { - match last_item.node.node_plan() { - NodePlan::Branch { children, .. } | - NodePlan::NibbledBranch { children, .. } => - for i in 0..children.len() { - if children[i].is_some() && !last_item.accessed_children_node.at(i) - { - has_hash_to_write = true; - break - } - }, - _ => (), - } - } - - match item.node.node_plan() { - NodePlan::Branch { children, .. } | - NodePlan::NibbledBranch { children, .. } => { - for i in 0..children.len() { - if let Some(child) = &children[i] { - if !item.accessed_children_node.at(i) { - match child.build(node_data) { - NodeHandle::Hash(hash_slice) => { - let mut hash = TrieHash::::default(); - hash.as_mut().copy_from_slice(hash_slice); - let op = Op::, Vec>::HashChild( - hash, i as u8, - ); - let init_len = output.buf_len(); - op.encode_into(output); - let written = output.buf_len() - init_len; - res = self.limits.add_node(written, 0, false) - }, - NodeHandle::Inline(_) => { - // As been accessed if needed (inline are not mark). - }, - } - } - } - } - }, - _ => (), - } - if has_hash_to_write { - if Self::flush_compact_content_pop( - output, - stacked_pop, - items, - None, - &mut self.limits, - ) { - res = true; - } - } - }, - } - res - } - #[must_use] fn record_value_node(&mut self, value: Vec, depth: usize) -> bool { if !self.check_start_at(depth) { @@ -648,49 +529,6 @@ impl Recorder { } res } - - fn finalize(&mut self, items: &Vec) { - match &mut self.output { - RecorderStateInner::Compact { output, proof, stacked_pos } => { - let restarted_from = 0; - if stacked_pos.len() > restarted_from { - // halted: complete up to 0 and write all nodes keeping stack. - let mut items = items.iter().rev(); - while let Some(pos) = stacked_pos.pop() { - loop { - let item = items.next().expect("pos stacked with an item"); - if !item.is_inline { - proof[pos] = crate::trie_codec::encode_node_internal::( - &item.node, - item.accessed_value_node, - item.accessed_children_node, - ) - .expect("TODO error handling, can it actually fail?"); - break - } - } - } - } - for entry in core::mem::take(proof) { - output.write_entry(entry.into()); - } - }, - RecorderStateInner::Stream(_output) => { - // all written - }, - RecorderStateInner::Content { output: _, stacked_push, stacked_pop: _ } => { - // TODO protect existing stack as for compact - assert!(stacked_push.is_none()); - // TODO could use function with &item and &[item] as param - // to skip this clone. - for i in (0..items.len()).rev() { - let item = items.get(i).expect("bounded iter"); - let items = &items[..i]; - let _ = self.record_popped_node(item, &items); - } - }, - } - } } impl Limits { @@ -1033,7 +871,7 @@ pub fn record_query_plan< stack = &mut from.stack; } if !stack.pop() { - stack.recorder.finalize(&stack.items); + stack.finalize(); return Ok(from) } }, @@ -1132,7 +970,7 @@ pub fn record_query_plan< )); stack.prefix.pop(); from.currently_query_item = Some(query.to_owned()); - stack.recorder.finalize(&stack.items); + stack.finalize(); return Ok(from) }, } @@ -1155,7 +993,7 @@ pub fn record_query_plan< break } } - stack.recorder.finalize(&stack.items); + stack.finalize(); Ok(from) } @@ -1247,7 +1085,7 @@ fn iter_prefix( )); stack.prefix.pop(); from.currently_query_item = prev_query.map(|q| q.to_owned()); - stack.recorder.finalize(&stack.items); + stack.finalize(); return Ok((from, true)) }, } @@ -1453,10 +1291,13 @@ impl RecordStack { if self.iter_prefix.map(|(l, _)| l == self.items.len()).unwrap_or(false) { return false } - if let Some(item) = self.items.pop() { - if self.recorder.record_popped_node(&item, &self.items) { + let at = self.items.len(); + if at > 0 { + if self.record_popped_node(at - 1) { self.halt = true; } + } + if let Some(item) = self.items.pop() { let depth = self.items.last().map(|i| i.depth).unwrap_or(0); self.prefix.drop_lasts(self.prefix.len() - depth); if depth == item.depth { @@ -1476,6 +1317,167 @@ impl RecordStack { fn exit_prefix_iter(&mut self) { self.iter_prefix = None } + + #[must_use] + fn record_popped_node(&mut self, at: usize) -> bool { + let item = self.items.get(at).expect("bounded iter"); + let items = &self.items[..at]; + let mut res = false; + if !self.recorder.check_start_at(item.depth) { + return res + } + if let RecorderStateInner::Content { .. } = &self.recorder.output { + // if no value accessed, then we can have push then stack pop. + if self.recorder.flush_compact_content_pushes(item.depth) { + res = true; + } + } + + match &mut self.recorder.output { + RecorderStateInner::Stream(_) => (), + RecorderStateInner::Compact { proof, stacked_pos, .. } => + if !item.is_inline { + if let Some(at) = stacked_pos.pop() { + proof[at] = crate::trie_codec::encode_node_internal::( + &item.node, + item.accessed_value_node, + item.accessed_children_node, + ) + .expect("TODO error handling, can it actually fail?"); + } // else when restarting record, this is not to be recorded + }, + RecorderStateInner::Content { output, stacked_pop, .. } => { + if stacked_pop.is_none() { + *stacked_pop = Some(item.depth); + } + // two case: children to register or all children accessed. + let mut has_hash_to_write = false; + let node_data = item.node.data(); + if let Some(last_item) = items.last() { + match last_item.node.node_plan() { + NodePlan::Branch { children, .. } | + NodePlan::NibbledBranch { children, .. } => + for i in 0..children.len() { + if children[i].is_some() && !last_item.accessed_children_node.at(i) + { + has_hash_to_write = true; + break + } + }, + _ => (), + } + } + + match item.node.node_plan() { + NodePlan::Branch { children, .. } | + NodePlan::NibbledBranch { children, .. } => { + for i in 0..children.len() { + if let Some(child) = &children[i] { + if !item.accessed_children_node.at(i) { + match child.build(node_data) { + NodeHandle::Hash(hash_slice) => { + let mut hash = TrieHash::::default(); + hash.as_mut().copy_from_slice(hash_slice); + let op = Op::, Vec>::HashChild( + hash, i as u8, + ); + let init_len = output.buf_len(); + op.encode_into(output); + let written = output.buf_len() - init_len; + res = self.recorder.limits.add_node(written, 0, false) + }, + NodeHandle::Inline(_) => { + // As been accessed if needed (inline are not mark). + // TODO need to process + println!("TODO need to process"); + }, + } + } + } + } + }, + _ => (), + } + if has_hash_to_write { + if flush_compact_content_pop::( + output, + stacked_pop, + items, + None, + &mut self.recorder.limits, + ) { + res = true; + } + } + }, + } + res + } + + fn finalize(&mut self) { + let items = &self.items; + match &mut self.recorder.output { + RecorderStateInner::Compact { output, proof, stacked_pos } => { + let restarted_from = 0; + if stacked_pos.len() > restarted_from { + // halted: complete up to 0 and write all nodes keeping stack. + let mut items = items.iter().rev(); + while let Some(pos) = stacked_pos.pop() { + loop { + let item = items.next().expect("pos stacked with an item"); + if !item.is_inline { + proof[pos] = crate::trie_codec::encode_node_internal::( + &item.node, + item.accessed_value_node, + item.accessed_children_node, + ) + .expect("TODO error handling, can it actually fail?"); + break + } + } + } + } + for entry in core::mem::take(proof) { + output.write_entry(entry.into()); + } + }, + RecorderStateInner::Stream(_output) => { + // all written + }, + RecorderStateInner::Content { output: _, stacked_push, stacked_pop: _ } => { + // TODO protect existing stack as for compact + assert!(stacked_push.is_none()); + // TODO could use function with &item and &[item] as param + // to skip this clone. + for i in (0..items.len()).rev() { + let _ = self.record_popped_node(i); + } + }, + } + } +} + +#[must_use] +fn flush_compact_content_pop( + out: &mut O, + stacked_from: &mut Option, + items: &[CompactEncodingInfos], + add_depth: Option, + limits: &mut Limits, +) -> bool { + let Some(from) = stacked_from.take() else { + return false + }; + let pop_to = add_depth.unwrap_or_else(|| items.last().map(|i| i.depth).unwrap_or(0)); + debug_assert!(from > pop_to); + + debug_assert!(from - pop_to <= u16::max_value() as usize); + // Warning this implies key size limit of u16::max + let op = Op::, Vec>::KeyPop((from - pop_to) as u16); + let init_len = out.buf_len(); + op.encode_into(out); + let written = out.buf_len() - init_len; + limits.add_node(written, 0, false) } /// Proof reading iterator. @@ -2824,6 +2826,7 @@ where from_iter = true; self.proof.next() }) { + println!("read: {:?}", op); let Some(op) = op else { let r = self.stack.stack_pop(None, &self.expected_root); self.state = ReadProofState::Finished; diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index bff48250..baf1c846 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -269,10 +269,10 @@ fn test_query_plan_internal() { for (hash_only, kind) in [ (false, ProofKind::CompactContent), - (false, ProofKind::FullNodes), + /* (false, ProofKind::FullNodes), (true, ProofKind::FullNodes), (false, ProofKind::CompactNodes), - (true, ProofKind::CompactNodes), + (true, ProofKind::CompactNodes), */ ] { if (kind == ProofKind::CompactContent || kind == ProofKind::CompactNodes) && L::USE_EXTENSION @@ -290,6 +290,7 @@ fn test_query_plan_internal() { ignore_unordered: false, kind, }, + /* InMemQueryPlan { items: vec![ InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), @@ -307,10 +308,13 @@ fn test_query_plan_internal() { ignore_unordered: false, kind, }, + */ ]; for (nb_plan, query_plan) in query_plans.iter().enumerate() { - for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] - { + for limit_conf in [ + (1, false), /* (0, false), (1, false), (1, true), (2, false), (2, true), (3, + * true) */ + ] { let limit = limit_conf.0; let limit = (limit != 0).then(|| limit); let recorder = Recorder::new(kind, InMemoryRecorder::default(), limit, None); From 97c7440d13cc1b954559a857b053ee477c78942c Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 15 Jun 2023 11:33:52 +0200 Subject: [PATCH 069/154] halted state as mut ptr --- trie-db/src/query_plan.rs | 98 +++++++++++++++++++++------------------ trie-db/test/src/proof.rs | 2 +- 2 files changed, 55 insertions(+), 45 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 8b1c0a14..54d53c85 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -799,8 +799,8 @@ pub fn record_query_plan< >( db: &TrieDB, query_plan: &mut QueryPlan<'a, I>, - mut from: HaltedStateRecord, -) -> Result, VerifyError, CError>> { + from: &mut HaltedStateRecord, +) -> Result<(), VerifyError, CError>> { // TODO //) resto // let restore_buf; @@ -867,12 +867,12 @@ pub fn record_query_plan< Ordering::Equal | Ordering::Less => break, Ordering::Greater => { if query_plan.kind.record_inline() { - from = try_stack_inline_child(from, db, NIBBLE_LENGTH as u8)?; + try_stack_inline_child(from, NIBBLE_LENGTH as u8)?; stack = &mut from.stack; } if !stack.pop() { stack.finalize(); - return Ok(from) + return Ok(()) } }, } @@ -881,11 +881,10 @@ pub fn record_query_plan< }; if let Some((_, hash_only)) = stack.iter_prefix.clone() { // statefull halted during iteration. - let (f, halt) = iter_prefix::(from, Some(&query), db, hash_only, false, false)?; + let halt = iter_prefix::(from, Some(&query), Some(db), hash_only, false, false)?; if halt { - return Ok(f) + return Ok(()) } else { - from = f; stack = &mut from.stack; } from_query_ref = None; @@ -898,18 +897,17 @@ pub fn record_query_plan< if !stack.items.is_empty() { if slice_query.is_empty() { if query.as_prefix { - let (f, halt) = iter_prefix::( + let halt = iter_prefix::( from, Some(&query), - db, + Some(db), query.hash_only, true, false, )?; if halt { - return Ok(f) + return Ok(()) } else { - from = f; stack = &mut from.stack; } break false @@ -925,7 +923,7 @@ pub fn record_query_plan< let child_index = if stack.items.is_empty() { 0 } else { slice_query.at(0) }; if query_plan.kind.record_inline() { - from = try_stack_inline_child(from, db, child_index)?; + try_stack_inline_child(from, child_index)?; stack = &mut from.stack; } stack.items.last_mut().map(|i| { @@ -934,7 +932,7 @@ pub fn record_query_plan< }); match stack.try_stack_child( child_index, - db, + Some(db), dummy_parent_hash, Some(&mut slice_query), false, @@ -944,18 +942,17 @@ pub fn record_query_plan< break false, TryStackChildResult::StackedDescendIncomplete => { if query.as_prefix { - let (f, halt) = iter_prefix::( + let halt = iter_prefix::( from, Some(&query), - db, + Some(db), query.hash_only, true, false, )?; if halt { - return Ok(f) + return Ok(()) } else { - from = f; stack = &mut from.stack; } } @@ -971,21 +968,21 @@ pub fn record_query_plan< stack.prefix.pop(); from.currently_query_item = Some(query.to_owned()); stack.finalize(); - return Ok(from) + return Ok(()) }, } }; if touched { // try access value - stack.access_value(db, query.hash_only)?; + stack.access_value(Some(db), query.hash_only)?; } from_query_ref = None; prev_query = Some(query); } loop { if query_plan.kind.record_inline() { - from = try_stack_inline_child(from, db, NIBBLE_LENGTH as u8)?; + try_stack_inline_child(from, NIBBLE_LENGTH as u8)?; stack = &mut from.stack; } @@ -994,28 +991,26 @@ pub fn record_query_plan< } } stack.finalize(); - Ok(from) + Ok(()) } fn try_stack_inline_child<'a, L: TrieLayout, O: RecorderOutput>( - mut from: HaltedStateRecord, - db: &TrieDB, + from: &mut HaltedStateRecord, upper: u8, -) -> Result, VerifyError, CError>> { +) -> Result<(), VerifyError, CError>> { let dummy_parent_hash = TrieHash::::default(); let mut stack = &mut from.stack; if let Some(item) = stack.items.last() { let pre = item.next_descended_child; // TODO put next_descended child to 16 for leaf (skip some noop iter) for i in pre..upper as u8 { - match stack.try_stack_child(i, db, dummy_parent_hash, None, true)? { + match stack.try_stack_child(i, None, dummy_parent_hash, None, true)? { // only expect a stacked prefix here TryStackChildResult::Stacked => { - let (f, halt) = iter_prefix::(from, None, db, false, true, true)?; + let halt = iter_prefix::(from, None, None, false, true, true)?; if halt { // no halt on inline. unreachable!() } else { - from = f; stack = &mut from.stack; stack.pop(); } @@ -1026,17 +1021,17 @@ fn try_stack_inline_child<'a, L: TrieLayout, O: RecorderOutput>( } } stack.items.last_mut().map(|i| i.next_descended_child = upper); - Ok(from) + Ok(()) } fn iter_prefix( - mut from: HaltedStateRecord, + from: &mut HaltedStateRecord, prev_query: Option<&QueryPlanItem>, - db: &TrieDB, + db: Option<&TrieDB>, hash_only: bool, first_iter: bool, inline_iter: bool, -) -> Result<(HaltedStateRecord, bool), VerifyError, CError>> { +) -> Result, CError>> { let stack = &mut from.stack; let dummy_parent_hash = TrieHash::::default(); if first_iter { @@ -1079,14 +1074,15 @@ fn iter_prefix( } stack.halt = false; stack.prefix.push(child_index); - from.from = Some(( + let dest_from = Some(( stack.prefix.inner().to_vec(), (stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, )); stack.prefix.pop(); - from.currently_query_item = prev_query.map(|q| q.to_owned()); stack.finalize(); - return Ok((from, true)) + from.from = dest_from; + from.currently_query_item = prev_query.map(|q| q.to_owned()); + return Ok(true) }, } } @@ -1097,7 +1093,7 @@ fn iter_prefix( } } stack.exit_prefix_iter(); - Ok((from, false)) + Ok(false) } enum TryStackChildResult { @@ -1112,10 +1108,10 @@ impl RecordStack { fn try_stack_child<'a>( &mut self, child_index: u8, - db: &TrieDB, + db: Option<&TrieDB>, parent_hash: TrieHash, mut slice_query: Option<&mut NibbleSlice>, - inline_only: bool, + inline_only: bool, // TODO remove all inline only param and make it db is_none ) -> Result, CError>> { let mut is_inline = false; let prefix = &mut self.prefix; @@ -1148,7 +1144,7 @@ impl RecordStack { }, } } else { - NodeHandle::Hash(db.root().as_ref()) + NodeHandle::Hash(db.expect("non inline").root().as_ref()) }; if let &NodeHandle::Inline(_) = &child_handle { // TODO consider not going into inline for all proof but content. @@ -1171,9 +1167,21 @@ impl RecordStack { prefix.push(child_index); } // TODO handle cache first - let child_node = db - .get_raw_or_lookup_with_cache(parent_hash, child_handle, prefix.as_prefix(), false) - .map_err(|_| VerifyError::IncompleteProof)?; // actually incomplete db: TODO consider switching error + let child_node = if let Some(db) = db { + db.get_raw_or_lookup_with_cache(parent_hash, child_handle, prefix.as_prefix(), false) + .map_err(|_| VerifyError::IncompleteProof)? // actually incomplete db: TODO consider switching error + } else { + let NodeHandle::Inline(node_data) = child_handle else { + unreachable!("call on non inline node when db is None"); + }; + ( + OwnedNode::new::(node_data.to_vec()) + .map_err(|_| VerifyError::IncompleteProof)?, + None, + ) + }; + + // } // TODO put in proof (only if Hash or inline for content one) @@ -1241,7 +1249,7 @@ impl RecordStack { fn access_value<'a>( &mut self, - db: &TrieDB, + db: Option<&TrieDB>, hash_only: bool, ) -> Result, CError>> { let Some(item)= self.items.last_mut() else { @@ -1268,7 +1276,7 @@ impl RecordStack { item.accessed_value_node = true; let mut hash = TrieHash::::default(); hash.as_mut().copy_from_slice(hash_slice); - let Some(value) = db.db().get(&hash, self.prefix.as_prefix()) else { + let Some(value) = db.expect("non inline").db().get(&hash, self.prefix.as_prefix()) else { return Err(VerifyError::IncompleteProof); }; if self.recorder.record_value_node(value, self.prefix.len()) { @@ -1388,7 +1396,9 @@ impl RecordStack { }, NodeHandle::Inline(_) => { // As been accessed if needed (inline are not mark). - // TODO need to process + // from is HaltedStateRecord + // from = try_stack_inline_child(from, db, NIBBLE_LENGTH + // as u8)?; println!("TODO need to process"); }, } diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index baf1c846..92eb2a25 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -323,7 +323,7 @@ fn test_query_plan_internal() { let mut proofs: Vec>> = Default::default(); let mut query_plan_iter = query_plan.as_ref(); loop { - from = record_query_plan::(&db, &mut query_plan_iter, from).unwrap(); + record_query_plan::(&db, &mut query_plan_iter, &mut from).unwrap(); if limit.is_none() { assert!(from.is_finished()); From 6545b06dfe02ac2d69cda592450507dc29bce349 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 15 Jun 2023 15:57:32 +0200 Subject: [PATCH 070/154] some fixes --- trie-db/src/query_plan.rs | 681 +++++++++++++++++++------------------- trie-db/test/src/proof.rs | 11 +- 2 files changed, 348 insertions(+), 344 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 54d53c85..54495f27 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -477,7 +477,7 @@ impl Recorder { } // TODO this should be call also in all node (not only when not finding value): - // then could just be part of enter node? + // then could just be part of enter node? TODO rem #[must_use] fn record_skip_value(&mut self, items: &mut Vec) -> bool { let mut res = false; @@ -529,6 +529,24 @@ impl Recorder { } res } + + #[must_use] + fn touched_child_hash(&mut self, hash_slice: &[u8], i: u8) -> bool { + let mut res = false; + match &mut self.output { + RecorderStateInner::Content { output, .. } => { + let mut hash = TrieHash::::default(); + hash.as_mut().copy_from_slice(hash_slice); + let op = Op::, Vec>::HashChild(hash, i as u8); + let init_len = output.buf_len(); + op.encode_into(output); + let written = output.buf_len() - init_len; + res = self.limits.add_node(written, 0, false); + }, + _ => (), + } + res + } } impl Limits { @@ -698,6 +716,270 @@ impl HaltedStateRecord { pub fn finish(self) -> Recorder { self.stack.recorder } + + fn finalize(&mut self) { + let stack = &mut self.stack; + let items = &stack.items; + match &mut stack.recorder.output { + RecorderStateInner::Compact { output, proof, stacked_pos } => { + let restarted_from = 0; + if stacked_pos.len() > restarted_from { + // halted: complete up to 0 and write all nodes keeping stack. + let mut items = items.iter().rev(); + while let Some(pos) = stacked_pos.pop() { + loop { + let item = items.next().expect("pos stacked with an item"); + if !item.is_inline { + proof[pos] = crate::trie_codec::encode_node_internal::( + &item.node, + item.accessed_value_node, + item.accessed_children_node, + ) + .expect("TODO error handling, can it actually fail?"); + break + } + } + } + } + for entry in core::mem::take(proof) { + output.write_entry(entry.into()); + } + }, + RecorderStateInner::Stream(_output) => { + // all written + }, + RecorderStateInner::Content { output: _, stacked_push, stacked_pop: _ } => { + // TODO protect existing stack as for compact + assert!(stacked_push.is_none()); + // TODO could use function with &item and &[item] as param + // to skip this clone. + for i in (0..items.len()).rev() { + let _ = self.record_popped_node(i); + } + }, + } + } + + #[must_use] + fn record_popped_node(&mut self, at: usize) -> bool { + let item = self.stack.items.get(at).expect("bounded iter"); + let items = &self.stack.items[..at]; + let mut res = false; + if !self.stack.recorder.check_start_at(item.depth) { + return res + } + if let RecorderStateInner::Content { .. } = &self.stack.recorder.output { + // if no value accessed, then we can have push then stack pop. + if self.stack.recorder.flush_compact_content_pushes(item.depth) { + res = true; + } + } + + let mut process_children = None; + let mut has_hash_to_write = false; + match &mut self.stack.recorder.output { + RecorderStateInner::Stream(_) => (), + RecorderStateInner::Compact { proof, stacked_pos, .. } => + if !item.is_inline { + if let Some(at) = stacked_pos.pop() { + proof[at] = crate::trie_codec::encode_node_internal::( + &item.node, + item.accessed_value_node, + item.accessed_children_node, + ) + .expect("TODO error handling, can it actually fail?"); + } // else when restarting record, this is not to be recorded + }, + RecorderStateInner::Content { output, stacked_pop, .. } => { + // two case: children to register or all children accessed. + if let Some(last_item) = items.last() { + match last_item.node.node_plan() { + NodePlan::Branch { children, .. } | + NodePlan::NibbledBranch { children, .. } => + for i in 0..children.len() { + if children[i].is_some() && !last_item.accessed_children_node.at(i) + { + has_hash_to_write = true; + break + } + }, + _ => (), + } + } + + match item.node.node_plan() { + NodePlan::Branch { children, .. } | + NodePlan::NibbledBranch { children, .. } => { + process_children = Some(children.len()); + }, + _ => (), + } + }, + } + + if let Some(nb_children) = process_children { + self.try_stack_content_child(NIBBLE_LENGTH as u8) + .expect("stack inline do not fetch"); + } + let items = &self.stack.items[..at]; + match &mut self.stack.recorder.output { + RecorderStateInner::Content { output, stacked_pop, .. } => { + let item = &self.stack.items.get(at).expect("bounded iter"); + if stacked_pop.is_none() { + *stacked_pop = Some(item.depth); + } + + if has_hash_to_write { + if flush_compact_content_pop::( + output, + stacked_pop, + items, + None, + &mut self.stack.recorder.limits, + ) { + res = true; + } + } + }, + _ => (), + } + + res + } + + // Add child for content + fn try_stack_content_child( + &mut self, + upper: u8, + ) -> Result<(), VerifyError, CError>> { + let dummy_parent_hash = TrieHash::::default(); + if let Some(item) = self.stack.items.last() { + let pre = item.next_descended_child; // TODO put next_descended child to 16 for leaf (skip some noop iter) + for i in pre..upper as u8 { + match self.stack.try_stack_child(i, None, dummy_parent_hash, None, true)? { + // only expect a stacked prefix here + TryStackChildResult::Stacked => { + let halt = self.iter_prefix(None, None, false, true, true)?; + if halt { + // no halt on inline. + unreachable!() + } else { + self.pop(); + } + }, + TryStackChildResult::NotStackedBranch => (), + _ => break, + } + } + } + self.stack.items.last_mut().map(|i| i.next_descended_child = upper); + Ok(()) + } + + fn pop(&mut self) -> bool { + if self + .stack + .iter_prefix + .map(|(l, _)| l == self.stack.items.len()) + .unwrap_or(false) + { + return false + } + let at = self.stack.items.len(); + if at > 0 { + if self.record_popped_node(at - 1) { + self.stack.halt = true; + } + } + if let Some(item) = self.stack.items.pop() { + let depth = self.stack.items.last().map(|i| i.depth).unwrap_or(0); + self.stack.prefix.drop_lasts(self.stack.prefix.len() - depth); + if depth == item.depth { + // Two consecutive identical depth is an extension + self.pop(); + } + true + } else { + false + } + } + + fn iter_prefix( + &mut self, + prev_query: Option<&QueryPlanItem>, + db: Option<&TrieDB>, + hash_only: bool, + first_iter: bool, + inline_iter: bool, + ) -> Result, CError>> { + let dummy_parent_hash = TrieHash::::default(); + if first_iter { + self.stack.enter_prefix_iter(hash_only); + } + + // run prefix iteration + let mut stacked = first_iter; + loop { + // descend + loop { + if stacked { + // try access value in next node + self.stack.access_value(db, hash_only)?; + stacked = false; + } + + let child_index = if let Some(mut item) = self.stack.items.last_mut() { + if item.next_descended_child as usize >= NIBBLE_LENGTH { + break + } + item.next_descended_child += 1; + item.next_descended_child - 1 + } else { + break + }; + + match self.stack.try_stack_child( + child_index, + db, + dummy_parent_hash, + None, + inline_iter, + )? { + TryStackChildResult::Stacked => { + stacked = true; + }, + TryStackChildResult::NotStackedBranch => (), + TryStackChildResult::NotStacked => break, + TryStackChildResult::StackedDescendIncomplete => { + unreachable!("no slice query") + }, + TryStackChildResult::Halted => { + if let Some(mut item) = self.stack.items.last_mut() { + item.next_descended_child -= 1; + } + self.stack.halt = false; + self.stack.prefix.push(child_index); + let dest_from = Some(( + self.stack.prefix.inner().to_vec(), + (self.stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, + )); + self.stack.prefix.pop(); + self.finalize(); + self.from = dest_from; + self.currently_query_item = prev_query.map(|q| q.to_owned()); + return Ok(true) + }, + } + } + + // pop + if !self.pop() { + break + } + } + self.stack.exit_prefix_iter(); + Ok(false) + } } /// When process is halted keep execution state @@ -826,14 +1108,12 @@ pub fn record_query_plan< } } - let mut stack = &mut from.stack; - let mut prev_query: Option = None; let from_query = from.currently_query_item.take(); let mut from_query_ref = from_query.as_ref().map(|f| f.as_ref()); while let Some(query) = from_query_ref.clone().or_else(|| query_plan.items.next()) { if stateless { - let bound = stack.seek.as_ref().expect("Initiated for stateless"); + let bound = from.stack.seek.as_ref().expect("Initiated for stateless"); let bound = bound.as_leftnibbleslice(); let query_slice = LeftNibbleSlice::new(&query.key); if query_slice.starts_with(&bound) { @@ -847,7 +1127,7 @@ pub fn record_query_plan< } stateless = false; if !query.as_prefix { - stack.seek = None; + from.stack.seek = None; } } let common_nibbles = if let Some(slice_at) = statefull.take() { @@ -863,15 +1143,14 @@ pub fn record_query_plan< } } loop { - match stack.prefix.len().cmp(&common_nibbles) { + match from.stack.prefix.len().cmp(&common_nibbles) { Ordering::Equal | Ordering::Less => break, Ordering::Greater => { if query_plan.kind.record_inline() { - try_stack_inline_child(from, NIBBLE_LENGTH as u8)?; - stack = &mut from.stack; + from.try_stack_content_child(NIBBLE_LENGTH as u8)?; } - if !stack.pop() { - stack.finalize(); + if !from.pop() { + from.finalize(); return Ok(()) } }, @@ -879,13 +1158,11 @@ pub fn record_query_plan< } common_nibbles }; - if let Some((_, hash_only)) = stack.iter_prefix.clone() { + if let Some((_, hash_only)) = from.stack.iter_prefix.clone() { // statefull halted during iteration. - let halt = iter_prefix::(from, Some(&query), Some(db), hash_only, false, false)?; + let halt = from.iter_prefix(Some(&query), Some(db), hash_only, false, false)?; if halt { return Ok(()) - } else { - stack = &mut from.stack; } from_query_ref = None; prev_query = Some(query); @@ -894,43 +1171,34 @@ pub fn record_query_plan< // descend let mut slice_query = NibbleSlice::new_offset(&query.key, common_nibbles); let touched = loop { - if !stack.items.is_empty() { + if !from.stack.items.is_empty() { if slice_query.is_empty() { if query.as_prefix { - let halt = iter_prefix::( - from, - Some(&query), - Some(db), - query.hash_only, - true, - false, - )?; + let halt = + from.iter_prefix(Some(&query), Some(db), query.hash_only, true, false)?; if halt { return Ok(()) - } else { - stack = &mut from.stack; } break false } else { break true } } else { - if stack.recorder.record_skip_value(&mut stack.items) { - stack.halt = true; + if from.stack.recorder.record_skip_value(&mut from.stack.items) { + from.stack.halt = true; } } } - let child_index = if stack.items.is_empty() { 0 } else { slice_query.at(0) }; + let child_index = if from.stack.items.is_empty() { 0 } else { slice_query.at(0) }; if query_plan.kind.record_inline() { - try_stack_inline_child(from, child_index)?; - stack = &mut from.stack; + from.try_stack_content_child(child_index)?; } - stack.items.last_mut().map(|i| { + from.stack.items.last_mut().map(|i| { // TODO only needed for content but could be better to be always aligned i.next_descended_child = child_index + 1; }); - match stack.try_stack_child( + match from.stack.try_stack_child( child_index, Some(db), dummy_parent_hash, @@ -942,32 +1210,24 @@ pub fn record_query_plan< break false, TryStackChildResult::StackedDescendIncomplete => { if query.as_prefix { - let halt = iter_prefix::( - from, - Some(&query), - Some(db), - query.hash_only, - true, - false, - )?; + let halt = + from.iter_prefix(Some(&query), Some(db), query.hash_only, true, false)?; if halt { return Ok(()) - } else { - stack = &mut from.stack; } } break false }, TryStackChildResult::Halted => { - stack.halt = false; - stack.prefix.push(child_index); + from.stack.halt = false; + from.stack.prefix.push(child_index); from.from = Some(( - stack.prefix.inner().to_vec(), - (stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, + from.stack.prefix.inner().to_vec(), + (from.stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, )); - stack.prefix.pop(); + from.stack.prefix.pop(); from.currently_query_item = Some(query.to_owned()); - stack.finalize(); + from.finalize(); return Ok(()) }, } @@ -975,127 +1235,25 @@ pub fn record_query_plan< if touched { // try access value - stack.access_value(Some(db), query.hash_only)?; + from.stack.access_value(Some(db), query.hash_only)?; } from_query_ref = None; prev_query = Some(query); } + // TODO loop redundant with finalize?? loop { if query_plan.kind.record_inline() { - try_stack_inline_child(from, NIBBLE_LENGTH as u8)?; - stack = &mut from.stack; + from.try_stack_content_child(NIBBLE_LENGTH as u8)?; } - if !stack.pop() { + if !from.pop() { break } } - stack.finalize(); - Ok(()) -} - -fn try_stack_inline_child<'a, L: TrieLayout, O: RecorderOutput>( - from: &mut HaltedStateRecord, - upper: u8, -) -> Result<(), VerifyError, CError>> { - let dummy_parent_hash = TrieHash::::default(); - let mut stack = &mut from.stack; - if let Some(item) = stack.items.last() { - let pre = item.next_descended_child; // TODO put next_descended child to 16 for leaf (skip some noop iter) - for i in pre..upper as u8 { - match stack.try_stack_child(i, None, dummy_parent_hash, None, true)? { - // only expect a stacked prefix here - TryStackChildResult::Stacked => { - let halt = iter_prefix::(from, None, None, false, true, true)?; - if halt { - // no halt on inline. - unreachable!() - } else { - stack = &mut from.stack; - stack.pop(); - } - }, - TryStackChildResult::NotStackedBranch => (), - _ => break, - } - } - } - stack.items.last_mut().map(|i| i.next_descended_child = upper); + from.finalize(); Ok(()) } -fn iter_prefix( - from: &mut HaltedStateRecord, - prev_query: Option<&QueryPlanItem>, - db: Option<&TrieDB>, - hash_only: bool, - first_iter: bool, - inline_iter: bool, -) -> Result, CError>> { - let stack = &mut from.stack; - let dummy_parent_hash = TrieHash::::default(); - if first_iter { - stack.enter_prefix_iter(hash_only); - } - - // run prefix iteration - let mut stacked = first_iter; - loop { - // descend - loop { - if stacked { - // try access value in next node - stack.access_value(db, hash_only)?; - stacked = false; - } - - let child_index = if let Some(mut item) = stack.items.last_mut() { - if item.next_descended_child as usize >= NIBBLE_LENGTH { - break - } - item.next_descended_child += 1; - item.next_descended_child - 1 - } else { - break - }; - - match stack.try_stack_child(child_index, db, dummy_parent_hash, None, inline_iter)? { - TryStackChildResult::Stacked => { - stacked = true; - }, - TryStackChildResult::NotStackedBranch => (), - TryStackChildResult::NotStacked => break, - TryStackChildResult::StackedDescendIncomplete => { - unreachable!("no slice query") - }, - TryStackChildResult::Halted => { - if let Some(mut item) = stack.items.last_mut() { - item.next_descended_child -= 1; - } - stack.halt = false; - stack.prefix.push(child_index); - let dest_from = Some(( - stack.prefix.inner().to_vec(), - (stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, - )); - stack.prefix.pop(); - stack.finalize(); - from.from = dest_from; - from.currently_query_item = prev_query.map(|q| q.to_owned()); - return Ok(true) - }, - } - } - - // pop - if !stack.pop() { - break - } - } - stack.exit_prefix_iter(); - Ok(false) -} - enum TryStackChildResult { Stacked, NotStackedBranch, @@ -1115,11 +1273,10 @@ impl RecordStack { ) -> Result, CError>> { let mut is_inline = false; let prefix = &mut self.prefix; - let stack = &mut self.items; let mut descend_incomplete = false; let mut stack_extension = false; let mut from_branch = None; - let child_handle = if let Some(item) = stack.last_mut() { + let child_handle = if let Some(item) = self.items.last_mut() { let node_data = item.node.data(); match item.node.node_plan() { @@ -1146,17 +1303,23 @@ impl RecordStack { } else { NodeHandle::Hash(db.expect("non inline").root().as_ref()) }; - if let &NodeHandle::Inline(_) = &child_handle { - // TODO consider not going into inline for all proof but content. - // Returning NotStacked here sounds safe, then the is_inline field is not needed. - is_inline = true; - } else { - if inline_only { - return Ok(TryStackChildResult::NotStackedBranch) - } - if self.halt && from_branch.is_some() { - return Ok(TryStackChildResult::Halted) - } + match &child_handle { + NodeHandle::Inline(_) => { + // TODO consider not going into inline for all proof but content. + // Returning NotStacked here sounds safe, then the is_inline field is not needed. + is_inline = true; + }, + NodeHandle::Hash(hash) => { + if inline_only { + if self.recorder.touched_child_hash(hash, child_index) { + self.halt = true; + } + return Ok(TryStackChildResult::NotStackedBranch) + } + if self.halt && from_branch.is_some() { + return Ok(TryStackChildResult::Halted) + } + }, } if let Some(accessed_children_node) = from_branch { if !is_inline || self.recorder.mark_inline_access() { @@ -1226,13 +1389,15 @@ impl RecordStack { next_descended_child, is_inline, }; - if self - .recorder - .record_stacked_node(&infos, stack.is_empty(), child_index, &*stack) - { + if self.recorder.record_stacked_node( + &infos, + self.items.is_empty(), + child_index, + &self.items, + ) { self.halt = true; } - stack.push(infos); + self.items.push(infos); if stack_extension { let sbranch = self.try_stack_child(0, db, parent_hash, slice_query, inline_only)?; let TryStackChildResult::Stacked = sbranch else { @@ -1295,29 +1460,6 @@ impl RecordStack { Ok(true) } - fn pop(&mut self) -> bool { - if self.iter_prefix.map(|(l, _)| l == self.items.len()).unwrap_or(false) { - return false - } - let at = self.items.len(); - if at > 0 { - if self.record_popped_node(at - 1) { - self.halt = true; - } - } - if let Some(item) = self.items.pop() { - let depth = self.items.last().map(|i| i.depth).unwrap_or(0); - self.prefix.drop_lasts(self.prefix.len() - depth); - if depth == item.depth { - // Two consecutive identical depth is an extension - self.pop(); - } - true - } else { - false - } - } - fn enter_prefix_iter(&mut self, hash_only: bool) { self.iter_prefix = Some((self.items.len(), hash_only)); } @@ -1325,146 +1467,6 @@ impl RecordStack { fn exit_prefix_iter(&mut self) { self.iter_prefix = None } - - #[must_use] - fn record_popped_node(&mut self, at: usize) -> bool { - let item = self.items.get(at).expect("bounded iter"); - let items = &self.items[..at]; - let mut res = false; - if !self.recorder.check_start_at(item.depth) { - return res - } - if let RecorderStateInner::Content { .. } = &self.recorder.output { - // if no value accessed, then we can have push then stack pop. - if self.recorder.flush_compact_content_pushes(item.depth) { - res = true; - } - } - - match &mut self.recorder.output { - RecorderStateInner::Stream(_) => (), - RecorderStateInner::Compact { proof, stacked_pos, .. } => - if !item.is_inline { - if let Some(at) = stacked_pos.pop() { - proof[at] = crate::trie_codec::encode_node_internal::( - &item.node, - item.accessed_value_node, - item.accessed_children_node, - ) - .expect("TODO error handling, can it actually fail?"); - } // else when restarting record, this is not to be recorded - }, - RecorderStateInner::Content { output, stacked_pop, .. } => { - if stacked_pop.is_none() { - *stacked_pop = Some(item.depth); - } - // two case: children to register or all children accessed. - let mut has_hash_to_write = false; - let node_data = item.node.data(); - if let Some(last_item) = items.last() { - match last_item.node.node_plan() { - NodePlan::Branch { children, .. } | - NodePlan::NibbledBranch { children, .. } => - for i in 0..children.len() { - if children[i].is_some() && !last_item.accessed_children_node.at(i) - { - has_hash_to_write = true; - break - } - }, - _ => (), - } - } - - match item.node.node_plan() { - NodePlan::Branch { children, .. } | - NodePlan::NibbledBranch { children, .. } => { - for i in 0..children.len() { - if let Some(child) = &children[i] { - if !item.accessed_children_node.at(i) { - match child.build(node_data) { - NodeHandle::Hash(hash_slice) => { - let mut hash = TrieHash::::default(); - hash.as_mut().copy_from_slice(hash_slice); - let op = Op::, Vec>::HashChild( - hash, i as u8, - ); - let init_len = output.buf_len(); - op.encode_into(output); - let written = output.buf_len() - init_len; - res = self.recorder.limits.add_node(written, 0, false) - }, - NodeHandle::Inline(_) => { - // As been accessed if needed (inline are not mark). - // from is HaltedStateRecord - // from = try_stack_inline_child(from, db, NIBBLE_LENGTH - // as u8)?; - println!("TODO need to process"); - }, - } - } - } - } - }, - _ => (), - } - if has_hash_to_write { - if flush_compact_content_pop::( - output, - stacked_pop, - items, - None, - &mut self.recorder.limits, - ) { - res = true; - } - } - }, - } - res - } - - fn finalize(&mut self) { - let items = &self.items; - match &mut self.recorder.output { - RecorderStateInner::Compact { output, proof, stacked_pos } => { - let restarted_from = 0; - if stacked_pos.len() > restarted_from { - // halted: complete up to 0 and write all nodes keeping stack. - let mut items = items.iter().rev(); - while let Some(pos) = stacked_pos.pop() { - loop { - let item = items.next().expect("pos stacked with an item"); - if !item.is_inline { - proof[pos] = crate::trie_codec::encode_node_internal::( - &item.node, - item.accessed_value_node, - item.accessed_children_node, - ) - .expect("TODO error handling, can it actually fail?"); - break - } - } - } - } - for entry in core::mem::take(proof) { - output.write_entry(entry.into()); - } - }, - RecorderStateInner::Stream(_output) => { - // all written - }, - RecorderStateInner::Content { output: _, stacked_push, stacked_pop: _ } => { - // TODO protect existing stack as for compact - assert!(stacked_push.is_none()); - // TODO could use function with &item and &[item] as param - // to skip this clone. - for i in (0..items.len()).rev() { - let _ = self.record_popped_node(i); - } - }, - } - } } #[must_use] @@ -1476,8 +1478,8 @@ fn flush_compact_content_pop( limits: &mut Limits, ) -> bool { let Some(from) = stacked_from.take() else { - return false - }; + return false + }; let pop_to = add_depth.unwrap_or_else(|| items.last().map(|i| i.depth).unwrap_or(0)); debug_assert!(from > pop_to); @@ -2941,12 +2943,13 @@ where } from_iter = false; - // netx + // next let item = if match &op { Op::Value(..) | Op::HashValue(..) => true, _ => false, } { let Some(current) = self.current.as_ref() else { + // can be inline content at end of a proof: TODO continue reading let r = self.stack.stack_pop(None, &self.expected_root); self.state = ReadProofState::Finished; if let Err(e) = r { diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 92eb2a25..90635e11 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -285,12 +285,12 @@ fn test_query_plan_internal() { continue } let query_plans = [ + /* InMemQueryPlan { items: vec![InMemQueryPlanItem::new(b"".to_vec(), hash_only, true)], ignore_unordered: false, kind, }, - /* InMemQueryPlan { items: vec![ InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), @@ -299,20 +299,20 @@ fn test_query_plan_internal() { ignore_unordered: false, kind, }, + */ InMemQueryPlan { items: vec![ InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), - InMemQueryPlanItem::new(b"doge".to_vec(), hash_only, false), - InMemQueryPlanItem::new(b"horsey".to_vec(), hash_only, false), + // InMemQueryPlanItem::new(b"doge".to_vec(), hash_only, false), + // InMemQueryPlanItem::new(b"horsey".to_vec(), hash_only, false), ], ignore_unordered: false, kind, }, - */ ]; for (nb_plan, query_plan) in query_plans.iter().enumerate() { for limit_conf in [ - (1, false), /* (0, false), (1, false), (1, true), (2, false), (2, true), (3, + (0, false), /* (0, false), (1, false), (1, true), (2, false), (2, true), (3, * true) */ ] { let limit = limit_conf.0; @@ -397,6 +397,7 @@ fn test_query_plan_internal() { // assert_eq!(proofs.len(), 1); assert_eq!(proof.len(), 1); + break; let refs: Vec, Vec>> = match (limit.unwrap_or(0), nb_plan, nb) { (0, 0, 0) => vec![ From 93998b897239761b70c233ac7fa4d41996f27995 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 15 Jun 2023 16:16:58 +0200 Subject: [PATCH 071/154] mark hash to avoid useless keypop --- trie-db/src/query_plan.rs | 67 ++++++++++++++++++++------------------- trie-db/test/src/proof.rs | 6 ++-- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index 54495f27..c473e4fd 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -1314,6 +1314,11 @@ impl RecordStack { if self.recorder.touched_child_hash(hash, child_index) { self.halt = true; } + if self.recorder.mark_inline_access() { + if let Some(accessed_children_node) = from_branch { + accessed_children_node.set(child_index as usize, true); + } + } return Ok(TryStackChildResult::NotStackedBranch) } if self.halt && from_branch.is_some() { @@ -2948,47 +2953,43 @@ where Op::Value(..) | Op::HashValue(..) => true, _ => false, } { - let Some(current) = self.current.as_ref() else { - // can be inline content at end of a proof: TODO continue reading - let r = self.stack.stack_pop(None, &self.expected_root); - self.state = ReadProofState::Finished; - if let Err(e) = r { - self.state = ReadProofState::Finished; - return Some(Err(e)) - } - return None; // finished - }; let mut to_check_slice = to_check_slice.as_mut().expect("Init above"); let mut at_value = false; let mut next_query = false; - let query_slice = LeftNibbleSlice::new(¤t.key); - match self.stack.prefix.as_leftnibbleslice().cmp(&query_slice) { - Ordering::Equal => - if !self.stack.items.is_empty() { - at_value = true; - }, - Ordering::Less => (), - Ordering::Greater => - if current.as_prefix { - let query_slice = LeftNibbleSlice::new(¤t.key); - if self.stack.prefix.as_leftnibbleslice().starts_with(&query_slice) - { + if let Some(current) = self.current.as_ref() { + let query_slice = LeftNibbleSlice::new(¤t.key); + match self.stack.prefix.as_leftnibbleslice().cmp(&query_slice) { + Ordering::Equal => + if !self.stack.items.is_empty() { at_value = true; + }, + Ordering::Less => (), + Ordering::Greater => + if current.as_prefix { + let query_slice = LeftNibbleSlice::new(¤t.key); + if self + .stack + .prefix + .as_leftnibbleslice() + .starts_with(&query_slice) + { + at_value = true; + } else { + next_query = true; + } } else { next_query = true; - } + }, + } + if next_query { + self.buf_op = Some(op); + self.state = ReadProofState::SwitchQueryPlan; + if current.as_prefix { + break } else { - next_query = true; - }, - } - if next_query { - self.buf_op = Some(op); - self.state = ReadProofState::SwitchQueryPlan; - if current.as_prefix { - break - } else { - return Some(Ok(ReadProofItem::NoValue(¤t.key))) + return Some(Ok(ReadProofItem::NoValue(¤t.key))) + } } } diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 90635e11..5bf2a82f 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -303,8 +303,8 @@ fn test_query_plan_internal() { InMemQueryPlan { items: vec![ InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), - // InMemQueryPlanItem::new(b"doge".to_vec(), hash_only, false), - // InMemQueryPlanItem::new(b"horsey".to_vec(), hash_only, false), + // InMemQueryPlanItem::new(b"doge".to_vec(), hash_only, false), + // InMemQueryPlanItem::new(b"horsey".to_vec(), hash_only, false), ], ignore_unordered: false, kind, @@ -397,7 +397,7 @@ fn test_query_plan_internal() { // assert_eq!(proofs.len(), 1); assert_eq!(proof.len(), 1); - break; + break let refs: Vec, Vec>> = match (limit.unwrap_or(0), nb_plan, nb) { (0, 0, 0) => vec![ From 54814934bb3aa87c9bc620a229fe0f16793b1dfa Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 15 Jun 2023 17:39:48 +0200 Subject: [PATCH 072/154] fixes, awkward --- trie-db/src/query_plan.rs | 26 ++++++++++++++++---------- trie-db/test/src/proof.rs | 3 ++- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan.rs index c473e4fd..9d29291d 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan.rs @@ -818,8 +818,7 @@ impl HaltedStateRecord { } if let Some(nb_children) = process_children { - self.try_stack_content_child(NIBBLE_LENGTH as u8) - .expect("stack inline do not fetch"); + self.try_stack_content_child().expect("stack inline do not fetch"); } let items = &self.stack.items[..at]; match &mut self.stack.recorder.output { @@ -850,12 +849,12 @@ impl HaltedStateRecord { // Add child for content fn try_stack_content_child( &mut self, - upper: u8, + //upper: u8, ) -> Result<(), VerifyError, CError>> { let dummy_parent_hash = TrieHash::::default(); if let Some(item) = self.stack.items.last() { let pre = item.next_descended_child; // TODO put next_descended child to 16 for leaf (skip some noop iter) - for i in pre..upper as u8 { + for i in 0..NIBBLE_LENGTH as u8 { match self.stack.try_stack_child(i, None, dummy_parent_hash, None, true)? { // only expect a stacked prefix here TryStackChildResult::Stacked => { @@ -872,7 +871,10 @@ impl HaltedStateRecord { } } } - self.stack.items.last_mut().map(|i| i.next_descended_child = upper); + self.stack + .items + .last_mut() + .map(|i| i.next_descended_child = NIBBLE_LENGTH as u8); Ok(()) } @@ -1147,7 +1149,7 @@ pub fn record_query_plan< Ordering::Equal | Ordering::Less => break, Ordering::Greater => { if query_plan.kind.record_inline() { - from.try_stack_content_child(NIBBLE_LENGTH as u8)?; + from.try_stack_content_child()?; } if !from.pop() { from.finalize(); @@ -1191,9 +1193,9 @@ pub fn record_query_plan< } let child_index = if from.stack.items.is_empty() { 0 } else { slice_query.at(0) }; - if query_plan.kind.record_inline() { + /*if query_plan.kind.record_inline() { from.try_stack_content_child(child_index)?; - } + }*/ from.stack.items.last_mut().map(|i| { // TODO only needed for content but could be better to be always aligned i.next_descended_child = child_index + 1; @@ -1243,7 +1245,7 @@ pub fn record_query_plan< // TODO loop redundant with finalize?? loop { if query_plan.kind.record_inline() { - from.try_stack_content_child(NIBBLE_LENGTH as u8)?; + from.try_stack_content_child()?; } if !from.pop() { @@ -1269,7 +1271,7 @@ impl RecordStack { db: Option<&TrieDB>, parent_hash: TrieHash, mut slice_query: Option<&mut NibbleSlice>, - inline_only: bool, // TODO remove all inline only param and make it db is_none + inline_only: bool, // TODO remove all inline only param and make it db is_none TODO rename ) -> Result, CError>> { let mut is_inline = false; let prefix = &mut self.prefix; @@ -1277,6 +1279,10 @@ impl RecordStack { let mut stack_extension = false; let mut from_branch = None; let child_handle = if let Some(item) = self.items.last_mut() { + if inline_only && item.accessed_children_node.at(child_index as usize) { + return Ok(TryStackChildResult::NotStackedBranch) + } + let node_data = item.node.data(); match item.node.node_plan() { diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 5bf2a82f..7e5735b1 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -397,7 +397,8 @@ fn test_query_plan_internal() { // assert_eq!(proofs.len(), 1); assert_eq!(proof.len(), 1); - break + break; + let refs: Vec, Vec>> = match (limit.unwrap_or(0), nb_plan, nb) { (0, 0, 0) => vec![ From 7bcabbebb9cf4525d4c0c128008efdf11c7d46d3 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 15 Jun 2023 17:47:53 +0200 Subject: [PATCH 073/154] split test --- test-support/reference-trie/src/lib.rs | 4 +- trie-db/test/src/proof.rs | 934 ++++++++++++------------- 2 files changed, 463 insertions(+), 475 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 16728b1a..529c291f 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -51,10 +51,10 @@ macro_rules! test_layouts { ($test:ident, $test_internal:ident) => { #[test] fn $test() { - eprintln!("Running with layout `HashedValueNoExtThreshold`"); - // TODO $test_internal::<$crate::HashedValueNoExtThreshold<1>>(); eprintln!("Running with layout `SubstrateV1`"); $test_internal::<$crate::SubstrateV1<$crate::RefHasher>>(); + eprintln!("Running with layout `HashedValueNoExtThreshold`"); + $test_internal::<$crate::HashedValueNoExtThreshold<1>>(); eprintln!("Running with layout `HashedValueNoExt`"); $test_internal::<$crate::HashedValueNoExt>(); eprintln!("Running with layout `NoExtensionLayout`"); diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 7e5735b1..6bc6ef40 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -19,6 +19,7 @@ use std::collections::BTreeMap; use trie_db::{ proof::{generate_proof, verify_proof, VerifyError}, DBValue, Trie, TrieDBBuilder, TrieDBMutBuilder, TrieLayout, TrieMut, + query_plan::ProofKind, }; type MemoryDB = memory_db::MemoryDB< @@ -242,13 +243,30 @@ fn test_verify_decode_error_internal() { } } -test_layouts!(test_query_plan, test_query_plan_internal); -fn test_query_plan_internal() { +test_layouts!(test_query_plan_full, test_query_plan_full_internal); +fn test_query_plan_full_internal() { + test_query_plan_internal::(ProofKind::FullNodes, false); + test_query_plan_internal::(ProofKind::FullNodes, true); +} + +test_layouts!(test_query_plan_compact, test_query_plan_compact_internal); +fn test_query_plan_compact_internal() { + test_query_plan_internal::(ProofKind::CompactNodes, false); + test_query_plan_internal::(ProofKind::CompactNodes, true); +} + +test_layouts!(test_query_plan_content, test_query_plan_content_internal); +fn test_query_plan_content_internal() { + test_query_plan_internal::(ProofKind::CompactContent, false); + //test_query_plan_internal::(ProofKind::CompactNodes, true); +} + +fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { use trie_db::query_plan::{ compact_content_proof::IterOpProof, record_query_plan, verify_query_plan_iter, verify_query_plan_iter_content, HaltedStateCheck, HaltedStateCheckContent, HaltedStateCheckNode, HaltedStateRecord, InMemQueryPlan, InMemQueryPlanItem, - InMemoryRecorder, ProofKind, QueryPlan, ReadProofItem, Recorder, + InMemoryRecorder, QueryPlan, ReadProofItem, Recorder, }; let set = test_entries(); @@ -267,508 +285,478 @@ fn test_query_plan_internal() { }; let db = >::new(&db, &root).with_cache(&mut cache).build(); - for (hash_only, kind) in [ - (false, ProofKind::CompactContent), - /* (false, ProofKind::FullNodes), - (true, ProofKind::FullNodes), - (false, ProofKind::CompactNodes), - (true, ProofKind::CompactNodes), */ - ] { - if (kind == ProofKind::CompactContent || kind == ProofKind::CompactNodes) && - L::USE_EXTENSION - { - // Compact proofs are not supported with extensions. - // Requires changing the way extension are handled - // when decoding (putting on stack). - // Not implemented for `CompactContent`, would need - // to not append 0 after pushing an extension node. - continue - } - let query_plans = [ - /* - InMemQueryPlan { - items: vec![InMemQueryPlanItem::new(b"".to_vec(), hash_only, true)], - ignore_unordered: false, - kind, - }, - InMemQueryPlan { - items: vec![ - InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), - InMemQueryPlanItem::new(b"do".to_vec(), hash_only, true), - ], - ignore_unordered: false, - kind, - }, - */ - InMemQueryPlan { - items: vec![ - InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), - // InMemQueryPlanItem::new(b"doge".to_vec(), hash_only, false), - // InMemQueryPlanItem::new(b"horsey".to_vec(), hash_only, false), - ], - ignore_unordered: false, - kind, - }, - ]; - for (nb_plan, query_plan) in query_plans.iter().enumerate() { - for limit_conf in [ - (0, false), /* (0, false), (1, false), (1, true), (2, false), (2, true), (3, - * true) */ - ] { - let limit = limit_conf.0; - let limit = (limit != 0).then(|| limit); - let recorder = Recorder::new(kind, InMemoryRecorder::default(), limit, None); - let mut from = HaltedStateRecord::from_start(recorder); - // no limit - let mut proofs: Vec>> = Default::default(); - let mut query_plan_iter = query_plan.as_ref(); - loop { - record_query_plan::(&db, &mut query_plan_iter, &mut from).unwrap(); - - if limit.is_none() { - assert!(from.is_finished()); - } - if from.is_finished() { - if kind == ProofKind::CompactContent { - proofs.push(vec![from.finish().output().buffer]); - } else { - proofs.push(from.finish().output().nodes); - } - break - } - let rec = if limit_conf.1 { - query_plan_iter = query_plan.as_ref(); - from.stateless(Recorder::new( - kind, - InMemoryRecorder::default(), - limit, - None, - )) - } else { - from.statefull(Recorder::new( - kind, - InMemoryRecorder::default(), - limit, - None, - )) - }; + if (kind == ProofKind::CompactContent || kind == ProofKind::CompactNodes) && L::USE_EXTENSION { + // Compact proofs are not supported with extensions. + // Requires changing the way extension are handled + // when decoding (putting on stack). + // Not implemented for `CompactContent`, would need + // to not append 0 after pushing an extension node. + return + } + let query_plans = [ + InMemQueryPlan { + items: vec![InMemQueryPlanItem::new(b"".to_vec(), hash_only, true)], + ignore_unordered: false, + kind, + }, + InMemQueryPlan { + items: vec![ + InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), + InMemQueryPlanItem::new(b"do".to_vec(), hash_only, true), + ], + ignore_unordered: false, + kind, + }, + InMemQueryPlan { + items: vec![ + InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), + InMemQueryPlanItem::new(b"doge".to_vec(), hash_only, false), + InMemQueryPlanItem::new(b"horsey".to_vec(), hash_only, false), + ], + ignore_unordered: false, + kind, + }, + ]; + for (nb_plan, query_plan) in query_plans.iter().enumerate() { + for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] { + let limit = limit_conf.0; + let limit = (limit != 0).then(|| limit); + let recorder = Recorder::new(kind, InMemoryRecorder::default(), limit, None); + let mut from = HaltedStateRecord::from_start(recorder); + // no limit + let mut proofs: Vec>> = Default::default(); + let mut query_plan_iter = query_plan.as_ref(); + loop { + record_query_plan::(&db, &mut query_plan_iter, &mut from).unwrap(); + + if limit.is_none() { + assert!(from.is_finished()); + } + if from.is_finished() { if kind == ProofKind::CompactContent { - proofs.push(vec![rec.output().buffer]); - break // TODO remove + proofs.push(vec![from.finish().output().buffer]); } else { - proofs.push(rec.output().nodes); + proofs.push(from.finish().output().nodes); } + break } + let rec = if limit_conf.1 { + query_plan_iter = query_plan.as_ref(); + from.stateless(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) + } else { + from.statefull(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) + }; + if kind == ProofKind::CompactContent { + proofs.push(vec![rec.output().buffer]); + break // TODO remove + } else { + proofs.push(rec.output().nodes); + } + } + + let mut full_proof: Vec> = Default::default(); + proofs.reverse(); - let mut full_proof: Vec> = Default::default(); - proofs.reverse(); - - fn shifted(bytes: &[u8], aligned: bool) -> Vec { - let mut shifted: Vec = vec![]; - let last = bytes.len(); - bytes.iter().enumerate().for_each(|(i, b)| { - shifted.last_mut().map(|l| { - *l |= *b >> 4; - }); - if !(i == last - 1 && aligned) { - shifted.push(*b << 4); - } + fn shifted(bytes: &[u8], aligned: bool) -> Vec { + let mut shifted: Vec = vec![]; + let last = bytes.len(); + bytes.iter().enumerate().for_each(|(i, b)| { + shifted.last_mut().map(|l| { + *l |= *b >> 4; }); - shifted - } + if !(i == last - 1 && aligned) { + shifted.push(*b << 4); + } + }); + shifted + } - fn hash + Default>(b: &[u8]) -> H { - let mut hash = H::default(); - hash.as_mut().copy_from_slice(&b[..]); - hash - } + fn hash + Default>(b: &[u8]) -> H { + let mut hash = H::default(); + hash.as_mut().copy_from_slice(&b[..]); + hash + } - if kind == ProofKind::CompactContent { - let all = match L::MAX_INLINE_VALUE { - Some(1) => true, - Some(33) => false, - _ => continue, - }; - let mut nb = 0; - let mut proofs = proofs.clone(); - while let Some(proof) = proofs.pop() { - use trie_db::query_plan::compact_content_proof::Op; - // full on iter all - // assert_eq!(proofs.len(), 1); - assert_eq!(proof.len(), 1); - - break; - - let refs: Vec, Vec>> = - match (limit.unwrap_or(0), nb_plan, nb) { - (0, 0, 0) => vec![ - Op::KeyPush(b"alfa".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"bravo", false), 0xf0), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - ], - (0, 1, 0) => - if all { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::HashChild( - hash( - &[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, - 236, 20, 32, 247, 110, 189, 213, 140, 86, - 162, 229, 70, 86, 163, 223, 26, 52, 253, - 176, 201, 65, 248, - ][..], - ), - 1, - ), - Op::HashChild( - hash( - &[ - 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, - 78, 14, 161, 91, 109, 136, 84, 6, 128, 190, - 201, 49, 142, 21, 154, 250, 246, 133, 0, - 199, 138, 49, - ][..], - ), - 8, - ), - ] - } else { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - // inline ix 8 - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - Op::KeyPop(9), - Op::HashChild( - hash( - &[ - 225, 211, 100, 128, 231, 82, 240, 112, 33, - 165, 225, 30, 244, 128, 56, 45, 17, 21, - 138, 87, 3, 211, 231, 109, 244, 137, 208, - 244, 12, 65, 196, 119, - ][..], - ), - 1, - ), - ] - }, - (0, 2, 0) => - // bravo, doge, horsey - if all { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - // hash value here is not really good (could only be - // with child hashes when no hash query). - Op::HashValue(hash( + if kind == ProofKind::CompactContent { + let all = match L::MAX_INLINE_VALUE { + Some(1) => true, + Some(33) => false, + _ => continue, + }; + let mut nb = 0; + let mut proofs = proofs.clone(); + while let Some(proof) = proofs.pop() { + use trie_db::query_plan::compact_content_proof::Op; + // full on iter all + // assert_eq!(proofs.len(), 1); + assert_eq!(proof.len(), 1); + + let refs: Vec, Vec>> = + match (limit.unwrap_or(0), nb_plan, nb) { + (0, 0, 0) => vec![ + Op::KeyPush(b"alfa".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"bravo", false), 0xf0), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + ], + (0, 1, 0) => + if all { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::HashChild( + hash( &[ - 48, 51, 75, 77, 6, 75, 210, 124, 205, 63, 59, - 165, 81, 140, 222, 237, 196, 168, 203, 206, - 105, 245, 15, 154, 233, 147, 189, 123, 194, - 243, 179, 137, + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, + 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, + 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, + 248, ][..], - )), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::HashValue(hash( + ), + 1, + ), + Op::HashChild( + hash( &[ - 104, 225, 103, 23, 160, 148, 143, 214, 98, 64, - 250, 245, 134, 99, 233, 36, 28, 150, 26, 205, - 25, 165, 122, 211, 170, 180, 45, 82, 143, 71, - 191, 19, + 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, + 14, 161, 91, 109, 136, 84, 6, 128, 190, 201, + 49, 142, 21, 154, 250, 246, 133, 0, 199, 138, + 49, ][..], - )), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::HashValue(hash( + ), + 8, + ), + ] + } else { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + // inline ix 8 + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + Op::KeyPop(9), + Op::HashChild( + hash( &[ - 170, 195, 61, 227, 244, 86, 86, 205, 233, 84, - 40, 116, 166, 25, 158, 33, 18, 236, 208, 172, - 115, 246, 158, 34, 158, 170, 197, 139, 219, - 254, 124, 136, + 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, + 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, + 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, + 119, ][..], - )), - Op::KeyPop(5), - Op::HashChild( - hash( - &[ - 115, 96, 173, 184, 157, 30, 165, 173, 98, - 91, 45, 97, 173, 249, 2, 240, 133, 247, - 131, 7, 128, 195, 235, 114, 210, 152, 24, - 22, 105, 232, 147, 171, - ][..], - ), - 5, - ), - Op::KeyPop(4), - Op::HashChild( - hash( - &[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, - 236, 20, 32, 247, 110, 189, 213, 140, 86, - 162, 229, 70, 86, 163, 223, 26, 52, 253, - 176, 201, 65, 248, - ][..], - ), - 1, ), - ] - } else { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - Op::KeyPop(9), - Op::HashChild( - hash( - &[ - 225, 211, 100, 128, 231, 82, 240, 112, 33, - 165, 225, 30, 244, 128, 56, 45, 17, 21, - 138, 87, 3, 211, 231, 109, 244, 137, 208, - 244, 12, 65, 196, 119, - ][..], - ), - 1, - ), - ] - }, - (1, 2, 0) => - // bravo, doge, horsey - if all { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::HashChild( - hash( - &[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, - 236, 20, 32, 247, 110, 189, 213, 140, 86, - 162, 229, 70, 86, 163, 223, 26, 52, 253, - 176, 201, 65, 248, - ][..], - ), - 1, - ), - Op::HashChild( - hash( - &[ - 223, 91, 16, 28, 134, 71, 144, 93, 127, - 153, 131, 180, 101, 103, 252, 121, 200, 66, - 33, 188, 58, 187, 247, 197, 65, 169, 112, - 46, 241, 22, 96, 196, - ][..], - ), - 4, + 1, + ), + ] + }, + (0, 2, 0) => + // bravo, doge, horsey + if all { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + // hash value here is not really good (could only be + // with child hashes when no hash query). + Op::HashValue(hash( + &[ + 48, 51, 75, 77, 6, 75, 210, 124, 205, 63, 59, 165, + 81, 140, 222, 237, 196, 168, 203, 206, 105, 245, + 15, 154, 233, 147, 189, 123, 194, 243, 179, 137, + ][..], + )), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::HashValue(hash( + &[ + 104, 225, 103, 23, 160, 148, 143, 214, 98, 64, 250, + 245, 134, 99, 233, 36, 28, 150, 26, 205, 25, 165, + 122, 211, 170, 180, 45, 82, 143, 71, 191, 19, + ][..], + )), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::HashValue(hash( + &[ + 170, 195, 61, 227, 244, 86, 86, 205, 233, 84, 40, + 116, 166, 25, 158, 33, 18, 236, 208, 172, 115, 246, + 158, 34, 158, 170, 197, 139, 219, 254, 124, 136, + ][..], + )), + Op::KeyPop(5), + Op::HashChild( + hash( + &[ + 115, 96, 173, 184, 157, 30, 165, 173, 98, 91, + 45, 97, 173, 249, 2, 240, 133, 247, 131, 7, + 128, 195, 235, 114, 210, 152, 24, 22, 105, 232, + 147, 171, + ][..], ), - Op::HashChild( - hash( - &[ - 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, - 78, 14, 161, 91, 109, 136, 84, 6, 128, 190, - 201, 49, 142, 21, 154, 250, 246, 133, 0, - 199, 138, 49, - ][..], - ), - 8, + 5, + ), + Op::KeyPop(4), + Op::HashChild( + hash( + &[ + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, + 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, + 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, + 248, + ][..], ), - ] - } else { - break - /* - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - Op::KeyPop(9), - Op::HashChild( - (&[ + 1, + ), + ] + } else { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + Op::KeyPop(9), + Op::HashChild( + hash( + &[ 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, 119, - ][..]) - .into(), - 1, + ][..], ), - ] - */ - }, - - _ => break, - }; - let mut encoded = InMemoryRecorder::default(); - for r in refs { - r.encode_into(&mut encoded); - } - assert_eq!(proof[0], encoded.buffer); - nb += 1; + 1, + ), + ] + }, + (1, 2, 0) => + // bravo, doge, horsey + if all { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::HashChild( + hash( + &[ + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, + 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, + 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, + 248, + ][..], + ), + 1, + ), + Op::HashChild( + hash( + &[ + 223, 91, 16, 28, 134, 71, 144, 93, 127, 153, + 131, 180, 101, 103, 252, 121, 200, 66, 33, 188, + 58, 187, 247, 197, 65, 169, 112, 46, 241, 22, + 96, 196, + ][..], + ), + 4, + ), + Op::HashChild( + hash( + &[ + 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, + 14, 161, 91, 109, 136, 84, 6, 128, 190, 201, + 49, 142, 21, 154, 250, 246, 133, 0, 199, 138, + 49, + ][..], + ), + 8, + ), + ] + } else { + break + /* + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + Op::KeyPop(9), + Op::HashChild( + (&[ + 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, + 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, + 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, + 119, + ][..]) + .into(), + 1, + ), + ] + */ + }, + + _ => break, + }; + let mut encoded = InMemoryRecorder::default(); + for r in refs { + r.encode_into(&mut encoded); } - // continue + assert_eq!(proof[0], encoded.buffer); + nb += 1; } + // continue + } - let mut query_plan_iter: QueryPlan<_> = query_plan.as_ref(); - let is_content_proof = kind == ProofKind::CompactContent; - let mut run_state: Option> = Some(if is_content_proof { - HaltedStateCheck::Content(query_plan_iter.into()) + let mut query_plan_iter: QueryPlan<_> = query_plan.as_ref(); + let is_content_proof = kind == ProofKind::CompactContent; + let mut run_state: Option> = Some(if is_content_proof { + HaltedStateCheck::Content(query_plan_iter.into()) + } else { + HaltedStateCheck::Node(query_plan_iter.into()) + }); + let mut has_run_full = false; + while let Some(state) = run_state.take() { + let proof = if let Some(proof) = proofs.pop() { + full_proof.extend_from_slice(&proof); + proof } else { - HaltedStateCheck::Node(query_plan_iter.into()) - }); - let mut has_run_full = false; - while let Some(state) = run_state.take() { - let proof = if let Some(proof) = proofs.pop() { - full_proof.extend_from_slice(&proof); - proof - } else { - if full_proof.is_empty() { - break - } - proofs.clear(); - std::mem::take(&mut full_proof) - }; - let (mut verify_iter, mut verify_iter_content) = if is_content_proof { - ( - None, - Some( - verify_query_plan_iter_content::>( - state, - (&proof[0]).into(), - Some(root.clone()), - ) - .unwrap(), - ), - ) + if full_proof.is_empty() { + break + } + proofs.clear(); + std::mem::take(&mut full_proof) + }; + let (mut verify_iter, mut verify_iter_content) = if is_content_proof { + ( + None, + Some( + verify_query_plan_iter_content::>( + state, + (&proof[0]).into(), + Some(root.clone()), + ) + .unwrap(), + ), + ) + } else { + ( + Some( + verify_query_plan_iter::( + state, + proof.into_iter(), + Some(root.clone()), + ) + .unwrap(), + ), + None, + ) + }; + let mut next_item = || { + if let Some(verify_iter) = verify_iter.as_mut() { + verify_iter.next() + } else if let Some(verify_iter_content) = verify_iter_content.as_mut() { + verify_iter_content.next() } else { - ( - Some( - verify_query_plan_iter::( - state, - proof.into_iter(), - Some(root.clone()), - ) - .unwrap(), - ), - None, - ) - }; - let mut next_item = || { - if let Some(verify_iter) = verify_iter.as_mut() { - verify_iter.next() - } else if let Some(verify_iter_content) = verify_iter_content.as_mut() { - verify_iter_content.next() - } else { - None - } - }; - - let content: BTreeMap<_, _> = set.iter().cloned().collect(); - let mut in_prefix = false; - while let Some(item) = next_item() { - match item.unwrap() { - ReadProofItem::Hash(key, hash) => { - assert!(hash_only); - assert_eq!( - content.get(&*key).map(|v| L::Hash::hash(&v.as_ref())), - Some(hash) - ); - }, - ReadProofItem::Value(key, value) => { - assert_eq!(content.get(&*key), Some(&value.as_ref())); - }, - ReadProofItem::NoValue(key) => { - assert_eq!(content.get(key), None); - }, - ReadProofItem::StartPrefix(_prefix) => { - in_prefix = true; - }, - ReadProofItem::EndPrefix => { - assert!(in_prefix); - in_prefix = false; - }, - ReadProofItem::Halted(resume) => { - run_state = Some(*resume); - break - }, - } + None } - if run_state.is_none() && !has_run_full { - has_run_full = true; - query_plan_iter = query_plan.as_ref(); - run_state = Some(if is_content_proof { - HaltedStateCheck::Content(query_plan_iter.into()) - } else { - HaltedStateCheck::Node(query_plan_iter.into()) - }); + }; + + let content: BTreeMap<_, _> = set.iter().cloned().collect(); + let mut in_prefix = false; + while let Some(item) = next_item() { + match item.unwrap() { + ReadProofItem::Hash(key, hash) => { + assert!(hash_only); + assert_eq!( + content.get(&*key).map(|v| L::Hash::hash(&v.as_ref())), + Some(hash) + ); + }, + ReadProofItem::Value(key, value) => { + assert_eq!(content.get(&*key), Some(&value.as_ref())); + }, + ReadProofItem::NoValue(key) => { + assert_eq!(content.get(key), None); + }, + ReadProofItem::StartPrefix(_prefix) => { + in_prefix = true; + }, + ReadProofItem::EndPrefix => { + assert!(in_prefix); + in_prefix = false; + }, + ReadProofItem::Halted(resume) => { + run_state = Some(*resume); + break + }, } } - if !has_run_full { - panic!("did not run full proof") + if run_state.is_none() && !has_run_full { + has_run_full = true; + query_plan_iter = query_plan.as_ref(); + run_state = Some(if is_content_proof { + HaltedStateCheck::Content(query_plan_iter.into()) + } else { + HaltedStateCheck::Node(query_plan_iter.into()) + }); } } + if !has_run_full { + panic!("did not run full proof") + } } } } From a3800285a9412d6ceafbdff8ef8607f7b4a19bc5 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 15 Jun 2023 18:34:53 +0200 Subject: [PATCH 074/154] split a bit --- trie-db/src/content_proof.rs | 261 ++++ trie-db/src/lib.rs | 1 + .../src/{query_plan.rs => query_plan/mod.rs} | 1307 +---------------- trie-db/src/query_plan/record.rs | 1072 ++++++++++++++ trie-db/src/query_plan/verify.rs | 17 + trie-db/src/query_plan/verify_content.rs | 255 ++++ trie-db/test/src/proof.rs | 10 +- 7 files changed, 1620 insertions(+), 1303 deletions(-) create mode 100644 trie-db/src/content_proof.rs rename trie-db/src/{query_plan.rs => query_plan/mod.rs} (62%) create mode 100644 trie-db/src/query_plan/record.rs create mode 100644 trie-db/src/query_plan/verify.rs create mode 100644 trie-db/src/query_plan/verify_content.rs diff --git a/trie-db/src/content_proof.rs b/trie-db/src/content_proof.rs new file mode 100644 index 00000000..06b7f434 --- /dev/null +++ b/trie-db/src/content_proof.rs @@ -0,0 +1,261 @@ +// Copyright 2023, 2023 Parity Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +//! Compact content proof, a sequence of content as met in the trie. +//! Exception for hashes that are only encoded when node is popped, allowing to merge a few more key manipulation op. +//! Values are return as seen to avoid the need to keep them all (warning a return value may be from an invalid proof +//! and action on those value usually need to be revertible). +//! Proof validity as for compact proof is only possible to check at the end of the proof reading. + +/// Representation of each encoded action +/// for building the proof. +/// TODO ref variant for encoding ?? or key using V and use Op<&H, &[u8]>. + +use core::marker::PhantomData; +use crate::query_plan::RecorderOutput; + +#[derive(Debug)] +pub enum Op { + // key content followed by a mask for last byte. + // If mask erase some content the content need to + // be set at 0 (or error). + // Two consecutive `KeyPush` are invalid. + KeyPush(Vec, u8), /* TODO could use BackingByteVec (but Vec for new as it scale + * encode) */ + // Last call to pop is implicit (up to root), defining + // one will result in an error. + // Two consecutive `KeyPop` are invalid. + // TODO should be compact encoding of number. + KeyPop(u16), + // u8 is child index, shorthand for key push one nibble followed by key pop. + HashChild(H, u8), + // All value variant are only after a `KeyPush` or at first position. + HashValue(H), + Value(V), + // This is not strictly necessary, only if the proof is not sized, otherwhise if we know + // the stream will end it can be skipped. + EndProof, +} + +// Limiting size to u32 (could also just use a terminal character). +#[derive(Debug, PartialEq, Eq)] +#[repr(transparent)] +struct VarInt(u32); + +impl VarInt { + fn encoded_len(&self) -> usize { + if self.0 == 0 { + return 1 + } + let len = 32 - self.0.leading_zeros() as usize; + if len % 7 == 0 { + len / 7 + } else { + len / 7 + 1 + } + /* + match self.0 { + l if l < 2 ^ 7 => 1, // leading 0: 25 + l if l < 2 ^ 14 => 2, // leading 0: 18 + + l if l < 2 ^ 21 => 3, // 11 + l if l < 2 ^ 28 => 4, // 4 + _ => 5, + } + */ + } + + fn encode_into(&self, out: &mut impl RecorderOutput) { + let mut to_encode = self.0; + for _ in 0..self.encoded_len() - 1 { + out.write_bytes(&[0b1000_0000 | to_encode as u8]); + to_encode >>= 7; + } + out.write_bytes(&[to_encode as u8]); + } + + fn decode(encoded: &[u8]) -> Result<(Self, usize), ()> { + let mut value = 0u32; + for (i, byte) in encoded.iter().enumerate() { + let last = byte & 0b1000_0000 == 0; + value |= ((byte & 0b0111_1111) as u32) << (i * 7); + if last { + return Ok((VarInt(value), i + 1)) + } + } + Err(()) + } +} + +#[test] +fn varint_encode_decode() { + let mut buf = super::InMemoryRecorder::default(); + for i in 0..u16::MAX as u32 + 1 { + VarInt(i).encode_into(&mut buf); + assert_eq!(buf.buffer.len(), VarInt(i).encoded_len()); + assert_eq!(Ok((VarInt(i), buf.buffer.len())), VarInt::decode(&buf.buffer)); + buf.buffer.clear(); + } +} + +impl, V: AsRef<[u8]>> Op { + /// Calculate encoded len. + pub fn encoded_len(&self) -> usize { + let mut len = 1; + match self { + Op::KeyPush(key, _mask) => { + len += VarInt(key.len() as u32).encoded_len(); + len += key.len(); + len += 1; + }, + Op::KeyPop(nb) => { + len += VarInt(*nb as u32).encoded_len(); + }, + Op::HashChild(hash, _at) => { + len += hash.as_ref().len(); + len += 1; + }, + Op::HashValue(hash) => { + len += hash.as_ref().len(); + }, + Op::Value(value) => { + len += VarInt(value.as_ref().len() as u32).encoded_len(); + len += value.as_ref().len(); + }, + Op::EndProof => (), + } + len + } + + /// Write op. + pub fn encode_into(&self, out: &mut impl RecorderOutput) { + match self { + Op::KeyPush(key, mask) => { + out.write_bytes(&[0]); + VarInt(key.len() as u32).encode_into(out); + out.write_bytes(&key); + out.write_bytes(&[*mask]); + }, + Op::KeyPop(nb) => { + out.write_bytes(&[1]); + VarInt(*nb as u32).encode_into(out); + }, + Op::HashChild(hash, at) => { + out.write_bytes(&[2]); + out.write_bytes(hash.as_ref()); + out.write_bytes(&[*at]); + }, + Op::HashValue(hash) => { + out.write_bytes(&[3]); + out.write_bytes(hash.as_ref()); + }, + Op::Value(value) => { + out.write_bytes(&[4]); + let value = value.as_ref(); + VarInt(value.len() as u32).encode_into(out); + out.write_bytes(&value); + }, + Op::EndProof => { + out.write_bytes(&[5]); + }, + } + } +} + +impl + AsMut<[u8]> + Default> Op> { + /// Read an op, return op and number byte read. Or error if invalid encoded. + pub fn decode(encoded: &[u8]) -> Result<(Self, usize), ()> { + let mut i = 0; + if i >= encoded.len() { + return Err(()) + } + Ok(match encoded[i] { + 0 => { + let (len, offset) = VarInt::decode(&encoded[i + 1..])?; + i += 1 + offset; + if i + len.0 as usize >= encoded.len() { + return Err(()) + } + let key = &encoded[i..i + len.0 as usize]; + let mask = encoded[i + len.0 as usize]; + (Op::KeyPush(key.to_vec(), mask), i + len.0 as usize + 1) + }, + 1 => { + let (len, offset) = VarInt::decode(&encoded[i + 1..])?; + if len.0 > u16::MAX as u32 { + return Err(()) + } + (Op::KeyPop(len.0 as u16), i + 1 + offset) + }, + 2 => { + let mut hash = H::default(); + let end = i + 1 + hash.as_ref().len(); + if end >= encoded.len() { + return Err(()) + } + hash.as_mut().copy_from_slice(&encoded[i + 1..end]); + let mask = encoded[end]; + (Op::HashChild(hash, mask), end + 1) + }, + 3 => { + let mut hash = H::default(); + let end = i + 1 + hash.as_ref().len(); + if end >= encoded.len() { + return Err(()) + } + hash.as_mut().copy_from_slice(&encoded[i + 1..end]); + (Op::HashValue(hash), end) + }, + 4 => { + let (len, offset) = VarInt::decode(&encoded[i + 1..])?; + i += 1 + offset; + if i + len.0 as usize > encoded.len() { + return Err(()) + } + let value = &encoded[i..i + len.0 as usize]; + (Op::Value(value.to_vec()), i + len.0 as usize) + }, + 5 => (Op::EndProof, 1), + _ => return Err(()), + }) + } +} + +/// Iterator on op from a in memory encoded proof. +pub struct IterOpProof + AsMut<[u8]> + Default, B: AsRef<[u8]>>( + B, + usize, + PhantomData, +); + +impl + AsMut<[u8]> + Default, B: AsRef<[u8]>> From for IterOpProof { + fn from(b: B) -> Self { + Self(b, 0, PhantomData) + } +} + +impl + AsMut<[u8]> + Default, B: AsRef<[u8]>> Iterator for IterOpProof { + type Item = Option>>; + + fn next(&mut self) -> Option { + match Op::decode(&self.0.as_ref()[self.1..]) { + Ok((op, len)) => { + self.1 += len; + Some(Some(op)) + }, + Err(_) => Some(None), + } + } +} diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 590ad4fa..63c03acc 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -41,6 +41,7 @@ use self::rstd::{boxed::Box, vec::Vec}; use hash_db::MaybeDebug; use node::NodeOwned; +pub mod content_proof; pub mod node; pub mod proof; pub mod query_plan; diff --git a/trie-db/src/query_plan.rs b/trie-db/src/query_plan/mod.rs similarity index 62% rename from trie-db/src/query_plan.rs rename to trie-db/src/query_plan/mod.rs index 9d29291d..3761ebd6 100644 --- a/trie-db/src/query_plan.rs +++ b/trie-db/src/query_plan/mod.rs @@ -36,8 +36,12 @@ use crate::{ }, CError, ChildReference, DBValue, NibbleVec, Trie, TrieDB, TrieHash, TrieLayout, }; -use compact_content_proof::Op; +use crate::content_proof::Op; use hash_db::Hasher; +pub use record::{record_query_plan, HaltedStateRecord, Recorder}; + +pub mod verify_content; +mod record; /// Item to query, in memory. #[derive(Default)] @@ -167,13 +171,14 @@ pub enum ProofKind { */ /// Content oriented proof, no nodes are written, just a /// sequence of accessed by lexicographical order as described - /// in compact_content_proof::Op. + /// in verify_content::Op. /// As with compact node, checking validity of proof need to /// load the full proof or up to the halt point. CompactContent, } impl ProofKind { + // Do we need to record child hash and inline value individually. fn record_inline(&self) -> bool { match self { ProofKind::FullNodes | ProofKind::CompactNodes => false, @@ -217,6 +222,7 @@ struct CompactEncodingInfos { /// Node in memory content. node: OwnedNode, /// Flags indicating whether each child is omitted in the encoded node. + /// For some encoding, it also record if the child has already been written. accessed_children_node: Bitmap, /// Skip value if value node is after. accessed_value_node: bool, @@ -269,15 +275,6 @@ impl RecorderOutput for InMemoryRecorder { } } -/// Simplified recorder. -pub struct Recorder { - output: RecorderStateInner, - limits: Limits, - // on restore only record content AFTER this position. - start_at: Option, - _ph: PhantomData, -} - /// Limits to size proof to record. struct Limits { remaining_node: Option, @@ -285,270 +282,6 @@ struct Limits { kind: ProofKind, } -impl Recorder { - fn mark_inline_access(&self) -> bool { - match &self.output { - RecorderStateInner::Content { .. } => true, - _ => false, - } - } - - /// Check and update start at record. - /// When return true, do record. - fn check_start_at(&mut self, depth: usize) -> bool { - if self.start_at.map(|s| s > depth).unwrap_or(false) { - false - } else { - self.start_at = None; - true - } - } - - /// Get back output handle from a recorder. - pub fn output(self) -> O { - match self.output { - RecorderStateInner::Stream(output) | - RecorderStateInner::Compact { output, .. } | - RecorderStateInner::Content { output, .. } => output, - } - } - - /// Instantiate a new recorder. - pub fn new( - kind: ProofKind, - output: O, - limit_node: Option, - limit_size: Option, - ) -> Self { - let output = match kind { - ProofKind::FullNodes => RecorderStateInner::Stream(output), - ProofKind::CompactNodes => - RecorderStateInner::Compact { output, proof: Vec::new(), stacked_pos: Vec::new() }, - ProofKind::CompactContent => - RecorderStateInner::Content { output, stacked_push: None, stacked_pop: None }, - }; - let limits = Limits { remaining_node: limit_node, remaining_size: limit_size, kind }; - Self { output, limits, start_at: None, _ph: PhantomData } - } - - #[must_use] - fn record_stacked_node( - &mut self, - item: &CompactEncodingInfos, - is_root: bool, - parent_index: u8, - items: &Vec, - ) -> bool { - if !self.check_start_at(item.depth) { - return false - } - let mut res = false; - match &mut self.output { - RecorderStateInner::Stream(output) => - if !item.is_inline { - res = self.limits.add_node( - item.node.data().len(), - L::Codec::DELTA_COMPACT_OMITTED_NODE, - is_root, - ); - output.write_entry(item.node.data().into()); - }, - RecorderStateInner::Compact { output: _, proof, stacked_pos } => - if !item.is_inline { - res = self.limits.add_node( - item.node.data().len(), - L::Codec::DELTA_COMPACT_OMITTED_NODE, - is_root, - ); - stacked_pos.push(proof.len()); - proof.push(Vec::new()); - }, - RecorderStateInner::Content { output, stacked_push, stacked_pop } => { - if flush_compact_content_pop::( - output, - stacked_pop, - items, - None, - &mut self.limits, - ) { - res = true - } - if stacked_push.is_none() { - *stacked_push = Some(NibbleVec::new()); - } - if let Some(buff) = stacked_push.as_mut() { - if !is_root { - buff.push(parent_index); - } - let node_data = item.node.data(); - - match item.node.node_plan() { - NodePlan::Branch { .. } => (), - | NodePlan::Empty => (), - NodePlan::Leaf { partial, .. } | - NodePlan::NibbledBranch { partial, .. } | - NodePlan::Extension { partial, .. } => { - let partial = partial.build(node_data); - buff.append_optional_slice_and_nibble(Some(&partial), None); - }, - } - } - }, - } - res - } - - #[must_use] - fn flush_compact_content_pushes(&mut self, depth: usize) -> bool { - let mut res = false; - if !self.check_start_at(depth) { - // TODO actually should be unreachable - return res - } - if let RecorderStateInner::Content { output, stacked_push, .. } = &mut self.output { - if let Some(buff) = stacked_push.take() { - let mask = if buff.len() % 2 == 0 { 0xff } else { 0xf0 }; - let op = Op::, Vec>::KeyPush(buff.inner().to_vec(), mask); - let init_len = output.buf_len(); - op.encode_into(output); - let written = output.buf_len() - init_len; - res = self.limits.add_node(written, 0, false); - } - } - res - } - - #[must_use] - fn record_value_node(&mut self, value: Vec, depth: usize) -> bool { - if !self.check_start_at(depth) { - return false - } - - let mut res = false; - if let RecorderStateInner::Content { .. } = &self.output { - res = self.flush_compact_content_pushes(depth); - } - match &mut self.output { - RecorderStateInner::Stream(output) => { - res = self.limits.add_value(value.len(), L::Codec::DELTA_COMPACT_OMITTED_VALUE); - output.write_entry(value.into()); - }, - RecorderStateInner::Compact { output: _, proof, stacked_pos: _ } => { - res = self.limits.add_value(value.len(), L::Codec::DELTA_COMPACT_OMITTED_VALUE); - proof.push(value.into()); - }, - RecorderStateInner::Content { output, .. } => { - let op = Op::, Vec>::Value(value); - let init_len = output.buf_len(); - op.encode_into(output); - let written = output.buf_len() - init_len; - res |= self.limits.add_node(written, 0, false) - }, - } - res - } - - #[must_use] - fn record_value_inline(&mut self, value: &[u8], depth: usize) -> bool { - let mut res = false; - if !self.check_start_at(depth) { - return res - } - if let RecorderStateInner::Content { .. } = &self.output { - if self.flush_compact_content_pushes(depth) { - res = true; - } - } - - match &mut self.output { - RecorderStateInner::Compact { .. } | RecorderStateInner::Stream(_) => { - // not writing inline value (already - // in parent node). - }, - RecorderStateInner::Content { output, .. } => { - let op = Op::, &[u8]>::Value(value); - let init_len = output.buf_len(); - op.encode_into(output); - let written = output.buf_len() - init_len; - res = self.limits.add_node(written, 0, false); - }, - } - res - } - - // TODO this should be call also in all node (not only when not finding value): - // then could just be part of enter node? TODO rem - #[must_use] - fn record_skip_value(&mut self, items: &mut Vec) -> bool { - let mut res = false; - let mut op = None; - if let RecorderStateInner::Content { .. } = &self.output { - if let Some(item) = items.last_mut() { - if item.accessed_value_node { - return res - } - item.accessed_value_node = true; - if !self.check_start_at(item.depth) { - return res - } - let node_data = item.node.data(); - - match item.node.node_plan() { - NodePlan::Leaf { value, .. } | - NodePlan::Branch { value: Some(value), .. } | - NodePlan::NibbledBranch { value: Some(value), .. } => { - op = Some(match value.build(node_data) { - Value::Node(hash_slice) => { - let mut hash = TrieHash::::default(); - hash.as_mut().copy_from_slice(hash_slice); - Op::<_, Vec>::HashValue(hash) - }, - Value::Inline(value) => - Op::, Vec>::Value(value.to_vec()), - }); - }, - _ => return res, - } - - if self.flush_compact_content_pushes(item.depth) { - res = true; - } - } - } - - if let Some(op) = op { - match &mut self.output { - RecorderStateInner::Content { output, .. } => { - let init_len = output.buf_len(); - op.encode_into(output); - let written = output.buf_len() - init_len; - res = self.limits.add_node(written, 0, false); - }, - _ => (), - } - } - res - } - - #[must_use] - fn touched_child_hash(&mut self, hash_slice: &[u8], i: u8) -> bool { - let mut res = false; - match &mut self.output { - RecorderStateInner::Content { output, .. } => { - let mut hash = TrieHash::::default(); - hash.as_mut().copy_from_slice(hash_slice); - let op = Op::, Vec>::HashChild(hash, i as u8); - let init_len = output.buf_len(); - op.encode_into(output); - let written = output.buf_len() - init_len; - res = self.limits.add_node(written, 0, false); - }, - _ => (), - } - res - } -} - impl Limits { #[must_use] fn add_node(&mut self, size: usize, hash_size: usize, is_root: bool) -> bool { @@ -635,355 +368,6 @@ impl Limits { } } -enum RecorderStateInner { - /// For FullNodes proofs, just send node to this stream. - Stream(O), - /// For FullNodes proofs, Requires keeping all proof before sending it. - Compact { - output: O, - proof: Vec>, - /// Stacked position in proof to modify proof as needed - /// when information got accessed. - stacked_pos: Vec, - }, - /// For FullNodes proofs, just send node to this stream. - Content { - output: O, - // push current todos. - stacked_push: Option, - // pop from depth. - stacked_pop: Option, - }, -} - -/// When process is halted keep execution state -/// to restore later. -pub struct HaltedStateRecord { - currently_query_item: Option, - stack: RecordStack, - // This indicate a restore point, it takes precedence over - // stack and currently_query_item. - from: Option<(Vec, bool)>, -} - -impl HaltedStateRecord { - /// Indicate we reuse the query plan iterator - /// and stack. - pub fn statefull(&mut self, recorder: Recorder) -> Recorder { - let result = core::mem::replace(&mut self.stack.recorder, recorder); - result - } - - /// Indicate to use stateless (on a fresh proof - /// and a fresh query plan iterator). - pub fn stateless(&mut self, recorder: Recorder) -> Recorder { - let new_start = Self::from_start(recorder); - let old = core::mem::replace(self, new_start); - self.from = old.from; - self.currently_query_item = None; - old.stack.recorder - } - - /// Init from start. - pub fn from_start(recorder: Recorder) -> Self { - Self::from_at(recorder, None) - } - - /// Init from position or start. - pub fn from_at(recorder: Recorder, at: Option<(Vec, bool)>) -> Self { - HaltedStateRecord { - currently_query_item: None, - stack: RecordStack { - recorder, - items: Vec::new(), - prefix: NibbleVec::new(), - iter_prefix: None, - halt: false, - seek: None, - }, - from: at, - } - } - - pub fn stopped_at(&self) -> Option<(Vec, bool)> { - self.from.clone() - } - - pub fn is_finished(&self) -> bool { - self.from == None - } - - pub fn finish(self) -> Recorder { - self.stack.recorder - } - - fn finalize(&mut self) { - let stack = &mut self.stack; - let items = &stack.items; - match &mut stack.recorder.output { - RecorderStateInner::Compact { output, proof, stacked_pos } => { - let restarted_from = 0; - if stacked_pos.len() > restarted_from { - // halted: complete up to 0 and write all nodes keeping stack. - let mut items = items.iter().rev(); - while let Some(pos) = stacked_pos.pop() { - loop { - let item = items.next().expect("pos stacked with an item"); - if !item.is_inline { - proof[pos] = crate::trie_codec::encode_node_internal::( - &item.node, - item.accessed_value_node, - item.accessed_children_node, - ) - .expect("TODO error handling, can it actually fail?"); - break - } - } - } - } - for entry in core::mem::take(proof) { - output.write_entry(entry.into()); - } - }, - RecorderStateInner::Stream(_output) => { - // all written - }, - RecorderStateInner::Content { output: _, stacked_push, stacked_pop: _ } => { - // TODO protect existing stack as for compact - assert!(stacked_push.is_none()); - // TODO could use function with &item and &[item] as param - // to skip this clone. - for i in (0..items.len()).rev() { - let _ = self.record_popped_node(i); - } - }, - } - } - - #[must_use] - fn record_popped_node(&mut self, at: usize) -> bool { - let item = self.stack.items.get(at).expect("bounded iter"); - let items = &self.stack.items[..at]; - let mut res = false; - if !self.stack.recorder.check_start_at(item.depth) { - return res - } - if let RecorderStateInner::Content { .. } = &self.stack.recorder.output { - // if no value accessed, then we can have push then stack pop. - if self.stack.recorder.flush_compact_content_pushes(item.depth) { - res = true; - } - } - - let mut process_children = None; - let mut has_hash_to_write = false; - match &mut self.stack.recorder.output { - RecorderStateInner::Stream(_) => (), - RecorderStateInner::Compact { proof, stacked_pos, .. } => - if !item.is_inline { - if let Some(at) = stacked_pos.pop() { - proof[at] = crate::trie_codec::encode_node_internal::( - &item.node, - item.accessed_value_node, - item.accessed_children_node, - ) - .expect("TODO error handling, can it actually fail?"); - } // else when restarting record, this is not to be recorded - }, - RecorderStateInner::Content { output, stacked_pop, .. } => { - // two case: children to register or all children accessed. - if let Some(last_item) = items.last() { - match last_item.node.node_plan() { - NodePlan::Branch { children, .. } | - NodePlan::NibbledBranch { children, .. } => - for i in 0..children.len() { - if children[i].is_some() && !last_item.accessed_children_node.at(i) - { - has_hash_to_write = true; - break - } - }, - _ => (), - } - } - - match item.node.node_plan() { - NodePlan::Branch { children, .. } | - NodePlan::NibbledBranch { children, .. } => { - process_children = Some(children.len()); - }, - _ => (), - } - }, - } - - if let Some(nb_children) = process_children { - self.try_stack_content_child().expect("stack inline do not fetch"); - } - let items = &self.stack.items[..at]; - match &mut self.stack.recorder.output { - RecorderStateInner::Content { output, stacked_pop, .. } => { - let item = &self.stack.items.get(at).expect("bounded iter"); - if stacked_pop.is_none() { - *stacked_pop = Some(item.depth); - } - - if has_hash_to_write { - if flush_compact_content_pop::( - output, - stacked_pop, - items, - None, - &mut self.stack.recorder.limits, - ) { - res = true; - } - } - }, - _ => (), - } - - res - } - - // Add child for content - fn try_stack_content_child( - &mut self, - //upper: u8, - ) -> Result<(), VerifyError, CError>> { - let dummy_parent_hash = TrieHash::::default(); - if let Some(item) = self.stack.items.last() { - let pre = item.next_descended_child; // TODO put next_descended child to 16 for leaf (skip some noop iter) - for i in 0..NIBBLE_LENGTH as u8 { - match self.stack.try_stack_child(i, None, dummy_parent_hash, None, true)? { - // only expect a stacked prefix here - TryStackChildResult::Stacked => { - let halt = self.iter_prefix(None, None, false, true, true)?; - if halt { - // no halt on inline. - unreachable!() - } else { - self.pop(); - } - }, - TryStackChildResult::NotStackedBranch => (), - _ => break, - } - } - } - self.stack - .items - .last_mut() - .map(|i| i.next_descended_child = NIBBLE_LENGTH as u8); - Ok(()) - } - - fn pop(&mut self) -> bool { - if self - .stack - .iter_prefix - .map(|(l, _)| l == self.stack.items.len()) - .unwrap_or(false) - { - return false - } - let at = self.stack.items.len(); - if at > 0 { - if self.record_popped_node(at - 1) { - self.stack.halt = true; - } - } - if let Some(item) = self.stack.items.pop() { - let depth = self.stack.items.last().map(|i| i.depth).unwrap_or(0); - self.stack.prefix.drop_lasts(self.stack.prefix.len() - depth); - if depth == item.depth { - // Two consecutive identical depth is an extension - self.pop(); - } - true - } else { - false - } - } - - fn iter_prefix( - &mut self, - prev_query: Option<&QueryPlanItem>, - db: Option<&TrieDB>, - hash_only: bool, - first_iter: bool, - inline_iter: bool, - ) -> Result, CError>> { - let dummy_parent_hash = TrieHash::::default(); - if first_iter { - self.stack.enter_prefix_iter(hash_only); - } - - // run prefix iteration - let mut stacked = first_iter; - loop { - // descend - loop { - if stacked { - // try access value in next node - self.stack.access_value(db, hash_only)?; - stacked = false; - } - - let child_index = if let Some(mut item) = self.stack.items.last_mut() { - if item.next_descended_child as usize >= NIBBLE_LENGTH { - break - } - item.next_descended_child += 1; - item.next_descended_child - 1 - } else { - break - }; - - match self.stack.try_stack_child( - child_index, - db, - dummy_parent_hash, - None, - inline_iter, - )? { - TryStackChildResult::Stacked => { - stacked = true; - }, - TryStackChildResult::NotStackedBranch => (), - TryStackChildResult::NotStacked => break, - TryStackChildResult::StackedDescendIncomplete => { - unreachable!("no slice query") - }, - TryStackChildResult::Halted => { - if let Some(mut item) = self.stack.items.last_mut() { - item.next_descended_child -= 1; - } - self.stack.halt = false; - self.stack.prefix.push(child_index); - let dest_from = Some(( - self.stack.prefix.inner().to_vec(), - (self.stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, - )); - self.stack.prefix.pop(); - self.finalize(); - self.from = dest_from; - self.currently_query_item = prev_query.map(|q| q.to_owned()); - return Ok(true) - }, - } - } - - // pop - if !self.pop() { - break - } - } - self.stack.exit_prefix_iter(); - Ok(false) - } -} - /// When process is halted keep execution state /// to restore later. pub enum HaltedStateCheck<'a, L: TrieLayout, C, D: SplitFirst> { @@ -1062,200 +446,6 @@ impl<'a, L: TrieLayout, C> From> for HaltedStateCheckContent<'a } } -struct RecordStack { - recorder: Recorder, - items: Vec, - prefix: NibbleVec, - iter_prefix: Option<(usize, bool)>, - seek: Option, - halt: bool, -} - -/// Run query plan on a full db and record it. -/// -/// TODO output and restart are mutually exclusive. -> enum -/// or remove output from halted state. -pub fn record_query_plan< - 'a, - L: TrieLayout, - I: Iterator>, - O: RecorderOutput, ->( - db: &TrieDB, - query_plan: &mut QueryPlan<'a, I>, - from: &mut HaltedStateRecord, -) -> Result<(), VerifyError, CError>> { - // TODO - //) resto - // let restore_buf; - let dummy_parent_hash = TrieHash::::default(); - let mut stateless = false; - let mut statefull = None; - // When define we iter prefix in a node but really want the next non inline. - if let Some(lower_bound) = from.from.take() { - if from.currently_query_item.is_none() { - stateless = true; - let mut bound = NibbleVec::new(); - bound.append_optional_slice_and_nibble(Some(&NibbleSlice::new(&lower_bound.0)), None); - if lower_bound.1 { - bound.pop(); - } - from.stack.recorder.start_at = Some(bound.len()); - from.stack.seek = Some(bound); - } else { - let bound_len = lower_bound.0.len() * nibble_ops::NIBBLE_PER_BYTE - - if lower_bound.1 { 2 } else { 1 }; - // from.stack.recorder.start_at = Some(bound_len); - statefull = Some(bound_len); - } - } - - let mut prev_query: Option = None; - let from_query = from.currently_query_item.take(); - let mut from_query_ref = from_query.as_ref().map(|f| f.as_ref()); - while let Some(query) = from_query_ref.clone().or_else(|| query_plan.items.next()) { - if stateless { - let bound = from.stack.seek.as_ref().expect("Initiated for stateless"); - let bound = bound.as_leftnibbleslice(); - let query_slice = LeftNibbleSlice::new(&query.key); - if query_slice.starts_with(&bound) { - } else if query.as_prefix { - if bound.starts_with(&query_slice) { - } else { - continue - } - } else { - continue - } - stateless = false; - if !query.as_prefix { - from.stack.seek = None; - } - } - let common_nibbles = if let Some(slice_at) = statefull.take() { - slice_at - } else { - let (ordered, common_nibbles) = - prev_query.as_ref().map(|p| p.before(&query)).unwrap_or((true, 0)); - if !ordered { - if query_plan.ignore_unordered { - continue - } else { - return Err(VerifyError::UnorderedKey(query.key.to_vec())) - } - } - loop { - match from.stack.prefix.len().cmp(&common_nibbles) { - Ordering::Equal | Ordering::Less => break, - Ordering::Greater => { - if query_plan.kind.record_inline() { - from.try_stack_content_child()?; - } - if !from.pop() { - from.finalize(); - return Ok(()) - } - }, - } - } - common_nibbles - }; - if let Some((_, hash_only)) = from.stack.iter_prefix.clone() { - // statefull halted during iteration. - let halt = from.iter_prefix(Some(&query), Some(db), hash_only, false, false)?; - if halt { - return Ok(()) - } - from_query_ref = None; - prev_query = Some(query); - continue - } - // descend - let mut slice_query = NibbleSlice::new_offset(&query.key, common_nibbles); - let touched = loop { - if !from.stack.items.is_empty() { - if slice_query.is_empty() { - if query.as_prefix { - let halt = - from.iter_prefix(Some(&query), Some(db), query.hash_only, true, false)?; - if halt { - return Ok(()) - } - break false - } else { - break true - } - } else { - if from.stack.recorder.record_skip_value(&mut from.stack.items) { - from.stack.halt = true; - } - } - } - - let child_index = if from.stack.items.is_empty() { 0 } else { slice_query.at(0) }; - /*if query_plan.kind.record_inline() { - from.try_stack_content_child(child_index)?; - }*/ - from.stack.items.last_mut().map(|i| { - // TODO only needed for content but could be better to be always aligned - i.next_descended_child = child_index + 1; - }); - match from.stack.try_stack_child( - child_index, - Some(db), - dummy_parent_hash, - Some(&mut slice_query), - false, - )? { - TryStackChildResult::Stacked => {}, - TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked => - break false, - TryStackChildResult::StackedDescendIncomplete => { - if query.as_prefix { - let halt = - from.iter_prefix(Some(&query), Some(db), query.hash_only, true, false)?; - if halt { - return Ok(()) - } - } - break false - }, - TryStackChildResult::Halted => { - from.stack.halt = false; - from.stack.prefix.push(child_index); - from.from = Some(( - from.stack.prefix.inner().to_vec(), - (from.stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, - )); - from.stack.prefix.pop(); - from.currently_query_item = Some(query.to_owned()); - from.finalize(); - return Ok(()) - }, - } - }; - - if touched { - // try access value - from.stack.access_value(Some(db), query.hash_only)?; - } - from_query_ref = None; - prev_query = Some(query); - } - // TODO loop redundant with finalize?? - loop { - if query_plan.kind.record_inline() { - from.try_stack_content_child()?; - } - - if !from.pop() { - break - } - } - from.finalize(); - Ok(()) -} - enum TryStackChildResult { Stacked, NotStackedBranch, @@ -1264,245 +454,6 @@ enum TryStackChildResult { Halted, } -impl RecordStack { - fn try_stack_child<'a>( - &mut self, - child_index: u8, - db: Option<&TrieDB>, - parent_hash: TrieHash, - mut slice_query: Option<&mut NibbleSlice>, - inline_only: bool, // TODO remove all inline only param and make it db is_none TODO rename - ) -> Result, CError>> { - let mut is_inline = false; - let prefix = &mut self.prefix; - let mut descend_incomplete = false; - let mut stack_extension = false; - let mut from_branch = None; - let child_handle = if let Some(item) = self.items.last_mut() { - if inline_only && item.accessed_children_node.at(child_index as usize) { - return Ok(TryStackChildResult::NotStackedBranch) - } - - let node_data = item.node.data(); - - match item.node.node_plan() { - NodePlan::Empty | NodePlan::Leaf { .. } => - return Ok(TryStackChildResult::NotStacked), - NodePlan::Extension { child, .. } => - if child_index == 0 { - let child_handle = child.build(node_data); - if let &NodeHandle::Hash(_) = &child_handle { - item.accessed_children_node.set(child_index as usize, true); - } - child_handle - } else { - return Ok(TryStackChildResult::NotStacked) - }, - NodePlan::NibbledBranch { children, .. } | NodePlan::Branch { children, .. } => - if let Some(child) = &children[child_index as usize] { - from_branch = Some(&mut item.accessed_children_node); - child.build(node_data) - } else { - return Ok(TryStackChildResult::NotStackedBranch) - }, - } - } else { - NodeHandle::Hash(db.expect("non inline").root().as_ref()) - }; - match &child_handle { - NodeHandle::Inline(_) => { - // TODO consider not going into inline for all proof but content. - // Returning NotStacked here sounds safe, then the is_inline field is not needed. - is_inline = true; - }, - NodeHandle::Hash(hash) => { - if inline_only { - if self.recorder.touched_child_hash(hash, child_index) { - self.halt = true; - } - if self.recorder.mark_inline_access() { - if let Some(accessed_children_node) = from_branch { - accessed_children_node.set(child_index as usize, true); - } - } - return Ok(TryStackChildResult::NotStackedBranch) - } - if self.halt && from_branch.is_some() { - return Ok(TryStackChildResult::Halted) - } - }, - } - if let Some(accessed_children_node) = from_branch { - if !is_inline || self.recorder.mark_inline_access() { - accessed_children_node.set(child_index as usize, true); - } - - slice_query.as_mut().map(|s| s.advance(1)); - prefix.push(child_index); - } - // TODO handle cache first - let child_node = if let Some(db) = db { - db.get_raw_or_lookup_with_cache(parent_hash, child_handle, prefix.as_prefix(), false) - .map_err(|_| VerifyError::IncompleteProof)? // actually incomplete db: TODO consider switching error - } else { - let NodeHandle::Inline(node_data) = child_handle else { - unreachable!("call on non inline node when db is None"); - }; - ( - OwnedNode::new::(node_data.to_vec()) - .map_err(|_| VerifyError::IncompleteProof)?, - None, - ) - }; - - // } - - // TODO put in proof (only if Hash or inline for content one) - - let node_data = child_node.0.data(); - //println!("r: {:?}", &node_data); - - match child_node.0.node_plan() { - NodePlan::Branch { .. } => (), - | NodePlan::Empty => (), - NodePlan::Leaf { partial, .. } | - NodePlan::NibbledBranch { partial, .. } | - NodePlan::Extension { partial, .. } => { - let partial = partial.build(node_data); - prefix.append_partial(partial.right()); - if let Some(s) = slice_query.as_mut() { - if s.starts_with(&partial) { - s.advance(partial.len()); - } else { - descend_incomplete = true; - } - } - }, - } - if let NodePlan::Extension { .. } = child_node.0.node_plan() { - stack_extension = true; - } - let next_descended_child = if let Some(seek) = self.seek.as_ref() { - if prefix.len() < seek.len() { - seek.at(prefix.len()) - } else { - self.seek = None; - 0 - } - } else { - 0 - }; - let infos = CompactEncodingInfos { - node: child_node.0, - accessed_children_node: Default::default(), - accessed_value_node: false, - depth: prefix.len(), - next_descended_child, - is_inline, - }; - if self.recorder.record_stacked_node( - &infos, - self.items.is_empty(), - child_index, - &self.items, - ) { - self.halt = true; - } - self.items.push(infos); - if stack_extension { - let sbranch = self.try_stack_child(0, db, parent_hash, slice_query, inline_only)?; - let TryStackChildResult::Stacked = sbranch else { - return Err(VerifyError::InvalidChildReference(b"branch in db should follow extension".to_vec())); - }; - } - - if descend_incomplete { - Ok(TryStackChildResult::StackedDescendIncomplete) - } else { - Ok(TryStackChildResult::Stacked) - } - } - - fn access_value<'a>( - &mut self, - db: Option<&TrieDB>, - hash_only: bool, - ) -> Result, CError>> { - let Some(item)= self.items.last_mut() else { - return Ok(false) - }; - // TODO this could be reuse from iterator, but it seems simple - // enough here too. - let node_data = item.node.data(); - - let value = match item.node.node_plan() { - NodePlan::Leaf { value, .. } => value.build(node_data), - NodePlan::Branch { value, .. } | NodePlan::NibbledBranch { value, .. } => { - if let Some(value) = value { - value.build(node_data) - } else { - return Ok(false) - } - }, - _ => return Ok(false), - }; - match value { - Value::Node(hash_slice) => - if !hash_only { - item.accessed_value_node = true; - let mut hash = TrieHash::::default(); - hash.as_mut().copy_from_slice(hash_slice); - let Some(value) = db.expect("non inline").db().get(&hash, self.prefix.as_prefix()) else { - return Err(VerifyError::IncompleteProof); - }; - if self.recorder.record_value_node(value, self.prefix.len()) { - self.halt = true; - } - } else { - if self.recorder.record_skip_value(&mut self.items) { - self.halt = true; - } - }, - Value::Inline(value) => - if self.recorder.record_value_inline(value, self.prefix.len()) { - self.halt = true; - }, - } - Ok(true) - } - - fn enter_prefix_iter(&mut self, hash_only: bool) { - self.iter_prefix = Some((self.items.len(), hash_only)); - } - - fn exit_prefix_iter(&mut self) { - self.iter_prefix = None - } -} - -#[must_use] -fn flush_compact_content_pop( - out: &mut O, - stacked_from: &mut Option, - items: &[CompactEncodingInfos], - add_depth: Option, - limits: &mut Limits, -) -> bool { - let Some(from) = stacked_from.take() else { - return false - }; - let pop_to = add_depth.unwrap_or_else(|| items.last().map(|i| i.depth).unwrap_or(0)); - debug_assert!(from > pop_to); - - debug_assert!(from - pop_to <= u16::max_value() as usize); - // Warning this implies key size limit of u16::max - let op = Op::, Vec>::KeyPop((from - pop_to) as u16); - let init_len = out.buf_len(); - op.encode_into(out); - let written = out.buf_len() - init_len; - limits.add_node(written, 0, false) -} - /// Proof reading iterator. pub struct ReadProofIterator<'a, L, C, D, P> where @@ -3202,245 +2153,3 @@ where buf_op: None, }) } - -pub mod compact_content_proof { - use super::RecorderOutput; - use core::marker::PhantomData; - - /// Representation of each encoded action - /// for building the proof. - /// TODO ref variant for encoding ?? or key using V and use Op<&H, &[u8]>. - #[derive(Debug)] - pub enum Op { - // key content followed by a mask for last byte. - // If mask erase some content the content need to - // be set at 0 (or error). - // Two consecutive `KeyPush` are invalid. - KeyPush(Vec, u8), /* TODO could use BackingByteVec (but Vec for new as it scale - * encode) */ - // Last call to pop is implicit (up to root), defining - // one will result in an error. - // Two consecutive `KeyPop` are invalid. - // TODO should be compact encoding of number. - KeyPop(u16), - // u8 is child index, shorthand for key push one nibble followed by key pop. - HashChild(H, u8), - // All value variant are only after a `KeyPush` or at first position. - HashValue(H), - Value(V), - // This is not strictly necessary, only if the proof is not sized, otherwhise if we know - // the stream will end it can be skipped. - EndProof, - } - - // Limiting size to u32 (could also just use a terminal character). - #[derive(Debug, PartialEq, Eq)] - #[repr(transparent)] - struct VarInt(u32); - - impl VarInt { - fn encoded_len(&self) -> usize { - if self.0 == 0 { - return 1 - } - let len = 32 - self.0.leading_zeros() as usize; - if len % 7 == 0 { - len / 7 - } else { - len / 7 + 1 - } - /* - match self.0 { - l if l < 2 ^ 7 => 1, // leading 0: 25 - l if l < 2 ^ 14 => 2, // leading 0: 18 - - l if l < 2 ^ 21 => 3, // 11 - l if l < 2 ^ 28 => 4, // 4 - _ => 5, - } - */ - } - - fn encode_into(&self, out: &mut impl RecorderOutput) { - let mut to_encode = self.0; - for _ in 0..self.encoded_len() - 1 { - out.write_bytes(&[0b1000_0000 | to_encode as u8]); - to_encode >>= 7; - } - out.write_bytes(&[to_encode as u8]); - } - - fn decode(encoded: &[u8]) -> Result<(Self, usize), ()> { - let mut value = 0u32; - for (i, byte) in encoded.iter().enumerate() { - let last = byte & 0b1000_0000 == 0; - value |= ((byte & 0b0111_1111) as u32) << (i * 7); - if last { - return Ok((VarInt(value), i + 1)) - } - } - Err(()) - } - } - - #[test] - fn varint_encode_decode() { - let mut buf = super::InMemoryRecorder::default(); - for i in 0..u16::MAX as u32 + 1 { - VarInt(i).encode_into(&mut buf); - assert_eq!(buf.buffer.len(), VarInt(i).encoded_len()); - assert_eq!(Ok((VarInt(i), buf.buffer.len())), VarInt::decode(&buf.buffer)); - buf.buffer.clear(); - } - } - - impl, V: AsRef<[u8]>> Op { - /// Calculate encoded len. - pub fn encoded_len(&self) -> usize { - let mut len = 1; - match self { - Op::KeyPush(key, _mask) => { - len += VarInt(key.len() as u32).encoded_len(); - len += key.len(); - len += 1; - }, - Op::KeyPop(nb) => { - len += VarInt(*nb as u32).encoded_len(); - }, - Op::HashChild(hash, _at) => { - len += hash.as_ref().len(); - len += 1; - }, - Op::HashValue(hash) => { - len += hash.as_ref().len(); - }, - Op::Value(value) => { - len += VarInt(value.as_ref().len() as u32).encoded_len(); - len += value.as_ref().len(); - }, - Op::EndProof => (), - } - len - } - - /// Write op. - pub fn encode_into(&self, out: &mut impl RecorderOutput) { - match self { - Op::KeyPush(key, mask) => { - out.write_bytes(&[0]); - VarInt(key.len() as u32).encode_into(out); - out.write_bytes(&key); - out.write_bytes(&[*mask]); - }, - Op::KeyPop(nb) => { - out.write_bytes(&[1]); - VarInt(*nb as u32).encode_into(out); - }, - Op::HashChild(hash, at) => { - out.write_bytes(&[2]); - out.write_bytes(hash.as_ref()); - out.write_bytes(&[*at]); - }, - Op::HashValue(hash) => { - out.write_bytes(&[3]); - out.write_bytes(hash.as_ref()); - }, - Op::Value(value) => { - out.write_bytes(&[4]); - let value = value.as_ref(); - VarInt(value.len() as u32).encode_into(out); - out.write_bytes(&value); - }, - Op::EndProof => { - out.write_bytes(&[5]); - }, - } - } - } - - impl + AsMut<[u8]> + Default> Op> { - /// Read an op, return op and number byte read. Or error if invalid encoded. - pub fn decode(encoded: &[u8]) -> Result<(Self, usize), ()> { - let mut i = 0; - if i >= encoded.len() { - return Err(()) - } - Ok(match encoded[i] { - 0 => { - let (len, offset) = VarInt::decode(&encoded[i + 1..])?; - i += 1 + offset; - if i + len.0 as usize >= encoded.len() { - return Err(()) - } - let key = &encoded[i..i + len.0 as usize]; - let mask = encoded[i + len.0 as usize]; - (Op::KeyPush(key.to_vec(), mask), i + len.0 as usize + 1) - }, - 1 => { - let (len, offset) = VarInt::decode(&encoded[i + 1..])?; - if len.0 > u16::MAX as u32 { - return Err(()) - } - (Op::KeyPop(len.0 as u16), i + 1 + offset) - }, - 2 => { - let mut hash = H::default(); - let end = i + 1 + hash.as_ref().len(); - if end >= encoded.len() { - return Err(()) - } - hash.as_mut().copy_from_slice(&encoded[i + 1..end]); - let mask = encoded[end]; - (Op::HashChild(hash, mask), end + 1) - }, - 3 => { - let mut hash = H::default(); - let end = i + 1 + hash.as_ref().len(); - if end >= encoded.len() { - return Err(()) - } - hash.as_mut().copy_from_slice(&encoded[i + 1..end]); - (Op::HashValue(hash), end) - }, - 4 => { - let (len, offset) = VarInt::decode(&encoded[i + 1..])?; - i += 1 + offset; - if i + len.0 as usize > encoded.len() { - return Err(()) - } - let value = &encoded[i..i + len.0 as usize]; - (Op::Value(value.to_vec()), i + len.0 as usize) - }, - 5 => (Op::EndProof, 1), - _ => return Err(()), - }) - } - } - - /// Iterator on op from a in memory encoded proof. - pub struct IterOpProof + AsMut<[u8]> + Default, B: AsRef<[u8]>>( - B, - usize, - PhantomData, - ); - - impl + AsMut<[u8]> + Default, B: AsRef<[u8]>> From for IterOpProof { - fn from(b: B) -> Self { - Self(b, 0, PhantomData) - } - } - - impl + AsMut<[u8]> + Default, B: AsRef<[u8]>> Iterator for IterOpProof { - type Item = Option>>; - - fn next(&mut self) -> Option { - match Op::decode(&self.0.as_ref()[self.1..]) { - Ok((op, len)) => { - self.1 += len; - Some(Some(op)) - }, - Err(_) => Some(None), - } - } - } -} diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs new file mode 100644 index 00000000..a519a351 --- /dev/null +++ b/trie-db/src/query_plan/record.rs @@ -0,0 +1,1072 @@ +// Copyright 2023, 2023 Parity Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Record query plan proof. + +use super::*; + +/// Simplified recorder. +pub struct Recorder { + output: RecorderStateInner, + limits: Limits, + // on restore only record content AFTER this position. + start_at: Option, + _ph: PhantomData, +} + +impl Recorder { + // TODO rename same as record_inline functio + fn mark_inline_access(&self) -> bool { + match &self.output { + RecorderStateInner::Content { .. } => true, + _ => false, + } + } + + /// Check and update start at record. + /// When return true, do record. + /// TODO debug why it is needed. + fn check_start_at(&mut self, depth: usize) -> bool { + if self.start_at.map(|s| s > depth).unwrap_or(false) { + false + } else { + self.start_at = None; + true + } + } + + /// Get back output handle from a recorder. + pub fn output(self) -> O { + match self.output { + RecorderStateInner::Stream(output) | + RecorderStateInner::Compact { output, .. } | + RecorderStateInner::Content { output, .. } => output, + } + } + + /// Instantiate a new recorder. + pub fn new( + kind: ProofKind, + output: O, + limit_node: Option, + limit_size: Option, + ) -> Self { + let output = match kind { + ProofKind::FullNodes => RecorderStateInner::Stream(output), + ProofKind::CompactNodes => + RecorderStateInner::Compact { output, proof: Vec::new(), stacked_pos: Vec::new() }, + ProofKind::CompactContent => + RecorderStateInner::Content { output, stacked_push: None, stacked_pop: None }, + }; + let limits = Limits { remaining_node: limit_node, remaining_size: limit_size, kind }; + Self { output, limits, start_at: None, _ph: PhantomData } + } + + #[must_use] + fn record_stacked_node( + &mut self, + item: &CompactEncodingInfos, + is_root: bool, + parent_index: u8, + items: &Vec, + ) -> bool { + if !self.check_start_at(item.depth) { + return false + } + let mut res = false; + match &mut self.output { + RecorderStateInner::Stream(output) => + if !item.is_inline { + res = self.limits.add_node( + item.node.data().len(), + L::Codec::DELTA_COMPACT_OMITTED_NODE, + is_root, + ); + output.write_entry(item.node.data().into()); + }, + RecorderStateInner::Compact { output: _, proof, stacked_pos } => + if !item.is_inline { + res = self.limits.add_node( + item.node.data().len(), + L::Codec::DELTA_COMPACT_OMITTED_NODE, + is_root, + ); + stacked_pos.push(proof.len()); + proof.push(Vec::new()); + }, + RecorderStateInner::Content { output, stacked_push, stacked_pop } => { + if flush_compact_content_pop::( + output, + stacked_pop, + items, + None, + &mut self.limits, + ) { + res = true + } + if stacked_push.is_none() { + *stacked_push = Some(NibbleVec::new()); + } + if let Some(buff) = stacked_push.as_mut() { + if !is_root { + buff.push(parent_index); + } + let node_data = item.node.data(); + + match item.node.node_plan() { + NodePlan::Branch { .. } => (), + | NodePlan::Empty => (), + NodePlan::Leaf { partial, .. } | + NodePlan::NibbledBranch { partial, .. } | + NodePlan::Extension { partial, .. } => { + let partial = partial.build(node_data); + buff.append_optional_slice_and_nibble(Some(&partial), None); + }, + } + } + }, + } + res + } + + #[must_use] + fn flush_compact_content_pushes(&mut self, depth: usize) -> bool { + let mut res = false; + if !self.check_start_at(depth) { + // TODO actually should be unreachable + return res + } + if let RecorderStateInner::Content { output, stacked_push, .. } = &mut self.output { + if let Some(buff) = stacked_push.take() { + let mask = if buff.len() % 2 == 0 { 0xff } else { 0xf0 }; + let op = Op::, Vec>::KeyPush(buff.inner().to_vec(), mask); + let init_len = output.buf_len(); + op.encode_into(output); + let written = output.buf_len() - init_len; + res = self.limits.add_node(written, 0, false); + } + } + res + } + + #[must_use] + fn record_value_node(&mut self, value: Vec, depth: usize) -> bool { + if !self.check_start_at(depth) { + return false + } + + let mut res = false; + if let RecorderStateInner::Content { .. } = &self.output { + res = self.flush_compact_content_pushes(depth); + } + match &mut self.output { + RecorderStateInner::Stream(output) => { + res = self.limits.add_value(value.len(), L::Codec::DELTA_COMPACT_OMITTED_VALUE); + output.write_entry(value.into()); + }, + RecorderStateInner::Compact { output: _, proof, stacked_pos: _ } => { + res = self.limits.add_value(value.len(), L::Codec::DELTA_COMPACT_OMITTED_VALUE); + proof.push(value.into()); + }, + RecorderStateInner::Content { output, .. } => { + let op = Op::, Vec>::Value(value); + let init_len = output.buf_len(); + op.encode_into(output); + let written = output.buf_len() - init_len; + res |= self.limits.add_node(written, 0, false) + }, + } + res + } + + #[must_use] + fn record_value_inline(&mut self, value: &[u8], depth: usize) -> bool { + let mut res = false; + if !self.check_start_at(depth) { + return res + } + if let RecorderStateInner::Content { .. } = &self.output { + if self.flush_compact_content_pushes(depth) { + res = true; + } + } + + match &mut self.output { + RecorderStateInner::Compact { .. } | RecorderStateInner::Stream(_) => { + // not writing inline value (already + // in parent node). + }, + RecorderStateInner::Content { output, .. } => { + let op = Op::, &[u8]>::Value(value); + let init_len = output.buf_len(); + op.encode_into(output); + let written = output.buf_len() - init_len; + res = self.limits.add_node(written, 0, false); + }, + } + res + } + + #[must_use] + fn record_skip_value(&mut self, items: &mut Vec) -> bool { + let mut res = false; + let mut op = None; + if let RecorderStateInner::Content { .. } = &self.output { + if let Some(item) = items.last_mut() { + if item.accessed_value_node { + return res + } + item.accessed_value_node = true; + if !self.check_start_at(item.depth) { + return res + } + let node_data = item.node.data(); + + match item.node.node_plan() { + NodePlan::Leaf { value, .. } | + NodePlan::Branch { value: Some(value), .. } | + NodePlan::NibbledBranch { value: Some(value), .. } => { + op = Some(match value.build(node_data) { + Value::Node(hash_slice) => { + let mut hash = TrieHash::::default(); + hash.as_mut().copy_from_slice(hash_slice); + Op::<_, Vec>::HashValue(hash) + }, + Value::Inline(value) => + Op::, Vec>::Value(value.to_vec()), + }); + }, + _ => return res, + } + + if self.flush_compact_content_pushes(item.depth) { + res = true; + } + } + } + + if let Some(op) = op { + match &mut self.output { + RecorderStateInner::Content { output, .. } => { + let init_len = output.buf_len(); + op.encode_into(output); + let written = output.buf_len() - init_len; + res = self.limits.add_node(written, 0, false); + }, + _ => (), + } + } + res + } + + #[must_use] + fn touched_child_hash(&mut self, hash_slice: &[u8], i: u8) -> bool { + let mut res = false; + match &mut self.output { + RecorderStateInner::Content { output, .. } => { + let mut hash = TrieHash::::default(); + hash.as_mut().copy_from_slice(hash_slice); + let op = Op::, Vec>::HashChild(hash, i as u8); + let init_len = output.buf_len(); + op.encode_into(output); + let written = output.buf_len() - init_len; + res = self.limits.add_node(written, 0, false); + }, + _ => (), + } + res + } +} + +enum RecorderStateInner { + /// For FullNodes proofs, just send node to this stream. + Stream(O), + /// For FullNodes proofs, Requires keeping all proof before sending it. + Compact { + output: O, + proof: Vec>, + /// Stacked position in proof to modify proof as needed + /// when information got accessed. + stacked_pos: Vec, + }, + /// For FullNodes proofs, just send node to this stream. + Content { + output: O, + // push current todos. + stacked_push: Option, + // pop from depth. + stacked_pop: Option, + }, +} + +/// When process is halted keep execution state +/// to restore later. +pub struct HaltedStateRecord { + currently_query_item: Option, + stack: RecordStack, + // This indicate a restore point, it takes precedence over + // stack and currently_query_item. + from: Option<(Vec, bool)>, +} + +impl HaltedStateRecord { + /// Indicate we reuse the query plan iterator + /// and stack. + pub fn statefull(&mut self, recorder: Recorder) -> Recorder { + let result = core::mem::replace(&mut self.stack.recorder, recorder); + result + } + + /// Indicate to use stateless (on a fresh proof + /// and a fresh query plan iterator). + pub fn stateless(&mut self, recorder: Recorder) -> Recorder { + let new_start = Self::from_start(recorder); + let old = core::mem::replace(self, new_start); + self.from = old.from; + self.currently_query_item = None; + old.stack.recorder + } + + /// Init from start. + pub fn from_start(recorder: Recorder) -> Self { + Self::from_at(recorder, None) + } + + /// Init from position or start. + pub fn from_at(recorder: Recorder, at: Option<(Vec, bool)>) -> Self { + HaltedStateRecord { + currently_query_item: None, + stack: RecordStack { + recorder, + items: Vec::new(), + prefix: NibbleVec::new(), + iter_prefix: None, + halt: false, + seek: None, + }, + from: at, + } + } + + pub fn stopped_at(&self) -> Option<(Vec, bool)> { + self.from.clone() + } + + pub fn is_finished(&self) -> bool { + self.from == None + } + + pub fn finish(self) -> Recorder { + self.stack.recorder + } + + fn finalize(&mut self) { + let stack = &mut self.stack; + let items = &stack.items; + match &mut stack.recorder.output { + RecorderStateInner::Compact { output, proof, stacked_pos } => { + let restarted_from = 0; + if stacked_pos.len() > restarted_from { + // halted: complete up to 0 and write all nodes keeping stack. + let mut items = items.iter().rev(); + while let Some(pos) = stacked_pos.pop() { + loop { + let item = items.next().expect("pos stacked with an item"); + if !item.is_inline { + proof[pos] = crate::trie_codec::encode_node_internal::( + &item.node, + item.accessed_value_node, + item.accessed_children_node, + ) + .expect("TODO error handling, can it actually fail?"); + break + } + } + } + } + for entry in core::mem::take(proof) { + output.write_entry(entry.into()); + } + }, + RecorderStateInner::Stream(_output) => { + // all written + }, + RecorderStateInner::Content { output: _, stacked_push, stacked_pop: _ } => { + // TODO protect existing stack as for compact + assert!(stacked_push.is_none()); + // TODO could use function with &item and &[item] as param + // to skip this clone. + for i in (0..items.len()).rev() { + let _ = self.record_popped_node(i); + } + }, + } + } + + #[must_use] + fn record_popped_node(&mut self, at: usize) -> bool { + let item = self.stack.items.get(at).expect("bounded iter"); + let items = &self.stack.items[..at]; + let mut res = false; + if !self.stack.recorder.check_start_at(item.depth) { + return res + } + if let RecorderStateInner::Content { .. } = &self.stack.recorder.output { + // if no value accessed, then we can have push then stack pop. + if self.stack.recorder.flush_compact_content_pushes(item.depth) { + res = true; + } + } + + let mut process_children = None; + let mut has_hash_to_write = false; + match &mut self.stack.recorder.output { + RecorderStateInner::Stream(_) => (), + RecorderStateInner::Compact { proof, stacked_pos, .. } => + if !item.is_inline { + if let Some(at) = stacked_pos.pop() { + proof[at] = crate::trie_codec::encode_node_internal::( + &item.node, + item.accessed_value_node, + item.accessed_children_node, + ) + .expect("TODO error handling, can it actually fail?"); + } // else when restarting record, this is not to be recorded + }, + RecorderStateInner::Content { output, stacked_pop, .. } => { + // two case: children to register or all children accessed. + if let Some(last_item) = items.last() { + match last_item.node.node_plan() { + NodePlan::Branch { children, .. } | + NodePlan::NibbledBranch { children, .. } => + for i in 0..children.len() { + if children[i].is_some() && !last_item.accessed_children_node.at(i) + { + has_hash_to_write = true; + break + } + }, + _ => (), + } + } + + match item.node.node_plan() { + NodePlan::Branch { children, .. } | + NodePlan::NibbledBranch { children, .. } => { + process_children = Some(children.len()); + }, + _ => (), + } + }, + } + + if let Some(nb_children) = process_children { + self.try_stack_content_child().expect("stack inline do not fetch"); + } + let items = &self.stack.items[..at]; + match &mut self.stack.recorder.output { + RecorderStateInner::Content { output, stacked_pop, .. } => { + let item = &self.stack.items.get(at).expect("bounded iter"); + if stacked_pop.is_none() { + *stacked_pop = Some(item.depth); + } + + if has_hash_to_write { + if flush_compact_content_pop::( + output, + stacked_pop, + items, + None, + &mut self.stack.recorder.limits, + ) { + res = true; + } + } + }, + _ => (), + } + + res + } + + // Add child for content + fn try_stack_content_child( + &mut self, + //upper: u8, + ) -> Result<(), VerifyError, CError>> { + let dummy_parent_hash = TrieHash::::default(); + if let Some(item) = self.stack.items.last() { + let pre = item.next_descended_child; // TODO put next_descended child to 16 for leaf (skip some noop iter) + for i in 0..NIBBLE_LENGTH as u8 { + match self.stack.try_stack_child(i, None, dummy_parent_hash, None, true)? { + // only expect a stacked prefix here + TryStackChildResult::Stacked => { + let halt = self.iter_prefix(None, None, false, true, true)?; + if halt { + // no halt on inline. + unreachable!() + } else { + self.pop(); + } + }, + TryStackChildResult::NotStackedBranch => (), + _ => break, + } + } + } + self.stack + .items + .last_mut() + .map(|i| i.next_descended_child = NIBBLE_LENGTH as u8); + Ok(()) + } + + fn pop(&mut self) -> bool { + if self + .stack + .iter_prefix + .map(|(l, _)| l == self.stack.items.len()) + .unwrap_or(false) + { + return false + } + let at = self.stack.items.len(); + if at > 0 { + if self.record_popped_node(at - 1) { + self.stack.halt = true; + } + } + if let Some(item) = self.stack.items.pop() { + let depth = self.stack.items.last().map(|i| i.depth).unwrap_or(0); + self.stack.prefix.drop_lasts(self.stack.prefix.len() - depth); + if depth == item.depth { + // Two consecutive identical depth is an extension + self.pop(); + } + true + } else { + false + } + } + + fn iter_prefix( + &mut self, + prev_query: Option<&QueryPlanItem>, + db: Option<&TrieDB>, + hash_only: bool, + first_iter: bool, + inline_iter: bool, + ) -> Result, CError>> { + let dummy_parent_hash = TrieHash::::default(); + if first_iter { + self.stack.enter_prefix_iter(hash_only); + } + + // run prefix iteration + let mut stacked = first_iter; + loop { + // descend + loop { + if stacked { + // try access value in next node + self.stack.access_value(db, hash_only)?; + stacked = false; + } + + let child_index = if let Some(mut item) = self.stack.items.last_mut() { + if item.next_descended_child as usize >= NIBBLE_LENGTH { + break + } + item.next_descended_child += 1; + item.next_descended_child - 1 + } else { + break + }; + + match self.stack.try_stack_child( + child_index, + db, + dummy_parent_hash, + None, + inline_iter, + )? { + TryStackChildResult::Stacked => { + stacked = true; + }, + TryStackChildResult::NotStackedBranch => (), + TryStackChildResult::NotStacked => break, + TryStackChildResult::StackedDescendIncomplete => { + unreachable!("no slice query") + }, + TryStackChildResult::Halted => { + if let Some(mut item) = self.stack.items.last_mut() { + item.next_descended_child -= 1; + } + self.stack.halt = false; + self.stack.prefix.push(child_index); + let dest_from = Some(( + self.stack.prefix.inner().to_vec(), + (self.stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, + )); + self.stack.prefix.pop(); + self.finalize(); + self.from = dest_from; + self.currently_query_item = prev_query.map(|q| q.to_owned()); + return Ok(true) + }, + } + } + + // pop + if !self.pop() { + break + } + } + self.stack.exit_prefix_iter(); + Ok(false) + } +} + +struct RecordStack { + recorder: Recorder, + items: Vec, + prefix: NibbleVec, + iter_prefix: Option<(usize, bool)>, + seek: Option, + halt: bool, +} + +/// Run query plan on a full db and record it. +/// +/// TODO output and restart are mutually exclusive. -> enum +/// or remove output from halted state. +pub fn record_query_plan< + 'a, + L: TrieLayout, + I: Iterator>, + O: RecorderOutput, +>( + db: &TrieDB, + query_plan: &mut QueryPlan<'a, I>, + from: &mut HaltedStateRecord, +) -> Result<(), VerifyError, CError>> { + // TODO + //) resto + // let restore_buf; + let dummy_parent_hash = TrieHash::::default(); + let mut stateless = false; + let mut statefull = None; + // When define we iter prefix in a node but really want the next non inline. + if let Some(lower_bound) = from.from.take() { + if from.currently_query_item.is_none() { + stateless = true; + let mut bound = NibbleVec::new(); + bound.append_optional_slice_and_nibble(Some(&NibbleSlice::new(&lower_bound.0)), None); + if lower_bound.1 { + bound.pop(); + } + from.stack.recorder.start_at = Some(bound.len()); + from.stack.seek = Some(bound); + } else { + let bound_len = lower_bound.0.len() * nibble_ops::NIBBLE_PER_BYTE - + if lower_bound.1 { 2 } else { 1 }; + // from.stack.recorder.start_at = Some(bound_len); + statefull = Some(bound_len); + } + } + + let mut prev_query: Option = None; + let from_query = from.currently_query_item.take(); + let mut from_query_ref = from_query.as_ref().map(|f| f.as_ref()); + while let Some(query) = from_query_ref.clone().or_else(|| query_plan.items.next()) { + if stateless { + let bound = from.stack.seek.as_ref().expect("Initiated for stateless"); + let bound = bound.as_leftnibbleslice(); + let query_slice = LeftNibbleSlice::new(&query.key); + if query_slice.starts_with(&bound) { + } else if query.as_prefix { + if bound.starts_with(&query_slice) { + } else { + continue + } + } else { + continue + } + stateless = false; + if !query.as_prefix { + from.stack.seek = None; + } + } + let common_nibbles = if let Some(slice_at) = statefull.take() { + slice_at + } else { + let (ordered, common_nibbles) = + prev_query.as_ref().map(|p| p.before(&query)).unwrap_or((true, 0)); + if !ordered { + if query_plan.ignore_unordered { + continue + } else { + return Err(VerifyError::UnorderedKey(query.key.to_vec())) + } + } + loop { + match from.stack.prefix.len().cmp(&common_nibbles) { + Ordering::Equal | Ordering::Less => break, + Ordering::Greater => { + if query_plan.kind.record_inline() { + from.try_stack_content_child()?; + } + if !from.pop() { + from.finalize(); + return Ok(()) + } + }, + } + } + common_nibbles + }; + if let Some((_, hash_only)) = from.stack.iter_prefix.clone() { + // statefull halted during iteration. + let halt = from.iter_prefix(Some(&query), Some(db), hash_only, false, false)?; + if halt { + return Ok(()) + } + from_query_ref = None; + prev_query = Some(query); + continue + } + // descend + let mut slice_query = NibbleSlice::new_offset(&query.key, common_nibbles); + let touched = loop { + if !from.stack.items.is_empty() { + if slice_query.is_empty() { + if query.as_prefix { + let halt = + from.iter_prefix(Some(&query), Some(db), query.hash_only, true, false)?; + if halt { + return Ok(()) + } + break false + } else { + break true + } + } else { + if from.stack.recorder.record_skip_value(&mut from.stack.items) { + from.stack.halt = true; + } + } + } + + let child_index = if from.stack.items.is_empty() { 0 } else { slice_query.at(0) }; + /*if query_plan.kind.record_inline() { + from.try_stack_content_child(child_index)?; + }*/ + from.stack.items.last_mut().map(|i| { + // TODO only needed for content but could be better to be always aligned + i.next_descended_child = child_index + 1; + }); + match from.stack.try_stack_child( + child_index, + Some(db), + dummy_parent_hash, + Some(&mut slice_query), + false, + )? { + TryStackChildResult::Stacked => {}, + TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked => + break false, + TryStackChildResult::StackedDescendIncomplete => { + if query.as_prefix { + let halt = + from.iter_prefix(Some(&query), Some(db), query.hash_only, true, false)?; + if halt { + return Ok(()) + } + } + break false + }, + TryStackChildResult::Halted => { + from.stack.halt = false; + from.stack.prefix.push(child_index); + from.from = Some(( + from.stack.prefix.inner().to_vec(), + (from.stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, + )); + from.stack.prefix.pop(); + from.currently_query_item = Some(query.to_owned()); + from.finalize(); + return Ok(()) + }, + } + }; + + if touched { + // try access value + from.stack.access_value(Some(db), query.hash_only)?; + } + from_query_ref = None; + prev_query = Some(query); + } + // TODO loop redundant with finalize?? + loop { + if query_plan.kind.record_inline() { + from.try_stack_content_child()?; + } + + if !from.pop() { + break + } + } + from.finalize(); + Ok(()) +} + +impl RecordStack { + fn try_stack_child<'a>( + &mut self, + child_index: u8, + db: Option<&TrieDB>, + parent_hash: TrieHash, + mut slice_query: Option<&mut NibbleSlice>, + inline_only: bool, // TODO remove all inline only param and make it db is_none TODO rename + ) -> Result, CError>> { + let mut is_inline = false; + let prefix = &mut self.prefix; + let mut descend_incomplete = false; + let mut stack_extension = false; + let mut from_branch = None; + let child_handle = if let Some(item) = self.items.last_mut() { + if inline_only && item.accessed_children_node.at(child_index as usize) { + return Ok(TryStackChildResult::NotStackedBranch) + } + + let node_data = item.node.data(); + + match item.node.node_plan() { + NodePlan::Empty | NodePlan::Leaf { .. } => + return Ok(TryStackChildResult::NotStacked), + NodePlan::Extension { child, .. } => + if child_index == 0 { + let child_handle = child.build(node_data); + if let &NodeHandle::Hash(_) = &child_handle { + item.accessed_children_node.set(child_index as usize, true); + } + child_handle + } else { + return Ok(TryStackChildResult::NotStacked) + }, + NodePlan::NibbledBranch { children, .. } | NodePlan::Branch { children, .. } => + if let Some(child) = &children[child_index as usize] { + from_branch = Some(&mut item.accessed_children_node); + child.build(node_data) + } else { + return Ok(TryStackChildResult::NotStackedBranch) + }, + } + } else { + NodeHandle::Hash(db.expect("non inline").root().as_ref()) + }; + match &child_handle { + NodeHandle::Inline(_) => { + // TODO consider not going into inline for all proof but content. + // Returning NotStacked here sounds safe, then the is_inline field is not needed. + is_inline = true; + }, + NodeHandle::Hash(hash) => { + if inline_only { + if self.recorder.touched_child_hash(hash, child_index) { + self.halt = true; + } + if self.recorder.mark_inline_access() { + if let Some(accessed_children_node) = from_branch { + accessed_children_node.set(child_index as usize, true); + } + } + return Ok(TryStackChildResult::NotStackedBranch) + } + if self.halt && from_branch.is_some() { + return Ok(TryStackChildResult::Halted) + } + }, + } + if let Some(accessed_children_node) = from_branch { + if !is_inline || self.recorder.mark_inline_access() { + accessed_children_node.set(child_index as usize, true); + } + + slice_query.as_mut().map(|s| s.advance(1)); + prefix.push(child_index); + } + // TODO handle cache first + let child_node = if let Some(db) = db { + db.get_raw_or_lookup_with_cache(parent_hash, child_handle, prefix.as_prefix(), false) + .map_err(|_| VerifyError::IncompleteProof)? // actually incomplete db: TODO consider switching error + } else { + let NodeHandle::Inline(node_data) = child_handle else { + unreachable!("call on non inline node when db is None"); + }; + ( + OwnedNode::new::(node_data.to_vec()) + .map_err(|_| VerifyError::IncompleteProof)?, + None, + ) + }; + + // } + + // TODO put in proof (only if Hash or inline for content one) + + let node_data = child_node.0.data(); + //println!("r: {:?}", &node_data); + + match child_node.0.node_plan() { + NodePlan::Branch { .. } => (), + | NodePlan::Empty => (), + NodePlan::Leaf { partial, .. } | + NodePlan::NibbledBranch { partial, .. } | + NodePlan::Extension { partial, .. } => { + let partial = partial.build(node_data); + prefix.append_partial(partial.right()); + if let Some(s) = slice_query.as_mut() { + if s.starts_with(&partial) { + s.advance(partial.len()); + } else { + descend_incomplete = true; + } + } + }, + } + if let NodePlan::Extension { .. } = child_node.0.node_plan() { + stack_extension = true; + } + let next_descended_child = if let Some(seek) = self.seek.as_ref() { + if prefix.len() < seek.len() { + seek.at(prefix.len()) + } else { + self.seek = None; + 0 + } + } else { + 0 + }; + let infos = CompactEncodingInfos { + node: child_node.0, + accessed_children_node: Default::default(), + accessed_value_node: false, + depth: prefix.len(), + next_descended_child, + is_inline, + }; + if self.recorder.record_stacked_node( + &infos, + self.items.is_empty(), + child_index, + &self.items, + ) { + self.halt = true; + } + self.items.push(infos); + if stack_extension { + let sbranch = self.try_stack_child(0, db, parent_hash, slice_query, inline_only)?; + let TryStackChildResult::Stacked = sbranch else { + return Err(VerifyError::InvalidChildReference(b"branch in db should follow extension".to_vec())); + }; + } + + if descend_incomplete { + Ok(TryStackChildResult::StackedDescendIncomplete) + } else { + Ok(TryStackChildResult::Stacked) + } + } + + fn access_value<'a>( + &mut self, + db: Option<&TrieDB>, + hash_only: bool, + ) -> Result, CError>> { + let Some(item)= self.items.last_mut() else { + return Ok(false) + }; + // TODO this could be reuse from iterator, but it seems simple + // enough here too. + let node_data = item.node.data(); + + let value = match item.node.node_plan() { + NodePlan::Leaf { value, .. } => value.build(node_data), + NodePlan::Branch { value, .. } | NodePlan::NibbledBranch { value, .. } => { + if let Some(value) = value { + value.build(node_data) + } else { + return Ok(false) + } + }, + _ => return Ok(false), + }; + match value { + Value::Node(hash_slice) => + if !hash_only { + item.accessed_value_node = true; + let mut hash = TrieHash::::default(); + hash.as_mut().copy_from_slice(hash_slice); + let Some(value) = db.expect("non inline").db().get(&hash, self.prefix.as_prefix()) else { + return Err(VerifyError::IncompleteProof); + }; + if self.recorder.record_value_node(value, self.prefix.len()) { + self.halt = true; + } + } else { + if self.recorder.record_skip_value(&mut self.items) { + self.halt = true; + } + }, + Value::Inline(value) => + if self.recorder.record_value_inline(value, self.prefix.len()) { + self.halt = true; + }, + } + Ok(true) + } + + fn enter_prefix_iter(&mut self, hash_only: bool) { + self.iter_prefix = Some((self.items.len(), hash_only)); + } + + fn exit_prefix_iter(&mut self) { + self.iter_prefix = None + } +} + +#[must_use] +fn flush_compact_content_pop( + out: &mut O, + stacked_from: &mut Option, + items: &[CompactEncodingInfos], + add_depth: Option, + limits: &mut Limits, +) -> bool { + let Some(from) = stacked_from.take() else { + return false + }; + let pop_to = add_depth.unwrap_or_else(|| items.last().map(|i| i.depth).unwrap_or(0)); + debug_assert!(from > pop_to); + + debug_assert!(from - pop_to <= u16::max_value() as usize); + // Warning this implies key size limit of u16::max + let op = Op::, Vec>::KeyPop((from - pop_to) as u16); + let init_len = out.buf_len(); + op.encode_into(out); + let written = out.buf_len() - init_len; + limits.add_node(written, 0, false) +} diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs new file mode 100644 index 00000000..16bcae4f --- /dev/null +++ b/trie-db/src/query_plan/verify.rs @@ -0,0 +1,17 @@ +// Copyright 2023, 2023 Parity Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Verify query plan proof. + + diff --git a/trie-db/src/query_plan/verify_content.rs b/trie-db/src/query_plan/verify_content.rs new file mode 100644 index 00000000..ef3cb4d6 --- /dev/null +++ b/trie-db/src/query_plan/verify_content.rs @@ -0,0 +1,255 @@ +// Copyright 2023, 2023 Parity Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Verify query plan proof for content proofs. + +use super::RecorderOutput; +use core::marker::PhantomData; + +/// Representation of each encoded action +/// for building the proof. +/// TODO ref variant for encoding ?? or key using V and use Op<&H, &[u8]>. +#[derive(Debug)] +pub enum Op { + // key content followed by a mask for last byte. + // If mask erase some content the content need to + // be set at 0 (or error). + // Two consecutive `KeyPush` are invalid. + KeyPush(Vec, u8), /* TODO could use BackingByteVec (but Vec for new as it scale + * encode) */ + // Last call to pop is implicit (up to root), defining + // one will result in an error. + // Two consecutive `KeyPop` are invalid. + // TODO should be compact encoding of number. + KeyPop(u16), + // u8 is child index, shorthand for key push one nibble followed by key pop. + HashChild(H, u8), + // All value variant are only after a `KeyPush` or at first position. + HashValue(H), + Value(V), + // This is not strictly necessary, only if the proof is not sized, otherwhise if we know + // the stream will end it can be skipped. + EndProof, +} + +// Limiting size to u32 (could also just use a terminal character). +#[derive(Debug, PartialEq, Eq)] +#[repr(transparent)] +struct VarInt(u32); + +impl VarInt { + fn encoded_len(&self) -> usize { + if self.0 == 0 { + return 1 + } + let len = 32 - self.0.leading_zeros() as usize; + if len % 7 == 0 { + len / 7 + } else { + len / 7 + 1 + } + /* + match self.0 { + l if l < 2 ^ 7 => 1, // leading 0: 25 + l if l < 2 ^ 14 => 2, // leading 0: 18 + + l if l < 2 ^ 21 => 3, // 11 + l if l < 2 ^ 28 => 4, // 4 + _ => 5, + } + */ + } + + fn encode_into(&self, out: &mut impl RecorderOutput) { + let mut to_encode = self.0; + for _ in 0..self.encoded_len() - 1 { + out.write_bytes(&[0b1000_0000 | to_encode as u8]); + to_encode >>= 7; + } + out.write_bytes(&[to_encode as u8]); + } + + fn decode(encoded: &[u8]) -> Result<(Self, usize), ()> { + let mut value = 0u32; + for (i, byte) in encoded.iter().enumerate() { + let last = byte & 0b1000_0000 == 0; + value |= ((byte & 0b0111_1111) as u32) << (i * 7); + if last { + return Ok((VarInt(value), i + 1)) + } + } + Err(()) + } +} + +#[test] +fn varint_encode_decode() { + let mut buf = super::InMemoryRecorder::default(); + for i in 0..u16::MAX as u32 + 1 { + VarInt(i).encode_into(&mut buf); + assert_eq!(buf.buffer.len(), VarInt(i).encoded_len()); + assert_eq!(Ok((VarInt(i), buf.buffer.len())), VarInt::decode(&buf.buffer)); + buf.buffer.clear(); + } +} + +impl, V: AsRef<[u8]>> Op { + /// Calculate encoded len. + pub fn encoded_len(&self) -> usize { + let mut len = 1; + match self { + Op::KeyPush(key, _mask) => { + len += VarInt(key.len() as u32).encoded_len(); + len += key.len(); + len += 1; + }, + Op::KeyPop(nb) => { + len += VarInt(*nb as u32).encoded_len(); + }, + Op::HashChild(hash, _at) => { + len += hash.as_ref().len(); + len += 1; + }, + Op::HashValue(hash) => { + len += hash.as_ref().len(); + }, + Op::Value(value) => { + len += VarInt(value.as_ref().len() as u32).encoded_len(); + len += value.as_ref().len(); + }, + Op::EndProof => (), + } + len + } + + /// Write op. + pub fn encode_into(&self, out: &mut impl RecorderOutput) { + match self { + Op::KeyPush(key, mask) => { + out.write_bytes(&[0]); + VarInt(key.len() as u32).encode_into(out); + out.write_bytes(&key); + out.write_bytes(&[*mask]); + }, + Op::KeyPop(nb) => { + out.write_bytes(&[1]); + VarInt(*nb as u32).encode_into(out); + }, + Op::HashChild(hash, at) => { + out.write_bytes(&[2]); + out.write_bytes(hash.as_ref()); + out.write_bytes(&[*at]); + }, + Op::HashValue(hash) => { + out.write_bytes(&[3]); + out.write_bytes(hash.as_ref()); + }, + Op::Value(value) => { + out.write_bytes(&[4]); + let value = value.as_ref(); + VarInt(value.len() as u32).encode_into(out); + out.write_bytes(&value); + }, + Op::EndProof => { + out.write_bytes(&[5]); + }, + } + } +} + +impl + AsMut<[u8]> + Default> Op> { + /// Read an op, return op and number byte read. Or error if invalid encoded. + pub fn decode(encoded: &[u8]) -> Result<(Self, usize), ()> { + let mut i = 0; + if i >= encoded.len() { + return Err(()) + } + Ok(match encoded[i] { + 0 => { + let (len, offset) = VarInt::decode(&encoded[i + 1..])?; + i += 1 + offset; + if i + len.0 as usize >= encoded.len() { + return Err(()) + } + let key = &encoded[i..i + len.0 as usize]; + let mask = encoded[i + len.0 as usize]; + (Op::KeyPush(key.to_vec(), mask), i + len.0 as usize + 1) + }, + 1 => { + let (len, offset) = VarInt::decode(&encoded[i + 1..])?; + if len.0 > u16::MAX as u32 { + return Err(()) + } + (Op::KeyPop(len.0 as u16), i + 1 + offset) + }, + 2 => { + let mut hash = H::default(); + let end = i + 1 + hash.as_ref().len(); + if end >= encoded.len() { + return Err(()) + } + hash.as_mut().copy_from_slice(&encoded[i + 1..end]); + let mask = encoded[end]; + (Op::HashChild(hash, mask), end + 1) + }, + 3 => { + let mut hash = H::default(); + let end = i + 1 + hash.as_ref().len(); + if end >= encoded.len() { + return Err(()) + } + hash.as_mut().copy_from_slice(&encoded[i + 1..end]); + (Op::HashValue(hash), end) + }, + 4 => { + let (len, offset) = VarInt::decode(&encoded[i + 1..])?; + i += 1 + offset; + if i + len.0 as usize > encoded.len() { + return Err(()) + } + let value = &encoded[i..i + len.0 as usize]; + (Op::Value(value.to_vec()), i + len.0 as usize) + }, + 5 => (Op::EndProof, 1), + _ => return Err(()), + }) + } +} + +/// Iterator on op from a in memory encoded proof. +pub struct IterOpProof + AsMut<[u8]> + Default, B: AsRef<[u8]>>( + B, + usize, + PhantomData, +); + +impl + AsMut<[u8]> + Default, B: AsRef<[u8]>> From for IterOpProof { + fn from(b: B) -> Self { + Self(b, 0, PhantomData) + } +} + +impl + AsMut<[u8]> + Default, B: AsRef<[u8]>> Iterator for IterOpProof { + type Item = Option>>; + + fn next(&mut self) -> Option { + match Op::decode(&self.0.as_ref()[self.1..]) { + Ok((op, len)) => { + self.1 += len; + Some(Some(op)) + }, + Err(_) => Some(None), + } + } +} diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 6bc6ef40..65c1e3f6 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -18,8 +18,8 @@ use reference_trie::{test_layouts, NoExtensionLayout, TestTrieCache}; use std::collections::BTreeMap; use trie_db::{ proof::{generate_proof, verify_proof, VerifyError}, - DBValue, Trie, TrieDBBuilder, TrieDBMutBuilder, TrieLayout, TrieMut, query_plan::ProofKind, + DBValue, Trie, TrieDBBuilder, TrieDBMutBuilder, TrieLayout, TrieMut, }; type MemoryDB = memory_db::MemoryDB< @@ -262,8 +262,9 @@ fn test_query_plan_content_internal() { } fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { + use trie_db::content_proof::IterOpProof; use trie_db::query_plan::{ - compact_content_proof::IterOpProof, record_query_plan, verify_query_plan_iter, + record_query_plan, verify_query_plan_iter, verify_query_plan_iter_content, HaltedStateCheck, HaltedStateCheckContent, HaltedStateCheckNode, HaltedStateRecord, InMemQueryPlan, InMemQueryPlanItem, InMemoryRecorder, QueryPlan, ReadProofItem, Recorder, @@ -386,7 +387,7 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { let mut nb = 0; let mut proofs = proofs.clone(); while let Some(proof) = proofs.pop() { - use trie_db::query_plan::compact_content_proof::Op; + use trie_db::content_proof::Op; // full on iter all // assert_eq!(proofs.len(), 1); assert_eq!(proof.len(), 1); @@ -680,12 +681,13 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { std::mem::take(&mut full_proof) }; let (mut verify_iter, mut verify_iter_content) = if is_content_proof { + let proof_iter: IterOpProof<_, _> = (&proof[0]).into(); ( None, Some( verify_query_plan_iter_content::>( state, - (&proof[0]).into(), + proof_iter, Some(root.clone()), ) .unwrap(), From 8c52ce55469fd1e9307e62ddd7c1befefb81278c Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 15 Jun 2023 18:58:13 +0200 Subject: [PATCH 075/154] fix warns --- trie-db/src/content_proof.rs | 10 +- trie-db/src/query_plan/mod.rs | 784 +---------------------- trie-db/src/query_plan/record.rs | 23 +- trie-db/src/query_plan/verify.rs | 395 ++++++++++++ trie-db/src/query_plan/verify_content.rs | 568 ++++++++++------ trie-db/test/src/proof.rs | 13 +- 6 files changed, 777 insertions(+), 1016 deletions(-) diff --git a/trie-db/src/content_proof.rs b/trie-db/src/content_proof.rs index 06b7f434..d1d78b8e 100644 --- a/trie-db/src/content_proof.rs +++ b/trie-db/src/content_proof.rs @@ -12,19 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. - //! Compact content proof, a sequence of content as met in the trie. -//! Exception for hashes that are only encoded when node is popped, allowing to merge a few more key manipulation op. -//! Values are return as seen to avoid the need to keep them all (warning a return value may be from an invalid proof -//! and action on those value usually need to be revertible). +//! Exception for hashes that are only encoded when node is popped, allowing to merge a few more key +//! manipulation op. Values are return as seen to avoid the need to keep them all (warning a return +//! value may be from an invalid proof and action on those value usually need to be revertible). //! Proof validity as for compact proof is only possible to check at the end of the proof reading. +use crate::query_plan::RecorderOutput; /// Representation of each encoded action /// for building the proof. /// TODO ref variant for encoding ?? or key using V and use Op<&H, &[u8]>. - use core::marker::PhantomData; -use crate::query_plan::RecorderOutput; #[derive(Debug)] pub enum Op { diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index 3761ebd6..5d426cae 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -24,6 +24,7 @@ use core::marker::PhantomData; use crate::{ + content_proof::Op, nibble::{nibble_ops, nibble_ops::NIBBLE_LENGTH, LeftNibbleSlice, NibbleSlice}, node::{NodeHandle, NodePlan, OwnedNode, Value}, node_codec::NodeCodec, @@ -36,12 +37,16 @@ use crate::{ }, CError, ChildReference, DBValue, NibbleVec, Trie, TrieDB, TrieHash, TrieLayout, }; -use crate::content_proof::Op; use hash_db::Hasher; pub use record::{record_query_plan, HaltedStateRecord, Recorder}; +pub use verify::verify_query_plan_iter; +use verify::HaltedStateCheckNode; +pub use verify_content::verify_query_plan_iter_content; +use verify_content::HaltedStateCheckContent; -pub mod verify_content; mod record; +mod verify; +mod verify_content; /// Item to query, in memory. #[derive(Default)] @@ -171,7 +176,7 @@ pub enum ProofKind { */ /// Content oriented proof, no nodes are written, just a /// sequence of accessed by lexicographical order as described - /// in verify_content::Op. + /// in content_proof::Op. /// As with compact node, checking validity of proof need to /// load the full proof or up to the halt point. CompactContent, @@ -375,77 +380,6 @@ pub enum HaltedStateCheck<'a, L: TrieLayout, C, D: SplitFirst> { Content(HaltedStateCheckContent<'a, L, C>), } -/// When process is halted keep execution state -/// to restore later. -pub struct HaltedStateCheckNode<'a, L: TrieLayout, C, D: SplitFirst> { - query_plan: QueryPlan<'a, C>, - current: Option>, - stack: ReadStack, - state: ReadProofState, - restore_offset: usize, -} - -impl<'a, L: TrieLayout, C, D: SplitFirst> From> - for HaltedStateCheckNode<'a, L, C, D> -{ - fn from(query_plan: QueryPlan<'a, C>) -> Self { - let is_compact = match query_plan.kind { - ProofKind::FullNodes => false, - ProofKind::CompactNodes => true, - _ => false, - }; - - HaltedStateCheckNode { - stack: ReadStack { - items: Default::default(), - start_items: 0, - prefix: Default::default(), - is_compact, - expect_value: false, - iter_prefix: None, - _ph: PhantomData, - }, - state: ReadProofState::NotStarted, - current: None, - restore_offset: 0, - query_plan, - } - } -} - -/// When process is halted keep execution state -/// to restore later. -pub struct HaltedStateCheckContent<'a, L: TrieLayout, C> { - query_plan: QueryPlan<'a, C>, - current: Option>, - stack: ReadContentStack, - state: ReadProofState, - restore_offset: usize, -} - -impl<'a, L: TrieLayout, C> From> for HaltedStateCheckContent<'a, L, C> { - fn from(query_plan: QueryPlan<'a, C>) -> Self { - HaltedStateCheckContent { - stack: ReadContentStack { - items: Default::default(), - start_items: 0, - prefix: Default::default(), - expect_value: false, - iter_prefix: None, - is_prev_push_key: false, - is_prev_pop_key: false, - is_prev_hash_child: None, - first: true, - _ph: PhantomData, - }, - state: ReadProofState::NotStarted, - current: None, - restore_offset: 0, - query_plan, - } - } -} - enum TryStackChildResult { Stacked, NotStackedBranch, @@ -454,45 +388,6 @@ enum TryStackChildResult { Halted, } -/// Proof reading iterator. -pub struct ReadProofIterator<'a, L, C, D, P> -where - L: TrieLayout, - C: Iterator>, - P: Iterator, - D: SplitFirst, -{ - // always needed, this is option only - // to avoid unsafe code when halting. - query_plan: Option>, - proof: P, - is_compact: bool, - expected_root: Option>, - current: Option>, - state: ReadProofState, - stack: ReadStack, - restore_offset: usize, -} - -/// Proof reading iterator. -pub struct ReadProofContentIterator<'a, L, C, P> -where - L: TrieLayout, - C: Iterator>, - P: Iterator, Vec>>>, -{ - // always needed, this is option only - // to avoid unsafe code when halting. - query_plan: Option>, - proof: P, - expected_root: Option>, - current: Option>, - state: ReadProofState, - stack: ReadContentStack, - restore_offset: usize, - buf_op: Option, Vec>>, -} - #[derive(Eq, PartialEq)] enum ReadProofState { /// Iteration not started. @@ -1339,7 +1234,6 @@ impl ReadContentStack { let ItemContentStack { children, value: v, depth, .. } = self.items.pop().expect("checked"); debug_assert!(branch_d == depth); - let pr = NibbleSlice::new_offset(&key_branch, branch_d); let hashed; let value = if let Some(v) = v.as_ref() { @@ -1491,665 +1385,3 @@ pub enum ReadProofItem<'a, L: TrieLayout, C, D: SplitFirst> { /// TODO unused implement EndPrefix, } - -impl<'a, L, C, D, P> Iterator for ReadProofIterator<'a, L, C, D, P> -where - L: TrieLayout, - C: Iterator>, - P: Iterator, - D: SplitFirst, -{ - type Item = Result, VerifyError, CError>>; - - fn next(&mut self) -> Option { - if self.state == ReadProofState::Finished { - return None - } - let check_hash = self.expected_root.is_some(); - if self.state == ReadProofState::Halted { - self.state = ReadProofState::Running; - } - let mut to_check_slice = self - .current - .as_ref() - .map(|n| NibbleSlice::new_offset(n.key, self.restore_offset)); - - // read proof - loop { - if self.state == ReadProofState::SwitchQueryPlan || - self.state == ReadProofState::NotStarted - { - let query_plan = self.query_plan.as_mut().expect("Removed with state"); - if let Some(next) = query_plan.items.next() { - let (ordered, common_nibbles) = if let Some(old) = self.current.as_ref() { - old.before(&next) - } else { - (true, 0) - }; - if !ordered { - if query_plan.ignore_unordered { - continue - } else { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::UnorderedKey(next.key.to_vec()))) - } - } - - let r = self.stack.pop_until(common_nibbles, &self.expected_root, false); - if let Err(e) = r { - self.state = ReadProofState::Finished; - return Some(Err(e)) - } - self.state = ReadProofState::Running; - self.current = Some(next); - to_check_slice = self - .current - .as_ref() - .map(|n| NibbleSlice::new_offset(n.key, common_nibbles)); - } else { - self.state = ReadProofState::PlanConsumed; - self.current = None; - break - } - }; - let did_prefix = self.stack.iter_prefix.is_some(); - while let Some((_, accessed_value_node, hash_only)) = self.stack.iter_prefix.clone() { - // prefix iteration - if !accessed_value_node { - self.stack.iter_prefix.as_mut().map(|s| { - s.1 = true; - }); - match self.stack.access_value(&mut self.proof, check_hash, hash_only) { - Ok((Some(value), None)) => - return Some(Ok(ReadProofItem::Value( - self.stack.prefix.inner().to_vec().into(), - value, - ))), - Ok((None, Some(hash))) => - return Some(Ok(ReadProofItem::Hash( - self.stack.prefix.inner().to_vec().into(), - hash, - ))), - Ok((None, None)) => (), - Ok(_) => unreachable!(), - Err(e) => { - self.state = ReadProofState::Finished; - return Some(Err(e)) - }, - }; - } - while let Some(child_index) = self.stack.items.last_mut().and_then(|last| { - if last.next_descended_child as usize >= NIBBLE_LENGTH { - None - } else { - let child_index = last.next_descended_child; - last.next_descended_child += 1; - Some(child_index) - } - }) { - let r = match self.stack.try_stack_child( - child_index, - &mut self.proof, - &self.expected_root, - None, - false, - ) { - Ok(r) => r, - Err(e) => { - self.state = ReadProofState::Finished; - return Some(Err(e)) - }, - }; - match r { - TryStackChildResult::Stacked => { - self.stack.iter_prefix.as_mut().map(|p| { - p.1 = false; - }); - break - }, - TryStackChildResult::StackedDescendIncomplete => { - unreachable!("slice query none"); - }, - TryStackChildResult::NotStacked => break, - TryStackChildResult::NotStackedBranch => (), - TryStackChildResult::Halted => { - if let Some(last) = self.stack.items.last_mut() { - last.next_descended_child -= 1; - } - return self.halt(None) - }, - } - } - if self.stack.iter_prefix.as_ref().map(|p| p.1).unwrap_or_default() { - if !match self.stack.pop(&self.expected_root) { - Ok(r) => r, - Err(e) => { - self.state = ReadProofState::Finished; - return Some(Err(e)) - }, - } { - // end iter - self.stack.exit_prefix_iter(); - } - } - } - if did_prefix { - // exit a prefix iter, next content looping - self.state = ReadProofState::SwitchQueryPlan; - continue - } - let to_check = self.current.as_ref().expect("Init above"); - let to_check_len = to_check.key.len() * nibble_ops::NIBBLE_PER_BYTE; - let mut to_check_slice = to_check_slice.as_mut().expect("Init above"); - let as_prefix = to_check.as_prefix; // TODO useless? - let hash_only = to_check.hash_only; // TODO useless? - let mut at_value = false; - match self.stack.prefix.len().cmp(&to_check_len) { - Ordering::Equal => - if !self.stack.items.is_empty() { - at_value = true; - }, - Ordering::Less => (), - Ordering::Greater => { - unreachable!(); - }, - } - - if at_value { - if as_prefix { - self.stack.enter_prefix_iter(hash_only); - continue - } - self.state = ReadProofState::SwitchQueryPlan; - match self.stack.access_value(&mut self.proof, check_hash, hash_only) { - Ok((Some(value), None)) => - return Some(Ok(ReadProofItem::Value(to_check.key.into(), value))), - Ok((None, Some(hash))) => - return Some(Ok(ReadProofItem::Hash(to_check.key.into(), hash))), - Ok((None, None)) => return Some(Ok(ReadProofItem::NoValue(to_check.key))), - Ok(_) => unreachable!(), - Err(e) => { - self.state = ReadProofState::Finished; - return Some(Err(e)) - }, - } - } - - let child_index = if self.stack.items.len() == 0 { - // dummy - 0 - } else { - to_check_slice.at(0) - }; - let r = match self.stack.try_stack_child( - child_index, - &mut self.proof, - &self.expected_root, - Some(&mut to_check_slice), - to_check.as_prefix, - ) { - Ok(r) => r, - Err(e) => { - self.state = ReadProofState::Finished; - return Some(Err(e)) - }, - }; - match r { - TryStackChildResult::Stacked => (), - TryStackChildResult::StackedDescendIncomplete => { - if as_prefix { - self.stack.enter_prefix_iter(hash_only); - continue - } - self.state = ReadProofState::SwitchQueryPlan; - return Some(Ok(ReadProofItem::NoValue(to_check.key))) - }, - TryStackChildResult::NotStacked => { - self.state = ReadProofState::SwitchQueryPlan; - return Some(Ok(ReadProofItem::NoValue(to_check.key))) - }, - TryStackChildResult::NotStackedBranch => { - self.state = ReadProofState::SwitchQueryPlan; - return Some(Ok(ReadProofItem::NoValue(to_check.key))) - }, - TryStackChildResult::Halted => return self.halt(Some(to_check_slice)), - } - } - - debug_assert!(self.state == ReadProofState::PlanConsumed); - if self.is_compact { - let stack_to = 0; // TODO restart is different - // let r = self.stack.pop_until(common_nibbles, &self.expected_root); - let r = self.stack.pop_until(stack_to, &self.expected_root, false); - if let Err(e) = r { - self.state = ReadProofState::Finished; - return Some(Err(e)) - } - } else { - if self.proof.next().is_some() { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) - } - } - self.state = ReadProofState::Finished; - return None - } -} - -impl<'a, L, C, P> Iterator for ReadProofContentIterator<'a, L, C, P> -where - L: TrieLayout, - C: Iterator>, - P: Iterator, Vec>>>, -{ - type Item = Result>, VerifyError, CError>>; - - fn next(&mut self) -> Option { - if self.state == ReadProofState::Finished { - return None - } - let check_hash = self.expected_root.is_some(); - if self.state == ReadProofState::Halted { - self.state = ReadProofState::Running; - } - let mut to_check_slice = self - .current - .as_ref() - .map(|n| NibbleSlice::new_offset(n.key, self.restore_offset)); - // read proof - loop { - if self.state == ReadProofState::SwitchQueryPlan || - self.state == ReadProofState::NotStarted - { - let query_plan = self.query_plan.as_mut().expect("Removed with state"); - if let Some(next) = query_plan.items.next() { - let (ordered, common_nibbles) = if let Some(old) = self.current.as_ref() { - old.before(&next) - } else { - (true, 0) - }; - if !ordered { - if query_plan.ignore_unordered { - continue - } else { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::UnorderedKey(next.key.to_vec()))) - } - } - - let r = self.stack.pop_until(common_nibbles, false); - if let Err(e) = r { - self.state = ReadProofState::Finished; - return Some(Err(e)) - } - self.state = ReadProofState::Running; - self.current = Some(next); - to_check_slice = self - .current - .as_ref() - .map(|n| NibbleSlice::new_offset(n.key, common_nibbles)); - } else { - self.state = ReadProofState::PlanConsumed; - self.current = None; - } - }; - - // op sequence check - let mut from_iter = false; - while let Some(op) = self.buf_op.take().map(Option::Some).or_else(|| { - from_iter = true; - self.proof.next() - }) { - println!("read: {:?}", op); - let Some(op) = op else { - let r = self.stack.stack_pop(None, &self.expected_root); - self.state = ReadProofState::Finished; - if let Err(e) = r { - self.state = ReadProofState::Finished; - return Some(Err(e)) - } - if let Some(c) = self.current.as_ref() { - if c.as_prefix { - // end prefix switch to next - self.state = ReadProofState::SwitchQueryPlan; - break; - } else { - // missing value - self.state = ReadProofState::SwitchQueryPlan; - return Some(Ok(ReadProofItem::NoValue(&c.key))); - } - } else { - return None; // finished - } - }; - if from_iter { - // check ordering logic - // TODO wrap in an error and put bools in a struct - match &op { - Op::KeyPush(..) => { - if self.stack.is_prev_push_key { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error - // TODO return - // Err(CompactDecoderError::ConsecutivePushKeys. - // into()) - } - self.stack.is_prev_push_key = true; - self.stack.is_prev_pop_key = false; - self.stack.is_prev_hash_child = None; - self.stack.first = false; - }, - Op::KeyPop(..) => { - if self.stack.is_prev_pop_key { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error - // return Err(CompactDecoderError::ConsecutivePopKeys. - // into()) - } - self.stack.is_prev_push_key = false; - self.stack.is_prev_pop_key = true; - self.stack.is_prev_hash_child = None; - self.stack.first = false; - }, - Op::HashChild(_, ix) => { - if let Some(prev_ix) = self.stack.is_prev_hash_child.as_ref() { - if prev_ix >= ix { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error - // return Err(CompactDecoderError::NotConsecutiveHash. - // into()) - } - } - // child ix on an existing content would be handle by iter_build. - self.stack.is_prev_push_key = false; - self.stack.is_prev_pop_key = false; - self.stack.is_prev_hash_child = Some(*ix); - }, - Op::Value(_) => { - // | Op::ValueForceInline(_) | Op::ValueForceHashed(_) => { - if !(self.stack.is_prev_push_key || self.stack.first) { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error - // return Err(CompactDecoderError::ValueNotAfterPush. - // into()) - } - self.stack.is_prev_push_key = false; - self.stack.is_prev_pop_key = false; - self.stack.is_prev_hash_child = None; - self.stack.first = false; - }, - _ => { - self.stack.is_prev_push_key = false; - self.stack.is_prev_pop_key = false; - self.stack.is_prev_hash_child = None; - self.stack.first = false; - }, - } - - // debug TODO make it log and external function - match &op { - Op::HashChild(hash, child_ix) => { - println!( - "ChildHash {:?}, {:?}, {:?}", - self.stack.prefix, child_ix, hash - ); - }, - Op::HashValue(hash) => { - println!("ValueHash {:?}, {:?}", self.stack.prefix, hash); - }, - Op::Value(value) => { - println!("Value {:?}, {:?}", self.stack.prefix, value); - }, - _ => (), - } - } - from_iter = false; - - // next - let item = if match &op { - Op::Value(..) | Op::HashValue(..) => true, - _ => false, - } { - let mut to_check_slice = to_check_slice.as_mut().expect("Init above"); - - let mut at_value = false; - let mut next_query = false; - if let Some(current) = self.current.as_ref() { - let query_slice = LeftNibbleSlice::new(¤t.key); - match self.stack.prefix.as_leftnibbleslice().cmp(&query_slice) { - Ordering::Equal => - if !self.stack.items.is_empty() { - at_value = true; - }, - Ordering::Less => (), - Ordering::Greater => - if current.as_prefix { - let query_slice = LeftNibbleSlice::new(¤t.key); - if self - .stack - .prefix - .as_leftnibbleslice() - .starts_with(&query_slice) - { - at_value = true; - } else { - next_query = true; - } - } else { - next_query = true; - }, - } - if next_query { - self.buf_op = Some(op); - self.state = ReadProofState::SwitchQueryPlan; - if current.as_prefix { - break - } else { - return Some(Ok(ReadProofItem::NoValue(¤t.key))) - } - } - } - - if at_value { - match &op { - Op::Value(value) => { - // TODO could get content from op with no clone. - Some(ReadProofItem::Value( - self.stack.prefix.inner().to_vec().into(), - value.clone(), - )) - }, - Op::HashValue(hash) => { - // TODO could get content from op with no clone. - Some(ReadProofItem::Hash( - self.stack.prefix.inner().to_vec().into(), - hash.clone(), - )) - }, - _ => unreachable!(), - } - } else { - match &op { - Op::Value(value) => { - // hash value here not value - if L::MAX_INLINE_VALUE - .map(|max| max as usize <= value.len()) - .unwrap_or(false) - { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousValue(value.clone()))) - } - }, - _ => (), - } - None - } - } else { - None - }; - - // act - let r = match op { - Op::KeyPush(partial, mask) => { - self.stack - .prefix - .append_slice(LeftNibbleSlice::new_with_mask(partial.as_slice(), mask)); - self.stack.stack_empty(self.stack.prefix.len()); - Ok(()) - }, - Op::KeyPop(nb_nibble) => { - let r = self.stack.stack_pop(Some(nb_nibble as usize), &self.expected_root); - self.stack.prefix.drop_lasts(nb_nibble.into()); - r - }, - Op::EndProof => break, - op => self.stack.set_cache_change(op.into()), - }; - if let Err(e) = r { - self.state = ReadProofState::Finished; - return Some(Err(e)) - } - if let Some(r) = item { - if self.current.as_ref().map(|c| !c.as_prefix).unwrap_or(true) { - self.state = ReadProofState::SwitchQueryPlan; // TODO this is same as content NOne? - } - return Some(Ok(r)) - } - } - if self.state != ReadProofState::SwitchQueryPlan && self.current.is_some() { - // TODO return halt instead - return Some(Err(VerifyError::IncompleteProof)) - } - self.state = ReadProofState::SwitchQueryPlan; - } - - self.state = ReadProofState::Finished; - if self.proof.next().is_some() { - return Some(Err(VerifyError::ExtraneousNode)) - } else { - return None - } - } -} - -impl<'a, L, C, D, P> ReadProofIterator<'a, L, C, D, P> -where - L: TrieLayout, - C: Iterator>, - P: Iterator, - D: SplitFirst, -{ - fn halt( - &mut self, - to_check_slice: Option<&mut NibbleSlice>, - ) -> Option, VerifyError, CError>>> { - if self.is_compact { - let stack_to = 0; // TODO restart is different - let r = self.stack.pop_until(stack_to, &self.expected_root, true); - if let Err(e) = r { - self.state = ReadProofState::Finished; - return Some(Err(e)) - } - } - self.state = ReadProofState::Finished; - let query_plan = crate::rstd::mem::replace(&mut self.query_plan, None); - let query_plan = query_plan.expect("Init with state"); - let current = crate::rstd::mem::take(&mut self.current); - let mut stack = crate::rstd::mem::replace( - &mut self.stack, - ReadStack { - items: Default::default(), - start_items: 0, - prefix: Default::default(), - is_compact: self.is_compact, - expect_value: false, - iter_prefix: None, - _ph: PhantomData, - }, - ); - stack.start_items = stack.items.len(); - Some(Ok(ReadProofItem::Halted(Box::new(HaltedStateCheck::Node(HaltedStateCheckNode { - query_plan, - current, - stack, - state: ReadProofState::Halted, - restore_offset: to_check_slice.map(|s| s.offset()).unwrap_or(0), - }))))) - } -} - -/// Read the proof. -/// -/// If expected root is None, then we do not check hashes at all. -pub fn verify_query_plan_iter<'a, L, C, D, P>( - state: HaltedStateCheck<'a, L, C, D>, - proof: P, - expected_root: Option>, -) -> Result, VerifyError, CError>> -where - L: TrieLayout, - C: Iterator>, - P: Iterator, - D: SplitFirst, -{ - let HaltedStateCheck::Node(state) = state else { - return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent - }; - let HaltedStateCheckNode { query_plan, current, stack, state, restore_offset } = state; - - match query_plan.kind { - ProofKind::CompactContent => { - return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent - }, - _ => (), - }; - - Ok(ReadProofIterator { - query_plan: Some(query_plan), - proof, - is_compact: stack.is_compact, - expected_root, - current, - state, - stack, - restore_offset, - }) -} - -/// Read the proof. -/// -/// If expected root is None, then we do not check hashes at all. -pub fn verify_query_plan_iter_content<'a, L, C, P>( - state: HaltedStateCheck<'a, L, C, Vec>, - proof: P, - expected_root: Option>, -) -> Result, VerifyError, CError>> -where - L: TrieLayout, - C: Iterator>, - P: Iterator, Vec>>>, -{ - let HaltedStateCheck::Content(state) = state else { - return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent - }; - - let HaltedStateCheckContent { query_plan, current, stack, state, restore_offset } = state; - - match query_plan.kind { - ProofKind::CompactContent => (), - _ => { - return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent - }, - }; - - Ok(ReadProofContentIterator { - query_plan: Some(query_plan), - proof, - expected_root, - current, - state, - stack, - restore_offset, - buf_op: None, - }) -} diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index a519a351..8ebdd77e 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -429,7 +429,7 @@ impl HaltedStateRecord { } } - let mut process_children = None; + let mut process_children = false; let mut has_hash_to_write = false; match &mut self.stack.recorder.output { RecorderStateInner::Stream(_) => (), @@ -444,34 +444,28 @@ impl HaltedStateRecord { .expect("TODO error handling, can it actually fail?"); } // else when restarting record, this is not to be recorded }, - RecorderStateInner::Content { output, stacked_pop, .. } => { + RecorderStateInner::Content { .. } => { // two case: children to register or all children accessed. if let Some(last_item) = items.last() { match last_item.node.node_plan() { NodePlan::Branch { children, .. } | - NodePlan::NibbledBranch { children, .. } => + NodePlan::NibbledBranch { children, .. } => { + process_children = true; for i in 0..children.len() { if children[i].is_some() && !last_item.accessed_children_node.at(i) { has_hash_to_write = true; break } - }, + } + }, _ => (), } } - - match item.node.node_plan() { - NodePlan::Branch { children, .. } | - NodePlan::NibbledBranch { children, .. } => { - process_children = Some(children.len()); - }, - _ => (), - } }, } - if let Some(nb_children) = process_children { + if process_children { self.try_stack_content_child().expect("stack inline do not fetch"); } let items = &self.stack.items[..at]; @@ -506,8 +500,7 @@ impl HaltedStateRecord { //upper: u8, ) -> Result<(), VerifyError, CError>> { let dummy_parent_hash = TrieHash::::default(); - if let Some(item) = self.stack.items.last() { - let pre = item.next_descended_child; // TODO put next_descended child to 16 for leaf (skip some noop iter) + if !self.stack.items.is_empty() { for i in 0..NIBBLE_LENGTH as u8 { match self.stack.try_stack_child(i, None, dummy_parent_hash, None, true)? { // only expect a stacked prefix here diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 16bcae4f..8dc1b0df 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -14,4 +14,399 @@ //! Verify query plan proof. +use super::*; +use core::marker::PhantomData; +use crate::{ + nibble::{nibble_ops, nibble_ops::NIBBLE_LENGTH, NibbleSlice}, + proof::VerifyError, + rstd::{boxed::Box, result::Result}, + CError, TrieHash, TrieLayout, +}; +pub use record::{record_query_plan, HaltedStateRecord, Recorder}; + +/// Proof reading iterator. +pub struct ReadProofIterator<'a, L, C, D, P> +where + L: TrieLayout, + C: Iterator>, + P: Iterator, + D: SplitFirst, +{ + // always needed, this is option only + // to avoid unsafe code when halting. + query_plan: Option>, + proof: P, + is_compact: bool, + expected_root: Option>, + current: Option>, + state: ReadProofState, + stack: ReadStack, + restore_offset: usize, +} + +/// Read the proof. +/// +/// If expected root is None, then we do not check hashes at all. +pub fn verify_query_plan_iter<'a, L, C, D, P>( + state: HaltedStateCheck<'a, L, C, D>, + proof: P, + expected_root: Option>, +) -> Result, VerifyError, CError>> +where + L: TrieLayout, + C: Iterator>, + P: Iterator, + D: SplitFirst, +{ + let HaltedStateCheck::Node(state) = state else { + return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent + }; + let HaltedStateCheckNode { query_plan, current, stack, state, restore_offset } = state; + + match query_plan.kind { + ProofKind::CompactContent => { + return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent + }, + _ => (), + }; + + Ok(ReadProofIterator { + query_plan: Some(query_plan), + proof, + is_compact: stack.is_compact, + expected_root, + current, + state, + stack, + restore_offset, + }) +} + +impl<'a, L, C, D, P> ReadProofIterator<'a, L, C, D, P> +where + L: TrieLayout, + C: Iterator>, + P: Iterator, + D: SplitFirst, +{ + fn halt( + &mut self, + to_check_slice: Option<&mut NibbleSlice>, + ) -> Option, VerifyError, CError>>> { + if self.is_compact { + let stack_to = 0; // TODO restart is different + let r = self.stack.pop_until(stack_to, &self.expected_root, true); + if let Err(e) = r { + self.state = ReadProofState::Finished; + return Some(Err(e)) + } + } + self.state = ReadProofState::Finished; + let query_plan = crate::rstd::mem::replace(&mut self.query_plan, None); + let query_plan = query_plan.expect("Init with state"); + let current = crate::rstd::mem::take(&mut self.current); + let mut stack = crate::rstd::mem::replace( + &mut self.stack, + ReadStack { + items: Default::default(), + start_items: 0, + prefix: Default::default(), + is_compact: self.is_compact, + expect_value: false, + iter_prefix: None, + _ph: PhantomData, + }, + ); + stack.start_items = stack.items.len(); + Some(Ok(ReadProofItem::Halted(Box::new(HaltedStateCheck::Node(HaltedStateCheckNode { + query_plan, + current, + stack, + state: ReadProofState::Halted, + restore_offset: to_check_slice.map(|s| s.offset()).unwrap_or(0), + }))))) + } +} + +impl<'a, L, C, D, P> Iterator for ReadProofIterator<'a, L, C, D, P> +where + L: TrieLayout, + C: Iterator>, + P: Iterator, + D: SplitFirst, +{ + type Item = Result, VerifyError, CError>>; + + fn next(&mut self) -> Option { + if self.state == ReadProofState::Finished { + return None + } + let check_hash = self.expected_root.is_some(); + if self.state == ReadProofState::Halted { + self.state = ReadProofState::Running; + } + let mut to_check_slice = self + .current + .as_ref() + .map(|n| NibbleSlice::new_offset(n.key, self.restore_offset)); + + // read proof + loop { + if self.state == ReadProofState::SwitchQueryPlan || + self.state == ReadProofState::NotStarted + { + let query_plan = self.query_plan.as_mut().expect("Removed with state"); + if let Some(next) = query_plan.items.next() { + let (ordered, common_nibbles) = if let Some(old) = self.current.as_ref() { + old.before(&next) + } else { + (true, 0) + }; + if !ordered { + if query_plan.ignore_unordered { + continue + } else { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::UnorderedKey(next.key.to_vec()))) + } + } + + let r = self.stack.pop_until(common_nibbles, &self.expected_root, false); + if let Err(e) = r { + self.state = ReadProofState::Finished; + return Some(Err(e)) + } + self.state = ReadProofState::Running; + self.current = Some(next); + to_check_slice = self + .current + .as_ref() + .map(|n| NibbleSlice::new_offset(n.key, common_nibbles)); + } else { + self.state = ReadProofState::PlanConsumed; + self.current = None; + break + } + }; + let did_prefix = self.stack.iter_prefix.is_some(); + while let Some((_, accessed_value_node, hash_only)) = self.stack.iter_prefix.clone() { + // prefix iteration + if !accessed_value_node { + self.stack.iter_prefix.as_mut().map(|s| { + s.1 = true; + }); + match self.stack.access_value(&mut self.proof, check_hash, hash_only) { + Ok((Some(value), None)) => + return Some(Ok(ReadProofItem::Value( + self.stack.prefix.inner().to_vec().into(), + value, + ))), + Ok((None, Some(hash))) => + return Some(Ok(ReadProofItem::Hash( + self.stack.prefix.inner().to_vec().into(), + hash, + ))), + Ok((None, None)) => (), + Ok(_) => unreachable!(), + Err(e) => { + self.state = ReadProofState::Finished; + return Some(Err(e)) + }, + }; + } + while let Some(child_index) = self.stack.items.last_mut().and_then(|last| { + if last.next_descended_child as usize >= NIBBLE_LENGTH { + None + } else { + let child_index = last.next_descended_child; + last.next_descended_child += 1; + Some(child_index) + } + }) { + let r = match self.stack.try_stack_child( + child_index, + &mut self.proof, + &self.expected_root, + None, + false, + ) { + Ok(r) => r, + Err(e) => { + self.state = ReadProofState::Finished; + return Some(Err(e)) + }, + }; + match r { + TryStackChildResult::Stacked => { + self.stack.iter_prefix.as_mut().map(|p| { + p.1 = false; + }); + break + }, + TryStackChildResult::StackedDescendIncomplete => { + unreachable!("slice query none"); + }, + TryStackChildResult::NotStacked => break, + TryStackChildResult::NotStackedBranch => (), + TryStackChildResult::Halted => { + if let Some(last) = self.stack.items.last_mut() { + last.next_descended_child -= 1; + } + return self.halt(None) + }, + } + } + if self.stack.iter_prefix.as_ref().map(|p| p.1).unwrap_or_default() { + if !match self.stack.pop(&self.expected_root) { + Ok(r) => r, + Err(e) => { + self.state = ReadProofState::Finished; + return Some(Err(e)) + }, + } { + // end iter + self.stack.exit_prefix_iter(); + } + } + } + if did_prefix { + // exit a prefix iter, next content looping + self.state = ReadProofState::SwitchQueryPlan; + continue + } + let to_check = self.current.as_ref().expect("Init above"); + let to_check_len = to_check.key.len() * nibble_ops::NIBBLE_PER_BYTE; + let mut to_check_slice = to_check_slice.as_mut().expect("Init above"); + let as_prefix = to_check.as_prefix; // TODO useless? + let hash_only = to_check.hash_only; // TODO useless? + let mut at_value = false; + match self.stack.prefix.len().cmp(&to_check_len) { + Ordering::Equal => + if !self.stack.items.is_empty() { + at_value = true; + }, + Ordering::Less => (), + Ordering::Greater => { + unreachable!(); + }, + } + + if at_value { + if as_prefix { + self.stack.enter_prefix_iter(hash_only); + continue + } + self.state = ReadProofState::SwitchQueryPlan; + match self.stack.access_value(&mut self.proof, check_hash, hash_only) { + Ok((Some(value), None)) => + return Some(Ok(ReadProofItem::Value(to_check.key.into(), value))), + Ok((None, Some(hash))) => + return Some(Ok(ReadProofItem::Hash(to_check.key.into(), hash))), + Ok((None, None)) => return Some(Ok(ReadProofItem::NoValue(to_check.key))), + Ok(_) => unreachable!(), + Err(e) => { + self.state = ReadProofState::Finished; + return Some(Err(e)) + }, + } + } + + let child_index = if self.stack.items.len() == 0 { + // dummy + 0 + } else { + to_check_slice.at(0) + }; + let r = match self.stack.try_stack_child( + child_index, + &mut self.proof, + &self.expected_root, + Some(&mut to_check_slice), + to_check.as_prefix, + ) { + Ok(r) => r, + Err(e) => { + self.state = ReadProofState::Finished; + return Some(Err(e)) + }, + }; + match r { + TryStackChildResult::Stacked => (), + TryStackChildResult::StackedDescendIncomplete => { + if as_prefix { + self.stack.enter_prefix_iter(hash_only); + continue + } + self.state = ReadProofState::SwitchQueryPlan; + return Some(Ok(ReadProofItem::NoValue(to_check.key))) + }, + TryStackChildResult::NotStacked => { + self.state = ReadProofState::SwitchQueryPlan; + return Some(Ok(ReadProofItem::NoValue(to_check.key))) + }, + TryStackChildResult::NotStackedBranch => { + self.state = ReadProofState::SwitchQueryPlan; + return Some(Ok(ReadProofItem::NoValue(to_check.key))) + }, + TryStackChildResult::Halted => return self.halt(Some(to_check_slice)), + } + } + + debug_assert!(self.state == ReadProofState::PlanConsumed); + if self.is_compact { + let stack_to = 0; // TODO restart is different + // let r = self.stack.pop_until(common_nibbles, &self.expected_root); + let r = self.stack.pop_until(stack_to, &self.expected_root, false); + if let Err(e) = r { + self.state = ReadProofState::Finished; + return Some(Err(e)) + } + } else { + if self.proof.next().is_some() { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) + } + } + self.state = ReadProofState::Finished; + return None + } +} + +/// When process is halted keep execution state +/// to restore later. +pub struct HaltedStateCheckNode<'a, L: TrieLayout, C, D: SplitFirst> { + query_plan: QueryPlan<'a, C>, + current: Option>, + stack: ReadStack, + state: ReadProofState, + restore_offset: usize, +} + +impl<'a, L: TrieLayout, C, D: SplitFirst> From> + for HaltedStateCheckNode<'a, L, C, D> +{ + fn from(query_plan: QueryPlan<'a, C>) -> Self { + let is_compact = match query_plan.kind { + ProofKind::FullNodes => false, + ProofKind::CompactNodes => true, + _ => false, + }; + + HaltedStateCheckNode { + stack: ReadStack { + items: Default::default(), + start_items: 0, + prefix: Default::default(), + is_compact, + expect_value: false, + iter_prefix: None, + _ph: PhantomData, + }, + state: ReadProofState::NotStarted, + current: None, + restore_offset: 0, + query_plan, + } + } +} diff --git a/trie-db/src/query_plan/verify_content.rs b/trie-db/src/query_plan/verify_content.rs index ef3cb4d6..b5f83a40 100644 --- a/trie-db/src/query_plan/verify_content.rs +++ b/trie-db/src/query_plan/verify_content.rs @@ -14,242 +14,384 @@ //! Verify query plan proof for content proofs. -use super::RecorderOutput; +use super::*; use core::marker::PhantomData; -/// Representation of each encoded action -/// for building the proof. -/// TODO ref variant for encoding ?? or key using V and use Op<&H, &[u8]>. -#[derive(Debug)] -pub enum Op { - // key content followed by a mask for last byte. - // If mask erase some content the content need to - // be set at 0 (or error). - // Two consecutive `KeyPush` are invalid. - KeyPush(Vec, u8), /* TODO could use BackingByteVec (but Vec for new as it scale - * encode) */ - // Last call to pop is implicit (up to root), defining - // one will result in an error. - // Two consecutive `KeyPop` are invalid. - // TODO should be compact encoding of number. - KeyPop(u16), - // u8 is child index, shorthand for key push one nibble followed by key pop. - HashChild(H, u8), - // All value variant are only after a `KeyPush` or at first position. - HashValue(H), - Value(V), - // This is not strictly necessary, only if the proof is not sized, otherwhise if we know - // the stream will end it can be skipped. - EndProof, +use crate::{ + content_proof::Op, nibble::LeftNibbleSlice, proof::VerifyError, rstd::result::Result, CError, + TrieHash, TrieLayout, +}; +pub use record::{record_query_plan, HaltedStateRecord, Recorder}; + +/// Proof reading iterator. +pub struct ReadProofContentIterator<'a, L, C, P> +where + L: TrieLayout, + C: Iterator>, + P: Iterator, Vec>>>, +{ + // always needed, this is option only + // to avoid unsafe code when halting. + query_plan: Option>, + proof: P, + expected_root: Option>, + current: Option>, + state: ReadProofState, + stack: ReadContentStack, + buf_op: Option, Vec>>, } -// Limiting size to u32 (could also just use a terminal character). -#[derive(Debug, PartialEq, Eq)] -#[repr(transparent)] -struct VarInt(u32); +/// Read the proof. +/// +/// If expected root is None, then we do not check hashes at all. +pub fn verify_query_plan_iter_content<'a, L, C, P>( + state: HaltedStateCheck<'a, L, C, Vec>, + proof: P, + expected_root: Option>, +) -> Result, VerifyError, CError>> +where + L: TrieLayout, + C: Iterator>, + P: Iterator, Vec>>>, +{ + let HaltedStateCheck::Content(state) = state else { + return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent + }; -impl VarInt { - fn encoded_len(&self) -> usize { - if self.0 == 0 { - return 1 - } - let len = 32 - self.0.leading_zeros() as usize; - if len % 7 == 0 { - len / 7 - } else { - len / 7 + 1 - } - /* - match self.0 { - l if l < 2 ^ 7 => 1, // leading 0: 25 - l if l < 2 ^ 14 => 2, // leading 0: 18 - - l if l < 2 ^ 21 => 3, // 11 - l if l < 2 ^ 28 => 4, // 4 - _ => 5, - } - */ - } + let HaltedStateCheckContent { query_plan, current, stack, state } = state; - fn encode_into(&self, out: &mut impl RecorderOutput) { - let mut to_encode = self.0; - for _ in 0..self.encoded_len() - 1 { - out.write_bytes(&[0b1000_0000 | to_encode as u8]); - to_encode >>= 7; - } - out.write_bytes(&[to_encode as u8]); - } + match query_plan.kind { + ProofKind::CompactContent => (), + _ => { + return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent + }, + }; - fn decode(encoded: &[u8]) -> Result<(Self, usize), ()> { - let mut value = 0u32; - for (i, byte) in encoded.iter().enumerate() { - let last = byte & 0b1000_0000 == 0; - value |= ((byte & 0b0111_1111) as u32) << (i * 7); - if last { - return Ok((VarInt(value), i + 1)) - } - } - Err(()) - } + Ok(ReadProofContentIterator { + query_plan: Some(query_plan), + proof, + expected_root, + current, + state, + stack, + buf_op: None, + }) } -#[test] -fn varint_encode_decode() { - let mut buf = super::InMemoryRecorder::default(); - for i in 0..u16::MAX as u32 + 1 { - VarInt(i).encode_into(&mut buf); - assert_eq!(buf.buffer.len(), VarInt(i).encoded_len()); - assert_eq!(Ok((VarInt(i), buf.buffer.len())), VarInt::decode(&buf.buffer)); - buf.buffer.clear(); - } -} +impl<'a, L, C, P> Iterator for ReadProofContentIterator<'a, L, C, P> +where + L: TrieLayout, + C: Iterator>, + P: Iterator, Vec>>>, +{ + type Item = Result>, VerifyError, CError>>; -impl, V: AsRef<[u8]>> Op { - /// Calculate encoded len. - pub fn encoded_len(&self) -> usize { - let mut len = 1; - match self { - Op::KeyPush(key, _mask) => { - len += VarInt(key.len() as u32).encoded_len(); - len += key.len(); - len += 1; - }, - Op::KeyPop(nb) => { - len += VarInt(*nb as u32).encoded_len(); - }, - Op::HashChild(hash, _at) => { - len += hash.as_ref().len(); - len += 1; - }, - Op::HashValue(hash) => { - len += hash.as_ref().len(); - }, - Op::Value(value) => { - len += VarInt(value.as_ref().len() as u32).encoded_len(); - len += value.as_ref().len(); - }, - Op::EndProof => (), + fn next(&mut self) -> Option { + if self.state == ReadProofState::Finished { + return None } - len - } - - /// Write op. - pub fn encode_into(&self, out: &mut impl RecorderOutput) { - match self { - Op::KeyPush(key, mask) => { - out.write_bytes(&[0]); - VarInt(key.len() as u32).encode_into(out); - out.write_bytes(&key); - out.write_bytes(&[*mask]); - }, - Op::KeyPop(nb) => { - out.write_bytes(&[1]); - VarInt(*nb as u32).encode_into(out); - }, - Op::HashChild(hash, at) => { - out.write_bytes(&[2]); - out.write_bytes(hash.as_ref()); - out.write_bytes(&[*at]); - }, - Op::HashValue(hash) => { - out.write_bytes(&[3]); - out.write_bytes(hash.as_ref()); - }, - Op::Value(value) => { - out.write_bytes(&[4]); - let value = value.as_ref(); - VarInt(value.len() as u32).encode_into(out); - out.write_bytes(&value); - }, - Op::EndProof => { - out.write_bytes(&[5]); - }, + if self.state == ReadProofState::Halted { + self.state = ReadProofState::Running; } - } -} + // read proof + loop { + if self.state == ReadProofState::SwitchQueryPlan || + self.state == ReadProofState::NotStarted + { + let query_plan = self.query_plan.as_mut().expect("Removed with state"); + if let Some(next) = query_plan.items.next() { + let (ordered, common_nibbles) = if let Some(old) = self.current.as_ref() { + old.before(&next) + } else { + (true, 0) + }; + if !ordered { + if query_plan.ignore_unordered { + continue + } else { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::UnorderedKey(next.key.to_vec()))) + } + } -impl + AsMut<[u8]> + Default> Op> { - /// Read an op, return op and number byte read. Or error if invalid encoded. - pub fn decode(encoded: &[u8]) -> Result<(Self, usize), ()> { - let mut i = 0; - if i >= encoded.len() { - return Err(()) - } - Ok(match encoded[i] { - 0 => { - let (len, offset) = VarInt::decode(&encoded[i + 1..])?; - i += 1 + offset; - if i + len.0 as usize >= encoded.len() { - return Err(()) + let r = self.stack.pop_until(common_nibbles, false); + if let Err(e) = r { + self.state = ReadProofState::Finished; + return Some(Err(e)) + } + self.state = ReadProofState::Running; + self.current = Some(next); + } else { + self.state = ReadProofState::PlanConsumed; + self.current = None; } - let key = &encoded[i..i + len.0 as usize]; - let mask = encoded[i + len.0 as usize]; - (Op::KeyPush(key.to_vec(), mask), i + len.0 as usize + 1) - }, - 1 => { - let (len, offset) = VarInt::decode(&encoded[i + 1..])?; - if len.0 > u16::MAX as u32 { - return Err(()) + }; + + // op sequence check + let mut from_iter = false; + while let Some(op) = self.buf_op.take().map(Option::Some).or_else(|| { + from_iter = true; + self.proof.next() + }) { + println!("read: {:?}", op); + let Some(op) = op else { + let r = self.stack.stack_pop(None, &self.expected_root); + self.state = ReadProofState::Finished; + if let Err(e) = r { + self.state = ReadProofState::Finished; + return Some(Err(e)) + } + if let Some(c) = self.current.as_ref() { + if c.as_prefix { + // end prefix switch to next + self.state = ReadProofState::SwitchQueryPlan; + break; + } else { + // missing value + self.state = ReadProofState::SwitchQueryPlan; + return Some(Ok(ReadProofItem::NoValue(&c.key))); + } + } else { + return None; // finished + } + }; + if from_iter { + // check ordering logic + // TODO wrap in an error and put bools in a struct + match &op { + Op::KeyPush(..) => { + if self.stack.is_prev_push_key { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error + // TODO return + // Err(CompactDecoderError::ConsecutivePushKeys. + // into()) + } + self.stack.is_prev_push_key = true; + self.stack.is_prev_pop_key = false; + self.stack.is_prev_hash_child = None; + self.stack.first = false; + }, + Op::KeyPop(..) => { + if self.stack.is_prev_pop_key { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error + // return Err(CompactDecoderError::ConsecutivePopKeys. + // into()) + } + self.stack.is_prev_push_key = false; + self.stack.is_prev_pop_key = true; + self.stack.is_prev_hash_child = None; + self.stack.first = false; + }, + Op::HashChild(_, ix) => { + if let Some(prev_ix) = self.stack.is_prev_hash_child.as_ref() { + if prev_ix >= ix { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error + // return Err(CompactDecoderError::NotConsecutiveHash. + // into()) + } + } + // child ix on an existing content would be handle by iter_build. + self.stack.is_prev_push_key = false; + self.stack.is_prev_pop_key = false; + self.stack.is_prev_hash_child = Some(*ix); + }, + Op::Value(_) => { + // | Op::ValueForceInline(_) | Op::ValueForceHashed(_) => { + if !(self.stack.is_prev_push_key || self.stack.first) { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error + // return Err(CompactDecoderError::ValueNotAfterPush. + // into()) + } + self.stack.is_prev_push_key = false; + self.stack.is_prev_pop_key = false; + self.stack.is_prev_hash_child = None; + self.stack.first = false; + }, + _ => { + self.stack.is_prev_push_key = false; + self.stack.is_prev_pop_key = false; + self.stack.is_prev_hash_child = None; + self.stack.first = false; + }, + } + + // debug TODO make it log and external function + match &op { + Op::HashChild(hash, child_ix) => { + println!( + "ChildHash {:?}, {:?}, {:?}", + self.stack.prefix, child_ix, hash + ); + }, + Op::HashValue(hash) => { + println!("ValueHash {:?}, {:?}", self.stack.prefix, hash); + }, + Op::Value(value) => { + println!("Value {:?}, {:?}", self.stack.prefix, value); + }, + _ => (), + } } - (Op::KeyPop(len.0 as u16), i + 1 + offset) - }, - 2 => { - let mut hash = H::default(); - let end = i + 1 + hash.as_ref().len(); - if end >= encoded.len() { - return Err(()) + from_iter = false; + + // next + let item = if match &op { + Op::Value(..) | Op::HashValue(..) => true, + _ => false, + } { + let mut at_value = false; + let mut next_query = false; + if let Some(current) = self.current.as_ref() { + let query_slice = LeftNibbleSlice::new(¤t.key); + match self.stack.prefix.as_leftnibbleslice().cmp(&query_slice) { + Ordering::Equal => + if !self.stack.items.is_empty() { + at_value = true; + }, + Ordering::Less => (), + Ordering::Greater => + if current.as_prefix { + let query_slice = LeftNibbleSlice::new(¤t.key); + if self + .stack + .prefix + .as_leftnibbleslice() + .starts_with(&query_slice) + { + at_value = true; + } else { + next_query = true; + } + } else { + next_query = true; + }, + } + if next_query { + self.buf_op = Some(op); + self.state = ReadProofState::SwitchQueryPlan; + if current.as_prefix { + break + } else { + return Some(Ok(ReadProofItem::NoValue(¤t.key))) + } + } + } + + if at_value { + match &op { + Op::Value(value) => { + // TODO could get content from op with no clone. + Some(ReadProofItem::Value( + self.stack.prefix.inner().to_vec().into(), + value.clone(), + )) + }, + Op::HashValue(hash) => { + // TODO could get content from op with no clone. + Some(ReadProofItem::Hash( + self.stack.prefix.inner().to_vec().into(), + hash.clone(), + )) + }, + _ => unreachable!(), + } + } else { + match &op { + Op::Value(value) => { + // hash value here not value + if L::MAX_INLINE_VALUE + .map(|max| max as usize <= value.len()) + .unwrap_or(false) + { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousValue(value.clone()))) + } + }, + _ => (), + } + None + } + } else { + None + }; + + // act + let r = match op { + Op::KeyPush(partial, mask) => { + self.stack + .prefix + .append_slice(LeftNibbleSlice::new_with_mask(partial.as_slice(), mask)); + self.stack.stack_empty(self.stack.prefix.len()); + Ok(()) + }, + Op::KeyPop(nb_nibble) => { + let r = self.stack.stack_pop(Some(nb_nibble as usize), &self.expected_root); + self.stack.prefix.drop_lasts(nb_nibble.into()); + r + }, + Op::EndProof => break, + op => self.stack.set_cache_change(op.into()), + }; + if let Err(e) = r { + self.state = ReadProofState::Finished; + return Some(Err(e)) } - hash.as_mut().copy_from_slice(&encoded[i + 1..end]); - let mask = encoded[end]; - (Op::HashChild(hash, mask), end + 1) - }, - 3 => { - let mut hash = H::default(); - let end = i + 1 + hash.as_ref().len(); - if end >= encoded.len() { - return Err(()) + if let Some(r) = item { + if self.current.as_ref().map(|c| !c.as_prefix).unwrap_or(true) { + self.state = ReadProofState::SwitchQueryPlan; // TODO this is same as content NOne? + } + return Some(Ok(r)) } - hash.as_mut().copy_from_slice(&encoded[i + 1..end]); - (Op::HashValue(hash), end) - }, - 4 => { - let (len, offset) = VarInt::decode(&encoded[i + 1..])?; - i += 1 + offset; - if i + len.0 as usize > encoded.len() { - return Err(()) - } - let value = &encoded[i..i + len.0 as usize]; - (Op::Value(value.to_vec()), i + len.0 as usize) - }, - 5 => (Op::EndProof, 1), - _ => return Err(()), - }) - } -} - -/// Iterator on op from a in memory encoded proof. -pub struct IterOpProof + AsMut<[u8]> + Default, B: AsRef<[u8]>>( - B, - usize, - PhantomData, -); + } + if self.state != ReadProofState::SwitchQueryPlan && self.current.is_some() { + // TODO return halt instead + return Some(Err(VerifyError::IncompleteProof)) + } + self.state = ReadProofState::SwitchQueryPlan; + } -impl + AsMut<[u8]> + Default, B: AsRef<[u8]>> From for IterOpProof { - fn from(b: B) -> Self { - Self(b, 0, PhantomData) + /* + self.state = ReadProofState::Finished; + if self.proof.next().is_some() { + return Some(Err(VerifyError::ExtraneousNode)) + } else { + return None + } + */ } } -impl + AsMut<[u8]> + Default, B: AsRef<[u8]>> Iterator for IterOpProof { - type Item = Option>>; +/// When process is halted keep execution state +/// to restore later. +pub struct HaltedStateCheckContent<'a, L: TrieLayout, C> { + query_plan: QueryPlan<'a, C>, + current: Option>, + stack: ReadContentStack, + state: ReadProofState, +} - fn next(&mut self) -> Option { - match Op::decode(&self.0.as_ref()[self.1..]) { - Ok((op, len)) => { - self.1 += len; - Some(Some(op)) +impl<'a, L: TrieLayout, C> From> for HaltedStateCheckContent<'a, L, C> { + fn from(query_plan: QueryPlan<'a, C>) -> Self { + HaltedStateCheckContent { + stack: ReadContentStack { + items: Default::default(), + start_items: 0, + prefix: Default::default(), + expect_value: false, + iter_prefix: None, + is_prev_push_key: false, + is_prev_pop_key: false, + is_prev_hash_child: None, + first: true, + _ph: PhantomData, }, - Err(_) => Some(None), + state: ReadProofState::NotStarted, + current: None, + query_plan, } } } diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 65c1e3f6..885384a4 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -262,12 +262,13 @@ fn test_query_plan_content_internal() { } fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { - use trie_db::content_proof::IterOpProof; - use trie_db::query_plan::{ - record_query_plan, verify_query_plan_iter, - verify_query_plan_iter_content, HaltedStateCheck, HaltedStateCheckContent, - HaltedStateCheckNode, HaltedStateRecord, InMemQueryPlan, InMemQueryPlanItem, - InMemoryRecorder, QueryPlan, ReadProofItem, Recorder, + use trie_db::{ + content_proof::IterOpProof, + query_plan::{ + record_query_plan, verify_query_plan_iter, verify_query_plan_iter_content, + HaltedStateCheck, HaltedStateRecord, InMemQueryPlan, InMemQueryPlanItem, + InMemoryRecorder, QueryPlan, ReadProofItem, Recorder, + }, }; let set = test_entries(); From afa9425860977c329a3e86465eeef854a2c12926 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 16 Jun 2023 09:12:05 +0200 Subject: [PATCH 076/154] move test and fuzzing logic --- trie-db/fuzz/Cargo.toml | 2 + trie-db/fuzz/fuzz_targets/no_ext_insert.rs | 2 +- .../fuzz/fuzz_targets/no_ext_insert_rem.rs | 2 +- trie-db/fuzz/fuzz_targets/prefix_iter.rs | 2 +- trie-db/fuzz/fuzz_targets/prefix_seek_iter.rs | 2 +- trie-db/fuzz/fuzz_targets/seek_iter.rs | 2 +- trie-db/fuzz/fuzz_targets/trie_codec_proof.rs | 2 +- .../fuzz/fuzz_targets/trie_proof_invalid.rs | 2 +- trie-db/fuzz/fuzz_targets/trie_proof_valid.rs | 2 +- trie-db/fuzz/fuzz_targets/trie_root.rs | 2 +- .../fuzz/fuzz_targets/trie_root_fix_len.rs | 2 +- trie-db/fuzz/fuzz_targets/trie_root_new.rs | 2 +- trie-db/test/Cargo.toml | 6 +- trie-db/{fuzz/src/lib.rs => test/src/fuzz.rs} | 0 trie-db/test/src/lib.rs | 13 +- trie-db/test/src/proof.rs | 531 +---------------- trie-db/test/src/query_plan.rs | 543 ++++++++++++++++++ 17 files changed, 567 insertions(+), 550 deletions(-) rename trie-db/{fuzz/src/lib.rs => test/src/fuzz.rs} (100%) create mode 100644 trie-db/test/src/query_plan.rs diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml index 84a03257..ef8a4b41 100644 --- a/trie-db/fuzz/Cargo.toml +++ b/trie-db/fuzz/Cargo.toml @@ -17,6 +17,8 @@ array-bytes = "6.0.0" [dependencies.trie-db] path = ".." +[dependencies.trie-db-test] +path = "../test" [dependencies.libfuzzer-sys] version = "0.4.6" diff --git a/trie-db/fuzz/fuzz_targets/no_ext_insert.rs b/trie-db/fuzz/fuzz_targets/no_ext_insert.rs index ce741e8a..de43db0c 100644 --- a/trie-db/fuzz/fuzz_targets/no_ext_insert.rs +++ b/trie-db/fuzz/fuzz_targets/no_ext_insert.rs @@ -1,7 +1,7 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use trie_db_fuzz::fuzz_that_no_extension_insert; +use trie_db_test::fuzz::fuzz_that_no_extension_insert; fuzz_target!(|data: &[u8]| { // fuzzed code goes here diff --git a/trie-db/fuzz/fuzz_targets/no_ext_insert_rem.rs b/trie-db/fuzz/fuzz_targets/no_ext_insert_rem.rs index e6edb578..35d05f89 100644 --- a/trie-db/fuzz/fuzz_targets/no_ext_insert_rem.rs +++ b/trie-db/fuzz/fuzz_targets/no_ext_insert_rem.rs @@ -1,7 +1,7 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use trie_db_fuzz::fuzz_that_no_extension_insert_remove; +use trie_db_test::fuzz::fuzz_that_no_extension_insert_remove; fuzz_target!(|data: &[u8]| { // fuzzed code goes here diff --git a/trie-db/fuzz/fuzz_targets/prefix_iter.rs b/trie-db/fuzz/fuzz_targets/prefix_iter.rs index 85a4add0..351be348 100644 --- a/trie-db/fuzz/fuzz_targets/prefix_iter.rs +++ b/trie-db/fuzz/fuzz_targets/prefix_iter.rs @@ -1,7 +1,7 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use trie_db_fuzz::fuzz_prefix_iter; +use trie_db_test::fuzz::fuzz_prefix_iter; fuzz_target!(|data: &[u8]| { fuzz_prefix_iter::(data); diff --git a/trie-db/fuzz/fuzz_targets/prefix_seek_iter.rs b/trie-db/fuzz/fuzz_targets/prefix_seek_iter.rs index 23e1f734..9806bc1a 100644 --- a/trie-db/fuzz/fuzz_targets/prefix_seek_iter.rs +++ b/trie-db/fuzz/fuzz_targets/prefix_seek_iter.rs @@ -1,7 +1,7 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use trie_db_fuzz::{fuzz_prefix_seek_iter, PrefixSeekTestInput}; +use trie_db_test::fuzz::{fuzz_prefix_seek_iter, PrefixSeekTestInput}; fuzz_target!(|data: PrefixSeekTestInput| { fuzz_prefix_seek_iter::>(data); diff --git a/trie-db/fuzz/fuzz_targets/seek_iter.rs b/trie-db/fuzz/fuzz_targets/seek_iter.rs index a41b6d60..bcf36d76 100644 --- a/trie-db/fuzz/fuzz_targets/seek_iter.rs +++ b/trie-db/fuzz/fuzz_targets/seek_iter.rs @@ -1,7 +1,7 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use trie_db_fuzz::fuzz_seek_iter; +use trie_db_test::fuzz::fuzz_seek_iter; fuzz_target!(|data: &[u8]| { fuzz_seek_iter::(data); diff --git a/trie-db/fuzz/fuzz_targets/trie_codec_proof.rs b/trie-db/fuzz/fuzz_targets/trie_codec_proof.rs index ba7e92b6..6b593e48 100644 --- a/trie-db/fuzz/fuzz_targets/trie_codec_proof.rs +++ b/trie-db/fuzz/fuzz_targets/trie_codec_proof.rs @@ -1,7 +1,7 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use trie_db_fuzz::fuzz_that_trie_codec_proofs; +use trie_db_test::fuzz::fuzz_that_trie_codec_proofs; fuzz_target!(|data: &[u8]| { fuzz_that_trie_codec_proofs::(data); diff --git a/trie-db/fuzz/fuzz_targets/trie_proof_invalid.rs b/trie-db/fuzz/fuzz_targets/trie_proof_invalid.rs index 263f8573..aa0a5f24 100644 --- a/trie-db/fuzz/fuzz_targets/trie_proof_invalid.rs +++ b/trie-db/fuzz/fuzz_targets/trie_proof_invalid.rs @@ -1,7 +1,7 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use trie_db_fuzz::fuzz_that_verify_rejects_invalid_proofs; +use trie_db_test::fuzz::fuzz_that_verify_rejects_invalid_proofs; fuzz_target!(|data: &[u8]| { fuzz_that_verify_rejects_invalid_proofs::(data); diff --git a/trie-db/fuzz/fuzz_targets/trie_proof_valid.rs b/trie-db/fuzz/fuzz_targets/trie_proof_valid.rs index c28b1ae1..be9b3907 100644 --- a/trie-db/fuzz/fuzz_targets/trie_proof_valid.rs +++ b/trie-db/fuzz/fuzz_targets/trie_proof_valid.rs @@ -1,7 +1,7 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use trie_db_fuzz::fuzz_that_verify_accepts_valid_proofs; +use trie_db_test::fuzz::fuzz_that_verify_accepts_valid_proofs; fuzz_target!(|data: &[u8]| { fuzz_that_verify_accepts_valid_proofs::(data); diff --git a/trie-db/fuzz/fuzz_targets/trie_root.rs b/trie-db/fuzz/fuzz_targets/trie_root.rs index 49d5a0b0..b69fbc5a 100644 --- a/trie-db/fuzz/fuzz_targets/trie_root.rs +++ b/trie-db/fuzz/fuzz_targets/trie_root.rs @@ -1,7 +1,7 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use trie_db_fuzz::fuzz_that_reference_trie_root; +use trie_db_test::fuzz::fuzz_that_reference_trie_root; fuzz_target!(|data: &[u8]| { fuzz_that_reference_trie_root::(data); diff --git a/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs b/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs index 04591bae..68651c3f 100644 --- a/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs +++ b/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs @@ -1,7 +1,7 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use trie_db_fuzz::fuzz_that_reference_trie_root_fix_length; +use trie_db_test::fuzz::fuzz_that_reference_trie_root_fix_length; fuzz_target!(|data: &[u8]| { fuzz_that_reference_trie_root_fix_length::(data); diff --git a/trie-db/fuzz/fuzz_targets/trie_root_new.rs b/trie-db/fuzz/fuzz_targets/trie_root_new.rs index be597711..2e0e8615 100644 --- a/trie-db/fuzz/fuzz_targets/trie_root_new.rs +++ b/trie-db/fuzz/fuzz_targets/trie_root_new.rs @@ -1,7 +1,7 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use trie_db_fuzz::fuzz_that_compare_implementations; +use trie_db_test::fuzz::fuzz_that_compare_implementations; fuzz_target!(|data: &[u8]| { // fuzzed code goes here diff --git a/trie-db/test/Cargo.toml b/trie-db/test/Cargo.toml index 547df945..3308f26e 100644 --- a/trie-db/test/Cargo.toml +++ b/trie-db/test/Cargo.toml @@ -15,13 +15,15 @@ harness = false trie-db = { path = "..", version = "0.27.0"} hash-db = { path = "../../hash-db", version = "0.16.0"} memory-db = { path = "../../memory-db", version = "0.32.0" } -rand = { version = "0.8", default-features = false, features = ["small_rng"] } trie-standardmap = { path = "../../test-support/trie-standardmap", version = "0.16.0" } reference-trie = { path = "../../test-support/reference-trie", version = "0.29.0" } -hex-literal = "0.3" +arbitrary = { version = "1.3.0", features = ["derive"] } +array-bytes = "6.0.0" criterion = "0.4.0" env_logger = { version = "0.10", default-features = false } +hex-literal = "0.3" log = "0.4" +rand = { version = "0.8", default-features = false, features = ["small_rng"] } [dev-dependencies] array-bytes = "6.0.0" diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/test/src/fuzz.rs similarity index 100% rename from trie-db/fuzz/src/lib.rs rename to trie-db/test/src/fuzz.rs diff --git a/trie-db/test/src/lib.rs b/trie-db/test/src/lib.rs index 7802247e..e379db56 100644 --- a/trie-db/test/src/lib.rs +++ b/trie-db/test/src/lib.rs @@ -14,25 +14,16 @@ //! Tests for trie-db crate. -#[cfg(test)] mod fatdb; -#[cfg(test)] mod fatdbmut; -#[cfg(test)] +pub mod fuzz; mod iter_build; -#[cfg(test)] mod iterator; -#[cfg(test)] mod proof; -#[cfg(test)] +mod query_plan; mod recorder; -#[cfg(test)] mod sectriedb; -#[cfg(test)] mod sectriedbmut; -#[cfg(test)] mod trie_codec; -#[cfg(test)] mod triedb; -#[cfg(test)] mod triedbmut; diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index 885384a4..b78c3366 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -13,22 +13,22 @@ // limitations under the License. use hash_db::Hasher; -use reference_trie::{test_layouts, NoExtensionLayout, TestTrieCache}; +use reference_trie::{test_layouts, NoExtensionLayout}; -use std::collections::BTreeMap; use trie_db::{ proof::{generate_proof, verify_proof, VerifyError}, - query_plan::ProofKind, DBValue, Trie, TrieDBBuilder, TrieDBMutBuilder, TrieLayout, TrieMut, }; -type MemoryDB = memory_db::MemoryDB< +/// Testing memory db type. +pub type MemoryDB = memory_db::MemoryDB< ::Hash, memory_db::HashKey<::Hash>, DBValue, >; -fn test_entries() -> Vec<(&'static [u8], &'static [u8])> { +/// Set of entries for base testing. +pub fn test_entries() -> Vec<(&'static [u8], &'static [u8])> { vec![ // "alfa" is at a hash-referenced leaf node. (b"alfa", &[0; 32]), @@ -242,524 +242,3 @@ fn test_verify_decode_error_internal() { result => panic!("expected VerifyError::DecodeError, got {:?}", result), } } - -test_layouts!(test_query_plan_full, test_query_plan_full_internal); -fn test_query_plan_full_internal() { - test_query_plan_internal::(ProofKind::FullNodes, false); - test_query_plan_internal::(ProofKind::FullNodes, true); -} - -test_layouts!(test_query_plan_compact, test_query_plan_compact_internal); -fn test_query_plan_compact_internal() { - test_query_plan_internal::(ProofKind::CompactNodes, false); - test_query_plan_internal::(ProofKind::CompactNodes, true); -} - -test_layouts!(test_query_plan_content, test_query_plan_content_internal); -fn test_query_plan_content_internal() { - test_query_plan_internal::(ProofKind::CompactContent, false); - //test_query_plan_internal::(ProofKind::CompactNodes, true); -} - -fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { - use trie_db::{ - content_proof::IterOpProof, - query_plan::{ - record_query_plan, verify_query_plan_iter, verify_query_plan_iter_content, - HaltedStateCheck, HaltedStateRecord, InMemQueryPlan, InMemQueryPlanItem, - InMemoryRecorder, QueryPlan, ReadProofItem, Recorder, - }, - }; - let set = test_entries(); - - let mut cache = TestTrieCache::::default(); - - let (db, root) = { - let mut db = >::default(); - let mut root = Default::default(); - { - let mut trie = >::new(&mut db, &mut root).build(); - for (key, value) in set.iter() { - trie.insert(key, value).unwrap(); - } - } - (db, root) - }; - let db = >::new(&db, &root).with_cache(&mut cache).build(); - - if (kind == ProofKind::CompactContent || kind == ProofKind::CompactNodes) && L::USE_EXTENSION { - // Compact proofs are not supported with extensions. - // Requires changing the way extension are handled - // when decoding (putting on stack). - // Not implemented for `CompactContent`, would need - // to not append 0 after pushing an extension node. - return - } - let query_plans = [ - InMemQueryPlan { - items: vec![InMemQueryPlanItem::new(b"".to_vec(), hash_only, true)], - ignore_unordered: false, - kind, - }, - InMemQueryPlan { - items: vec![ - InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), - InMemQueryPlanItem::new(b"do".to_vec(), hash_only, true), - ], - ignore_unordered: false, - kind, - }, - InMemQueryPlan { - items: vec![ - InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), - InMemQueryPlanItem::new(b"doge".to_vec(), hash_only, false), - InMemQueryPlanItem::new(b"horsey".to_vec(), hash_only, false), - ], - ignore_unordered: false, - kind, - }, - ]; - for (nb_plan, query_plan) in query_plans.iter().enumerate() { - for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] { - let limit = limit_conf.0; - let limit = (limit != 0).then(|| limit); - let recorder = Recorder::new(kind, InMemoryRecorder::default(), limit, None); - let mut from = HaltedStateRecord::from_start(recorder); - // no limit - let mut proofs: Vec>> = Default::default(); - let mut query_plan_iter = query_plan.as_ref(); - loop { - record_query_plan::(&db, &mut query_plan_iter, &mut from).unwrap(); - - if limit.is_none() { - assert!(from.is_finished()); - } - if from.is_finished() { - if kind == ProofKind::CompactContent { - proofs.push(vec![from.finish().output().buffer]); - } else { - proofs.push(from.finish().output().nodes); - } - break - } - let rec = if limit_conf.1 { - query_plan_iter = query_plan.as_ref(); - from.stateless(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) - } else { - from.statefull(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) - }; - if kind == ProofKind::CompactContent { - proofs.push(vec![rec.output().buffer]); - break // TODO remove - } else { - proofs.push(rec.output().nodes); - } - } - - let mut full_proof: Vec> = Default::default(); - proofs.reverse(); - - fn shifted(bytes: &[u8], aligned: bool) -> Vec { - let mut shifted: Vec = vec![]; - let last = bytes.len(); - bytes.iter().enumerate().for_each(|(i, b)| { - shifted.last_mut().map(|l| { - *l |= *b >> 4; - }); - if !(i == last - 1 && aligned) { - shifted.push(*b << 4); - } - }); - shifted - } - - fn hash + Default>(b: &[u8]) -> H { - let mut hash = H::default(); - hash.as_mut().copy_from_slice(&b[..]); - hash - } - - if kind == ProofKind::CompactContent { - let all = match L::MAX_INLINE_VALUE { - Some(1) => true, - Some(33) => false, - _ => continue, - }; - let mut nb = 0; - let mut proofs = proofs.clone(); - while let Some(proof) = proofs.pop() { - use trie_db::content_proof::Op; - // full on iter all - // assert_eq!(proofs.len(), 1); - assert_eq!(proof.len(), 1); - - let refs: Vec, Vec>> = - match (limit.unwrap_or(0), nb_plan, nb) { - (0, 0, 0) => vec![ - Op::KeyPush(b"alfa".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"bravo", false), 0xf0), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - ], - (0, 1, 0) => - if all { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::HashChild( - hash( - &[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, - 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, - 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, - 248, - ][..], - ), - 1, - ), - Op::HashChild( - hash( - &[ - 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, - 14, 161, 91, 109, 136, 84, 6, 128, 190, 201, - 49, 142, 21, 154, 250, 246, 133, 0, 199, 138, - 49, - ][..], - ), - 8, - ), - ] - } else { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - // inline ix 8 - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - Op::KeyPop(9), - Op::HashChild( - hash( - &[ - 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, - 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, - 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, - 119, - ][..], - ), - 1, - ), - ] - }, - (0, 2, 0) => - // bravo, doge, horsey - if all { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - // hash value here is not really good (could only be - // with child hashes when no hash query). - Op::HashValue(hash( - &[ - 48, 51, 75, 77, 6, 75, 210, 124, 205, 63, 59, 165, - 81, 140, 222, 237, 196, 168, 203, 206, 105, 245, - 15, 154, 233, 147, 189, 123, 194, 243, 179, 137, - ][..], - )), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::HashValue(hash( - &[ - 104, 225, 103, 23, 160, 148, 143, 214, 98, 64, 250, - 245, 134, 99, 233, 36, 28, 150, 26, 205, 25, 165, - 122, 211, 170, 180, 45, 82, 143, 71, 191, 19, - ][..], - )), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::HashValue(hash( - &[ - 170, 195, 61, 227, 244, 86, 86, 205, 233, 84, 40, - 116, 166, 25, 158, 33, 18, 236, 208, 172, 115, 246, - 158, 34, 158, 170, 197, 139, 219, 254, 124, 136, - ][..], - )), - Op::KeyPop(5), - Op::HashChild( - hash( - &[ - 115, 96, 173, 184, 157, 30, 165, 173, 98, 91, - 45, 97, 173, 249, 2, 240, 133, 247, 131, 7, - 128, 195, 235, 114, 210, 152, 24, 22, 105, 232, - 147, 171, - ][..], - ), - 5, - ), - Op::KeyPop(4), - Op::HashChild( - hash( - &[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, - 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, - 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, - 248, - ][..], - ), - 1, - ), - ] - } else { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - Op::KeyPop(9), - Op::HashChild( - hash( - &[ - 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, - 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, - 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, - 119, - ][..], - ), - 1, - ), - ] - }, - (1, 2, 0) => - // bravo, doge, horsey - if all { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::HashChild( - hash( - &[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, - 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, - 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, - 248, - ][..], - ), - 1, - ), - Op::HashChild( - hash( - &[ - 223, 91, 16, 28, 134, 71, 144, 93, 127, 153, - 131, 180, 101, 103, 252, 121, 200, 66, 33, 188, - 58, 187, 247, 197, 65, 169, 112, 46, 241, 22, - 96, 196, - ][..], - ), - 4, - ), - Op::HashChild( - hash( - &[ - 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, - 14, 161, 91, 109, 136, 84, 6, 128, 190, 201, - 49, 142, 21, 154, 250, 246, 133, 0, 199, 138, - 49, - ][..], - ), - 8, - ), - ] - } else { - break - /* - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - Op::KeyPop(9), - Op::HashChild( - (&[ - 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, - 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, - 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, - 119, - ][..]) - .into(), - 1, - ), - ] - */ - }, - - _ => break, - }; - let mut encoded = InMemoryRecorder::default(); - for r in refs { - r.encode_into(&mut encoded); - } - assert_eq!(proof[0], encoded.buffer); - nb += 1; - } - // continue - } - - let mut query_plan_iter: QueryPlan<_> = query_plan.as_ref(); - let is_content_proof = kind == ProofKind::CompactContent; - let mut run_state: Option> = Some(if is_content_proof { - HaltedStateCheck::Content(query_plan_iter.into()) - } else { - HaltedStateCheck::Node(query_plan_iter.into()) - }); - let mut has_run_full = false; - while let Some(state) = run_state.take() { - let proof = if let Some(proof) = proofs.pop() { - full_proof.extend_from_slice(&proof); - proof - } else { - if full_proof.is_empty() { - break - } - proofs.clear(); - std::mem::take(&mut full_proof) - }; - let (mut verify_iter, mut verify_iter_content) = if is_content_proof { - let proof_iter: IterOpProof<_, _> = (&proof[0]).into(); - ( - None, - Some( - verify_query_plan_iter_content::>( - state, - proof_iter, - Some(root.clone()), - ) - .unwrap(), - ), - ) - } else { - ( - Some( - verify_query_plan_iter::( - state, - proof.into_iter(), - Some(root.clone()), - ) - .unwrap(), - ), - None, - ) - }; - let mut next_item = || { - if let Some(verify_iter) = verify_iter.as_mut() { - verify_iter.next() - } else if let Some(verify_iter_content) = verify_iter_content.as_mut() { - verify_iter_content.next() - } else { - None - } - }; - - let content: BTreeMap<_, _> = set.iter().cloned().collect(); - let mut in_prefix = false; - while let Some(item) = next_item() { - match item.unwrap() { - ReadProofItem::Hash(key, hash) => { - assert!(hash_only); - assert_eq!( - content.get(&*key).map(|v| L::Hash::hash(&v.as_ref())), - Some(hash) - ); - }, - ReadProofItem::Value(key, value) => { - assert_eq!(content.get(&*key), Some(&value.as_ref())); - }, - ReadProofItem::NoValue(key) => { - assert_eq!(content.get(key), None); - }, - ReadProofItem::StartPrefix(_prefix) => { - in_prefix = true; - }, - ReadProofItem::EndPrefix => { - assert!(in_prefix); - in_prefix = false; - }, - ReadProofItem::Halted(resume) => { - run_state = Some(*resume); - break - }, - } - } - if run_state.is_none() && !has_run_full { - has_run_full = true; - query_plan_iter = query_plan.as_ref(); - run_state = Some(if is_content_proof { - HaltedStateCheck::Content(query_plan_iter.into()) - } else { - HaltedStateCheck::Node(query_plan_iter.into()) - }); - } - } - if !has_run_full { - panic!("did not run full proof") - } - } - } -} diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs new file mode 100644 index 00000000..fd0a4eaf --- /dev/null +++ b/trie-db/test/src/query_plan.rs @@ -0,0 +1,543 @@ +// Copyright 2019, 2020 Parity Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Query plan tests. + +use crate::proof::{test_entries, MemoryDB}; +use hash_db::Hasher; +use reference_trie::{test_layouts, TestTrieCache}; + +use std::collections::BTreeMap; +use trie_db::{ + content_proof::IterOpProof, + query_plan::{ + record_query_plan, verify_query_plan_iter, verify_query_plan_iter_content, + HaltedStateCheck, HaltedStateRecord, InMemQueryPlan, InMemQueryPlanItem, InMemoryRecorder, + ProofKind, QueryPlan, ReadProofItem, Recorder, + }, + TrieDBBuilder, TrieDBMutBuilder, TrieLayout, TrieMut, +}; + +test_layouts!(test_query_plan_full, test_query_plan_full_internal); +fn test_query_plan_full_internal() { + test_query_plan_internal::(ProofKind::FullNodes, false); + test_query_plan_internal::(ProofKind::FullNodes, true); +} + +test_layouts!(test_query_plan_compact, test_query_plan_compact_internal); +fn test_query_plan_compact_internal() { + test_query_plan_internal::(ProofKind::CompactNodes, false); + test_query_plan_internal::(ProofKind::CompactNodes, true); +} + +test_layouts!(test_query_plan_content, test_query_plan_content_internal); +fn test_query_plan_content_internal() { + test_query_plan_internal::(ProofKind::CompactContent, false); + //test_query_plan_internal::(ProofKind::CompactNodes, true); +} + +fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { + let set = test_entries(); + + let mut cache = TestTrieCache::::default(); + + let (db, root) = { + let mut db = >::default(); + let mut root = Default::default(); + { + let mut trie = >::new(&mut db, &mut root).build(); + for (key, value) in set.iter() { + trie.insert(key, value).unwrap(); + } + } + (db, root) + }; + let db = >::new(&db, &root).with_cache(&mut cache).build(); + + if (kind == ProofKind::CompactContent || kind == ProofKind::CompactNodes) && L::USE_EXTENSION { + // Compact proofs are not supported with extensions. + // Requires changing the way extension are handled + // when decoding (putting on stack). + // Not implemented for `CompactContent`, would need + // to not append 0 after pushing an extension node. + return + } + let query_plans = [ + InMemQueryPlan { + items: vec![InMemQueryPlanItem::new(b"".to_vec(), hash_only, true)], + ignore_unordered: false, + kind, + }, + InMemQueryPlan { + items: vec![ + InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), + InMemQueryPlanItem::new(b"do".to_vec(), hash_only, true), + ], + ignore_unordered: false, + kind, + }, + InMemQueryPlan { + items: vec![ + InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), + InMemQueryPlanItem::new(b"doge".to_vec(), hash_only, false), + InMemQueryPlanItem::new(b"horsey".to_vec(), hash_only, false), + ], + ignore_unordered: false, + kind, + }, + ]; + for (nb_plan, query_plan) in query_plans.iter().enumerate() { + for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] { + let limit = limit_conf.0; + let limit = (limit != 0).then(|| limit); + let recorder = Recorder::new(kind, InMemoryRecorder::default(), limit, None); + let mut from = HaltedStateRecord::from_start(recorder); + // no limit + let mut proofs: Vec>> = Default::default(); + let mut query_plan_iter = query_plan.as_ref(); + loop { + record_query_plan::(&db, &mut query_plan_iter, &mut from).unwrap(); + + if limit.is_none() { + assert!(from.is_finished()); + } + if from.is_finished() { + if kind == ProofKind::CompactContent { + proofs.push(vec![from.finish().output().buffer]); + } else { + proofs.push(from.finish().output().nodes); + } + break + } + let rec = if limit_conf.1 { + query_plan_iter = query_plan.as_ref(); + from.stateless(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) + } else { + from.statefull(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) + }; + if kind == ProofKind::CompactContent { + proofs.push(vec![rec.output().buffer]); + break // TODO remove + } else { + proofs.push(rec.output().nodes); + } + } + + let mut full_proof: Vec> = Default::default(); + proofs.reverse(); + + fn shifted(bytes: &[u8], aligned: bool) -> Vec { + let mut shifted: Vec = vec![]; + let last = bytes.len(); + bytes.iter().enumerate().for_each(|(i, b)| { + shifted.last_mut().map(|l| { + *l |= *b >> 4; + }); + if !(i == last - 1 && aligned) { + shifted.push(*b << 4); + } + }); + shifted + } + + fn hash + Default>(b: &[u8]) -> H { + let mut hash = H::default(); + hash.as_mut().copy_from_slice(&b[..]); + hash + } + + if kind == ProofKind::CompactContent { + let all = match L::MAX_INLINE_VALUE { + Some(1) => true, + Some(33) => false, + _ => continue, + }; + let mut nb = 0; + let mut proofs = proofs.clone(); + while let Some(proof) = proofs.pop() { + use trie_db::content_proof::Op; + // full on iter all + // assert_eq!(proofs.len(), 1); + assert_eq!(proof.len(), 1); + + let refs: Vec, Vec>> = + match (limit.unwrap_or(0), nb_plan, nb) { + (0, 0, 0) => vec![ + Op::KeyPush(b"alfa".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"bravo", false), 0xf0), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + ], + (0, 1, 0) => + if all { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::HashChild( + hash( + &[ + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, + 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, + 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, + 248, + ][..], + ), + 1, + ), + Op::HashChild( + hash( + &[ + 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, + 14, 161, 91, 109, 136, 84, 6, 128, 190, 201, + 49, 142, 21, 154, 250, 246, 133, 0, 199, 138, + 49, + ][..], + ), + 8, + ), + ] + } else { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + // inline ix 8 + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + Op::KeyPop(9), + Op::HashChild( + hash( + &[ + 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, + 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, + 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, + 119, + ][..], + ), + 1, + ), + ] + }, + (0, 2, 0) => + // bravo, doge, horsey + if all { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + // hash value here is not really good (could only be + // with child hashes when no hash query). + Op::HashValue(hash( + &[ + 48, 51, 75, 77, 6, 75, 210, 124, 205, 63, 59, 165, + 81, 140, 222, 237, 196, 168, 203, 206, 105, 245, + 15, 154, 233, 147, 189, 123, 194, 243, 179, 137, + ][..], + )), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::HashValue(hash( + &[ + 104, 225, 103, 23, 160, 148, 143, 214, 98, 64, 250, + 245, 134, 99, 233, 36, 28, 150, 26, 205, 25, 165, + 122, 211, 170, 180, 45, 82, 143, 71, 191, 19, + ][..], + )), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::HashValue(hash( + &[ + 170, 195, 61, 227, 244, 86, 86, 205, 233, 84, 40, + 116, 166, 25, 158, 33, 18, 236, 208, 172, 115, 246, + 158, 34, 158, 170, 197, 139, 219, 254, 124, 136, + ][..], + )), + Op::KeyPop(5), + Op::HashChild( + hash( + &[ + 115, 96, 173, 184, 157, 30, 165, 173, 98, 91, + 45, 97, 173, 249, 2, 240, 133, 247, 131, 7, + 128, 195, 235, 114, 210, 152, 24, 22, 105, 232, + 147, 171, + ][..], + ), + 5, + ), + Op::KeyPop(4), + Op::HashChild( + hash( + &[ + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, + 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, + 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, + 248, + ][..], + ), + 1, + ), + ] + } else { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + Op::KeyPop(9), + Op::HashChild( + hash( + &[ + 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, + 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, + 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, + 119, + ][..], + ), + 1, + ), + ] + }, + (1, 2, 0) => + // bravo, doge, horsey + if all { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::HashChild( + hash( + &[ + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, + 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, + 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, + 248, + ][..], + ), + 1, + ), + Op::HashChild( + hash( + &[ + 223, 91, 16, 28, 134, 71, 144, 93, 127, 153, + 131, 180, 101, 103, 252, 121, 200, 66, 33, 188, + 58, 187, 247, 197, 65, 169, 112, 46, 241, 22, + 96, 196, + ][..], + ), + 4, + ), + Op::HashChild( + hash( + &[ + 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, + 14, 161, 91, 109, 136, 84, 6, 128, 190, 201, + 49, 142, 21, 154, 250, 246, 133, 0, 199, 138, + 49, + ][..], + ), + 8, + ), + ] + } else { + break + /* + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + Op::KeyPop(9), + Op::HashChild( + (&[ + 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, + 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, + 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, + 119, + ][..]) + .into(), + 1, + ), + ] + */ + }, + + _ => break, + }; + let mut encoded = InMemoryRecorder::default(); + for r in refs { + r.encode_into(&mut encoded); + } + assert_eq!(proof[0], encoded.buffer); + nb += 1; + } + // continue + } + + let mut query_plan_iter: QueryPlan<_> = query_plan.as_ref(); + let is_content_proof = kind == ProofKind::CompactContent; + let mut run_state: Option> = Some(if is_content_proof { + HaltedStateCheck::Content(query_plan_iter.into()) + } else { + HaltedStateCheck::Node(query_plan_iter.into()) + }); + let mut has_run_full = false; + while let Some(state) = run_state.take() { + let proof = if let Some(proof) = proofs.pop() { + full_proof.extend_from_slice(&proof); + proof + } else { + if full_proof.is_empty() { + break + } + proofs.clear(); + std::mem::take(&mut full_proof) + }; + let (mut verify_iter, mut verify_iter_content) = if is_content_proof { + let proof_iter: IterOpProof<_, _> = (&proof[0]).into(); + ( + None, + Some( + verify_query_plan_iter_content::>( + state, + proof_iter, + Some(root.clone()), + ) + .unwrap(), + ), + ) + } else { + ( + Some( + verify_query_plan_iter::( + state, + proof.into_iter(), + Some(root.clone()), + ) + .unwrap(), + ), + None, + ) + }; + let mut next_item = || { + if let Some(verify_iter) = verify_iter.as_mut() { + verify_iter.next() + } else if let Some(verify_iter_content) = verify_iter_content.as_mut() { + verify_iter_content.next() + } else { + None + } + }; + + let content: BTreeMap<_, _> = set.iter().cloned().collect(); + let mut in_prefix = false; + while let Some(item) = next_item() { + match item.unwrap() { + ReadProofItem::Hash(key, hash) => { + assert!(hash_only); + assert_eq!( + content.get(&*key).map(|v| L::Hash::hash(&v.as_ref())), + Some(hash) + ); + }, + ReadProofItem::Value(key, value) => { + assert_eq!(content.get(&*key), Some(&value.as_ref())); + }, + ReadProofItem::NoValue(key) => { + assert_eq!(content.get(key), None); + }, + ReadProofItem::StartPrefix(_prefix) => { + in_prefix = true; + }, + ReadProofItem::EndPrefix => { + assert!(in_prefix); + in_prefix = false; + }, + ReadProofItem::Halted(resume) => { + run_state = Some(*resume); + break + }, + } + } + if run_state.is_none() && !has_run_full { + has_run_full = true; + query_plan_iter = query_plan.as_ref(); + run_state = Some(if is_content_proof { + HaltedStateCheck::Content(query_plan_iter.into()) + } else { + HaltedStateCheck::Node(query_plan_iter.into()) + }); + } + } + if !has_run_full { + panic!("did not run full proof") + } + } + } +} From 0398bf13b9b8fa711bf955c19013de98acd5ca78 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 16 Jun 2023 11:33:14 +0200 Subject: [PATCH 077/154] fuzzing --- trie-db/src/query_plan/mod.rs | 5 +- trie-db/test/src/fuzz.rs | 366 ++++++++++++++++++++++++++++++++++ 2 files changed, 369 insertions(+), 2 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index 5d426cae..f16c7474 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -49,7 +49,7 @@ mod verify; mod verify_content; /// Item to query, in memory. -#[derive(Default)] +#[derive(Default, Clone, Debug)] pub struct InMemQueryPlanItem { key: Vec, hash_only: bool, @@ -106,6 +106,7 @@ impl<'a> QueryPlanItem<'a> { } /// Query plan in memory. +#[derive(Clone, Debug)] pub struct InMemQueryPlan { pub items: Vec, pub kind: ProofKind, @@ -149,7 +150,7 @@ pub struct QueryPlan<'a, I> { } /// Different proof support. -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum ProofKind { /// Proof is a sequence of fully encoded node, this is not /// size efficient but allows streaming a proof better, since diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index f9de8c07..51a61a15 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -471,3 +471,369 @@ fn test_trie_codec_proof(entries: Vec<(Vec, Vec)>, keys: assert_eq!(trie.get(key.as_slice()).unwrap(), expected_value); } } + +/// Query plan proof fuzzing. +pub mod query_plan { + use super::*; + use crate::proof::{test_entries, MemoryDB}; + use arbitrary::Arbitrary; + use rand::{rngs::SmallRng, RngCore, SeedableRng}; + use reference_trie::TestTrieCache; + use std::collections::{BTreeMap, BTreeSet}; + use trie_db::{ + content_proof::IterOpProof, + query_plan::{ + record_query_plan, verify_query_plan_iter, verify_query_plan_iter_content, + HaltedStateCheck, HaltedStateRecord, InMemQueryPlan, InMemQueryPlanItem, + InMemoryRecorder, ProofKind, QueryPlan, ReadProofItem, Recorder, + }, + TrieHash, TrieLayout, + }; + + const KEY_SIZES: [usize; 7] = [1, 2, 3, 4, 5, 29, 300]; + + // deterministic generator. + type Rng = SmallRng; + + /// Config for fuzzing. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct Conf { + /// Seed. + pub seed: u64, + /// proof kind. + pub kind: ProofKind, + /// number of items in state. + pub nb_key_value: usize, + /// number of different small value. + pub nb_small_value_set: usize, + /// number of different small value. + pub nb_big_value_set: usize, + /// Test querying hash only + pub hash_only: bool, + /// Limit the number of non inline value per proof + /// TODO could be arbitrary. + pub limit: usize, + /// Do we create proof on same memory. + pub proof_spawn_with_persistence: bool, + /* + /// number of query existing. + pub nb_existing_value_query: usize, + /// number of query existing. + pub nb_missing_value_query: usize, + /// prefix query (can reduce `nb_existing_value_query`). + pub nb_prefix_query: usize, + */ + } + + #[derive(Clone)] + pub struct FuzzContext { + pub reference: BTreeMap, Vec>, + pub db: MemoryDB, + pub root: TrieHash, + pub conf: Conf, + pub small_values: BTreeSet>, + pub big_values: BTreeSet>, + pub values: Vec>, + } + + fn bytes_set( + rng: &mut Rng, + nb: usize, + sizes: &[usize], + max_byte_value: Option, + ) -> BTreeSet> { + if let Some(max_byte_value) = max_byte_value { + let max_nb_value = sizes.len() * max_byte_value; + if nb > (max_nb_value / 2) { + panic!("too many value {}, max is {}", nb, max_nb_value / 2); + } + } + let mut set = BTreeSet::new(); + let mut buff = vec![0u8; nb * 2]; + while set.len() < nb { + rng.fill_bytes(&mut buff); + for i in 0..buff.len() / 2 { + let size = buff[i * 2] as usize % sizes.len(); + let value = if let Some(max_byte_value) = max_byte_value { + let byte = buff[(i * 2) + 1] % max_byte_value as u8; + vec![byte; sizes[size]] + } else { + let mut value = vec![0u8; sizes[size]]; + rng.fill_bytes(&mut value); + value + }; + set.insert(value); + } + } + set + } + + fn small_value_set(rng: &mut Rng, nb: usize) -> BTreeSet> { + let sizes = [1, 2, 30, 31, 32]; + let max_byte_value = 4; // avoid to many different values. + bytes_set(rng, nb, &sizes, Some(max_byte_value)) + } + + fn big_value_set(rng: &mut Rng, nb: usize) -> BTreeSet> { + let sizes = [33, 34, 301, 302]; + let max_byte_value = 4; // avoid to many different values. + bytes_set(rng, nb, &sizes, Some(max_byte_value)) + } + + fn key_set(rng: &mut Rng, nb: usize) -> BTreeSet> { + bytes_set(rng, nb, &KEY_SIZES[..], None) + } + + fn build_state(conf: Conf) -> FuzzContext { + let mut rng = Rng::seed_from_u64(conf.seed); + let mut reference = BTreeMap::, Vec>::new(); + let small_values = small_value_set(&mut rng, conf.nb_small_value_set); + let big_values = big_value_set(&mut rng, conf.nb_big_value_set); + let mut values: Vec> = small_values.iter().cloned().collect(); + values.extend(big_values.iter().cloned()); + let values = values; + let keys = key_set(&mut rng, conf.nb_key_value); + for k in keys.into_iter() { + let value_index = rng.next_u32() as usize % values.len(); + reference.insert(k, values[value_index].clone()); + } + + // add the test entries + for (key, value) in test_entries() { + reference.insert(key.to_vec(), value.to_vec()).unwrap(); + } + + let mut cache = TestTrieCache::::default(); + + let (db, root) = { + let mut db = >::default(); + let mut root = Default::default(); + { + let mut trie = >::new(&mut db, &mut root).build(); + for (key, value) in reference.iter() { + trie.insert(key, value).unwrap(); + } + } + (db, root) + }; + FuzzContext { reference, db, root, conf, small_values, big_values, values } + } + + #[derive(Arbitrary)] + enum ArbitraryKey { + Indexed(usize), + Random(Vec), + } + + /// Base arbitrary for fuzzing. + #[derive(Arbitrary)] + pub struct ArbitraryQueryPlan(Vec<(bool, ArbitraryKey)>); + + fn arbitrary_query_plan( + context: &mut FuzzContext, + plan: ArbitraryQueryPlan, + ) -> InMemQueryPlan { + let conf = &context.conf; + let mut set = BTreeSet::new(); + for (prefix, k) in plan.0.iter() { + // TODO Rc to avoid clone + match k { + ArbitraryKey::Indexed(at) => { + set.insert((context.values[at % context.values.len()].clone(), !prefix)); + }, + ArbitraryKey::Random(k) => { + set.insert((k.clone(), !prefix)); + }, + } + } + let mut prev_pref: Option> = None; + let mut query_plan = InMemQueryPlan { + items: Vec::with_capacity(set.len()), + kind: conf.kind, + ignore_unordered: false, + }; + for (key, not_prefix) in set.into_iter() { + if let Some(pref) = prev_pref.as_ref() { + if key.starts_with(pref) { + continue + } + prev_pref = None; + } + + if !not_prefix { + prev_pref = Some(key.clone()); + } + + query_plan.items.push(InMemQueryPlanItem::new(key, conf.hash_only, !not_prefix)); + } + query_plan + } + + /// Main entry point for query plan fuzzing. + pub fn fuzz_query_plan(mut context: FuzzContext, plan: ArbitraryQueryPlan) { + let query_plan = arbitrary_query_plan(&mut context, plan); + + let kind = context.conf.kind; + let limit = context.conf.limit; + let limit = (limit != 0).then(|| limit); + let recorder = Recorder::new(context.conf.kind, InMemoryRecorder::default(), limit, None); + let mut from = HaltedStateRecord::from_start(recorder); + let mut proofs: Vec>> = Default::default(); + let mut query_plan_iter = query_plan.as_ref(); + let mut cache = TestTrieCache::::default(); + let db = >::new(&context.db, &context.root) + .with_cache(&mut cache) + .build(); + loop { + record_query_plan::(&db, &mut query_plan_iter, &mut from).unwrap(); + + if limit.is_none() { + assert!(from.is_finished()); + } + if from.is_finished() { + if kind == ProofKind::CompactContent { + proofs.push(vec![from.finish().output().buffer]); + } else { + proofs.push(from.finish().output().nodes); + } + break + } + let rec = if context.conf.proof_spawn_with_persistence { + from.statefull(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) + } else { + query_plan_iter = query_plan.as_ref(); + from.stateless(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) + }; + if kind == ProofKind::CompactContent { + proofs.push(vec![rec.output().buffer]); + } else { + proofs.push(rec.output().nodes); + } + } + + check_proofs::( + proofs, + query_plan, + context.conf.kind, + context.root, + &context.reference, + context.conf.hash_only, + ); + } + + pub fn check_proofs( + mut proofs: Vec>>, + query_plan_in_mem: InMemQueryPlan, + kind: ProofKind, + root: TrieHash, + content: &BTreeMap, Vec>, + hash_only: bool, + ) { + let mut full_proof: Vec> = Default::default(); + proofs.reverse(); + + let is_content_proof = kind == ProofKind::CompactContent; + let query_plan: QueryPlan<_> = query_plan_in_mem.as_ref(); + let mut run_state: Option> = Some(if is_content_proof { + HaltedStateCheck::Content(query_plan.into()) + } else { + HaltedStateCheck::Node(query_plan.into()) + }); + let mut has_run_full = false; + while let Some(state) = run_state.take() { + let proof = if let Some(proof) = proofs.pop() { + full_proof.extend_from_slice(&proof); + proof + } else { + if full_proof.is_empty() { + break + } + proofs.clear(); + std::mem::take(&mut full_proof) + }; + let (mut verify_iter, mut verify_iter_content) = if is_content_proof { + let proof_iter: IterOpProof<_, _> = (&proof[0]).into(); + ( + None, + Some( + verify_query_plan_iter_content::>( + state, + proof_iter, + Some(root.clone()), + ) + .unwrap(), + ), + ) + } else { + ( + Some( + verify_query_plan_iter::( + state, + proof.into_iter(), + Some(root.clone()), + ) + .unwrap(), + ), + None, + ) + }; + let mut next_item = || { + if let Some(verify_iter) = verify_iter.as_mut() { + verify_iter.next() + } else if let Some(verify_iter_content) = verify_iter_content.as_mut() { + verify_iter_content.next() + } else { + None + } + }; + + let mut in_prefix = false; + // TODO need stricter check of query plan (advance query plan iter and expect all items + // touched!!! + while let Some(item) = next_item() { + match item.unwrap() { + ReadProofItem::Hash(key, hash) => { + assert!(hash_only); + assert_eq!( + content.get(&*key).map(|v| L::Hash::hash(&v.as_ref())), + Some(hash) + ); + }, + ReadProofItem::Value(key, value) => { + assert_eq!(content.get(&*key), Some(value.as_ref())); + }, + ReadProofItem::NoValue(key) => { + assert_eq!(content.get(key), None); + }, + ReadProofItem::StartPrefix(_prefix) => { + in_prefix = true; + }, + ReadProofItem::EndPrefix => { + assert!(in_prefix); + in_prefix = false; + }, + ReadProofItem::Halted(resume) => { + run_state = Some(*resume); + break + }, + } + } + if kind == ProofKind::FullNodes { + if run_state.is_none() && !has_run_full { + has_run_full = true; + let query_plan_iter = query_plan_in_mem.as_ref(); + run_state = Some(if is_content_proof { + HaltedStateCheck::Content(query_plan_iter.into()) + } else { + HaltedStateCheck::Node(query_plan_iter.into()) + }); + } + } else { + has_run_full = true; + } + } + if !has_run_full { + panic!("did not run full proof") + } + } +} From fa6f27c93e4828893be3cdc5029c47ef601780b0 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 16 Jun 2023 11:50:28 +0200 Subject: [PATCH 078/154] use check proof in test --- trie-db/test/src/fuzz.rs | 131 +----- trie-db/test/src/query_plan.rs | 775 +++++++++++++++++---------------- 2 files changed, 401 insertions(+), 505 deletions(-) diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index 51a61a15..bd67d053 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -481,11 +481,9 @@ pub mod query_plan { use reference_trie::TestTrieCache; use std::collections::{BTreeMap, BTreeSet}; use trie_db::{ - content_proof::IterOpProof, query_plan::{ - record_query_plan, verify_query_plan_iter, verify_query_plan_iter_content, - HaltedStateCheck, HaltedStateRecord, InMemQueryPlan, InMemQueryPlanItem, - InMemoryRecorder, ProofKind, QueryPlan, ReadProofItem, Recorder, + record_query_plan, HaltedStateRecord, InMemQueryPlan, InMemQueryPlanItem, + InMemoryRecorder, ProofKind, Recorder, }, TrieHash, TrieLayout, }; @@ -584,7 +582,8 @@ pub mod query_plan { bytes_set(rng, nb, &KEY_SIZES[..], None) } - fn build_state(conf: Conf) -> FuzzContext { + /// State building (out of fuzzing loop). + pub fn build_state(conf: Conf) -> FuzzContext { let mut rng = Rng::seed_from_u64(conf.seed); let mut reference = BTreeMap::, Vec>::new(); let small_values = small_value_set(&mut rng, conf.nb_small_value_set); @@ -603,8 +602,6 @@ pub mod query_plan { reference.insert(key.to_vec(), value.to_vec()).unwrap(); } - let mut cache = TestTrieCache::::default(); - let (db, root) = { let mut db = >::default(); let mut root = Default::default(); @@ -711,129 +708,13 @@ pub mod query_plan { } } - check_proofs::( + crate::query_plan::check_proofs::( proofs, - query_plan, + &query_plan, context.conf.kind, context.root, &context.reference, context.conf.hash_only, ); } - - pub fn check_proofs( - mut proofs: Vec>>, - query_plan_in_mem: InMemQueryPlan, - kind: ProofKind, - root: TrieHash, - content: &BTreeMap, Vec>, - hash_only: bool, - ) { - let mut full_proof: Vec> = Default::default(); - proofs.reverse(); - - let is_content_proof = kind == ProofKind::CompactContent; - let query_plan: QueryPlan<_> = query_plan_in_mem.as_ref(); - let mut run_state: Option> = Some(if is_content_proof { - HaltedStateCheck::Content(query_plan.into()) - } else { - HaltedStateCheck::Node(query_plan.into()) - }); - let mut has_run_full = false; - while let Some(state) = run_state.take() { - let proof = if let Some(proof) = proofs.pop() { - full_proof.extend_from_slice(&proof); - proof - } else { - if full_proof.is_empty() { - break - } - proofs.clear(); - std::mem::take(&mut full_proof) - }; - let (mut verify_iter, mut verify_iter_content) = if is_content_proof { - let proof_iter: IterOpProof<_, _> = (&proof[0]).into(); - ( - None, - Some( - verify_query_plan_iter_content::>( - state, - proof_iter, - Some(root.clone()), - ) - .unwrap(), - ), - ) - } else { - ( - Some( - verify_query_plan_iter::( - state, - proof.into_iter(), - Some(root.clone()), - ) - .unwrap(), - ), - None, - ) - }; - let mut next_item = || { - if let Some(verify_iter) = verify_iter.as_mut() { - verify_iter.next() - } else if let Some(verify_iter_content) = verify_iter_content.as_mut() { - verify_iter_content.next() - } else { - None - } - }; - - let mut in_prefix = false; - // TODO need stricter check of query plan (advance query plan iter and expect all items - // touched!!! - while let Some(item) = next_item() { - match item.unwrap() { - ReadProofItem::Hash(key, hash) => { - assert!(hash_only); - assert_eq!( - content.get(&*key).map(|v| L::Hash::hash(&v.as_ref())), - Some(hash) - ); - }, - ReadProofItem::Value(key, value) => { - assert_eq!(content.get(&*key), Some(value.as_ref())); - }, - ReadProofItem::NoValue(key) => { - assert_eq!(content.get(key), None); - }, - ReadProofItem::StartPrefix(_prefix) => { - in_prefix = true; - }, - ReadProofItem::EndPrefix => { - assert!(in_prefix); - in_prefix = false; - }, - ReadProofItem::Halted(resume) => { - run_state = Some(*resume); - break - }, - } - } - if kind == ProofKind::FullNodes { - if run_state.is_none() && !has_run_full { - has_run_full = true; - let query_plan_iter = query_plan_in_mem.as_ref(); - run_state = Some(if is_content_proof { - HaltedStateCheck::Content(query_plan_iter.into()) - } else { - HaltedStateCheck::Node(query_plan_iter.into()) - }); - } - } else { - has_run_full = true; - } - } - if !has_run_full { - panic!("did not run full proof") - } - } } diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index fd0a4eaf..e15a5b19 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -26,7 +26,7 @@ use trie_db::{ HaltedStateCheck, HaltedStateRecord, InMemQueryPlan, InMemQueryPlanItem, InMemoryRecorder, ProofKind, QueryPlan, ReadProofItem, Recorder, }, - TrieDBBuilder, TrieDBMutBuilder, TrieLayout, TrieMut, + TrieDBBuilder, TrieDBMutBuilder, TrieHash, TrieLayout, TrieMut, }; test_layouts!(test_query_plan_full, test_query_plan_full_internal); @@ -97,7 +97,7 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { kind, }, ]; - for (nb_plan, query_plan) in query_plans.iter().enumerate() { + for (_nb_plan, query_plan) in query_plans.iter().enumerate() { for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] { let limit = limit_conf.0; let limit = (limit != 0).then(|| limit); @@ -133,411 +133,426 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { proofs.push(rec.output().nodes); } } + let content: BTreeMap<_, _> = + set.iter().map(|(k, v)| (k.to_vec(), v.to_vec())).collect(); + check_proofs::(proofs, query_plan, kind, root, &content, hash_only); - let mut full_proof: Vec> = Default::default(); - proofs.reverse(); - - fn shifted(bytes: &[u8], aligned: bool) -> Vec { - let mut shifted: Vec = vec![]; - let last = bytes.len(); - bytes.iter().enumerate().for_each(|(i, b)| { - shifted.last_mut().map(|l| { - *l |= *b >> 4; - }); - if !(i == last - 1 && aligned) { - shifted.push(*b << 4); + /* TODO this static check keep it somehow ?? + if kind == ProofKind::CompactContent { + fn shifted(bytes: &[u8], aligned: bool) -> Vec { + let mut shifted: Vec = vec![]; + let last = bytes.len(); + bytes.iter().enumerate().for_each(|(i, b)| { + shifted.last_mut().map(|l| { + *l |= *b >> 4; + }); + if !(i == last - 1 && aligned) { + shifted.push(*b << 4); + } + }); + shifted } - }); - shifted - } - fn hash + Default>(b: &[u8]) -> H { - let mut hash = H::default(); - hash.as_mut().copy_from_slice(&b[..]); - hash - } + fn hash + Default>(b: &[u8]) -> H { + let mut hash = H::default(); + hash.as_mut().copy_from_slice(&b[..]); + hash + } - if kind == ProofKind::CompactContent { - let all = match L::MAX_INLINE_VALUE { - Some(1) => true, - Some(33) => false, - _ => continue, - }; - let mut nb = 0; - let mut proofs = proofs.clone(); - while let Some(proof) = proofs.pop() { - use trie_db::content_proof::Op; - // full on iter all - // assert_eq!(proofs.len(), 1); - assert_eq!(proof.len(), 1); + let all = match L::MAX_INLINE_VALUE { + Some(1) => true, + Some(33) => false, + _ => continue, + }; + let mut nb = 0; + let mut proofs = proofs.clone(); + while let Some(proof) = proofs.pop() { + use trie_db::content_proof::Op; + // full on iter all + // assert_eq!(proofs.len(), 1); + assert_eq!(proof.len(), 1); - let refs: Vec, Vec>> = - match (limit.unwrap_or(0), nb_plan, nb) { - (0, 0, 0) => vec![ - Op::KeyPush(b"alfa".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"bravo", false), 0xf0), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - ], - (0, 1, 0) => - if all { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::HashChild( - hash( - &[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, - 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, - 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, - 248, - ][..], + let refs: Vec, Vec>> = + match (limit.unwrap_or(0), nb_plan, nb) { + (0, 0, 0) => vec![ + Op::KeyPush(b"alfa".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"bravo", false), 0xf0), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + ], + (0, 1, 0) => + if all { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::HashChild( + hash( + &[ + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, + 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, + 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, + 248, + ][..], + ), + 1, ), - 1, - ), - Op::HashChild( - hash( - &[ - 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, - 14, 161, 91, 109, 136, 84, 6, 128, 190, 201, - 49, 142, 21, 154, 250, 246, 133, 0, 199, 138, - 49, - ][..], + Op::HashChild( + hash( + &[ + 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, + 14, 161, 91, 109, 136, 84, 6, 128, 190, 201, + 49, 142, 21, 154, 250, 246, 133, 0, 199, 138, + 49, + ][..], + ), + 8, + ), + ] + } else { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + // inline ix 8 + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + Op::KeyPop(9), + Op::HashChild( + hash( + &[ + 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, + 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, + 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, + 119, + ][..], + ), + 1, ), - 8, - ), - ] - } else { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - // inline ix 8 - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - Op::KeyPop(9), - Op::HashChild( - hash( + ] + }, + (0, 2, 0) => + // bravo, doge, horsey + if all { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + // hash value here is not really good (could only be + // with child hashes when no hash query). + Op::HashValue(hash( &[ - 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, - 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, - 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, - 119, + 48, 51, 75, 77, 6, 75, 210, 124, 205, 63, 59, 165, + 81, 140, 222, 237, 196, 168, 203, 206, 105, 245, + 15, 154, 233, 147, 189, 123, 194, 243, 179, 137, ][..], - ), - 1, - ), - ] - }, - (0, 2, 0) => - // bravo, doge, horsey - if all { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - // hash value here is not really good (could only be - // with child hashes when no hash query). - Op::HashValue(hash( - &[ - 48, 51, 75, 77, 6, 75, 210, 124, 205, 63, 59, 165, - 81, 140, 222, 237, 196, 168, 203, 206, 105, 245, - 15, 154, 233, 147, 189, 123, 194, 243, 179, 137, - ][..], - )), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::HashValue(hash( - &[ - 104, 225, 103, 23, 160, 148, 143, 214, 98, 64, 250, - 245, 134, 99, 233, 36, 28, 150, 26, 205, 25, 165, - 122, 211, 170, 180, 45, 82, 143, 71, 191, 19, - ][..], - )), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::HashValue(hash( - &[ - 170, 195, 61, 227, 244, 86, 86, 205, 233, 84, 40, - 116, 166, 25, 158, 33, 18, 236, 208, 172, 115, 246, - 158, 34, 158, 170, 197, 139, 219, 254, 124, 136, - ][..], - )), - Op::KeyPop(5), - Op::HashChild( - hash( + )), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::HashValue(hash( &[ - 115, 96, 173, 184, 157, 30, 165, 173, 98, 91, - 45, 97, 173, 249, 2, 240, 133, 247, 131, 7, - 128, 195, 235, 114, 210, 152, 24, 22, 105, 232, - 147, 171, + 104, 225, 103, 23, 160, 148, 143, 214, 98, 64, 250, + 245, 134, 99, 233, 36, 28, 150, 26, 205, 25, 165, + 122, 211, 170, 180, 45, 82, 143, 71, 191, 19, ][..], - ), - 5, - ), - Op::KeyPop(4), - Op::HashChild( - hash( + )), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::HashValue(hash( &[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, - 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, - 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, - 248, + 170, 195, 61, 227, 244, 86, 86, 205, 233, 84, 40, + 116, 166, 25, 158, 33, 18, 236, 208, 172, 115, 246, + 158, 34, 158, 170, 197, 139, 219, 254, 124, 136, ][..], + )), + Op::KeyPop(5), + Op::HashChild( + hash( + &[ + 115, 96, 173, 184, 157, 30, 165, 173, 98, 91, + 45, 97, 173, 249, 2, 240, 133, 247, 131, 7, + 128, 195, 235, 114, 210, 152, 24, 22, 105, 232, + 147, 171, + ][..], + ), + 5, ), - 1, - ), - ] - } else { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - Op::KeyPop(9), - Op::HashChild( - hash( - &[ + Op::KeyPop(4), + Op::HashChild( + hash( + &[ + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, + 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, + 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, + 248, + ][..], + ), + 1, + ), + ] + } else { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + Op::KeyPop(9), + Op::HashChild( + hash( + &[ + 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, + 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, + 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, + 119, + ][..], + ), + 1, + ), + ] + }, + (1, 2, 0) => + // bravo, doge, horsey + if all { + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::HashChild( + hash( + &[ + 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, + 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, + 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, + 248, + ][..], + ), + 1, + ), + Op::HashChild( + hash( + &[ + 223, 91, 16, 28, 134, 71, 144, 93, 127, 153, + 131, 180, 101, 103, 252, 121, 200, 66, 33, 188, + 58, 187, 247, 197, 65, 169, 112, 46, 241, 22, + 96, 196, + ][..], + ), + 4, + ), + Op::HashChild( + hash( + &[ + 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, + 14, 161, 91, 109, 136, 84, 6, 128, 190, 201, + 49, 142, 21, 154, 250, 246, 133, 0, 199, 138, + 49, + ][..], + ), + 8, + ), + ] + } else { + break + /* + vec![ + Op::KeyPush(b"bravo".to_vec(), 0xff), + Op::Value(b"bravo".to_vec()), + Op::KeyPop(9), + Op::KeyPush(shifted(b"do", false), 0xf0), + Op::Value(b"verb".to_vec()), + Op::KeyPush(b"g".to_vec(), 0xff), + Op::Value(b"puppy".to_vec()), + Op::KeyPush(b"e".to_vec(), 0xff), + Op::Value([0; 32].to_vec()), + Op::KeyPop(7), + Op::KeyPush(shifted(b"horse", false), 0xf0), + Op::Value(b"stallion".to_vec()), + Op::KeyPop(5), + Op::KeyPush(shifted(b"use", false), 0xf0), + Op::Value(b"building".to_vec()), + Op::KeyPop(9), + Op::HashChild( + (&[ 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, 119, - ][..], - ), - 1, - ), - ] - }, - (1, 2, 0) => - // bravo, doge, horsey - if all { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::HashChild( - hash( - &[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, - 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, - 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, - 248, - ][..], - ), - 1, - ), - Op::HashChild( - hash( - &[ - 223, 91, 16, 28, 134, 71, 144, 93, 127, 153, - 131, 180, 101, 103, 252, 121, 200, 66, 33, 188, - 58, 187, 247, 197, 65, 169, 112, 46, 241, 22, - 96, 196, - ][..], + ][..]) + .into(), + 1, ), - 4, - ), - Op::HashChild( - hash( - &[ - 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, - 14, 161, 91, 109, 136, 84, 6, 128, 190, 201, - 49, 142, 21, 154, 250, 246, 133, 0, 199, 138, - 49, - ][..], - ), - 8, - ), - ] - } else { - break - /* - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - Op::KeyPop(9), - Op::HashChild( - (&[ - 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, - 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, - 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, - 119, - ][..]) - .into(), - 1, - ), - ] - */ - }, + ] + */ + }, - _ => break, - }; - let mut encoded = InMemoryRecorder::default(); - for r in refs { - r.encode_into(&mut encoded); + _ => break, + }; + let mut encoded = InMemoryRecorder::default(); + for r in refs { + r.encode_into(&mut encoded); + } + assert_eq!(proof[0], encoded.buffer); + nb += 1; } - assert_eq!(proof[0], encoded.buffer); - nb += 1; - } - // continue - } + // continue + */ + } + } +} +/// Proof checking. +pub fn check_proofs( + mut proofs: Vec>>, + query_plan_in_mem: &InMemQueryPlan, + kind: ProofKind, + root: TrieHash, + content: &BTreeMap, Vec>, + hash_only: bool, +) { + let mut full_proof: Vec> = Default::default(); + proofs.reverse(); - let mut query_plan_iter: QueryPlan<_> = query_plan.as_ref(); - let is_content_proof = kind == ProofKind::CompactContent; - let mut run_state: Option> = Some(if is_content_proof { - HaltedStateCheck::Content(query_plan_iter.into()) - } else { - HaltedStateCheck::Node(query_plan_iter.into()) - }); - let mut has_run_full = false; - while let Some(state) = run_state.take() { - let proof = if let Some(proof) = proofs.pop() { - full_proof.extend_from_slice(&proof); - proof - } else { - if full_proof.is_empty() { - break - } - proofs.clear(); - std::mem::take(&mut full_proof) - }; - let (mut verify_iter, mut verify_iter_content) = if is_content_proof { - let proof_iter: IterOpProof<_, _> = (&proof[0]).into(); - ( - None, - Some( - verify_query_plan_iter_content::>( - state, - proof_iter, - Some(root.clone()), - ) - .unwrap(), - ), + let is_content_proof = kind == ProofKind::CompactContent; + let query_plan: QueryPlan<_> = query_plan_in_mem.as_ref(); + let mut run_state: Option> = Some(if is_content_proof { + HaltedStateCheck::Content(query_plan.into()) + } else { + HaltedStateCheck::Node(query_plan.into()) + }); + let mut has_run_full = false; + while let Some(state) = run_state.take() { + let proof = if let Some(proof) = proofs.pop() { + full_proof.extend_from_slice(&proof); + proof + } else { + if full_proof.is_empty() { + break + } + proofs.clear(); + std::mem::take(&mut full_proof) + }; + let (mut verify_iter, mut verify_iter_content) = if is_content_proof { + let proof_iter: IterOpProof<_, _> = (&proof[0]).into(); + ( + None, + Some( + verify_query_plan_iter_content::>( + state, + proof_iter, + Some(root.clone()), ) - } else { - ( - Some( - verify_query_plan_iter::( - state, - proof.into_iter(), - Some(root.clone()), - ) - .unwrap(), - ), - None, + .unwrap(), + ), + ) + } else { + ( + Some( + verify_query_plan_iter::( + state, + proof.into_iter(), + Some(root.clone()), ) - }; - let mut next_item = || { - if let Some(verify_iter) = verify_iter.as_mut() { - verify_iter.next() - } else if let Some(verify_iter_content) = verify_iter_content.as_mut() { - verify_iter_content.next() - } else { - None - } - }; + .unwrap(), + ), + None, + ) + }; + let mut next_item = || { + if let Some(verify_iter) = verify_iter.as_mut() { + verify_iter.next() + } else if let Some(verify_iter_content) = verify_iter_content.as_mut() { + verify_iter_content.next() + } else { + None + } + }; - let content: BTreeMap<_, _> = set.iter().cloned().collect(); - let mut in_prefix = false; - while let Some(item) = next_item() { - match item.unwrap() { - ReadProofItem::Hash(key, hash) => { - assert!(hash_only); - assert_eq!( - content.get(&*key).map(|v| L::Hash::hash(&v.as_ref())), - Some(hash) - ); - }, - ReadProofItem::Value(key, value) => { - assert_eq!(content.get(&*key), Some(&value.as_ref())); - }, - ReadProofItem::NoValue(key) => { - assert_eq!(content.get(key), None); - }, - ReadProofItem::StartPrefix(_prefix) => { - in_prefix = true; - }, - ReadProofItem::EndPrefix => { - assert!(in_prefix); - in_prefix = false; - }, - ReadProofItem::Halted(resume) => { - run_state = Some(*resume); - break - }, - } - } - if run_state.is_none() && !has_run_full { - has_run_full = true; - query_plan_iter = query_plan.as_ref(); - run_state = Some(if is_content_proof { - HaltedStateCheck::Content(query_plan_iter.into()) - } else { - HaltedStateCheck::Node(query_plan_iter.into()) - }); - } + let mut in_prefix = false; + // TODO need stricter check of query plan (advance query plan iter and expect all items + // touched!!! + while let Some(item) = next_item() { + match item.unwrap() { + ReadProofItem::Hash(key, hash) => { + assert!(hash_only); + assert_eq!(content.get(&*key).map(|v| L::Hash::hash(&v.as_ref())), Some(hash)); + }, + ReadProofItem::Value(key, value) => { + assert_eq!(content.get(&*key), Some(value.as_ref())); + }, + ReadProofItem::NoValue(key) => { + assert_eq!(content.get(key), None); + }, + ReadProofItem::StartPrefix(_prefix) => { + in_prefix = true; + }, + ReadProofItem::EndPrefix => { + assert!(in_prefix); + in_prefix = false; + }, + ReadProofItem::Halted(resume) => { + run_state = Some(*resume); + break + }, } - if !has_run_full { - panic!("did not run full proof") + } + if kind == ProofKind::FullNodes { + if run_state.is_none() && !has_run_full { + has_run_full = true; + let query_plan_iter = query_plan_in_mem.as_ref(); + run_state = Some(if is_content_proof { + HaltedStateCheck::Content(query_plan_iter.into()) + } else { + HaltedStateCheck::Node(query_plan_iter.into()) + }); } + } else { + has_run_full = true; } } + if !has_run_full { + panic!("did not run full proof") + } } From aa0821a77243f1f3835558f8e7fbe1eed41a13a3 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 16 Jun 2023 12:20:16 +0200 Subject: [PATCH 079/154] fuzzer and test for full node no limit. --- trie-db/fuzz/Cargo.toml | 5 ++++ trie-db/fuzz/fuzz_targets/query_plan_1.rs | 16 ++++++++++++ trie-db/test/src/fuzz.rs | 32 +++++++++++++++++++---- 3 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 trie-db/fuzz/fuzz_targets/query_plan_1.rs diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml index ef8a4b41..6349082e 100644 --- a/trie-db/fuzz/Cargo.toml +++ b/trie-db/fuzz/Cargo.toml @@ -14,6 +14,7 @@ memory-db = { path = "../../memory-db", version = "0.32.0" } reference-trie = { path = "../../test-support/reference-trie", version = "0.29.0" } arbitrary = { version = "1.3.0", features = ["derive"] } array-bytes = "6.0.0" +lazy_static = "1.4.0" [dependencies.trie-db] path = ".." @@ -70,3 +71,7 @@ path = "fuzz_targets/trie_proof_invalid.rs" [[bin]] name = "prefix_seek_iter" path = "fuzz_targets/prefix_seek_iter.rs" + +[[bin]] +name = "query_plan_1" +path = "fuzz_targets/query_plan_1.rs" diff --git a/trie-db/fuzz/fuzz_targets/query_plan_1.rs b/trie-db/fuzz/fuzz_targets/query_plan_1.rs new file mode 100644 index 00000000..0c11fa76 --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/query_plan_1.rs @@ -0,0 +1,16 @@ +#![no_main] +use lazy_static::lazy_static; +use libfuzzer_sys::fuzz_target; +use reference_trie::{RefHasher, SubstrateV1}; +use trie_db::query_plan::ProofKind; +use trie_db_test::fuzz::query_plan::{ + build_state, fuzz_query_plan, ArbitraryQueryPlan, Conf, FuzzContext, CONF1, +}; + +lazy_static! { + static ref CONTEXT: FuzzContext> = build_state(CONF1); +} + +fuzz_target!(|plan: ArbitraryQueryPlan| { + fuzz_query_plan::>(&CONTEXT, plan); +}); diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index bd67d053..acb9ba58 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -616,18 +616,18 @@ pub mod query_plan { FuzzContext { reference, db, root, conf, small_values, big_values, values } } - #[derive(Arbitrary)] + #[derive(Arbitrary, Clone, Debug)] enum ArbitraryKey { Indexed(usize), Random(Vec), } /// Base arbitrary for fuzzing. - #[derive(Arbitrary)] + #[derive(Arbitrary, Clone, Debug)] pub struct ArbitraryQueryPlan(Vec<(bool, ArbitraryKey)>); fn arbitrary_query_plan( - context: &mut FuzzContext, + context: &FuzzContext, plan: ArbitraryQueryPlan, ) -> InMemQueryPlan { let conf = &context.conf; @@ -667,8 +667,8 @@ pub mod query_plan { } /// Main entry point for query plan fuzzing. - pub fn fuzz_query_plan(mut context: FuzzContext, plan: ArbitraryQueryPlan) { - let query_plan = arbitrary_query_plan(&mut context, plan); + pub fn fuzz_query_plan(context: &FuzzContext, plan: ArbitraryQueryPlan) { + let query_plan = arbitrary_query_plan(context, plan); let kind = context.conf.kind; let limit = context.conf.limit; @@ -717,4 +717,26 @@ pub mod query_plan { context.conf.hash_only, ); } + + /// Fuzzing conf 1. + pub const CONF1: Conf = Conf { + seed: 0u64, + kind: ProofKind::FullNodes, + nb_key_value: 300, + nb_small_value_set: 5, + nb_big_value_set: 5, + hash_only: false, + limit: 0, // no limit + proof_spawn_with_persistence: false, + }; + + #[test] + fn fuzz_query_plan_1() { + use reference_trie::{RefHasher, SubstrateV1}; + let plans = [ArbitraryQueryPlan(vec![])]; + let context: FuzzContext> = build_state(CONF1); + for plan in plans { + fuzz_query_plan::>(&context, plan.clone()); + } + } } From f5ea967b3934f2c2a10da356434eacf884cb60e4 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 16 Jun 2023 12:37:20 +0200 Subject: [PATCH 080/154] fix --- trie-db/test/src/fuzz.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index acb9ba58..c9b0a1fc 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -599,7 +599,7 @@ pub mod query_plan { // add the test entries for (key, value) in test_entries() { - reference.insert(key.to_vec(), value.to_vec()).unwrap(); + reference.insert(key.to_vec(), value.to_vec()); } let (db, root) = { From c9d81c16c52cd04fc7a70c5d6437403211e5fcc3 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 16 Jun 2023 13:07:28 +0200 Subject: [PATCH 081/154] fail (attempt to iter prefix on untouched prefix). --- trie-db/test/src/fuzz.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index c9b0a1fc..eab98ed0 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -733,7 +733,10 @@ pub mod query_plan { #[test] fn fuzz_query_plan_1() { use reference_trie::{RefHasher, SubstrateV1}; - let plans = [ArbitraryQueryPlan(vec![])]; + let plans = [ + ArbitraryQueryPlan(vec![(true, ArbitraryKey::Indexed(43218140957))]), + ArbitraryQueryPlan(vec![]), + ]; let context: FuzzContext> = build_state(CONF1); for plan in plans { fuzz_query_plan::>(&context, plan.clone()); From 5bfd50ed9a48f875185dee49db64d887f55470ff Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 19 Jun 2023 14:35:47 +0200 Subject: [PATCH 082/154] fix prefix not fully stacked --- trie-db/src/query_plan/mod.rs | 2 +- trie-db/src/query_plan/record.rs | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index f16c7474..53b3b411 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -775,7 +775,7 @@ impl ReadStack { } self.prefix.append_partial(partial.right()); } else { - return Ok(TryStackChildResult::StackedDescendIncomplete) + return Ok(TryStackChildResult::NotStacked) } }, } diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 8ebdd77e..a5a79e1d 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -837,6 +837,7 @@ impl RecordStack { let mut is_inline = false; let prefix = &mut self.prefix; let mut descend_incomplete = false; + let mut descend_incomplete_stacked = false; let mut stack_extension = false; let mut from_branch = None; let child_handle = if let Some(item) = self.items.last_mut() { @@ -936,6 +937,7 @@ impl RecordStack { s.advance(partial.len()); } else { descend_incomplete = true; + descend_incomplete_stacked = partial.starts_with(s); } } }, @@ -978,7 +980,11 @@ impl RecordStack { } if descend_incomplete { - Ok(TryStackChildResult::StackedDescendIncomplete) + if descend_incomplete_stacked { + Ok(TryStackChildResult::StackedDescendIncomplete) + } else { + Ok(TryStackChildResult::NotStacked) + } } else { Ok(TryStackChildResult::Stacked) } From 3a110e6d28a9db7d0152d46765ff66c3c17da497 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 19 Jun 2023 17:53:27 +0200 Subject: [PATCH 083/154] transition between values is broken. --- trie-db/src/query_plan/record.rs | 4 +++- trie-db/test/src/fuzz.rs | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index a5a79e1d..08cdb13b 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -740,7 +740,9 @@ pub fn record_query_plan< continue } // descend - let mut slice_query = NibbleSlice::new_offset(&query.key, common_nibbles); + let add = if from.stack.items.len() == 0 { 0 } else { 1 }; + let mut slice_query = NibbleSlice::new_offset(&query.key, from.stack.prefix.len() + add); + let touched = loop { if !from.stack.items.is_empty() { if slice_query.is_empty() { diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index eab98ed0..38f9aaaf 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -734,6 +734,10 @@ pub mod query_plan { fn fuzz_query_plan_1() { use reference_trie::{RefHasher, SubstrateV1}; let plans = [ + ArbitraryQueryPlan(vec![ + (false, ArbitraryKey::Indexed(17942346408707227648)), + (false, ArbitraryKey::Indexed(37833)), + ]), ArbitraryQueryPlan(vec![(true, ArbitraryKey::Indexed(43218140957))]), ArbitraryQueryPlan(vec![]), ]; From 126464152fdbd16869d57bbff990bdceb958ee08 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 21 Jun 2023 19:29:19 +0200 Subject: [PATCH 084/154] start rewrite --- trie-db/src/query_plan/mod.rs | 834 +---------------------- trie-db/src/query_plan/record.rs | 322 +++++---- trie-db/src/query_plan/verify.rs | 442 ++++++++++++ trie-db/src/query_plan/verify_content.rs | 350 +++++++++- trie-db/test/src/fuzz.rs | 12 +- trie-db/test/src/query_plan.rs | 12 +- 6 files changed, 1009 insertions(+), 963 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index 53b3b411..bd6cb27c 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -167,14 +167,6 @@ pub enum ProofKind { /// proof at once. CompactNodes, - /* TODO does not seem usefull, CompactContent is strictly better - /// Same encoding as CompactNodes, but with an alternate ordering that allows streaming - /// node and avoid unbound memory when building proof. - /// - /// Ordering is starting at first met proof and parent up to intersection with next - /// sibling access in a branch, then next leaf, and repeating, finishing with root node. - CompactNodesStream, - */ /// Content oriented proof, no nodes are written, just a /// sequence of accessed by lexicographical order as described /// in content_proof::Op. @@ -222,17 +214,16 @@ impl Bitmap { } } -// TODO rename #[derive(Clone)] -struct CompactEncodingInfos { +struct StackedNodeRecord { /// Node in memory content. node: OwnedNode, - /// Flags indicating whether each child is omitted in the encoded node. + /// Flags indicating whether each child is omitted (accessed) in the encoded node. /// For some encoding, it also record if the child has already been written. accessed_children_node: Bitmap, /// Skip value if value node is after. accessed_value_node: bool, - /// Depth of node in nible. + /// Depth of node in nibbles (actual depth of an attached value (post partial)). depth: usize, /// Next descended child, can also be use to get node position in parent /// (this minus one). @@ -382,11 +373,23 @@ pub enum HaltedStateCheck<'a, L: TrieLayout, C, D: SplitFirst> { } enum TryStackChildResult { - Stacked, - NotStackedBranch, + /// If there is no child to stack. NotStacked, - StackedDescendIncomplete, + /// Same indicating it is a branch so in case of iteration + /// we will attempt next child. + NotStackedBranch, + /// Nothing stacked, this is a next child attempt that allows + /// suspending proof registering of proof check iteration. Halted, + /// Child stacked and matched of the full partial key. + StackedFull, + /// Child stacked but part of partial key is into. + /// If prefix query plan item, this is part of the prefix. + StackedInto, + /// Child stacked but part of partial key is after. + /// Indicate that the query plan item need to be switched. + /// Next query plan item could still be using this stacked node (as any stacked variant). + StackedAfter, } #[derive(Eq, PartialEq)] @@ -405,7 +408,7 @@ enum ReadProofState { Finished, } -struct ItemStack { +struct StackedNodeCheck { node: ItemStackNode, children: Vec>>>, attached_value_hash: Option>, @@ -420,7 +423,6 @@ enum ValueSet { HashOnly(H), // ForceInline(V), // ForceHashed(V), - BranchHash(H, u8), } impl ValueSet { @@ -428,7 +430,7 @@ impl ValueSet { match self { ValueSet::Standard(v) => Some(v), //ValueSet::ForceInline(v) | ValueSet::ForceHashed(v) => Some(v), - ValueSet::HashOnly(..) | ValueSet::BranchHash(..) | ValueSet::None => None, + ValueSet::HashOnly(..) | ValueSet::None => None, } } } @@ -436,7 +438,6 @@ impl ValueSet { impl From> for ValueSet { fn from(op: Op) -> Self { match op { - Op::HashChild(hash, child_ix) => ValueSet::BranchHash(hash, child_ix), Op::HashValue(hash) => ValueSet::HashOnly(hash), Op::Value(value) => ValueSet::Standard(value), //Op::ValueForceInline(value) => ValueSet::ForceInline(value), @@ -452,9 +453,9 @@ struct ItemContentStack { depth: usize, } -impl Clone for ItemStack { +impl Clone for StackedNodeCheck { fn clone(&self) -> Self { - ItemStack { + StackedNodeCheck { node: self.node.clone(), children: self.children.clone(), attached_value_hash: self.attached_value_hash, @@ -480,7 +481,7 @@ enum ItemStackNode { Node(OwnedNode), } -impl From<(ItemStackNode, bool)> for ItemStack { +impl From<(ItemStackNode, bool)> for StackedNodeCheck { fn from((node, is_compact): (ItemStackNode, bool)) -> Self { let children = if !is_compact { Vec::new() @@ -531,11 +532,17 @@ impl From<(ItemStackNode, bool)> for ItemStack< } }; - ItemStack { node, depth: 0, next_descended_child: 0, children, attached_value_hash: None } + StackedNodeCheck { + node, + depth: 0, + next_descended_child: 0, + children, + attached_value_hash: None, + } } } -impl ItemStack { +impl StackedNodeCheck { fn data(&self) -> &[u8] { match &self.node { ItemStackNode::Inline(n) => n.data(), @@ -551,63 +558,6 @@ impl ItemStack { } } -struct ReadStack { - items: Vec>, - prefix: NibbleVec, - // limit and wether we return value and if hash only iteration. - iter_prefix: Option<(usize, bool, bool)>, - start_items: usize, - is_compact: bool, - expect_value: bool, - _ph: PhantomData, -} - -struct ReadContentStack { - items: Vec>, - prefix: NibbleVec, - // limit and wether we return value and if hash only iteration. - // TODO should be removable (just check current). - iter_prefix: Option<(usize, bool, bool)>, - start_items: usize, - is_prev_hash_child: Option, - expect_value: bool, - is_prev_push_key: bool, - is_prev_pop_key: bool, - first: bool, - _ph: PhantomData, -} - -impl Clone for ReadStack { - fn clone(&self) -> Self { - ReadStack { - items: self.items.clone(), - prefix: self.prefix.clone(), - start_items: self.start_items.clone(), - iter_prefix: self.iter_prefix, - is_compact: self.is_compact, - expect_value: self.expect_value, - _ph: PhantomData, - } - } -} - -impl Clone for ReadContentStack { - fn clone(&self) -> Self { - ReadContentStack { - items: self.items.clone(), - prefix: self.prefix.clone(), - start_items: self.start_items.clone(), - iter_prefix: self.iter_prefix, - expect_value: self.expect_value, - is_prev_push_key: self.is_prev_push_key, - is_prev_pop_key: self.is_prev_pop_key, - is_prev_hash_child: self.is_prev_hash_child, - first: self.first, - _ph: PhantomData, - } - } -} - fn verify_hash( data: &[u8], expected: &[u8], @@ -642,728 +592,6 @@ impl<'a> SplitFirst for &'a [u8] { } } -impl ReadStack { - fn try_stack_child( - &mut self, - child_index: u8, - proof: &mut impl Iterator, - expected_root: &Option>, - mut slice_query: Option<&mut NibbleSlice>, - query_prefix: bool, - ) -> Result, CError>> { - let check_hash = expected_root.is_some(); - let child_handle = if let Some(node) = self.items.last_mut() { - let node_data = node.data(); - - match node.node_plan() { - NodePlan::Empty | NodePlan::Leaf { .. } => - return Ok(TryStackChildResult::NotStacked), - NodePlan::Extension { .. } => { - unreachable!("Extension never stacked") - }, - NodePlan::NibbledBranch { children, .. } | NodePlan::Branch { children, .. } => - if let Some(child) = &children[child_index as usize] { - child.build(node_data) - } else { - return Ok(TryStackChildResult::NotStackedBranch) - }, - } - } else { - if self.is_compact { - NodeHandle::Inline(&[]) - } else { - NodeHandle::Hash(expected_root.as_ref().map(AsRef::as_ref).unwrap_or(&[])) - } - }; - let mut node: ItemStack<_, _> = match child_handle { - NodeHandle::Inline(data) => - if self.is_compact && data.len() == 0 { - // ommitted hash - let Some(mut encoded_node) = proof.next() else { - // halt happens with a hash, this is not. - return Err(VerifyError::IncompleteProof); - }; - if self.is_compact && - encoded_node.borrow().len() > 0 && - Some(encoded_node.borrow()[0]) == - ::ESCAPE_HEADER - { - self.expect_value = true; - // no value to visit TODO set a boolean to ensure we got a hash and don - // t expect reanding a node value - encoded_node.split_first(); - } - let node = match OwnedNode::new::(encoded_node) { - Ok(node) => node, - Err(e) => return Err(VerifyError::DecodeError(e)), - }; - (ItemStackNode::Node(node), self.is_compact).into() - } else { - // try access in inline then return - ( - ItemStackNode::Inline(match OwnedNode::new::(data.to_vec()) { - Ok(node) => node, - Err(e) => return Err(VerifyError::DecodeError(e)), - }), - self.is_compact, - ) - .into() - }, - NodeHandle::Hash(hash) => { - // TODO if is_compact allow only if restart bellow depth (otherwhise should be - // inline(0)) or means halted (if something in proof it is extraneous node) - let Some(mut encoded_node) = proof.next() else { - return Ok(TryStackChildResult::Halted); - }; - if self.is_compact && - encoded_node.borrow().len() > 0 && - Some(encoded_node.borrow()[0]) == - ::ESCAPE_HEADER - { - self.expect_value = true; - // no value to visit TODO set a boolean to ensure we got a hash and don - // t expect reanding a node value - encoded_node.split_first(); - } - let node = match OwnedNode::new::(encoded_node) { - Ok(node) => node, - Err(e) => return Err(VerifyError::DecodeError(e)), - }; - if !self.is_compact && check_hash { - verify_hash::(node.data(), hash)?; - } - (ItemStackNode::Node(node), self.is_compact).into() - }, - }; - let node_data = node.data(); - - let mut prefix_incomplete = false; - match node.node_plan() { - NodePlan::Branch { .. } => (), - | NodePlan::Empty => (), - NodePlan::Leaf { partial, .. } | - NodePlan::NibbledBranch { partial, .. } | - NodePlan::Extension { partial, .. } => { - let partial = partial.build(node_data); - if self.items.len() > 0 { - if let Some(slice) = slice_query.as_mut() { - slice.advance(1); - } - } - let ok = if let Some(slice) = slice_query.as_mut() { - if slice.starts_with(&partial) { - true - } else if query_prefix { - prefix_incomplete = true; - partial.starts_with(slice) - } else { - false - } - } else { - true - }; - if prefix_incomplete { - // end of query - slice_query = None; - } - if ok { - if self.items.len() > 0 { - self.prefix.push(child_index); - } - if let Some(slice) = slice_query.as_mut() { - slice.advance(partial.len()); - } - self.prefix.append_partial(partial.right()); - } else { - return Ok(TryStackChildResult::NotStacked) - } - }, - } - if let NodePlan::Extension { child, .. } = node.node_plan() { - let node_data = node.data(); - let child = child.build(node_data); - match child { - NodeHandle::Hash(hash) => { - let Some(encoded_branch) = proof.next() else { - // No halt on extension node (restart over a child index). - return Err(VerifyError::IncompleteProof); - }; - if self.is_compact { - let mut error_hash = TrieHash::::default(); - error_hash.as_mut().copy_from_slice(hash); - return Err(VerifyError::ExtraneousHashReference(error_hash)) - } - if check_hash { - verify_hash::(encoded_branch.borrow(), hash)?; - } - node = match OwnedNode::new::(encoded_branch) { - Ok(node) => (ItemStackNode::Node(node), self.is_compact).into(), - Err(e) => return Err(VerifyError::DecodeError(e)), - }; - }, - NodeHandle::Inline(data) => { - if self.is_compact && data.len() == 0 { - unimplemented!("This requires to put extension in stack"); - /* - // ommitted hash - let Some(encoded_node) = proof.next() else { - // halt happens with a hash, this is not. - return Err(VerifyError::IncompleteProof); - }; - node = match OwnedNode::new::(encoded_node) { - Ok(node) => (ItemStackNode::Node(node), self.is_compact).into(), - Err(e) => return Err(VerifyError::DecodeError(e)), - }; - */ - } else { - node = match OwnedNode::new::(data.to_vec()) { - Ok(node) => (ItemStackNode::Inline(node), self.is_compact).into(), - Err(e) => return Err(VerifyError::DecodeError(e)), - }; - } - }, - } - let NodePlan::Branch { .. } = node.node_plan() else { - return Err(VerifyError::IncompleteProof) // TODO make error type?? - }; - } - node.depth = self.prefix.len(); - // needed for compact - self.items.last_mut().map(|parent| { - parent.next_descended_child = child_index + 1; - }); - self.items.push(node); - if prefix_incomplete { - Ok(TryStackChildResult::StackedDescendIncomplete) - } else { - Ok(TryStackChildResult::Stacked) - } - } - - fn access_value( - &mut self, - proof: &mut impl Iterator, - check_hash: bool, - hash_only: bool, - ) -> Result<(Option>, Option>), VerifyError, CError>> { - if let Some(node) = self.items.last() { - let node_data = node.data(); - - let value = match node.node_plan() { - NodePlan::Leaf { value, .. } => Some(value.build(node_data)), - NodePlan::Branch { value, .. } | NodePlan::NibbledBranch { value, .. } => - value.as_ref().map(|v| v.build(node_data)), - _ => return Ok((None, None)), - }; - if let Some(value) = value { - match value { - Value::Inline(value) => - if self.expect_value { - assert!(self.is_compact); - self.expect_value = false; - if hash_only { - return Err(VerifyError::ExtraneousValue(Default::default())) - } - - let Some(value) = proof.next() else { - return Err(VerifyError::IncompleteProof); - }; - if check_hash { - let hash = L::Hash::hash(value.borrow()); - self.items.last_mut().map(|i| i.attached_value_hash = Some(hash)); - } - return Ok((Some(value.borrow().to_vec()), None)) - } else { - if hash_only { - let hash = L::Hash::hash(value.borrow()); - return Ok((None, Some(hash))) - } - return Ok((Some(value.to_vec()), None)) - }, - Value::Node(hash) => { - if self.expect_value { - if hash_only { - return Err(VerifyError::ExtraneousValue(Default::default())) - } - self.expect_value = false; - let mut error_hash = TrieHash::::default(); - error_hash.as_mut().copy_from_slice(hash); - return Err(VerifyError::ExtraneousHashReference(error_hash)) - } - if hash_only { - let mut result_hash = TrieHash::::default(); - result_hash.as_mut().copy_from_slice(hash); - return Ok((None, Some(result_hash))) - } - let Some(value) = proof.next() else { - return Err(VerifyError::IncompleteProof); - }; - if check_hash { - verify_hash::(value.borrow(), hash)?; - } - return Ok((Some(value.borrow().to_vec()), None)) - }, - } - } - } else { - return Err(VerifyError::IncompleteProof) - } - - Ok((None, None)) - } - - fn pop( - &mut self, - expected_root: &Option>, - ) -> Result, CError>> { - if self.iter_prefix.as_ref().map(|p| p.0 == self.items.len()).unwrap_or(false) { - return Ok(false) - } - if let Some(last) = self.items.pop() { - let depth = self.items.last().map(|i| i.depth).unwrap_or(0); - self.prefix.drop_lasts(self.prefix.len() - depth); - if self.is_compact && expected_root.is_some() { - match last.node { - ItemStackNode::Inline(_) => (), - ItemStackNode::Node(node) => { - let origin = self.start_items; - let node_data = node.data(); - let node = node.node_plan().build(node_data); - let encoded_node = crate::trie_codec::encode_read_node_internal::( - node, - &last.children, - last.attached_value_hash.as_ref().map(|h| h.as_ref()), - ); - - //println!("{:?}", encoded_node); - if self.items.len() == origin { - if let Some(parent) = self.items.last() { - let at = parent.next_descended_child - 1; - if let Some(Some(ChildReference::Hash(expected))) = - parent.children.get(at as usize) - { - verify_hash::(&encoded_node, expected.as_ref())?; - } else { - return Err(VerifyError::RootMismatch(Default::default())) - } - } else { - let expected = expected_root.as_ref().expect("checked above"); - verify_hash::(&encoded_node, expected.as_ref())?; - } - } else if self.items.len() < origin { - // popped origin, need to check against new origin - self.start_items = self.items.len(); - } else { - let hash = L::Hash::hash(&encoded_node); - if let Some(parent) = self.items.last_mut() { - let at = parent.next_descended_child - 1; - match parent.children[at as usize] { - Some(ChildReference::Hash(expected)) => { - // can append if chunks are concatenated (not progressively - // checked) - verify_hash::(&encoded_node, expected.as_ref())?; - }, - None => { - // Complete - parent.children[at as usize] = - Some(ChildReference::Hash(hash)); - }, - Some(ChildReference::Inline(_h, size)) if size == 0 => { - // Complete - parent.children[at as usize] = - Some(ChildReference::Hash(hash)); - }, - _ => - // only non inline are stacked - return Err(VerifyError::RootMismatch(Default::default())), - } - } else { - if &Some(hash) != expected_root { - return Err(VerifyError::RootMismatch(hash)) - } - } - } - }, - } - } - Ok(true) - } else { - Ok(false) - } - } - - fn pop_until( - &mut self, - target: usize, - expected_root: &Option>, - check_only: bool, - ) -> Result<(), VerifyError, CError>> { - if self.is_compact && expected_root.is_some() { - // TODO pop with check only, here unefficient implementation where we just restore - - let mut restore = None; - if check_only { - restore = Some(self.clone()); - self.iter_prefix = None; - } - // one by one - while let Some(last) = self.items.last() { - match last.depth.cmp(&target) { - Ordering::Greater => (), - // depth should match. - Ordering::Less => { - // TODO other error - return Err(VerifyError::ExtraneousNode) - }, - Ordering::Equal => return Ok(()), - } - // one by one - let _ = self.pop(expected_root)?; - } - - if let Some(old) = restore.take() { - *self = old; - return Ok(()) - } - } - loop { - if let Some(last) = self.items.last() { - match last.depth.cmp(&target) { - Ordering::Greater => (), - // depth should match. - Ordering::Less => break, - Ordering::Equal => { - self.prefix.drop_lasts(self.prefix.len() - last.depth); - return Ok(()) - }, - } - } else { - if target == 0 { - return Ok(()) - } else { - break - } - } - let _ = self.items.pop(); - } - // TODO other error - Err(VerifyError::ExtraneousNode) - } - - fn enter_prefix_iter(&mut self, hash_only: bool) { - self.iter_prefix = Some((self.items.len(), false, hash_only)); - } - - fn exit_prefix_iter(&mut self) { - self.iter_prefix = None - } -} - -impl ReadContentStack { - fn pop_until( - &mut self, - target: usize, - check_only: bool, // TODO used? - ) -> Result<(), VerifyError, CError>> { - // TODO pop with check only, here unefficient implementation where we just restore - - let mut restore = None; - if check_only { - restore = Some(self.clone()); - self.iter_prefix = None; - } - // one by one - while let Some(last) = self.items.last() { - // depth should match. - match last.depth.cmp(&target) { - Ordering::Greater => { - // TODO could implicit pop here with a variant - // that do not write redundant pop. - return Err(VerifyError::ExtraneousNode) // TODO more precise error - }, - Ordering::Less => { - if self.first { - // allowed to have next at a upper level - } else { - return Err(VerifyError::ExtraneousNode) - } - }, - Ordering::Equal => return Ok(()), - } - - // start_items update. - self.start_items = core::cmp::min(self.start_items, self.items.len()); - // one by one - let _ = self.items.pop(); - } - - if let Some(old) = restore.take() { - *self = old; - return Ok(()) - } - if self.items.is_empty() && target == 0 { - Ok(()) - } else { - Err(VerifyError::ExtraneousNode) - } - } - - #[inline(always)] - fn stack_empty(&mut self, depth: usize) { - /* - items: Vec>, - prefix: NibbleVec, - // limit and wether we return value and if hash only iteration. - iter_prefix: Option<(usize, bool, bool)>, - start_items: usize, - */ - - self.items.push(ItemContentStack { - children: vec![None; NIBBLE_LENGTH], - value: ValueSet::None, - depth, - }) - } - - #[inline(always)] - fn stack_pop( - &mut self, - nb_nibble: Option, - expected_root: &Option>, - ) -> Result<(), VerifyError, CError>> { - let target_depth = nb_nibble.map(|n| self.prefix.len() - n); - let mut first = true; - while self - .items - .last() - .map(|item| target_depth.map(|target| item.depth > target).unwrap_or(true)) - .unwrap_or(false) - { - let item = self.items.pop().expect("Checked"); - let mut from_depth = - self.items.last().map(|item| item.depth).unwrap_or(target_depth.unwrap_or(0)); - if let Some(from) = target_depth { - if from > from_depth { - self.stack_empty(from); - from_depth = from; - } - } - let depth = item.depth; - let is_root = target_depth.is_none() && self.items.is_empty(); - let inc = if is_root { 0 } else { 1 }; - - let child_reference = if item.children.iter().any(|child| child.is_some()) { - let nkey = (depth > (from_depth + inc)) - .then(|| (from_depth + inc, depth - from_depth - inc)); - if L::USE_EXTENSION { - let extension_only = first && - matches!(&item.value, &ValueSet::None) && - item.children.iter().filter(|child| child.is_some()).count() == 1; - self.items.push(item); // TODO this looks bad (pop then push, branch or leaf function should or should - // not pop instead) - // encode branch - self.standard_extension(depth, is_root, nkey, extension_only) - } else { - self.items.push(item); // TODO this looks bad (pop then push, branch or leaf function should or should - // not pop instead) - // encode branch - self.no_extension(depth, is_root, nkey) - } - } else { - // leaf with value - self.flush_value_change(from_depth + inc, item.depth, &item.value, is_root) - }; - - if self.items.is_empty() && !is_root { - self.stack_empty(from_depth); - } - - let items_len = self.items.len(); - if let Some(item) = self.items.last_mut() { - let child_ix = self.prefix.at(item.depth); - if let Some(hash) = item.children[child_ix as usize].as_ref() { - if items_len == self.start_items + 1 { - if expected_root.is_some() && hash != &child_reference { - return Err(VerifyError::HashMismatch(*child_reference.disp_hash())) - } - } else { - return Err(VerifyError::ExtraneousHashReference(*hash.disp_hash())) - // return Err(CompactDecoderError::HashChildNotOmitted.into()) - } - } - item.children[child_ix as usize] = Some(child_reference); - } else { - if let Some(root) = expected_root.as_ref() { - if nb_nibble.is_none() { - if root != child_reference.disp_hash() { - return Err(VerifyError::RootMismatch(*child_reference.disp_hash())) - } - } - } - } - first = false; - // TODO can skip hash checks when above start_items. - self.start_items = core::cmp::min(self.start_items, self.items.len()); - } - Ok(()) - } - - fn process(encoded_node: Vec, is_root: bool) -> ChildReference> { - let len = encoded_node.len(); - if !is_root && len < ::LENGTH { - let mut h = <::Out as Default>::default(); - h.as_mut()[..len].copy_from_slice(&encoded_node[..len]); - return ChildReference::Inline(h, len) - } - let hash = ::hash(encoded_node.as_slice()); - ChildReference::Hash(hash) - } - - // TODO factor with iter_build (reuse cacheaccum here). - #[inline(always)] - fn standard_extension( - &mut self, - branch_d: usize, - is_root: bool, - nkey: Option<(usize, usize)>, - extension_only: bool, - ) -> ChildReference> { - let key_branch = &self.prefix.inner().as_ref()[..]; - let last = self.items.len() - 1; - assert_eq!(self.items[last].depth, branch_d); - - let ItemContentStack { children, value: v, depth, .. } = self.items.pop().expect("checked"); - - debug_assert!(branch_d == depth); - - let hashed; - let value = if let Some(v) = v.as_ref() { - Some(if let Some(value) = Value::new_inline(v.as_ref(), L::MAX_INLINE_VALUE) { - value - } else { - let mut prefix = NibbleSlice::new_offset(&key_branch, 0); - prefix.advance(branch_d); - - hashed = ::hash(v.as_ref()); - Value::Node(hashed.as_ref()) - }) - } else { - None - }; - - // encode branch - let branch_hash = if !extension_only { - let encoded = L::Codec::branch_node(children.iter(), value); - Self::process(encoded, is_root && nkey.is_none()) - } else { - // This is hacky but extension only store as first children - children[0].unwrap() - }; - - if let Some(nkeyix) = nkey { - let pr = NibbleSlice::new_offset(&key_branch, nkeyix.0); - let nib = pr.right_range_iter(nkeyix.1); - let encoded = L::Codec::extension_node(nib, nkeyix.1, branch_hash); - Self::process(encoded, is_root) - } else { - branch_hash - } - } - - #[inline(always)] - fn no_extension( - &mut self, - branch_d: usize, - is_root: bool, - nkey: Option<(usize, usize)>, - ) -> ChildReference> { - let key_branch = &self.prefix.inner().as_ref()[..]; - let ItemContentStack { children, value: v, depth, .. } = self.items.pop().expect("checked"); - - debug_assert!(branch_d == depth); - // encode branch - let nkeyix = nkey.unwrap_or((branch_d, 0)); - let pr = NibbleSlice::new_offset(&key_branch, nkeyix.0); - let hashed; - let value = if let Some(v) = v.as_ref() { - Some(if let Some(value) = Value::new_inline(v.as_ref(), L::MAX_INLINE_VALUE) { - value - } else { - let mut prefix = NibbleSlice::new_offset(&key_branch, 0); - prefix.advance(branch_d); - hashed = ::hash(v.as_ref()); - Value::Node(hashed.as_ref()) - }) - } else { - if let ValueSet::HashOnly(h) = &v { - Some(Value::Node(h.as_ref())) - } else { - None - } - }; - - let encoded = L::Codec::branch_node_nibbled( - pr.right_range_iter(nkeyix.1), - nkeyix.1, - children.iter(), - value, - ); - Self::process(encoded, is_root) - } - - fn flush_value_change<'a>( - &mut self, - from_depth: usize, - to_depth: usize, - value: &ValueSet, Vec>, - is_root: bool, - ) -> ChildReference> { - let key_content = &self.prefix.inner().as_ref()[..]; - let k2 = &key_content[..to_depth / nibble_ops::NIBBLE_PER_BYTE]; - let pr = NibbleSlice::new_offset(k2, from_depth); - - let hashed; - let value = match value { - ValueSet::Standard(v) => - if let Some(value) = Value::new_inline(v.as_ref(), L::MAX_INLINE_VALUE) { - value - } else { - hashed = ::hash(v.as_ref()); - Value::Node(hashed.as_ref()) - }, - ValueSet::HashOnly(h) => { - Value::Node(h.as_ref()) // TODO may have following hash and fail? ont if leaf - }, - ValueSet::BranchHash(..) | ValueSet::None => unreachable!("Not in cache accum"), - }; - let encoded = L::Codec::leaf_node(pr.right_iter(), pr.len(), value); - Self::process(encoded, is_root) - } - - #[inline(always)] - fn set_cache_change( - &mut self, - change: ValueSet, Vec>, - ) -> Result<(), VerifyError, CError>> { - if self.items.is_empty() { - self.stack_empty(0); - } - let last = self.items.len() - 1; - let mut item = &mut self.items[last]; - match change { - ValueSet::BranchHash(h, i) => { - if let Some(hash) = item.children[i as usize].as_ref() { - return Err(VerifyError::ExtraneousHashReference(*hash.disp_hash())) - //return Err(CompactDecoderError::HashChildNotOmitted.into()) TODO - } - item.children[i as usize] = Some(ChildReference::Hash(h)); - }, - value => item.value = value, - } - Ok(()) - } -} - /// Content return on success when reading proof. pub enum ReadProofItem<'a, L: TrieLayout, C, D: SplitFirst> { /// Successfull read of proof, not all content read. diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 08cdb13b..0db34541 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -26,8 +26,7 @@ pub struct Recorder { } impl Recorder { - // TODO rename same as record_inline functio - fn mark_inline_access(&self) -> bool { + fn record_inline(&self) -> bool { match &self.output { RecorderStateInner::Content { .. } => true, _ => false, @@ -36,12 +35,12 @@ impl Recorder { /// Check and update start at record. /// When return true, do record. - /// TODO debug why it is needed. + /// Else already was. fn check_start_at(&mut self, depth: usize) -> bool { if self.start_at.map(|s| s > depth).unwrap_or(false) { false } else { - self.start_at = None; + //self.start_at = None; true } } @@ -76,10 +75,10 @@ impl Recorder { #[must_use] fn record_stacked_node( &mut self, - item: &CompactEncodingInfos, + item: &StackedNodeRecord, is_root: bool, parent_index: u8, - items: &Vec, + items: &Vec, ) -> bool { if !self.check_start_at(item.depth) { return false @@ -88,7 +87,7 @@ impl Recorder { match &mut self.output { RecorderStateInner::Stream(output) => if !item.is_inline { - res = self.limits.add_node( + res |= self.limits.add_node( item.node.data().len(), L::Codec::DELTA_COMPACT_OMITTED_NODE, is_root, @@ -97,7 +96,7 @@ impl Recorder { }, RecorderStateInner::Compact { output: _, proof, stacked_pos } => if !item.is_inline { - res = self.limits.add_node( + res |= self.limits.add_node( item.node.data().len(), L::Codec::DELTA_COMPACT_OMITTED_NODE, is_root, @@ -106,15 +105,13 @@ impl Recorder { proof.push(Vec::new()); }, RecorderStateInner::Content { output, stacked_push, stacked_pop } => { - if flush_compact_content_pop::( + res |= flush_compact_content_pop::( output, stacked_pop, items, None, &mut self.limits, - ) { - res = true - } + ); if stacked_push.is_none() { *stacked_push = Some(NibbleVec::new()); } @@ -154,7 +151,7 @@ impl Recorder { let init_len = output.buf_len(); op.encode_into(output); let written = output.buf_len() - init_len; - res = self.limits.add_node(written, 0, false); + res |= self.limits.add_node(written, 0, false); } } res @@ -168,15 +165,15 @@ impl Recorder { let mut res = false; if let RecorderStateInner::Content { .. } = &self.output { - res = self.flush_compact_content_pushes(depth); + res |= self.flush_compact_content_pushes(depth); } match &mut self.output { RecorderStateInner::Stream(output) => { - res = self.limits.add_value(value.len(), L::Codec::DELTA_COMPACT_OMITTED_VALUE); + res |= self.limits.add_value(value.len(), L::Codec::DELTA_COMPACT_OMITTED_VALUE); output.write_entry(value.into()); }, RecorderStateInner::Compact { output: _, proof, stacked_pos: _ } => { - res = self.limits.add_value(value.len(), L::Codec::DELTA_COMPACT_OMITTED_VALUE); + res |= self.limits.add_value(value.len(), L::Codec::DELTA_COMPACT_OMITTED_VALUE); proof.push(value.into()); }, RecorderStateInner::Content { output, .. } => { @@ -197,9 +194,7 @@ impl Recorder { return res } if let RecorderStateInner::Content { .. } = &self.output { - if self.flush_compact_content_pushes(depth) { - res = true; - } + res |= self.flush_compact_content_pushes(depth); } match &mut self.output { @@ -212,14 +207,14 @@ impl Recorder { let init_len = output.buf_len(); op.encode_into(output); let written = output.buf_len() - init_len; - res = self.limits.add_node(written, 0, false); + res |= self.limits.add_node(written, 0, false); }, } res } #[must_use] - fn record_skip_value(&mut self, items: &mut Vec) -> bool { + fn record_skip_value(&mut self, items: &mut Vec) -> bool { let mut res = false; let mut op = None; if let RecorderStateInner::Content { .. } = &self.output { @@ -250,9 +245,7 @@ impl Recorder { _ => return res, } - if self.flush_compact_content_pushes(item.depth) { - res = true; - } + res |= self.flush_compact_content_pushes(item.depth); } } @@ -262,7 +255,7 @@ impl Recorder { let init_len = output.buf_len(); op.encode_into(output); let written = output.buf_len() - init_len; - res = self.limits.add_node(written, 0, false); + res |= self.limits.add_node(written, 0, false); }, _ => (), } @@ -281,7 +274,7 @@ impl Recorder { let init_len = output.buf_len(); op.encode_into(output); let written = output.buf_len() - init_len; - res = self.limits.add_node(written, 0, false); + res |= self.limits.add_node(written, 0, false); }, _ => (), } @@ -323,19 +316,19 @@ pub struct HaltedStateRecord { impl HaltedStateRecord { /// Indicate we reuse the query plan iterator /// and stack. - pub fn statefull(&mut self, recorder: Recorder) -> Recorder { + pub fn statefull(&mut self, recorder: Recorder) -> O { let result = core::mem::replace(&mut self.stack.recorder, recorder); - result + result.output() } /// Indicate to use stateless (on a fresh proof /// and a fresh query plan iterator). - pub fn stateless(&mut self, recorder: Recorder) -> Recorder { + pub fn stateless(&mut self, recorder: Recorder) -> O { let new_start = Self::from_start(recorder); let old = core::mem::replace(self, new_start); self.from = old.from; self.currently_query_item = None; - old.stack.recorder + old.stack.recorder.output() } /// Init from start. @@ -359,16 +352,19 @@ impl HaltedStateRecord { } } + /// If halted, postition where it was halted. pub fn stopped_at(&self) -> Option<(Vec, bool)> { self.from.clone() } - pub fn is_finished(&self) -> bool { - self.from == None + /// Check if the state is halted. + pub fn is_halted(&self) -> bool { + self.from.is_some() } - pub fn finish(self) -> Recorder { - self.stack.recorder + /// Finalize state, and return the proof output. + pub fn finish(self) -> O { + self.stack.recorder.output() } fn finalize(&mut self) { @@ -376,6 +372,7 @@ impl HaltedStateRecord { let items = &stack.items; match &mut stack.recorder.output { RecorderStateInner::Compact { output, proof, stacked_pos } => { + // TODO apply same as content : record popped node calls?? let restarted_from = 0; if stacked_pos.len() > restarted_from { // halted: complete up to 0 and write all nodes keeping stack. @@ -400,7 +397,7 @@ impl HaltedStateRecord { } }, RecorderStateInner::Stream(_output) => { - // all written + // all written on access }, RecorderStateInner::Content { output: _, stacked_push, stacked_pop: _ } => { // TODO protect existing stack as for compact @@ -414,20 +411,25 @@ impl HaltedStateRecord { } } + /// Callback on node before a node in the stack. + /// `at` is the the position in the stack (in some case we keep + /// the stack and will not pop the node). + /// Return true if should halt. #[must_use] fn record_popped_node(&mut self, at: usize) -> bool { - let item = self.stack.items.get(at).expect("bounded iter"); + let item = self.stack.items.get(at).expect("bounded check call"); let items = &self.stack.items[..at]; let mut res = false; if !self.stack.recorder.check_start_at(item.depth) { return res } + /* TODO rem Incorrect, if first child is inline, we would push more: push + * should only be flushed on first pop flush here. if let RecorderStateInner::Content { .. } = &self.stack.recorder.output { // if no value accessed, then we can have push then stack pop. - if self.stack.recorder.flush_compact_content_pushes(item.depth) { - res = true; - } + res |= self.stack.recorder.flush_compact_content_pushes(item.depth); } + */ let mut process_children = false; let mut has_hash_to_write = false; @@ -444,30 +446,20 @@ impl HaltedStateRecord { .expect("TODO error handling, can it actually fail?"); } // else when restarting record, this is not to be recorded }, - RecorderStateInner::Content { .. } => { - // two case: children to register or all children accessed. - if let Some(last_item) = items.last() { - match last_item.node.node_plan() { - NodePlan::Branch { children, .. } | - NodePlan::NibbledBranch { children, .. } => { - process_children = true; - for i in 0..children.len() { - if children[i].is_some() && !last_item.accessed_children_node.at(i) - { - has_hash_to_write = true; - break - } - } - }, - _ => (), + RecorderStateInner::Content { .. } => match item.node.node_plan() { + NodePlan::Branch { children, .. } | NodePlan::NibbledBranch { children, .. } => { + process_children = true; + for i in 0..children.len() { + if children[i].is_some() && !item.accessed_children_node.at(i) { + has_hash_to_write = true; + break + } } - } + }, + _ => (), }, } - if process_children { - self.try_stack_content_child().expect("stack inline do not fetch"); - } let items = &self.stack.items[..at]; match &mut self.stack.recorder.output { RecorderStateInner::Content { output, stacked_pop, .. } => { @@ -477,15 +469,16 @@ impl HaltedStateRecord { } if has_hash_to_write { - if flush_compact_content_pop::( + res |= flush_compact_content_pop::( output, stacked_pop, items, None, &mut self.stack.recorder.limits, - ) { - res = true; - } + ); + } + if process_children { + res |= self.try_stack_content_child(at).expect("stack inline do not fetch"); } }, _ => (), @@ -497,15 +490,21 @@ impl HaltedStateRecord { // Add child for content fn try_stack_content_child( &mut self, + at: usize, //upper: u8, - ) -> Result<(), VerifyError, CError>> { + ) -> Result, CError>> { + let mut res = false; + let Some(item) = self.stack.get(at) else { + return Ok(res); + }; let dummy_parent_hash = TrieHash::::default(); - if !self.stack.items.is_empty() { - for i in 0..NIBBLE_LENGTH as u8 { - match self.stack.try_stack_child(i, None, dummy_parent_hash, None, true)? { - // only expect a stacked prefix here - TryStackChildResult::Stacked => { - let halt = self.iter_prefix(None, None, false, true, true)?; + for i in 0..NIBBLE_LENGTH as u8 { + if !item.accessed_children_node.at(i as usize) { + match self.stack.try_stack_child(i, None, dummy_parent_hash, None)? { + // only expect a stacked full prefix or not stacked here + TryStackChildResult::StackedFull => { + item.accessed_children_node.set(i, true); + let halt = self.iter_prefix(None, None, false, true)?; if halt { // no halt on inline. unreachable!() @@ -514,15 +513,37 @@ impl HaltedStateRecord { } }, TryStackChildResult::NotStackedBranch => (), - _ => break, + TryStackChildResult::NotStacked => break, + _ => unreachable!(), + } + } + } + for i in 0..NIBBLE_LENGTH as u8 { + if !item.accessed_children_node.at(i as usize) { + let node_data = item.node.data(); + + match item.node.node_plan() { + NodePlan::Empty | NodePlan::Leaf { .. } => (), + NodePlan::Extension { child, .. } => (), + NodePlan::NibbledBranch { children, .. } | + NodePlan::Branch { children, .. } => + if let Some(child) = &children[i as usize] { + match child.build(node_data) { + NodeHandle::Hash(hash) => { + res |= self.stack.recorder.touched_child_hash(&hash, i); + }, + _ => (), + } + }, } } } + self.stack .items .last_mut() .map(|i| i.next_descended_child = NIBBLE_LENGTH as u8); - Ok(()) + Ok(res) } fn pop(&mut self) -> bool { @@ -536,9 +557,7 @@ impl HaltedStateRecord { } let at = self.stack.items.len(); if at > 0 { - if self.record_popped_node(at - 1) { - self.stack.halt = true; - } + self.stack.halt |= self.record_popped_node(at - 1); } if let Some(item) = self.stack.items.pop() { let depth = self.stack.items.last().map(|i| i.depth).unwrap_or(0); @@ -559,7 +578,6 @@ impl HaltedStateRecord { db: Option<&TrieDB>, hash_only: bool, first_iter: bool, - inline_iter: bool, ) -> Result, CError>> { let dummy_parent_hash = TrieHash::::default(); if first_iter { @@ -587,21 +605,14 @@ impl HaltedStateRecord { break }; - match self.stack.try_stack_child( - child_index, - db, - dummy_parent_hash, - None, - inline_iter, - )? { - TryStackChildResult::Stacked => { + match self.stack.try_stack_child(child_index, db, dummy_parent_hash, None)? { + TryStackChildResult::StackedFull => { stacked = true; }, + TryStackChildResult::StackedInto => unreachable!("Not following plan"), + TryStackChildResult::StackedAfter => unreachable!("Not following plan"), TryStackChildResult::NotStackedBranch => (), TryStackChildResult::NotStacked => break, - TryStackChildResult::StackedDescendIncomplete => { - unreachable!("no slice query") - }, TryStackChildResult::Halted => { if let Some(mut item) = self.stack.items.last_mut() { item.next_descended_child -= 1; @@ -633,7 +644,7 @@ impl HaltedStateRecord { struct RecordStack { recorder: Recorder, - items: Vec, + items: Vec, prefix: NibbleVec, iter_prefix: Option<(usize, bool)>, seek: Option, @@ -654,13 +665,10 @@ pub fn record_query_plan< query_plan: &mut QueryPlan<'a, I>, from: &mut HaltedStateRecord, ) -> Result<(), VerifyError, CError>> { - // TODO - //) resto - // let restore_buf; let dummy_parent_hash = TrieHash::::default(); let mut stateless = false; let mut statefull = None; - // When define we iter prefix in a node but really want the next non inline. + // From indicate we restart,. if let Some(lower_bound) = from.from.take() { if from.currently_query_item.is_none() { stateless = true; @@ -672,9 +680,9 @@ pub fn record_query_plan< from.stack.recorder.start_at = Some(bound.len()); from.stack.seek = Some(bound); } else { + // statefull case let bound_len = lower_bound.0.len() * nibble_ops::NIBBLE_PER_BYTE - if lower_bound.1 { 2 } else { 1 }; - // from.stack.recorder.start_at = Some(bound_len); statefull = Some(bound_len); } } @@ -684,6 +692,7 @@ pub fn record_query_plan< let mut from_query_ref = from_query.as_ref().map(|f| f.as_ref()); while let Some(query) = from_query_ref.clone().or_else(|| query_plan.items.next()) { if stateless { + // advance query plan let bound = from.stack.seek.as_ref().expect("Initiated for stateless"); let bound = bound.as_leftnibbleslice(); let query_slice = LeftNibbleSlice::new(&query.key); @@ -714,12 +723,33 @@ pub fn record_query_plan< } } loop { + let mut first = true; match from.stack.prefix.len().cmp(&common_nibbles) { - Ordering::Equal | Ordering::Less => break, + Ordering::Equal | Ordering::Less => break common_nibbles, Ordering::Greater => { + if first { + // may be into a node partial in such a way we don't need pop. + let parent_depth = if from.stack.items.len() > 1 { + from.stack.items[from.stack.items.len() - 2].depth + } else { + 0 + }; + if common_nibbles > parent_depth { + let query_slice = LeftNibbleSlice::new(&query.key); + if query_slice.starts_with(&from.stack.prefix.as_leftnibbleslice()) + { + break query_slice.len() + } + } + } + + /* TODO these seems redundant with pop try_stack call if query_plan.kind.record_inline() { - from.try_stack_content_child()?; + if from.stack.items.len() > 0 { + from.try_stack_content_child(from.stack.items.len() - 1)?; + } } + */ if !from.pop() { from.finalize(); return Ok(()) @@ -727,11 +757,10 @@ pub fn record_query_plan< }, } } - common_nibbles }; if let Some((_, hash_only)) = from.stack.iter_prefix.clone() { // statefull halted during iteration. - let halt = from.iter_prefix(Some(&query), Some(db), hash_only, false, false)?; + let halt = from.iter_prefix(Some(&query), Some(db), hash_only, false)?; if halt { return Ok(()) } @@ -740,15 +769,14 @@ pub fn record_query_plan< continue } // descend - let add = if from.stack.items.len() == 0 { 0 } else { 1 }; - let mut slice_query = NibbleSlice::new_offset(&query.key, from.stack.prefix.len() + add); + let mut slice_query = NibbleSlice::new_offset(&query.key, from.stack.prefix.len()); let touched = loop { if !from.stack.items.is_empty() { if slice_query.is_empty() { if query.as_prefix { let halt = - from.iter_prefix(Some(&query), Some(db), query.hash_only, true, false)?; + from.iter_prefix(Some(&query), Some(db), query.hash_only, true)?; if halt { return Ok(()) } @@ -776,15 +804,15 @@ pub fn record_query_plan< Some(db), dummy_parent_hash, Some(&mut slice_query), - false, )? { - TryStackChildResult::Stacked => {}, + TryStackChildResult::StackedFull => {}, TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked => break false, - TryStackChildResult::StackedDescendIncomplete => { + TryStackChildResult::StackedAfter => break false, + TryStackChildResult::StackedInto => { if query.as_prefix { let halt = - from.iter_prefix(Some(&query), Some(db), query.hash_only, true, false)?; + from.iter_prefix(Some(&query), Some(db), query.hash_only, true)?; if halt { return Ok(()) } @@ -813,6 +841,7 @@ pub fn record_query_plan< from_query_ref = None; prev_query = Some(query); } + /* // TODO loop redundant with finalize?? loop { if query_plan.kind.record_inline() { @@ -823,6 +852,7 @@ pub fn record_query_plan< break } } + */ from.finalize(); Ok(()) } @@ -834,8 +864,8 @@ impl RecordStack { db: Option<&TrieDB>, parent_hash: TrieHash, mut slice_query: Option<&mut NibbleSlice>, - inline_only: bool, // TODO remove all inline only param and make it db is_none TODO rename ) -> Result, CError>> { + let inline_only = db.is_none(); let mut is_inline = false; let prefix = &mut self.prefix; let mut descend_incomplete = false; @@ -843,7 +873,10 @@ impl RecordStack { let mut stack_extension = false; let mut from_branch = None; let child_handle = if let Some(item) = self.items.last_mut() { - if inline_only && item.accessed_children_node.at(child_index as usize) { + //if inline_only && item.accessed_children_node.at(child_index as usize) { + if item.accessed_children_node.at(child_index as usize) { + // TODO may be unreachable (or remove some pre checks). + // No reason to go twice in a same branch return Ok(TryStackChildResult::NotStackedBranch) } @@ -858,6 +891,10 @@ impl RecordStack { if let &NodeHandle::Hash(_) = &child_handle { item.accessed_children_node.set(child_index as usize, true); } + if inline_only { + // mark all accesses ( + item.accessed_children_node.set(child_index as usize, true); + } child_handle } else { return Ok(TryStackChildResult::NotStacked) @@ -871,7 +908,7 @@ impl RecordStack { }, } } else { - NodeHandle::Hash(db.expect("non inline").root().as_ref()) + NodeHandle::Hash(db.expect("Inline call on non empty stack only").root().as_ref()) }; match &child_handle { NodeHandle::Inline(_) => { @@ -881,23 +918,26 @@ impl RecordStack { }, NodeHandle::Hash(hash) => { if inline_only { + /* TODO this should write on pop not on stack... if self.recorder.touched_child_hash(hash, child_index) { self.halt = true; } - if self.recorder.mark_inline_access() { + */ + if self.recorder.record_inline() { + // ignore hash in inline call, but mark as accessed. if let Some(accessed_children_node) = from_branch { accessed_children_node.set(child_index as usize, true); } } return Ok(TryStackChildResult::NotStackedBranch) - } - if self.halt && from_branch.is_some() { + } else if self.halt && from_branch.is_some() { + // halt condition return Ok(TryStackChildResult::Halted) } }, } if let Some(accessed_children_node) = from_branch { - if !is_inline || self.recorder.mark_inline_access() { + if !is_inline || self.recorder.record_inline() { accessed_children_node.set(child_index as usize, true); } @@ -907,26 +947,20 @@ impl RecordStack { // TODO handle cache first let child_node = if let Some(db) = db { db.get_raw_or_lookup_with_cache(parent_hash, child_handle, prefix.as_prefix(), false) - .map_err(|_| VerifyError::IncompleteProof)? // actually incomplete db: TODO consider switching error + .map_err(|_| VerifyError::IncompleteProof)? + .0 // actually incomplete db: TODO consider switching error } else { let NodeHandle::Inline(node_data) = child_handle else { unreachable!("call on non inline node when db is None"); }; - ( - OwnedNode::new::(node_data.to_vec()) - .map_err(|_| VerifyError::IncompleteProof)?, - None, - ) + OwnedNode::new::(node_data.to_vec()) + .map_err(|_| VerifyError::IncompleteProof)? }; - // } - - // TODO put in proof (only if Hash or inline for content one) - - let node_data = child_node.0.data(); + let node_data = child_node.data(); //println!("r: {:?}", &node_data); - match child_node.0.node_plan() { + let result = match child_node.node_plan() { NodePlan::Branch { .. } => (), | NodePlan::Empty => (), NodePlan::Leaf { partial, .. } | @@ -937,18 +971,24 @@ impl RecordStack { if let Some(s) = slice_query.as_mut() { if s.starts_with(&partial) { s.advance(partial.len()); + TryStackChildResult::StackedFull } else { descend_incomplete = true; descend_incomplete_stacked = partial.starts_with(s); + if partial.starts_with(s) { + TryStackChildResult::StackedInto + } else { + TryStackChildResult::StackedAfter + } } } }, - } - if let NodePlan::Extension { .. } = child_node.0.node_plan() { + }; + if let NodePlan::Extension { .. } = child_node.node_plan() { stack_extension = true; } let next_descended_child = if let Some(seek) = self.seek.as_ref() { - if prefix.len() < seek.len() { + if result == TryStackChildResult::StackedFull && prefix.len() < seek.len() { seek.at(prefix.len()) } else { self.seek = None; @@ -957,39 +997,29 @@ impl RecordStack { } else { 0 }; - let infos = CompactEncodingInfos { - node: child_node.0, + let infos = StackedNodeRecord { + node: child_node, accessed_children_node: Default::default(), accessed_value_node: false, depth: prefix.len(), next_descended_child, is_inline, }; - if self.recorder.record_stacked_node( + self.halt |= self.recorder.record_stacked_node( &infos, self.items.is_empty(), child_index, &self.items, - ) { - self.halt = true; - } + ); self.items.push(infos); if stack_extension { - let sbranch = self.try_stack_child(0, db, parent_hash, slice_query, inline_only)?; - let TryStackChildResult::Stacked = sbranch else { + let sbranch = self.try_stack_child(0, db, parent_hash, slice_query)?; + let TryStackChildResult::StackedFull = sbranch else { return Err(VerifyError::InvalidChildReference(b"branch in db should follow extension".to_vec())); }; } - if descend_incomplete { - if descend_incomplete_stacked { - Ok(TryStackChildResult::StackedDescendIncomplete) - } else { - Ok(TryStackChildResult::NotStacked) - } - } else { - Ok(TryStackChildResult::Stacked) - } + Ok(result) } fn access_value<'a>( @@ -997,11 +1027,9 @@ impl RecordStack { db: Option<&TrieDB>, hash_only: bool, ) -> Result, CError>> { - let Some(item)= self.items.last_mut() else { + let Some(item) = self.items.last_mut() else { return Ok(false) }; - // TODO this could be reuse from iterator, but it seems simple - // enough here too. let node_data = item.node.data(); let value = match item.node.node_plan() { @@ -1053,7 +1081,7 @@ impl RecordStack { fn flush_compact_content_pop( out: &mut O, stacked_from: &mut Option, - items: &[CompactEncodingInfos], + items: &[StackedNodeRecord], add_depth: Option, limits: &mut Limits, ) -> bool { diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 8dc1b0df..b45a17eb 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -45,6 +45,31 @@ where restore_offset: usize, } +struct ReadStack { + items: Vec>, + prefix: NibbleVec, + // limit and wether we return value and if hash only iteration. + iter_prefix: Option<(usize, bool, bool)>, + start_items: usize, + is_compact: bool, + expect_value: bool, + _ph: PhantomData, +} + +impl Clone for ReadStack { + fn clone(&self) -> Self { + ReadStack { + items: self.items.clone(), + prefix: self.prefix.clone(), + start_items: self.start_items.clone(), + iter_prefix: self.iter_prefix, + is_compact: self.is_compact, + expect_value: self.expect_value, + _ph: PhantomData, + } + } +} + /// Read the proof. /// /// If expected root is None, then we do not check hashes at all. @@ -410,3 +435,420 @@ impl<'a, L: TrieLayout, C, D: SplitFirst> From> } } } + +impl ReadStack { + fn try_stack_child( + &mut self, + child_index: u8, + proof: &mut impl Iterator, + expected_root: &Option>, + mut slice_query: Option<&mut NibbleSlice>, + query_prefix: bool, + ) -> Result, CError>> { + let check_hash = expected_root.is_some(); + let child_handle = if let Some(node) = self.items.last_mut() { + let node_data = node.data(); + + match node.node_plan() { + NodePlan::Empty | NodePlan::Leaf { .. } => + return Ok(TryStackChildResult::NotStacked), + NodePlan::Extension { .. } => { + unreachable!("Extension never stacked") + }, + NodePlan::NibbledBranch { children, .. } | NodePlan::Branch { children, .. } => + if let Some(child) = &children[child_index as usize] { + child.build(node_data) + } else { + return Ok(TryStackChildResult::NotStackedBranch) + }, + } + } else { + if self.is_compact { + NodeHandle::Inline(&[]) + } else { + NodeHandle::Hash(expected_root.as_ref().map(AsRef::as_ref).unwrap_or(&[])) + } + }; + let mut node: StackedNodeCheck<_, _> = match child_handle { + NodeHandle::Inline(data) => + if self.is_compact && data.len() == 0 { + // ommitted hash + let Some(mut encoded_node) = proof.next() else { + // halt happens with a hash, this is not. + return Err(VerifyError::IncompleteProof); + }; + if self.is_compact && + encoded_node.borrow().len() > 0 && + Some(encoded_node.borrow()[0]) == + ::ESCAPE_HEADER + { + self.expect_value = true; + // no value to visit TODO set a boolean to ensure we got a hash and don + // t expect reanding a node value + encoded_node.split_first(); + } + let node = match OwnedNode::new::(encoded_node) { + Ok(node) => node, + Err(e) => return Err(VerifyError::DecodeError(e)), + }; + (ItemStackNode::Node(node), self.is_compact).into() + } else { + // try access in inline then return + ( + ItemStackNode::Inline(match OwnedNode::new::(data.to_vec()) { + Ok(node) => node, + Err(e) => return Err(VerifyError::DecodeError(e)), + }), + self.is_compact, + ) + .into() + }, + NodeHandle::Hash(hash) => { + // TODO if is_compact allow only if restart bellow depth (otherwhise should be + // inline(0)) or means halted (if something in proof it is extraneous node) + let Some(mut encoded_node) = proof.next() else { + return Ok(TryStackChildResult::Halted); + }; + if self.is_compact && + encoded_node.borrow().len() > 0 && + Some(encoded_node.borrow()[0]) == + ::ESCAPE_HEADER + { + self.expect_value = true; + // no value to visit TODO set a boolean to ensure we got a hash and don + // t expect reanding a node value + encoded_node.split_first(); + } + let node = match OwnedNode::new::(encoded_node) { + Ok(node) => node, + Err(e) => return Err(VerifyError::DecodeError(e)), + }; + if !self.is_compact && check_hash { + verify_hash::(node.data(), hash)?; + } + (ItemStackNode::Node(node), self.is_compact).into() + }, + }; + let node_data = node.data(); + + let mut prefix_incomplete = false; + match node.node_plan() { + NodePlan::Branch { .. } => (), + | NodePlan::Empty => (), + NodePlan::Leaf { partial, .. } | + NodePlan::NibbledBranch { partial, .. } | + NodePlan::Extension { partial, .. } => { + let partial = partial.build(node_data); + if self.items.len() > 0 { + if let Some(slice) = slice_query.as_mut() { + slice.advance(1); + } + } + let ok = if let Some(slice) = slice_query.as_mut() { + if slice.starts_with(&partial) { + true + } else if query_prefix { + prefix_incomplete = true; + partial.starts_with(slice) + } else { + false + } + } else { + true + }; + if prefix_incomplete { + // end of query + slice_query = None; + } + if ok { + if self.items.len() > 0 { + self.prefix.push(child_index); + } + if let Some(slice) = slice_query.as_mut() { + slice.advance(partial.len()); + } + self.prefix.append_partial(partial.right()); + } else { + return Ok(TryStackChildResult::NotStacked) + } + }, + } + if let NodePlan::Extension { child, .. } = node.node_plan() { + let node_data = node.data(); + let child = child.build(node_data); + match child { + NodeHandle::Hash(hash) => { + let Some(encoded_branch) = proof.next() else { + // No halt on extension node (restart over a child index). + return Err(VerifyError::IncompleteProof); + }; + if self.is_compact { + let mut error_hash = TrieHash::::default(); + error_hash.as_mut().copy_from_slice(hash); + return Err(VerifyError::ExtraneousHashReference(error_hash)) + } + if check_hash { + verify_hash::(encoded_branch.borrow(), hash)?; + } + node = match OwnedNode::new::(encoded_branch) { + Ok(node) => (ItemStackNode::Node(node), self.is_compact).into(), + Err(e) => return Err(VerifyError::DecodeError(e)), + }; + }, + NodeHandle::Inline(data) => { + if self.is_compact && data.len() == 0 { + unimplemented!("This requires to put extension in stack"); + /* + // ommitted hash + let Some(encoded_node) = proof.next() else { + // halt happens with a hash, this is not. + return Err(VerifyError::IncompleteProof); + }; + node = match OwnedNode::new::(encoded_node) { + Ok(node) => (ItemStackNode::Node(node), self.is_compact).into(), + Err(e) => return Err(VerifyError::DecodeError(e)), + }; + */ + } else { + node = match OwnedNode::new::(data.to_vec()) { + Ok(node) => (ItemStackNode::Inline(node), self.is_compact).into(), + Err(e) => return Err(VerifyError::DecodeError(e)), + }; + } + }, + } + let NodePlan::Branch { .. } = node.node_plan() else { + return Err(VerifyError::IncompleteProof) // TODO make error type?? + }; + } + node.depth = self.prefix.len(); + // needed for compact + self.items.last_mut().map(|parent| { + parent.next_descended_child = child_index + 1; + }); + self.items.push(node); + if prefix_incomplete { + Ok(TryStackChildResult::StackedDescendIncomplete) + } else { + Ok(TryStackChildResult::Stacked) + } + } + + fn access_value( + &mut self, + proof: &mut impl Iterator, + check_hash: bool, + hash_only: bool, + ) -> Result<(Option>, Option>), VerifyError, CError>> { + if let Some(node) = self.items.last() { + let node_data = node.data(); + + let value = match node.node_plan() { + NodePlan::Leaf { value, .. } => Some(value.build(node_data)), + NodePlan::Branch { value, .. } | NodePlan::NibbledBranch { value, .. } => + value.as_ref().map(|v| v.build(node_data)), + _ => return Ok((None, None)), + }; + if let Some(value) = value { + match value { + Value::Inline(value) => + if self.expect_value { + assert!(self.is_compact); + self.expect_value = false; + if hash_only { + return Err(VerifyError::ExtraneousValue(Default::default())) + } + + let Some(value) = proof.next() else { + return Err(VerifyError::IncompleteProof); + }; + if check_hash { + let hash = L::Hash::hash(value.borrow()); + self.items.last_mut().map(|i| i.attached_value_hash = Some(hash)); + } + return Ok((Some(value.borrow().to_vec()), None)) + } else { + if hash_only { + let hash = L::Hash::hash(value.borrow()); + return Ok((None, Some(hash))) + } + return Ok((Some(value.to_vec()), None)) + }, + Value::Node(hash) => { + if self.expect_value { + if hash_only { + return Err(VerifyError::ExtraneousValue(Default::default())) + } + self.expect_value = false; + let mut error_hash = TrieHash::::default(); + error_hash.as_mut().copy_from_slice(hash); + return Err(VerifyError::ExtraneousHashReference(error_hash)) + } + if hash_only { + let mut result_hash = TrieHash::::default(); + result_hash.as_mut().copy_from_slice(hash); + return Ok((None, Some(result_hash))) + } + let Some(value) = proof.next() else { + return Err(VerifyError::IncompleteProof); + }; + if check_hash { + verify_hash::(value.borrow(), hash)?; + } + return Ok((Some(value.borrow().to_vec()), None)) + }, + } + } + } else { + return Err(VerifyError::IncompleteProof) + } + + Ok((None, None)) + } + + fn pop( + &mut self, + expected_root: &Option>, + ) -> Result, CError>> { + if self.iter_prefix.as_ref().map(|p| p.0 == self.items.len()).unwrap_or(false) { + return Ok(false) + } + if let Some(last) = self.items.pop() { + let depth = self.items.last().map(|i| i.depth).unwrap_or(0); + self.prefix.drop_lasts(self.prefix.len() - depth); + if self.is_compact && expected_root.is_some() { + match last.node { + ItemStackNode::Inline(_) => (), + ItemStackNode::Node(node) => { + let origin = self.start_items; + let node_data = node.data(); + let node = node.node_plan().build(node_data); + let encoded_node = crate::trie_codec::encode_read_node_internal::( + node, + &last.children, + last.attached_value_hash.as_ref().map(|h| h.as_ref()), + ); + + //println!("{:?}", encoded_node); + if self.items.len() == origin { + if let Some(parent) = self.items.last() { + let at = parent.next_descended_child - 1; + if let Some(Some(ChildReference::Hash(expected))) = + parent.children.get(at as usize) + { + verify_hash::(&encoded_node, expected.as_ref())?; + } else { + return Err(VerifyError::RootMismatch(Default::default())) + } + } else { + let expected = expected_root.as_ref().expect("checked above"); + verify_hash::(&encoded_node, expected.as_ref())?; + } + } else if self.items.len() < origin { + // popped origin, need to check against new origin + self.start_items = self.items.len(); + } else { + let hash = L::Hash::hash(&encoded_node); + if let Some(parent) = self.items.last_mut() { + let at = parent.next_descended_child - 1; + match parent.children[at as usize] { + Some(ChildReference::Hash(expected)) => { + // can append if chunks are concatenated (not progressively + // checked) + verify_hash::(&encoded_node, expected.as_ref())?; + }, + None => { + // Complete + parent.children[at as usize] = + Some(ChildReference::Hash(hash)); + }, + Some(ChildReference::Inline(_h, size)) if size == 0 => { + // Complete + parent.children[at as usize] = + Some(ChildReference::Hash(hash)); + }, + _ => + // only non inline are stacked + return Err(VerifyError::RootMismatch(Default::default())), + } + } else { + if &Some(hash) != expected_root { + return Err(VerifyError::RootMismatch(hash)) + } + } + } + }, + } + } + Ok(true) + } else { + Ok(false) + } + } + + fn pop_until( + &mut self, + target: usize, + expected_root: &Option>, + check_only: bool, + ) -> Result<(), VerifyError, CError>> { + if self.is_compact && expected_root.is_some() { + // TODO pop with check only, here unefficient implementation where we just restore + + let mut restore = None; + if check_only { + restore = Some(self.clone()); + self.iter_prefix = None; + } + // one by one + while let Some(last) = self.items.last() { + match last.depth.cmp(&target) { + Ordering::Greater => (), + // depth should match. + Ordering::Less => { + // TODO other error + return Err(VerifyError::ExtraneousNode) + }, + Ordering::Equal => return Ok(()), + } + // one by one + let _ = self.pop(expected_root)?; + } + + if let Some(old) = restore.take() { + *self = old; + return Ok(()) + } + } + loop { + if let Some(last) = self.items.last() { + match last.depth.cmp(&target) { + Ordering::Greater => (), + // depth should match. + Ordering::Less => break, + Ordering::Equal => { + self.prefix.drop_lasts(self.prefix.len() - last.depth); + return Ok(()) + }, + } + } else { + if target == 0 { + return Ok(()) + } else { + break + } + } + let _ = self.items.pop(); + } + // TODO other error + Err(VerifyError::ExtraneousNode) + } + + fn enter_prefix_iter(&mut self, hash_only: bool) { + self.iter_prefix = Some((self.items.len(), false, hash_only)); + } + + fn exit_prefix_iter(&mut self) { + self.iter_prefix = None + } +} diff --git a/trie-db/src/query_plan/verify_content.rs b/trie-db/src/query_plan/verify_content.rs index b5f83a40..f29c4b1c 100644 --- a/trie-db/src/query_plan/verify_content.rs +++ b/trie-db/src/query_plan/verify_content.rs @@ -41,6 +41,38 @@ where buf_op: Option, Vec>>, } +struct ReadContentStack { + items: Vec>, + prefix: NibbleVec, + // limit and wether we return value and if hash only iteration. + // TODO should be removable (just check current). + iter_prefix: Option<(usize, bool, bool)>, + start_items: usize, + is_prev_hash_child: Option, + expect_value: bool, + is_prev_push_key: bool, + is_prev_pop_key: bool, + first: bool, + _ph: PhantomData, +} + +impl Clone for ReadContentStack { + fn clone(&self) -> Self { + ReadContentStack { + items: self.items.clone(), + prefix: self.prefix.clone(), + start_items: self.start_items.clone(), + iter_prefix: self.iter_prefix, + expect_value: self.expect_value, + is_prev_push_key: self.is_prev_push_key, + is_prev_pop_key: self.is_prev_pop_key, + is_prev_hash_child: self.is_prev_hash_child, + first: self.first, + _ph: PhantomData, + } + } +} + /// Read the proof. /// /// If expected root is None, then we do not check hashes at all. @@ -334,7 +366,11 @@ where r }, Op::EndProof => break, - op => self.stack.set_cache_change(op.into()), + Op::HashChild(hash, child_ix) => self.stack.set_branch_change(hash, child_ix), + op => { + self.stack.set_value_change(op.into()); + Ok(()) + }, }; if let Err(e) = r { self.state = ReadProofState::Finished; @@ -395,3 +431,315 @@ impl<'a, L: TrieLayout, C> From> for HaltedStateCheckContent<'a } } } + +impl ReadContentStack { + fn pop_until( + &mut self, + target: usize, + check_only: bool, // TODO used? + ) -> Result<(), VerifyError, CError>> { + // TODO pop with check only, here unefficient implementation where we just restore + + let mut restore = None; + if check_only { + restore = Some(self.clone()); + self.iter_prefix = None; + } + // one by one + while let Some(last) = self.items.last() { + // depth should match. + match last.depth.cmp(&target) { + Ordering::Greater => { + // TODO could implicit pop here with a variant + // that do not write redundant pop. + return Err(VerifyError::ExtraneousNode) // TODO more precise error + }, + Ordering::Less => { + if self.first { + // allowed to have next at a upper level + } else { + return Err(VerifyError::ExtraneousNode) + } + }, + Ordering::Equal => return Ok(()), + } + + // start_items update. + self.start_items = core::cmp::min(self.start_items, self.items.len()); + // one by one + let _ = self.items.pop(); + } + + if let Some(old) = restore.take() { + *self = old; + return Ok(()) + } + if self.items.is_empty() && target == 0 { + Ok(()) + } else { + Err(VerifyError::ExtraneousNode) + } + } + + #[inline(always)] + fn stack_empty(&mut self, depth: usize) { + /* + items: Vec>, + prefix: NibbleVec, + // limit and wether we return value and if hash only iteration. + iter_prefix: Option<(usize, bool, bool)>, + start_items: usize, + */ + + self.items.push(ItemContentStack { + children: vec![None; NIBBLE_LENGTH], + value: ValueSet::None, + depth, + }) + } + + #[inline(always)] + fn stack_pop( + &mut self, + nb_nibble: Option, + expected_root: &Option>, + ) -> Result<(), VerifyError, CError>> { + let target_depth = nb_nibble.map(|n| self.prefix.len() - n); + let mut first = true; + while self + .items + .last() + .map(|item| target_depth.map(|target| item.depth > target).unwrap_or(true)) + .unwrap_or(false) + { + let item = self.items.pop().expect("Checked"); + let mut from_depth = + self.items.last().map(|item| item.depth).unwrap_or(target_depth.unwrap_or(0)); + if let Some(from) = target_depth { + if from > from_depth { + self.stack_empty(from); + from_depth = from; + } + } + let depth = item.depth; + let is_root = target_depth.is_none() && self.items.is_empty(); + let inc = if is_root { 0 } else { 1 }; + + let child_reference = if item.children.iter().any(|child| child.is_some()) { + let nkey = (depth > (from_depth + inc)) + .then(|| (from_depth + inc, depth - from_depth - inc)); + if L::USE_EXTENSION { + let extension_only = first && + matches!(&item.value, &ValueSet::None) && + item.children.iter().filter(|child| child.is_some()).count() == 1; + self.items.push(item); // TODO this looks bad (pop then push, branch or leaf function should or should + // not pop instead) + // encode branch + self.standard_extension(depth, is_root, nkey, extension_only) + } else { + self.items.push(item); // TODO this looks bad (pop then push, branch or leaf function should or should + // not pop instead) + // encode branch + self.no_extension(depth, is_root, nkey) + } + } else { + // leaf with value + self.flush_value_change(from_depth + inc, item.depth, &item.value, is_root) + }; + + if self.items.is_empty() && !is_root { + self.stack_empty(from_depth); + } + + let items_len = self.items.len(); + if let Some(item) = self.items.last_mut() { + let child_ix = self.prefix.at(item.depth); + if let Some(hash) = item.children[child_ix as usize].as_ref() { + if items_len == self.start_items + 1 { + if expected_root.is_some() && hash != &child_reference { + return Err(VerifyError::HashMismatch(*child_reference.disp_hash())) + } + } else { + return Err(VerifyError::ExtraneousHashReference(*hash.disp_hash())) + // return Err(CompactDecoderError::HashChildNotOmitted.into()) + } + } + item.children[child_ix as usize] = Some(child_reference); + } else { + if let Some(root) = expected_root.as_ref() { + if nb_nibble.is_none() { + if root != child_reference.disp_hash() { + return Err(VerifyError::RootMismatch(*child_reference.disp_hash())) + } + } + } + } + first = false; + // TODO can skip hash checks when above start_items. + self.start_items = core::cmp::min(self.start_items, self.items.len()); + } + Ok(()) + } + + fn process(encoded_node: Vec, is_root: bool) -> ChildReference> { + let len = encoded_node.len(); + if !is_root && len < ::LENGTH { + let mut h = <::Out as Default>::default(); + h.as_mut()[..len].copy_from_slice(&encoded_node[..len]); + return ChildReference::Inline(h, len) + } + let hash = ::hash(encoded_node.as_slice()); + ChildReference::Hash(hash) + } + + // TODO factor with iter_build (reuse cacheaccum here). + #[inline(always)] + fn standard_extension( + &mut self, + branch_d: usize, + is_root: bool, + nkey: Option<(usize, usize)>, + extension_only: bool, + ) -> ChildReference> { + let key_branch = &self.prefix.inner().as_ref()[..]; + let last = self.items.len() - 1; + assert_eq!(self.items[last].depth, branch_d); + + let ItemContentStack { children, value: v, depth, .. } = self.items.pop().expect("checked"); + + debug_assert!(branch_d == depth); + + let hashed; + let value = if let Some(v) = v.as_ref() { + Some(if let Some(value) = Value::new_inline(v.as_ref(), L::MAX_INLINE_VALUE) { + value + } else { + let mut prefix = NibbleSlice::new_offset(&key_branch, 0); + prefix.advance(branch_d); + + hashed = ::hash(v.as_ref()); + Value::Node(hashed.as_ref()) + }) + } else { + None + }; + + // encode branch + let branch_hash = if !extension_only { + let encoded = L::Codec::branch_node(children.iter(), value); + Self::process(encoded, is_root && nkey.is_none()) + } else { + // This is hacky but extension only store as first children + children[0].unwrap() + }; + + if let Some(nkeyix) = nkey { + let pr = NibbleSlice::new_offset(&key_branch, nkeyix.0); + let nib = pr.right_range_iter(nkeyix.1); + let encoded = L::Codec::extension_node(nib, nkeyix.1, branch_hash); + Self::process(encoded, is_root) + } else { + branch_hash + } + } + + #[inline(always)] + fn no_extension( + &mut self, + branch_d: usize, + is_root: bool, + nkey: Option<(usize, usize)>, + ) -> ChildReference> { + let key_branch = &self.prefix.inner().as_ref()[..]; + let ItemContentStack { children, value: v, depth, .. } = self.items.pop().expect("checked"); + + debug_assert!(branch_d == depth); + // encode branch + let nkeyix = nkey.unwrap_or((branch_d, 0)); + let pr = NibbleSlice::new_offset(&key_branch, nkeyix.0); + let hashed; + let value = if let Some(v) = v.as_ref() { + Some(if let Some(value) = Value::new_inline(v.as_ref(), L::MAX_INLINE_VALUE) { + value + } else { + let mut prefix = NibbleSlice::new_offset(&key_branch, 0); + prefix.advance(branch_d); + hashed = ::hash(v.as_ref()); + Value::Node(hashed.as_ref()) + }) + } else { + if let ValueSet::HashOnly(h) = &v { + Some(Value::Node(h.as_ref())) + } else { + None + } + }; + + let encoded = L::Codec::branch_node_nibbled( + pr.right_range_iter(nkeyix.1), + nkeyix.1, + children.iter(), + value, + ); + Self::process(encoded, is_root) + } + + fn flush_value_change<'a>( + &mut self, + from_depth: usize, + to_depth: usize, + value: &ValueSet, Vec>, + is_root: bool, + ) -> ChildReference> { + let key_content = &self.prefix.inner().as_ref()[..]; + let k2 = &key_content[..to_depth / nibble_ops::NIBBLE_PER_BYTE]; + let pr = NibbleSlice::new_offset(k2, from_depth); + + let hashed; + let value = match value { + ValueSet::Standard(v) => + if let Some(value) = Value::new_inline(v.as_ref(), L::MAX_INLINE_VALUE) { + value + } else { + hashed = ::hash(v.as_ref()); + Value::Node(hashed.as_ref()) + }, + ValueSet::HashOnly(h) => { + Value::Node(h.as_ref()) // TODO may have following hash and fail? ont if leaf + }, + ValueSet::BranchHash(..) | ValueSet::None => unreachable!("Not in cache accum"), + }; + let encoded = L::Codec::leaf_node(pr.right_iter(), pr.len(), value); + Self::process(encoded, is_root) + } + + #[inline(always)] + fn set_value_change(&mut self, change: ValueSet, Vec>) { + if self.items.is_empty() { + self.stack_empty(0); + } + let last = self.items.len() - 1; + let mut item = &mut self.items[last]; + item.value = change; + } + + #[inline(always)] + fn set_branch_change( + &mut self, + branch_hash: TrieHash, + branch_index: u8, + ) -> Result<(), VerifyError, CError>> { + if self.items.is_empty() { + self.stack_empty(0); + } + let last = self.items.len() - 1; + let mut item = &mut self.items[last]; + let i = branch_index as usize; + if let Some(hash) = item.children[i].as_ref() { + return Err(VerifyError::ExtraneousHashReference(*hash.disp_hash())) + //return Err(CompactDecoderError::HashChildNotOmitted.into()) TODO + } + item.children[i] = Some(ChildReference::Hash(branch_hash)); + Ok(()) + } +} diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index 38f9aaaf..967603e3 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -685,13 +685,13 @@ pub mod query_plan { record_query_plan::(&db, &mut query_plan_iter, &mut from).unwrap(); if limit.is_none() { - assert!(from.is_finished()); + assert!(!from.is_halted()); } - if from.is_finished() { + if !from.is_halted() { if kind == ProofKind::CompactContent { - proofs.push(vec![from.finish().output().buffer]); + proofs.push(vec![from.output().buffer]); } else { - proofs.push(from.finish().output().nodes); + proofs.push(from.output().nodes); } break } @@ -702,9 +702,9 @@ pub mod query_plan { from.stateless(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) }; if kind == ProofKind::CompactContent { - proofs.push(vec![rec.output().buffer]); + proofs.push(vec![rec.buffer]); } else { - proofs.push(rec.output().nodes); + proofs.push(rec.nodes); } } diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index e15a5b19..2453e1c1 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -110,13 +110,13 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { record_query_plan::(&db, &mut query_plan_iter, &mut from).unwrap(); if limit.is_none() { - assert!(from.is_finished()); + assert!(!from.is_halted()); } - if from.is_finished() { + if !from.is_halted() { if kind == ProofKind::CompactContent { - proofs.push(vec![from.finish().output().buffer]); + proofs.push(vec![from.finish().buffer]); } else { - proofs.push(from.finish().output().nodes); + proofs.push(from.finish().nodes); } break } @@ -127,10 +127,10 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { from.statefull(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) }; if kind == ProofKind::CompactContent { - proofs.push(vec![rec.output().buffer]); + proofs.push(vec![rec.buffer]); break // TODO remove } else { - proofs.push(rec.output().nodes); + proofs.push(rec.nodes); } } let content: BTreeMap<_, _> = From bc8e6acd3adb92a2d3504b395c02bcd9fb5dd36a Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 21 Jun 2023 19:39:18 +0200 Subject: [PATCH 085/154] simplify by moving seek correctly --- trie-db/src/query_plan/mod.rs | 1 + trie-db/src/query_plan/record.rs | 32 +++++++++++++++++--------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index bd6cb27c..9bc44b59 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -372,6 +372,7 @@ pub enum HaltedStateCheck<'a, L: TrieLayout, C, D: SplitFirst> { Content(HaltedStateCheckContent<'a, L, C>), } +#[derive(Eq, PartialEq)] enum TryStackChildResult { /// If there is no child to stack. NotStacked, diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 0db34541..f4dd490f 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -494,7 +494,7 @@ impl HaltedStateRecord { //upper: u8, ) -> Result, CError>> { let mut res = false; - let Some(item) = self.stack.get(at) else { + let Some(item) = self.stack.items.get(at) else { return Ok(res); }; let dummy_parent_hash = TrieHash::::default(); @@ -503,7 +503,7 @@ impl HaltedStateRecord { match self.stack.try_stack_child(i, None, dummy_parent_hash, None)? { // only expect a stacked full prefix or not stacked here TryStackChildResult::StackedFull => { - item.accessed_children_node.set(i, true); + item.accessed_children_node.set(i as usize, true); let halt = self.iter_prefix(None, None, false, true)?; if halt { // no halt on inline. @@ -734,6 +734,9 @@ pub fn record_query_plan< } else { 0 }; + // seek got updated correctle. + debug_assert!(common_nibbles <= parent_depth); + /* TODO rem if debug_assert ok if common_nibbles > parent_depth { let query_slice = LeftNibbleSlice::new(&query.key); if query_slice.starts_with(&from.stack.prefix.as_leftnibbleslice()) @@ -741,6 +744,7 @@ pub fn record_query_plan< break query_slice.len() } } + */ } /* TODO these seems redundant with pop try_stack call @@ -868,8 +872,6 @@ impl RecordStack { let inline_only = db.is_none(); let mut is_inline = false; let prefix = &mut self.prefix; - let mut descend_incomplete = false; - let mut descend_incomplete_stacked = false; let mut stack_extension = false; let mut from_branch = None; let child_handle = if let Some(item) = self.items.last_mut() { @@ -961,26 +963,26 @@ impl RecordStack { //println!("r: {:?}", &node_data); let result = match child_node.node_plan() { - NodePlan::Branch { .. } => (), - | NodePlan::Empty => (), + NodePlan::Branch { .. } | NodePlan::Empty => TryStackChildResult::StackedFull, NodePlan::Leaf { partial, .. } | NodePlan::NibbledBranch { partial, .. } | NodePlan::Extension { partial, .. } => { let partial = partial.build(node_data); prefix.append_partial(partial.right()); if let Some(s) = slice_query.as_mut() { - if s.starts_with(&partial) { - s.advance(partial.len()); + let common = partial.common_prefix(s); + s.advance(common); + // s starts with partial + if common == partial.len() { TryStackChildResult::StackedFull + } else if common == s.len() { + // partial strats with s + TryStackChildResult::StackedInto } else { - descend_incomplete = true; - descend_incomplete_stacked = partial.starts_with(s); - if partial.starts_with(s) { - TryStackChildResult::StackedInto - } else { - TryStackChildResult::StackedAfter - } + TryStackChildResult::StackedAfter } + } else { + TryStackChildResult::StackedFull } }, }; From fa1670985312d77a6c6ad34baad8946f2215ab48 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 21 Jun 2023 19:45:27 +0200 Subject: [PATCH 086/154] prev asumption do not hold --- trie-db/src/query_plan/record.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index f4dd490f..ab2294f0 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -734,9 +734,6 @@ pub fn record_query_plan< } else { 0 }; - // seek got updated correctle. - debug_assert!(common_nibbles <= parent_depth); - /* TODO rem if debug_assert ok if common_nibbles > parent_depth { let query_slice = LeftNibbleSlice::new(&query.key); if query_slice.starts_with(&from.stack.prefix.as_leftnibbleslice()) @@ -744,7 +741,6 @@ pub fn record_query_plan< break query_slice.len() } } - */ } /* TODO these seems redundant with pop try_stack call From c830a300526f9e16a56e025e7f73184e221c0c17 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 22 Jun 2023 18:18:43 +0200 Subject: [PATCH 087/154] fix next condition (content is not) --- trie-db/src/nibble/leftnibbleslice.rs | 22 ++++++ trie-db/src/query_plan/mod.rs | 2 + trie-db/src/query_plan/record.rs | 53 +++++++------- trie-db/src/query_plan/verify.rs | 92 +++++++++++++----------- trie-db/src/query_plan/verify_content.rs | 4 +- trie-db/test/src/fuzz.rs | 4 +- 6 files changed, 106 insertions(+), 71 deletions(-) diff --git a/trie-db/src/nibble/leftnibbleslice.rs b/trie-db/src/nibble/leftnibbleslice.rs index 0424205f..9e9b8f66 100644 --- a/trie-db/src/nibble/leftnibbleslice.rs +++ b/trie-db/src/nibble/leftnibbleslice.rs @@ -82,6 +82,28 @@ impl<'a> LeftNibbleSlice<'a> { (0..partial.len()).all(|i| self.at(offset + i) == Some(partial.at(i))) } + /// How many of the same nibbles at the beginning do we match with `them`? + pub fn common_prefix(&self, them: &Self) -> usize { + let max = cmp::min(self.len, them.len); + let mut nb = 0; + for i in 0..(max / NIBBLE_PER_BYTE) { + if self.bytes[i] == them.bytes[i] { + nb += NIBBLE_PER_BYTE; + } else { + break + } + } + for i in 0..NIBBLE_PER_BYTE { + let s_at = self.at(nb + 1); + if s_at.is_some() && self.at(nb + i) == them.at(nb + i) { + nb += 1; + } else { + break + } + } + nb + } + fn cmp(&self, other: &Self) -> Ordering { let common_len = cmp::min(self.len(), other.len()); let common_byte_len = common_len / NIBBLE_PER_BYTE; diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index 9bc44b59..755bf272 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -175,6 +175,7 @@ pub enum ProofKind { CompactContent, } +/* impl ProofKind { // Do we need to record child hash and inline value individually. fn record_inline(&self) -> bool { @@ -184,6 +185,7 @@ impl ProofKind { } } } +*/ #[derive(Default, Clone, Copy)] struct Bitmap(u16); diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index ab2294f0..da2f1268 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -418,7 +418,6 @@ impl HaltedStateRecord { #[must_use] fn record_popped_node(&mut self, at: usize) -> bool { let item = self.stack.items.get(at).expect("bounded check call"); - let items = &self.stack.items[..at]; let mut res = false; if !self.stack.recorder.check_start_at(item.depth) { return res @@ -494,15 +493,18 @@ impl HaltedStateRecord { //upper: u8, ) -> Result, CError>> { let mut res = false; - let Some(item) = self.stack.items.get(at) else { - return Ok(res); - }; let dummy_parent_hash = TrieHash::::default(); for i in 0..NIBBLE_LENGTH as u8 { + let Some(item) = self.stack.items.get(at) else { + return Ok(res); + }; if !item.accessed_children_node.at(i as usize) { match self.stack.try_stack_child(i, None, dummy_parent_hash, None)? { // only expect a stacked full prefix or not stacked here TryStackChildResult::StackedFull => { + let Some(item) = self.stack.items.get_mut(at) else { + return Ok(res); + }; item.accessed_children_node.set(i as usize, true); let halt = self.iter_prefix(None, None, false, true)?; if halt { @@ -518,13 +520,16 @@ impl HaltedStateRecord { } } } + let Some(item) = self.stack.items.get(at) else { + return Ok(res); + }; for i in 0..NIBBLE_LENGTH as u8 { if !item.accessed_children_node.at(i as usize) { let node_data = item.node.data(); match item.node.node_plan() { NodePlan::Empty | NodePlan::Leaf { .. } => (), - NodePlan::Extension { child, .. } => (), + NodePlan::Extension { .. } => (), NodePlan::NibbledBranch { children, .. } | NodePlan::Branch { children, .. } => if let Some(child) = &children[i as usize] { @@ -710,7 +715,7 @@ pub fn record_query_plan< from.stack.seek = None; } } - let common_nibbles = if let Some(slice_at) = statefull.take() { + let _common_nibbles = if let Some(slice_at) = statefull.take() { slice_at } else { let (ordered, common_nibbles) = @@ -722,27 +727,27 @@ pub fn record_query_plan< return Err(VerifyError::UnorderedKey(query.key.to_vec())) } } + let query_slice = LeftNibbleSlice::new(&query.key); + // TODO this could also be passed around from try stack result then + // slice_query_len + let common_from = query_slice.common_prefix(&from.stack.prefix.as_leftnibbleslice()); + if common_from < common_nibbles { + /*if query.as_prefix { + let halt = + from.iter_prefix(Some(&query), Some(db), query.hash_only, true)?; + if halt { + return Ok(()) + } + }*/ + from_query_ref = None; + prev_query = Some(query); + continue + } + let common_nibbles = max(common_nibbles, common_from); loop { - let mut first = true; match from.stack.prefix.len().cmp(&common_nibbles) { Ordering::Equal | Ordering::Less => break common_nibbles, Ordering::Greater => { - if first { - // may be into a node partial in such a way we don't need pop. - let parent_depth = if from.stack.items.len() > 1 { - from.stack.items[from.stack.items.len() - 2].depth - } else { - 0 - }; - if common_nibbles > parent_depth { - let query_slice = LeftNibbleSlice::new(&query.key); - if query_slice.starts_with(&from.stack.prefix.as_leftnibbleslice()) - { - break query_slice.len() - } - } - } - /* TODO these seems redundant with pop try_stack call if query_plan.kind.record_inline() { if from.stack.items.len() > 0 { @@ -914,7 +919,7 @@ impl RecordStack { // Returning NotStacked here sounds safe, then the is_inline field is not needed. is_inline = true; }, - NodeHandle::Hash(hash) => { + NodeHandle::Hash(_) => { if inline_only { /* TODO this should write on pop not on stack... if self.recorder.touched_child_hash(hash, child_index) { diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index b45a17eb..9371ebec 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -40,9 +40,9 @@ where is_compact: bool, expected_root: Option>, current: Option>, + current_offset: usize, state: ReadProofState, stack: ReadStack, - restore_offset: usize, } struct ReadStack { @@ -104,7 +104,7 @@ where current, state, stack, - restore_offset, + current_offset: restore_offset, }) } @@ -117,7 +117,6 @@ where { fn halt( &mut self, - to_check_slice: Option<&mut NibbleSlice>, ) -> Option, VerifyError, CError>>> { if self.is_compact { let stack_to = 0; // TODO restart is different @@ -147,9 +146,9 @@ where Some(Ok(ReadProofItem::Halted(Box::new(HaltedStateCheck::Node(HaltedStateCheckNode { query_plan, current, + restore_offset: self.current_offset, stack, state: ReadProofState::Halted, - restore_offset: to_check_slice.map(|s| s.offset()).unwrap_or(0), }))))) } } @@ -174,7 +173,7 @@ where let mut to_check_slice = self .current .as_ref() - .map(|n| NibbleSlice::new_offset(n.key, self.restore_offset)); + .map(|n| NibbleSlice::new_offset(n.key, self.current_offset)); // read proof loop { @@ -197,11 +196,23 @@ where } } + let query_slice = LeftNibbleSlice::new(&next.key); + // TODO this could also be passed around from try stack result then + // slice_query_len + let common_from = + query_slice.common_prefix(&self.stack.prefix.as_leftnibbleslice()); + if common_from < common_nibbles { + self.current = Some(next); + self.state = ReadProofState::SwitchQueryPlan; + continue + } + let r = self.stack.pop_until(common_nibbles, &self.expected_root, false); if let Err(e) = r { self.state = ReadProofState::Finished; return Some(Err(e)) } + self.state = ReadProofState::Running; self.current = Some(next); to_check_slice = self @@ -254,7 +265,6 @@ where &mut self.proof, &self.expected_root, None, - false, ) { Ok(r) => r, Err(e) => { @@ -262,14 +272,15 @@ where return Some(Err(e)) }, }; + self.current_offset = to_check_slice.map(|s| s.offset()).unwrap_or(0); match r { - TryStackChildResult::Stacked => { + TryStackChildResult::StackedFull => { self.stack.iter_prefix.as_mut().map(|p| { p.1 = false; }); break }, - TryStackChildResult::StackedDescendIncomplete => { + TryStackChildResult::StackedAfter | TryStackChildResult::StackedInto => { unreachable!("slice query none"); }, TryStackChildResult::NotStacked => break, @@ -278,7 +289,7 @@ where if let Some(last) = self.stack.items.last_mut() { last.next_descended_child -= 1; } - return self.halt(None) + return self.halt() }, } } @@ -300,6 +311,7 @@ where self.state = ReadProofState::SwitchQueryPlan; continue } + let to_check = self.current.as_ref().expect("Init above"); let to_check_len = to_check.key.len() * nibble_ops::NIBBLE_PER_BYTE; let mut to_check_slice = to_check_slice.as_mut().expect("Init above"); @@ -313,7 +325,10 @@ where }, Ordering::Less => (), Ordering::Greater => { - unreachable!(); + // two consecutive query in a node that hide them (two miss in a same proof + // node). + self.state = ReadProofState::SwitchQueryPlan; + continue }, } @@ -348,7 +363,6 @@ where &mut self.proof, &self.expected_root, Some(&mut to_check_slice), - to_check.as_prefix, ) { Ok(r) => r, Err(e) => { @@ -356,9 +370,10 @@ where return Some(Err(e)) }, }; + self.current_offset = to_check_slice.offset(); match r { - TryStackChildResult::Stacked => (), - TryStackChildResult::StackedDescendIncomplete => { + TryStackChildResult::StackedFull => (), + TryStackChildResult::StackedInto => { if as_prefix { self.stack.enter_prefix_iter(hash_only); continue @@ -366,6 +381,10 @@ where self.state = ReadProofState::SwitchQueryPlan; return Some(Ok(ReadProofItem::NoValue(to_check.key))) }, + TryStackChildResult::StackedAfter => { + self.state = ReadProofState::SwitchQueryPlan; + return Some(Ok(ReadProofItem::NoValue(to_check.key))) + }, TryStackChildResult::NotStacked => { self.state = ReadProofState::SwitchQueryPlan; return Some(Ok(ReadProofItem::NoValue(to_check.key))) @@ -374,7 +393,7 @@ where self.state = ReadProofState::SwitchQueryPlan; return Some(Ok(ReadProofItem::NoValue(to_check.key))) }, - TryStackChildResult::Halted => return self.halt(Some(to_check_slice)), + TryStackChildResult::Halted => return self.halt(), } } @@ -443,7 +462,6 @@ impl ReadStack { proof: &mut impl Iterator, expected_root: &Option>, mut slice_query: Option<&mut NibbleSlice>, - query_prefix: bool, ) -> Result, CError>> { let check_hash = expected_root.is_some(); let child_handle = if let Some(node) = self.items.last_mut() { @@ -474,9 +492,9 @@ impl ReadStack { if self.is_compact && data.len() == 0 { // ommitted hash let Some(mut encoded_node) = proof.next() else { - // halt happens with a hash, this is not. - return Err(VerifyError::IncompleteProof); - }; + // halt happens with a hash, this is not. + return Err(VerifyError::IncompleteProof); + }; if self.is_compact && encoded_node.borrow().len() > 0 && Some(encoded_node.borrow()[0]) == @@ -530,8 +548,7 @@ impl ReadStack { }, }; let node_data = node.data(); - - let mut prefix_incomplete = false; + let mut result = TryStackChildResult::StackedFull; match node.node_plan() { NodePlan::Branch { .. } => (), | NodePlan::Empty => (), @@ -542,35 +559,28 @@ impl ReadStack { if self.items.len() > 0 { if let Some(slice) = slice_query.as_mut() { slice.advance(1); + self.prefix.push(child_index); } } - let ok = if let Some(slice) = slice_query.as_mut() { + result = if let Some(slice) = slice_query.as_mut() { if slice.starts_with(&partial) { - true - } else if query_prefix { - prefix_incomplete = true; - partial.starts_with(slice) + TryStackChildResult::StackedFull + } else if partial.starts_with(slice) { + TryStackChildResult::StackedInto } else { - false + TryStackChildResult::StackedAfter } } else { - true + TryStackChildResult::StackedInto }; - if prefix_incomplete { + if result == TryStackChildResult::StackedAfter { // end of query slice_query = None; } - if ok { - if self.items.len() > 0 { - self.prefix.push(child_index); - } - if let Some(slice) = slice_query.as_mut() { - slice.advance(partial.len()); - } - self.prefix.append_partial(partial.right()); - } else { - return Ok(TryStackChildResult::NotStacked) + if let Some(slice) = slice_query.as_mut() { + slice.advance(partial.len()); } + self.prefix.append_partial(partial.right()); }, } if let NodePlan::Extension { child, .. } = node.node_plan() { @@ -627,11 +637,7 @@ impl ReadStack { parent.next_descended_child = child_index + 1; }); self.items.push(node); - if prefix_incomplete { - Ok(TryStackChildResult::StackedDescendIncomplete) - } else { - Ok(TryStackChildResult::Stacked) - } + Ok(result) } fn access_value( diff --git a/trie-db/src/query_plan/verify_content.rs b/trie-db/src/query_plan/verify_content.rs index f29c4b1c..ac356c05 100644 --- a/trie-db/src/query_plan/verify_content.rs +++ b/trie-db/src/query_plan/verify_content.rs @@ -707,7 +707,7 @@ impl ReadContentStack { ValueSet::HashOnly(h) => { Value::Node(h.as_ref()) // TODO may have following hash and fail? ont if leaf }, - ValueSet::BranchHash(..) | ValueSet::None => unreachable!("Not in cache accum"), + ValueSet::None => unreachable!("Not in cache accum"), }; let encoded = L::Codec::leaf_node(pr.right_iter(), pr.len(), value); Self::process(encoded, is_root) @@ -733,7 +733,7 @@ impl ReadContentStack { self.stack_empty(0); } let last = self.items.len() - 1; - let mut item = &mut self.items[last]; + let item = &mut self.items[last]; let i = branch_index as usize; if let Some(hash) = item.children[i].as_ref() { return Err(VerifyError::ExtraneousHashReference(*hash.disp_hash())) diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index 967603e3..7c5f8548 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -689,9 +689,9 @@ pub mod query_plan { } if !from.is_halted() { if kind == ProofKind::CompactContent { - proofs.push(vec![from.output().buffer]); + proofs.push(vec![from.finish().buffer]); } else { - proofs.push(from.output().nodes); + proofs.push(from.finish().nodes); } break } From 86d37a36f116a57ab601eca904452fcc56236317 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 22 Jun 2023 19:33:40 +0200 Subject: [PATCH 088/154] some fixes --- trie-db/src/content_proof.rs | 2 +- trie-db/src/query_plan/record.rs | 2 +- trie-db/src/query_plan/verify.rs | 14 +++++++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/trie-db/src/content_proof.rs b/trie-db/src/content_proof.rs index d1d78b8e..d63d9854 100644 --- a/trie-db/src/content_proof.rs +++ b/trie-db/src/content_proof.rs @@ -99,7 +99,7 @@ impl VarInt { #[test] fn varint_encode_decode() { - let mut buf = super::InMemoryRecorder::default(); + let mut buf = crate::query_plan::InMemoryRecorder::default(); for i in 0..u16::MAX as u32 + 1 { VarInt(i).encode_into(&mut buf); assert_eq!(buf.buffer.len(), VarInt(i).encoded_len()); diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index da2f1268..f7f065b7 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -991,7 +991,7 @@ impl RecordStack { stack_extension = true; } let next_descended_child = if let Some(seek) = self.seek.as_ref() { - if result == TryStackChildResult::StackedFull && prefix.len() < seek.len() { + if result != TryStackChildResult::StackedAfter && prefix.len() < seek.len() { seek.at(prefix.len()) } else { self.seek = None; diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 9371ebec..eb772754 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -265,6 +265,7 @@ where &mut self.proof, &self.expected_root, None, + false, ) { Ok(r) => r, Err(e) => { @@ -363,6 +364,7 @@ where &mut self.proof, &self.expected_root, Some(&mut to_check_slice), + as_prefix, ) { Ok(r) => r, Err(e) => { @@ -462,6 +464,7 @@ impl ReadStack { proof: &mut impl Iterator, expected_root: &Option>, mut slice_query: Option<&mut NibbleSlice>, + query_as_prefix: bool, ) -> Result, CError>> { let check_hash = expected_root.is_some(); let child_handle = if let Some(node) = self.items.last_mut() { @@ -559,8 +562,8 @@ impl ReadStack { if self.items.len() > 0 { if let Some(slice) = slice_query.as_mut() { slice.advance(1); - self.prefix.push(child_index); } + self.prefix.push(child_index); } result = if let Some(slice) = slice_query.as_mut() { if slice.starts_with(&partial) { @@ -571,14 +574,15 @@ impl ReadStack { TryStackChildResult::StackedAfter } } else { - TryStackChildResult::StackedInto + TryStackChildResult::StackedFull }; if result == TryStackChildResult::StackedAfter { // end of query slice_query = None; - } - if let Some(slice) = slice_query.as_mut() { - slice.advance(partial.len()); + } else if let Some(slice) = slice_query.as_mut() { + if !query_as_prefix { + slice.advance(partial.len()); + } } self.prefix.append_partial(partial.right()); }, From 185c3051ce8f7554c1c810be78f90e02067a75f7 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 22 Jun 2023 20:23:20 +0200 Subject: [PATCH 089/154] register all after start at --- trie-db/src/query_plan/record.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index f7f065b7..6196a752 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -40,7 +40,7 @@ impl Recorder { if self.start_at.map(|s| s > depth).unwrap_or(false) { false } else { - //self.start_at = None; + self.start_at = None; true } } From e77ef00a24fd090ba3c5f6b39bbdb83e9a07bae9 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 22 Jun 2023 22:41:46 +0200 Subject: [PATCH 090/154] stack into don t need moving slice query at all --- trie-db/src/query_plan/verify.rs | 10 ++-------- trie-db/test/src/fuzz.rs | 1 + 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index eb772754..00e0df99 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -265,7 +265,6 @@ where &mut self.proof, &self.expected_root, None, - false, ) { Ok(r) => r, Err(e) => { @@ -364,7 +363,6 @@ where &mut self.proof, &self.expected_root, Some(&mut to_check_slice), - as_prefix, ) { Ok(r) => r, Err(e) => { @@ -464,7 +462,6 @@ impl ReadStack { proof: &mut impl Iterator, expected_root: &Option>, mut slice_query: Option<&mut NibbleSlice>, - query_as_prefix: bool, ) -> Result, CError>> { let check_hash = expected_root.is_some(); let child_handle = if let Some(node) = self.items.last_mut() { @@ -576,13 +573,10 @@ impl ReadStack { } else { TryStackChildResult::StackedFull }; - if result == TryStackChildResult::StackedAfter { + if result != TryStackChildResult::StackedFull { // end of query - slice_query = None; } else if let Some(slice) = slice_query.as_mut() { - if !query_as_prefix { - slice.advance(partial.len()); - } + slice.advance(partial.len()); } self.prefix.append_partial(partial.right()); }, diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index 7c5f8548..c071eb4c 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -734,6 +734,7 @@ pub mod query_plan { fn fuzz_query_plan_1() { use reference_trie::{RefHasher, SubstrateV1}; let plans = [ + ArbitraryQueryPlan(vec![(false, ArbitraryKey::Indexed(459829968682))]), ArbitraryQueryPlan(vec![ (false, ArbitraryKey::Indexed(17942346408707227648)), (false, ArbitraryKey::Indexed(37833)), From 8806d49fae9ed04c1978d6ff0e469ff32f0166de Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 23 Jun 2023 09:34:49 +0200 Subject: [PATCH 091/154] fix logic --- trie-db/src/query_plan/record.rs | 7 ++++--- trie-db/test/src/fuzz.rs | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 6196a752..0d699451 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -972,16 +972,17 @@ impl RecordStack { prefix.append_partial(partial.right()); if let Some(s) = slice_query.as_mut() { let common = partial.common_prefix(s); - s.advance(common); // s starts with partial - if common == partial.len() { + let r = if common == partial.len() { TryStackChildResult::StackedFull } else if common == s.len() { // partial strats with s TryStackChildResult::StackedInto } else { TryStackChildResult::StackedAfter - } + }; + s.advance(common); + r } else { TryStackChildResult::StackedFull } diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index c071eb4c..0d2fafa2 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -734,6 +734,7 @@ pub mod query_plan { fn fuzz_query_plan_1() { use reference_trie::{RefHasher, SubstrateV1}; let plans = [ + ArbitraryQueryPlan(vec![(true, ArbitraryKey::Random(vec![252, 63,149, 166, 164, 38]))]), ArbitraryQueryPlan(vec![(false, ArbitraryKey::Indexed(459829968682))]), ArbitraryQueryPlan(vec![ (false, ArbitraryKey::Indexed(17942346408707227648)), From 418adb836560c0da077cceb46f51a83ca42a7bde Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 23 Jun 2023 10:20:33 +0200 Subject: [PATCH 092/154] fix --- trie-db/src/query_plan/record.rs | 6 ++++-- trie-db/src/query_plan/verify.rs | 4 +++- trie-db/test/src/fuzz.rs | 9 ++++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 0d699451..e7b09ef5 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -731,7 +731,9 @@ pub fn record_query_plan< // TODO this could also be passed around from try stack result then // slice_query_len let common_from = query_slice.common_prefix(&from.stack.prefix.as_leftnibbleslice()); - if common_from < common_nibbles { + if common_from <= common_nibbles + /* && common_from != 1000 */ + { /*if query.as_prefix { let halt = from.iter_prefix(Some(&query), Some(db), query.hash_only, true)?; @@ -743,7 +745,7 @@ pub fn record_query_plan< prev_query = Some(query); continue } - let common_nibbles = max(common_nibbles, common_from); + //let common_nibbles = max(common_nibbles, common_from); loop { match from.stack.prefix.len().cmp(&common_nibbles) { Ordering::Equal | Ordering::Less => break common_nibbles, diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 00e0df99..588937fa 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -201,7 +201,9 @@ where // slice_query_len let common_from = query_slice.common_prefix(&self.stack.prefix.as_leftnibbleslice()); - if common_from < common_nibbles { + if common_from <= common_nibbles + /* && common_from != 0 */ + { self.current = Some(next); self.state = ReadProofState::SwitchQueryPlan; continue diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index 0d2fafa2..c2bbd306 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -734,7 +734,14 @@ pub mod query_plan { fn fuzz_query_plan_1() { use reference_trie::{RefHasher, SubstrateV1}; let plans = [ - ArbitraryQueryPlan(vec![(true, ArbitraryKey::Random(vec![252, 63,149, 166, 164, 38]))]), + ArbitraryQueryPlan(vec![ + (false, ArbitraryKey::Indexed(18446475631341993995)), + (true, ArbitraryKey::Indexed(254)), + ]), + ArbitraryQueryPlan(vec![( + true, + ArbitraryKey::Random(vec![252, 63, 149, 166, 164, 38]), + )]), ArbitraryQueryPlan(vec![(false, ArbitraryKey::Indexed(459829968682))]), ArbitraryQueryPlan(vec![ (false, ArbitraryKey::Indexed(17942346408707227648)), From eda16fe317ddf34e49646f90398113719407bad4 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 23 Jun 2023 10:42:08 +0200 Subject: [PATCH 093/154] fuzz for split (need to rework the iteration logic: is faster than non split which means a bug). --- trie-db/fuzz/Cargo.toml | 4 +++ trie-db/fuzz/fuzz_targets/query_plan_2.rs | 37 +++++++++++++++++++++++ trie-db/test/src/fuzz.rs | 18 +++++++---- 3 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 trie-db/fuzz/fuzz_targets/query_plan_2.rs diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml index 6349082e..a5cbf2db 100644 --- a/trie-db/fuzz/Cargo.toml +++ b/trie-db/fuzz/Cargo.toml @@ -75,3 +75,7 @@ path = "fuzz_targets/prefix_seek_iter.rs" [[bin]] name = "query_plan_1" path = "fuzz_targets/query_plan_1.rs" + +[[bin]] +name = "query_plan_2" +path = "fuzz_targets/query_plan_2.rs" diff --git a/trie-db/fuzz/fuzz_targets/query_plan_2.rs b/trie-db/fuzz/fuzz_targets/query_plan_2.rs new file mode 100644 index 00000000..fc549335 --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/query_plan_2.rs @@ -0,0 +1,37 @@ +#![no_main] +use lazy_static::lazy_static; +use libfuzzer_sys::fuzz_target; +use reference_trie::{RefHasher, SubstrateV1}; +use trie_db::query_plan::ProofKind; +use trie_db_test::fuzz::query_plan::{ + build_state, fuzz_query_plan_conf, ArbitraryQueryPlan, Conf, FuzzContext, CONF1, +}; +use arbitrary::Arbitrary; + +lazy_static! { + static ref CONTEXT: FuzzContext> = build_state(CONF1); +} + +#[derive(Debug, Clone, Copy, Arbitrary)] +#[repr(usize)] +enum SplitSize { + One = 1, + Two = 2, + Three = 3, + More = 10, + MoreMore = 50, +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Arbitrary)] +enum SplitKind { + Stateless, + Stateful, +} + +fuzz_target!(|input: (ArbitraryQueryPlan, SplitSize, SplitKind)| { + let (plan, split_size, split_kind) = input; + let mut conf = CONTEXT.conf.clone(); + conf.limit = split_size as usize; + conf.proof_spawn_with_persistence = split_kind == SplitKind::Stateful; + fuzz_query_plan_conf::>(&CONTEXT, conf, plan); +}); diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index c2bbd306..83f49866 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -668,12 +668,18 @@ pub mod query_plan { /// Main entry point for query plan fuzzing. pub fn fuzz_query_plan(context: &FuzzContext, plan: ArbitraryQueryPlan) { + let conf = context.conf.clone(); + fuzz_query_plan_conf(context, conf, plan); + } + + /// Main entry point for query plan fuzzing. + pub fn fuzz_query_plan_conf(context: &FuzzContext, conf: Conf, plan: ArbitraryQueryPlan) { let query_plan = arbitrary_query_plan(context, plan); - let kind = context.conf.kind; - let limit = context.conf.limit; + let kind = conf.kind; + let limit = conf.limit; let limit = (limit != 0).then(|| limit); - let recorder = Recorder::new(context.conf.kind, InMemoryRecorder::default(), limit, None); + let recorder = Recorder::new(conf.kind, InMemoryRecorder::default(), limit, None); let mut from = HaltedStateRecord::from_start(recorder); let mut proofs: Vec>> = Default::default(); let mut query_plan_iter = query_plan.as_ref(); @@ -695,7 +701,7 @@ pub mod query_plan { } break } - let rec = if context.conf.proof_spawn_with_persistence { + let rec = if conf.proof_spawn_with_persistence { from.statefull(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) } else { query_plan_iter = query_plan.as_ref(); @@ -711,10 +717,10 @@ pub mod query_plan { crate::query_plan::check_proofs::( proofs, &query_plan, - context.conf.kind, + conf.kind, context.root, &context.reference, - context.conf.hash_only, + conf.hash_only, ); } From b407d9065e4937f5e1f4ca31ca16e5ca8e2ea275 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 23 Jun 2023 10:57:44 +0200 Subject: [PATCH 094/154] fix initial condition --- trie-db/src/query_plan/record.rs | 4 +--- trie-db/src/query_plan/verify.rs | 4 +--- trie-db/test/src/fuzz.rs | 35 +++++++++++++++++++++++++++++++- trie-db/test/src/query_plan.rs | 2 +- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index e7b09ef5..5c39b1f2 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -731,9 +731,7 @@ pub fn record_query_plan< // TODO this could also be passed around from try stack result then // slice_query_len let common_from = query_slice.common_prefix(&from.stack.prefix.as_leftnibbleslice()); - if common_from <= common_nibbles - /* && common_from != 1000 */ - { + if common_from <= common_nibbles && common_from != 0 { /*if query.as_prefix { let halt = from.iter_prefix(Some(&query), Some(db), query.hash_only, true)?; diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 588937fa..15f2ed8a 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -201,9 +201,7 @@ where // slice_query_len let common_from = query_slice.common_prefix(&self.stack.prefix.as_leftnibbleslice()); - if common_from <= common_nibbles - /* && common_from != 0 */ - { + if common_from <= common_nibbles && common_from != 0 { self.current = Some(next); self.state = ReadProofState::SwitchQueryPlan; continue diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index 83f49866..be62c427 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -673,7 +673,11 @@ pub mod query_plan { } /// Main entry point for query plan fuzzing. - pub fn fuzz_query_plan_conf(context: &FuzzContext, conf: Conf, plan: ArbitraryQueryPlan) { + pub fn fuzz_query_plan_conf( + context: &FuzzContext, + conf: Conf, + plan: ArbitraryQueryPlan, + ) { let query_plan = arbitrary_query_plan(context, plan); let kind = conf.kind; @@ -761,4 +765,33 @@ pub mod query_plan { fuzz_query_plan::>(&context, plan.clone()); } } + + #[test] + fn fuzz_query_plan_2() { + use reference_trie::{RefHasher, SubstrateV1}; + let plans = [ + ArbitraryQueryPlan(vec![ + (false, ArbitraryKey::Indexed(18446475631341993995)), + (true, ArbitraryKey::Indexed(254)), + ]), + ArbitraryQueryPlan(vec![( + true, + ArbitraryKey::Random(vec![252, 63, 149, 166, 164, 38]), + )]), + ArbitraryQueryPlan(vec![(false, ArbitraryKey::Indexed(459829968682))]), + ArbitraryQueryPlan(vec![ + (false, ArbitraryKey::Indexed(17942346408707227648)), + (false, ArbitraryKey::Indexed(37833)), + ]), + ArbitraryQueryPlan(vec![(true, ArbitraryKey::Indexed(43218140957))]), + ArbitraryQueryPlan(vec![]), + ]; + let mut conf = CONF1.clone(); + let context: FuzzContext> = build_state(CONF1); + for plan in plans { + conf.limit = 2; + conf.proof_spawn_with_persistence = true; + fuzz_query_plan_conf::>(&context, conf, plan.clone()); + } + } } diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index 2453e1c1..91ea916f 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -44,7 +44,7 @@ fn test_query_plan_compact_internal() { test_layouts!(test_query_plan_content, test_query_plan_content_internal); fn test_query_plan_content_internal() { test_query_plan_internal::(ProofKind::CompactContent, false); - //test_query_plan_internal::(ProofKind::CompactNodes, true); + test_query_plan_internal::(ProofKind::CompactNodes, true); } fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { From 310e60c852ddb72e835358de79ed9d599d49f420 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 23 Jun 2023 11:38:07 +0200 Subject: [PATCH 095/154] failing fuzz --- trie-db/test/src/fuzz.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index be62c427..d971cc22 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -744,6 +744,32 @@ pub mod query_plan { fn fuzz_query_plan_1() { use reference_trie::{RefHasher, SubstrateV1}; let plans = [ + ArbitraryQueryPlan(vec![ + (false, ArbitraryKey::Indexed(18446181123756131797)), + ( + true, + ArbitraryKey::Random(vec![ + 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, + 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, + 225, 142, + ]), + ), + (false, ArbitraryKey::Random(vec![])), + (false, ArbitraryKey::Random(vec![])), + (false, ArbitraryKey::Random(vec![])), + (false, ArbitraryKey::Random(vec![])), + (false, ArbitraryKey::Random(vec![])), + (false, ArbitraryKey::Random(vec![])), + (false, ArbitraryKey::Random(vec![])), + (false, ArbitraryKey::Random(vec![])), + (false, ArbitraryKey::Random(vec![])), + (false, ArbitraryKey::Random(vec![])), + (false, ArbitraryKey::Random(vec![])), + (false, ArbitraryKey::Random(vec![225, 0, 245, 0, 0])), + (false, ArbitraryKey::Indexed(144115188067139583)), + (false, ArbitraryKey::Indexed(0)), + (false, ArbitraryKey::Indexed(0)), + ]), ArbitraryQueryPlan(vec![ (false, ArbitraryKey::Indexed(18446475631341993995)), (true, ArbitraryKey::Indexed(254)), From 2ae891ac464813f382227fd1d9cc0dc6d60d5481 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Fri, 23 Jun 2023 13:20:48 +0200 Subject: [PATCH 096/154] better logic --- trie-db/src/query_plan/record.rs | 8 +++++++- trie-db/src/query_plan/verify.rs | 8 +++++++- trie-db/test/src/fuzz.rs | 26 ++------------------------ 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 5c39b1f2..852a1177 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -731,7 +731,13 @@ pub fn record_query_plan< // TODO this could also be passed around from try stack result then // slice_query_len let common_from = query_slice.common_prefix(&from.stack.prefix.as_leftnibbleslice()); - if common_from <= common_nibbles && common_from != 0 { + let last_start_at = if from.stack.items.len() > 1 { + from.stack.items[from.stack.items.len() - 2].depth + } else { + 0 + }; + if common_from > last_start_at { + // if common_from <= common_nibbles && common_from != 0 { /*if query.as_prefix { let halt = from.iter_prefix(Some(&query), Some(db), query.hash_only, true)?; diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 15f2ed8a..ddcc405f 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -201,7 +201,13 @@ where // slice_query_len let common_from = query_slice.common_prefix(&self.stack.prefix.as_leftnibbleslice()); - if common_from <= common_nibbles && common_from != 0 { + let last_start_at = if self.stack.items.len() > 1 { + self.stack.items[self.stack.items.len() - 2].depth + } else { + 0 + }; + if common_from > last_start_at { + // if common_from <= common_nibbles && common_from != 0 { self.current = Some(next); self.state = ReadProofState::SwitchQueryPlan; continue diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index d971cc22..7738cfd6 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -745,30 +745,8 @@ pub mod query_plan { use reference_trie::{RefHasher, SubstrateV1}; let plans = [ ArbitraryQueryPlan(vec![ - (false, ArbitraryKey::Indexed(18446181123756131797)), - ( - true, - ArbitraryKey::Random(vec![ - 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, - 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, - 225, 142, - ]), - ), - (false, ArbitraryKey::Random(vec![])), - (false, ArbitraryKey::Random(vec![])), - (false, ArbitraryKey::Random(vec![])), - (false, ArbitraryKey::Random(vec![])), - (false, ArbitraryKey::Random(vec![])), - (false, ArbitraryKey::Random(vec![])), - (false, ArbitraryKey::Random(vec![])), - (false, ArbitraryKey::Random(vec![])), - (false, ArbitraryKey::Random(vec![])), - (false, ArbitraryKey::Random(vec![])), - (false, ArbitraryKey::Random(vec![])), - (false, ArbitraryKey::Random(vec![225, 0, 245, 0, 0])), - (false, ArbitraryKey::Indexed(144115188067139583)), - (false, ArbitraryKey::Indexed(0)), - (false, ArbitraryKey::Indexed(0)), + (false, ArbitraryKey::Random(vec![225])), + (true, ArbitraryKey::Random(vec![225, 225, 225, 142])), ]), ArbitraryQueryPlan(vec![ (false, ArbitraryKey::Indexed(18446475631341993995)), From df5fd7c66d7f4cd03bf97bb50fa71809d0f06b7d Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Fri, 23 Jun 2023 15:59:01 +0200 Subject: [PATCH 097/154] update criterion --- hash256-std-hasher/Cargo.toml | 2 +- memory-db/Cargo.toml | 2 +- test-support/reference-trie/Cargo.toml | 2 +- test-support/trie-bench/Cargo.toml | 2 +- trie-db/test/Cargo.toml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hash256-std-hasher/Cargo.toml b/hash256-std-hasher/Cargo.toml index addd32fb..bdeb08ef 100644 --- a/hash256-std-hasher/Cargo.toml +++ b/hash256-std-hasher/Cargo.toml @@ -16,7 +16,7 @@ harness = false crunchy = "0.2.1" [dev-dependencies] -criterion = "0.4.0" +criterion = "0.5.1" [features] default = ["std"] diff --git a/memory-db/Cargo.toml b/memory-db/Cargo.toml index adad836a..e800a2a2 100644 --- a/memory-db/Cargo.toml +++ b/memory-db/Cargo.toml @@ -12,7 +12,7 @@ hash-db = { version = "0.16.0", path = "../hash-db", default-features = false } [dev-dependencies] keccak-hasher = { path = "../test-support/keccak-hasher" } -criterion = "0.4.0" +criterion = "0.5.1" [features] default = ["std"] diff --git a/test-support/reference-trie/Cargo.toml b/test-support/reference-trie/Cargo.toml index 90fe577a..9f0ff79a 100644 --- a/test-support/reference-trie/Cargo.toml +++ b/test-support/reference-trie/Cargo.toml @@ -18,7 +18,7 @@ paste = "1.0.12" [dev-dependencies] trie-bench = { path = "../trie-bench" } -criterion = "0.4.0" +criterion = "0.5.1" [[bench]] name = "bench" diff --git a/test-support/trie-bench/Cargo.toml b/test-support/trie-bench/Cargo.toml index f8de1f96..e0779c1e 100644 --- a/test-support/trie-bench/Cargo.toml +++ b/test-support/trie-bench/Cargo.toml @@ -14,5 +14,5 @@ hash-db = { path = "../../hash-db" , version = "0.16.0"} memory-db = { path = "../../memory-db", version = "0.32.0" } trie-root = { path = "../../trie-root", version = "0.18.0" } trie-db = { path = "../../trie-db", version = "0.27.0" } -criterion = "0.4.0" +criterion = "0.5.1" parity-scale-codec = "3.0.0" diff --git a/trie-db/test/Cargo.toml b/trie-db/test/Cargo.toml index 3308f26e..cecfe751 100644 --- a/trie-db/test/Cargo.toml +++ b/trie-db/test/Cargo.toml @@ -19,7 +19,7 @@ trie-standardmap = { path = "../../test-support/trie-standardmap", version = "0. reference-trie = { path = "../../test-support/reference-trie", version = "0.29.0" } arbitrary = { version = "1.3.0", features = ["derive"] } array-bytes = "6.0.0" -criterion = "0.4.0" +criterion = "0.5.1" env_logger = { version = "0.10", default-features = false } hex-literal = "0.3" log = "0.4" From 5eca0557242fb517d582ab9cec9f73a2d8117374 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Fri, 23 Jun 2023 16:21:42 +0200 Subject: [PATCH 098/154] add query plan check (broken (start and end prefix are not emitted)). --- trie-db/src/query_plan/mod.rs | 2 +- trie-db/test/src/query_plan.rs | 77 ++++++++++++++++++++++++++++++---- 2 files changed, 71 insertions(+), 8 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index 755bf272..0fc1d632 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -69,7 +69,7 @@ impl InMemQueryPlanItem { } /// Item to query. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct QueryPlanItem<'a> { pub key: &'a [u8], pub hash_only: bool, diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index 91ea916f..04803eb4 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -24,7 +24,7 @@ use trie_db::{ query_plan::{ record_query_plan, verify_query_plan_iter, verify_query_plan_iter_content, HaltedStateCheck, HaltedStateRecord, InMemQueryPlan, InMemQueryPlanItem, InMemoryRecorder, - ProofKind, QueryPlan, ReadProofItem, Recorder, + ProofKind, QueryPlan, QueryPlanItem, ReadProofItem, Recorder, }, TrieDBBuilder, TrieDBMutBuilder, TrieHash, TrieLayout, TrieMut, }; @@ -443,6 +443,7 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { } } } + /// Proof checking. pub fn check_proofs( mut proofs: Vec>>, @@ -462,6 +463,8 @@ pub fn check_proofs( } else { HaltedStateCheck::Node(query_plan.into()) }); + let mut query_plan_iter: QueryPlan<_> = query_plan_in_mem.as_ref(); + let mut current_plan = query_plan_iter.items.next(); let mut has_run_full = false; while let Some(state) = run_state.take() { let proof = if let Some(proof) = proofs.pop() { @@ -511,26 +514,80 @@ pub fn check_proofs( }; let mut in_prefix = false; - // TODO need stricter check of query plan (advance query plan iter and expect all items - // touched!!! while let Some(item) = next_item() { match item.unwrap() { ReadProofItem::Hash(key, hash) => { assert!(hash_only); assert_eq!(content.get(&*key).map(|v| L::Hash::hash(&v.as_ref())), Some(hash)); + if in_prefix { + assert!(current_plan + .as_ref() + .map(|item| key.starts_with(item.key) && + item.hash_only && item.as_prefix) + .unwrap_or(false)); + } else { + assert_eq!( + current_plan.as_ref(), + Some(&QueryPlanItem { key: &key, hash_only: true, as_prefix: false }) + ); + current_plan = query_plan_iter.items.next(); + } }, ReadProofItem::Value(key, value) => { assert_eq!(content.get(&*key), Some(value.as_ref())); + if in_prefix { + assert!(current_plan + .as_ref() + .map(|item| key.starts_with(item.key) && + !item.hash_only && item.as_prefix) + .unwrap_or(false)); + } else { + assert_eq!( + current_plan.as_ref(), + Some(&QueryPlanItem { key: &key, hash_only: false, as_prefix: false }) + ); + current_plan = query_plan_iter.items.next(); + } }, ReadProofItem::NoValue(key) => { assert_eq!(content.get(key), None); + assert!(!in_prefix); + if hash_only { + assert_eq!( + current_plan.as_ref(), + Some(&QueryPlanItem { key: &key, hash_only: true, as_prefix: false }) + ); + } else { + assert_eq!( + current_plan.as_ref(), + Some(&QueryPlanItem { key: &key, hash_only: false, as_prefix: false }) + ); + } + current_plan = query_plan_iter.items.next(); }, - ReadProofItem::StartPrefix(_prefix) => { + ReadProofItem::StartPrefix(prefix) => { in_prefix = true; + if hash_only { + assert_eq!( + current_plan.as_ref(), + Some(&QueryPlanItem { key: &prefix, hash_only: true, as_prefix: true }) + ); + } else { + assert_eq!( + current_plan.as_ref(), + Some(&QueryPlanItem { + key: &prefix, + hash_only: false, + as_prefix: true + }) + ); + } }, ReadProofItem::EndPrefix => { assert!(in_prefix); in_prefix = false; + assert!(current_plan.as_ref().map(|item| item.as_prefix).unwrap_or(false)); + current_plan = query_plan_iter.items.next(); }, ReadProofItem::Halted(resume) => { run_state = Some(*resume); @@ -538,14 +595,20 @@ pub fn check_proofs( }, } } + if run_state.is_none() { + assert_eq!(current_plan.as_ref(), None) + } if kind == ProofKind::FullNodes { if run_state.is_none() && !has_run_full { has_run_full = true; - let query_plan_iter = query_plan_in_mem.as_ref(); + query_plan_iter = query_plan_in_mem.as_ref(); + current_plan = query_plan_iter.items.next(); + + let query_plan_iter_2 = query_plan_in_mem.as_ref(); run_state = Some(if is_content_proof { - HaltedStateCheck::Content(query_plan_iter.into()) + HaltedStateCheck::Content(query_plan_iter_2.into()) } else { - HaltedStateCheck::Node(query_plan_iter.into()) + HaltedStateCheck::Node(query_plan_iter_2.into()) }); } } else { From 40b80b29e3c73d0fee38d5ba778fc84a684dbc0b Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Fri, 23 Jun 2023 17:01:26 +0200 Subject: [PATCH 099/154] naive implementation --- trie-db/src/query_plan/mod.rs | 2 +- trie-db/src/query_plan/verify.rs | 57 ++++++++++++++++++++++++++++++-- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index 0fc1d632..0ce60d64 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -612,7 +612,7 @@ pub enum ReadProofItem<'a, L: TrieLayout, C, D: SplitFirst> { /// return when we read the proof with the query input (otherwhise /// we would need to indicate every child without a hash as a prefix). /// TODO unused implement - StartPrefix(&'a [u8]), + StartPrefix(Vec), /// End of a previously start prefix. /// TODO unused implement EndPrefix, diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index ddcc405f..51e9f3f0 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -43,6 +43,8 @@ where current_offset: usize, state: ReadProofState, stack: ReadStack, + buffed_result: + Option, VerifyError, CError>>>>, } struct ReadStack { @@ -53,6 +55,8 @@ struct ReadStack { start_items: usize, is_compact: bool, expect_value: bool, + send_enter_prefix: Option>, + send_exit_prefix: bool, _ph: PhantomData, } @@ -65,6 +69,8 @@ impl Clone for ReadStack { iter_prefix: self.iter_prefix, is_compact: self.is_compact, expect_value: self.expect_value, + send_enter_prefix: self.send_enter_prefix.clone(), + send_exit_prefix: self.send_exit_prefix, _ph: PhantomData, } } @@ -105,6 +111,7 @@ where state, stack, current_offset: restore_offset, + buffed_result: None, }) } @@ -130,6 +137,8 @@ where let query_plan = crate::rstd::mem::replace(&mut self.query_plan, None); let query_plan = query_plan.expect("Init with state"); let current = crate::rstd::mem::take(&mut self.current); + debug_assert!(!self.stack.send_exit_prefix); + debug_assert!(self.stack.send_enter_prefix.is_none()); let mut stack = crate::rstd::mem::replace( &mut self.stack, ReadStack { @@ -139,6 +148,8 @@ where is_compact: self.is_compact, expect_value: false, iter_prefix: None, + send_enter_prefix: None, + send_exit_prefix: false, _ph: PhantomData, }, ); @@ -163,6 +174,36 @@ where type Item = Result, VerifyError, CError>>; fn next(&mut self) -> Option { + debug_assert!(self.stack.send_enter_prefix.is_none()); + debug_assert!(!self.stack.send_exit_prefix); + if let Some(r) = self.buffed_result.take() { + return r + } + let r = self.next_inner(); + if let Some(k) = self.stack.send_enter_prefix.take() { + self.buffed_result = Some(r); + return Some(Ok(ReadProofItem::StartPrefix(k))) + } + if self.stack.send_exit_prefix { + self.buffed_result = Some(r); + self.stack.send_exit_prefix = false; + return Some(Ok(ReadProofItem::EndPrefix)) + } else { + r + } + } +} + +impl<'a, L, C, D, P> ReadProofIterator<'a, L, C, D, P> +where + L: TrieLayout, + C: Iterator>, + P: Iterator, + D: SplitFirst, +{ + fn next_inner( + &mut self, + ) -> Option, VerifyError, CError>>> { if self.state == ReadProofState::Finished { return None } @@ -340,7 +381,10 @@ where if at_value { if as_prefix { - self.stack.enter_prefix_iter(hash_only); + self.stack.enter_prefix_iter( + hash_only, + &self.current.as_ref().expect("enter prefix").key, + ); continue } self.state = ReadProofState::SwitchQueryPlan; @@ -381,7 +425,10 @@ where TryStackChildResult::StackedFull => (), TryStackChildResult::StackedInto => { if as_prefix { - self.stack.enter_prefix_iter(hash_only); + self.stack.enter_prefix_iter( + hash_only, + &self.current.as_ref().expect("enter prefix").key, + ); continue } self.state = ReadProofState::SwitchQueryPlan; @@ -451,6 +498,8 @@ impl<'a, L: TrieLayout, C, D: SplitFirst> From> is_compact, expect_value: false, iter_prefix: None, + send_enter_prefix: None, + send_exit_prefix: false, _ph: PhantomData, }, state: ReadProofState::NotStarted, @@ -854,11 +903,13 @@ impl ReadStack { Err(VerifyError::ExtraneousNode) } - fn enter_prefix_iter(&mut self, hash_only: bool) { + fn enter_prefix_iter(&mut self, hash_only: bool, key: &[u8]) { + self.send_enter_prefix = Some(key.to_vec()); self.iter_prefix = Some((self.items.len(), false, hash_only)); } fn exit_prefix_iter(&mut self) { + self.send_exit_prefix = true; self.iter_prefix = None } } From 4bd7bc1f4f35b9611095b52b1f57dc05ae1b7e69 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Fri, 23 Jun 2023 17:15:17 +0200 Subject: [PATCH 100/154] fix a bit more --- trie-db/src/query_plan/verify.rs | 50 ++++++++++++++------------------ trie-db/test/src/query_plan.rs | 2 +- 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 51e9f3f0..3fdab74f 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -43,6 +43,8 @@ where current_offset: usize, state: ReadProofState, stack: ReadStack, + send_enter_prefix: Option>, + send_exit_prefix: bool, buffed_result: Option, VerifyError, CError>>>>, } @@ -55,8 +57,6 @@ struct ReadStack { start_items: usize, is_compact: bool, expect_value: bool, - send_enter_prefix: Option>, - send_exit_prefix: bool, _ph: PhantomData, } @@ -69,8 +69,6 @@ impl Clone for ReadStack { iter_prefix: self.iter_prefix, is_compact: self.is_compact, expect_value: self.expect_value, - send_enter_prefix: self.send_enter_prefix.clone(), - send_exit_prefix: self.send_exit_prefix, _ph: PhantomData, } } @@ -111,6 +109,8 @@ where state, stack, current_offset: restore_offset, + send_enter_prefix: None, + send_exit_prefix: false, buffed_result: None, }) } @@ -137,8 +137,6 @@ where let query_plan = crate::rstd::mem::replace(&mut self.query_plan, None); let query_plan = query_plan.expect("Init with state"); let current = crate::rstd::mem::take(&mut self.current); - debug_assert!(!self.stack.send_exit_prefix); - debug_assert!(self.stack.send_enter_prefix.is_none()); let mut stack = crate::rstd::mem::replace( &mut self.stack, ReadStack { @@ -148,8 +146,6 @@ where is_compact: self.is_compact, expect_value: false, iter_prefix: None, - send_enter_prefix: None, - send_exit_prefix: false, _ph: PhantomData, }, ); @@ -162,6 +158,16 @@ where state: ReadProofState::Halted, }))))) } + + fn enter_prefix_iter(&mut self, hash_only: bool, key: &[u8]) { + self.send_enter_prefix = Some(key.to_vec()); + self.stack.iter_prefix = Some((self.stack.items.len(), false, hash_only)); + } + + fn exit_prefix_iter(&mut self) { + self.send_exit_prefix = true; + self.stack.iter_prefix = None + } } impl<'a, L, C, D, P> Iterator for ReadProofIterator<'a, L, C, D, P> @@ -174,19 +180,19 @@ where type Item = Result, VerifyError, CError>>; fn next(&mut self) -> Option { - debug_assert!(self.stack.send_enter_prefix.is_none()); - debug_assert!(!self.stack.send_exit_prefix); + debug_assert!(self.send_enter_prefix.is_none()); + debug_assert!(!self.send_exit_prefix); if let Some(r) = self.buffed_result.take() { return r } let r = self.next_inner(); - if let Some(k) = self.stack.send_enter_prefix.take() { + if let Some(k) = self.send_enter_prefix.take() { self.buffed_result = Some(r); return Some(Ok(ReadProofItem::StartPrefix(k))) } - if self.stack.send_exit_prefix { + if self.send_exit_prefix { self.buffed_result = Some(r); - self.stack.send_exit_prefix = false; + self.send_exit_prefix = false; return Some(Ok(ReadProofItem::EndPrefix)) } else { r @@ -349,7 +355,7 @@ where }, } { // end iter - self.stack.exit_prefix_iter(); + self.exit_prefix_iter(); } } } @@ -381,7 +387,7 @@ where if at_value { if as_prefix { - self.stack.enter_prefix_iter( + self.enter_prefix_iter( hash_only, &self.current.as_ref().expect("enter prefix").key, ); @@ -425,7 +431,7 @@ where TryStackChildResult::StackedFull => (), TryStackChildResult::StackedInto => { if as_prefix { - self.stack.enter_prefix_iter( + self.enter_prefix_iter( hash_only, &self.current.as_ref().expect("enter prefix").key, ); @@ -498,8 +504,6 @@ impl<'a, L: TrieLayout, C, D: SplitFirst> From> is_compact, expect_value: false, iter_prefix: None, - send_enter_prefix: None, - send_exit_prefix: false, _ph: PhantomData, }, state: ReadProofState::NotStarted, @@ -902,14 +906,4 @@ impl ReadStack { // TODO other error Err(VerifyError::ExtraneousNode) } - - fn enter_prefix_iter(&mut self, hash_only: bool, key: &[u8]) { - self.send_enter_prefix = Some(key.to_vec()); - self.iter_prefix = Some((self.items.len(), false, hash_only)); - } - - fn exit_prefix_iter(&mut self) { - self.send_exit_prefix = true; - self.iter_prefix = None - } } diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index 04803eb4..9f3d1208 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -466,6 +466,7 @@ pub fn check_proofs( let mut query_plan_iter: QueryPlan<_> = query_plan_in_mem.as_ref(); let mut current_plan = query_plan_iter.items.next(); let mut has_run_full = false; + let mut in_prefix = false; while let Some(state) = run_state.take() { let proof = if let Some(proof) = proofs.pop() { full_proof.extend_from_slice(&proof); @@ -513,7 +514,6 @@ pub fn check_proofs( } }; - let mut in_prefix = false; while let Some(item) = next_item() { match item.unwrap() { ReadProofItem::Hash(key, hash) => { From d13729397da0f5da7fab91da55de97b034fbfb5f Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 27 Jun 2023 12:00:49 +0200 Subject: [PATCH 101/154] a fix --- trie-db/src/query_plan/verify.rs | 17 ++++++++--------- trie-db/test/src/fuzz.rs | 6 ++++-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 3fdab74f..772ca217 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -440,17 +440,16 @@ where self.state = ReadProofState::SwitchQueryPlan; return Some(Ok(ReadProofItem::NoValue(to_check.key))) }, + TryStackChildResult::NotStackedBranch | + TryStackChildResult::NotStacked | TryStackChildResult::StackedAfter => { self.state = ReadProofState::SwitchQueryPlan; - return Some(Ok(ReadProofItem::NoValue(to_check.key))) - }, - TryStackChildResult::NotStacked => { - self.state = ReadProofState::SwitchQueryPlan; - return Some(Ok(ReadProofItem::NoValue(to_check.key))) - }, - TryStackChildResult::NotStackedBranch => { - self.state = ReadProofState::SwitchQueryPlan; - return Some(Ok(ReadProofItem::NoValue(to_check.key))) + if as_prefix { + self.send_enter_prefix = Some(to_check.key.to_vec()); + return Some(Ok(ReadProofItem::EndPrefix)) + } else { + return Some(Ok(ReadProofItem::NoValue(to_check.key))) + } }, TryStackChildResult::Halted => return self.halt(), } diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index 7738cfd6..53029058 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -744,7 +744,7 @@ pub mod query_plan { fn fuzz_query_plan_1() { use reference_trie::{RefHasher, SubstrateV1}; let plans = [ - ArbitraryQueryPlan(vec![ + /*ArbitraryQueryPlan(vec![ (false, ArbitraryKey::Random(vec![225])), (true, ArbitraryKey::Random(vec![225, 225, 225, 142])), ]), @@ -756,13 +756,15 @@ pub mod query_plan { true, ArbitraryKey::Random(vec![252, 63, 149, 166, 164, 38]), )]), - ArbitraryQueryPlan(vec![(false, ArbitraryKey::Indexed(459829968682))]), + ArbitraryQueryPlan(vec![(false, ArbitraryKey::Indexed(459829968682))]),*/ ArbitraryQueryPlan(vec![ (false, ArbitraryKey::Indexed(17942346408707227648)), (false, ArbitraryKey::Indexed(37833)), ]), + /* ArbitraryQueryPlan(vec![(true, ArbitraryKey::Indexed(43218140957))]), ArbitraryQueryPlan(vec![]), + */ ]; let context: FuzzContext> = build_state(CONF1); for plan in plans { From ce6eb29e28cd893647eab7ef09f81c895637dda0 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 27 Jun 2023 14:47:25 +0200 Subject: [PATCH 102/154] fix query plan iter --- trie-db/src/query_plan/verify.rs | 31 +++++++++++++++++++------------ trie-db/test/src/fuzz.rs | 6 ++---- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 772ca217..a3b7158d 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -254,10 +254,10 @@ where 0 }; if common_from > last_start_at { - // if common_from <= common_nibbles && common_from != 0 { + // if common_from <= common_nibbles && common_from != 0 { self.current = Some(next); - self.state = ReadProofState::SwitchQueryPlan; - continue + let current = self.current.as_ref().expect("current is set"); + return self.missing_switch_next(current.as_prefix, current.key) } let r = self.stack.pop_until(common_nibbles, &self.expected_root, false); @@ -380,8 +380,7 @@ where Ordering::Greater => { // two consecutive query in a node that hide them (two miss in a same proof // node). - self.state = ReadProofState::SwitchQueryPlan; - continue + return self.missing_switch_next(as_prefix, to_check.key) }, } @@ -443,13 +442,7 @@ where TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked | TryStackChildResult::StackedAfter => { - self.state = ReadProofState::SwitchQueryPlan; - if as_prefix { - self.send_enter_prefix = Some(to_check.key.to_vec()); - return Some(Ok(ReadProofItem::EndPrefix)) - } else { - return Some(Ok(ReadProofItem::NoValue(to_check.key))) - } + return self.missing_switch_next(as_prefix, to_check.key) }, TryStackChildResult::Halted => return self.halt(), } @@ -473,6 +466,20 @@ where self.state = ReadProofState::Finished; return None } + + fn missing_switch_next( + &mut self, + as_prefix: bool, + key: &'a [u8], + ) -> Option, VerifyError, CError>>> { + self.state = ReadProofState::SwitchQueryPlan; + if as_prefix { + self.send_enter_prefix = Some(key.to_vec()); + return Some(Ok(ReadProofItem::EndPrefix)) + } else { + return Some(Ok(ReadProofItem::NoValue(key))) + } + } } /// When process is halted keep execution state diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index 53029058..7738cfd6 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -744,7 +744,7 @@ pub mod query_plan { fn fuzz_query_plan_1() { use reference_trie::{RefHasher, SubstrateV1}; let plans = [ - /*ArbitraryQueryPlan(vec![ + ArbitraryQueryPlan(vec![ (false, ArbitraryKey::Random(vec![225])), (true, ArbitraryKey::Random(vec![225, 225, 225, 142])), ]), @@ -756,15 +756,13 @@ pub mod query_plan { true, ArbitraryKey::Random(vec![252, 63, 149, 166, 164, 38]), )]), - ArbitraryQueryPlan(vec![(false, ArbitraryKey::Indexed(459829968682))]),*/ + ArbitraryQueryPlan(vec![(false, ArbitraryKey::Indexed(459829968682))]), ArbitraryQueryPlan(vec![ (false, ArbitraryKey::Indexed(17942346408707227648)), (false, ArbitraryKey::Indexed(37833)), ]), - /* ArbitraryQueryPlan(vec![(true, ArbitraryKey::Indexed(43218140957))]), ArbitraryQueryPlan(vec![]), - */ ]; let context: FuzzContext> = build_state(CONF1); for plan in plans { From e08794782df6b7f03f86f8fbb2c1e67c3fe31881 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 27 Jun 2023 15:27:40 +0200 Subject: [PATCH 103/154] fix --- trie-db/src/query_plan/verify.rs | 10 +++++++--- trie-db/test/src/fuzz.rs | 4 ++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index a3b7158d..dd5022bb 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -224,6 +224,12 @@ where // read proof loop { + if self.send_exit_prefix { + debug_assert!(self.send_enter_prefix.is_none()); + debug_assert!(self.buffed_result.is_none()); + self.send_exit_prefix = false; + return Some(Ok(ReadProofItem::EndPrefix)) + } if self.state == ReadProofState::SwitchQueryPlan || self.state == ReadProofState::NotStarted { @@ -441,9 +447,7 @@ where }, TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked | - TryStackChildResult::StackedAfter => { - return self.missing_switch_next(as_prefix, to_check.key) - }, + TryStackChildResult::StackedAfter => return self.missing_switch_next(as_prefix, to_check.key), TryStackChildResult::Halted => return self.halt(), } } diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index 7738cfd6..dcc343ed 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -744,6 +744,10 @@ pub mod query_plan { fn fuzz_query_plan_1() { use reference_trie::{RefHasher, SubstrateV1}; let plans = [ + ArbitraryQueryPlan(vec![ + (true, ArbitraryKey::Random(vec![76])), + (true, ArbitraryKey::Random(vec![198, 198, 234, 35, 76, 76, 1])), + ]), ArbitraryQueryPlan(vec![ (false, ArbitraryKey::Random(vec![225])), (true, ArbitraryKey::Random(vec![225, 225, 225, 142])), From f0f41232de2053da2a654c36d23c353c71df21f5 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 27 Jun 2023 17:39:36 +0200 Subject: [PATCH 104/154] fix, into means follows child ix. --- trie-db/src/query_plan/mod.rs | 2 ++ trie-db/src/query_plan/record.rs | 18 +++++++++++++----- trie-db/src/query_plan/verify.rs | 27 ++++++++++++++++++--------- trie-db/test/src/fuzz.rs | 12 ++++++++---- 4 files changed, 41 insertions(+), 18 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index 0ce60d64..7a4768aa 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -403,6 +403,8 @@ enum ReadProofState { Running, /// Switch next item. SwitchQueryPlan, + /// Switch next item, previous access was into the child node. + SwitchQueryPlanInto, /// Proof read. PlanConsumed, /// Proof read. diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 852a1177..849c65d5 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -695,6 +695,7 @@ pub fn record_query_plan< let mut prev_query: Option = None; let from_query = from.currently_query_item.take(); let mut from_query_ref = from_query.as_ref().map(|f| f.as_ref()); + let mut prev_stacked_into = true; while let Some(query) = from_query_ref.clone().or_else(|| query_plan.items.next()) { if stateless { // advance query plan @@ -732,7 +733,8 @@ pub fn record_query_plan< // slice_query_len let common_from = query_slice.common_prefix(&from.stack.prefix.as_leftnibbleslice()); let last_start_at = if from.stack.items.len() > 1 { - from.stack.items[from.stack.items.len() - 2].depth + from.stack.items[from.stack.items.len() - 2].depth + + if prev_stacked_into { 0 } else { 1 } } else { 0 }; @@ -749,7 +751,7 @@ pub fn record_query_plan< prev_query = Some(query); continue } - //let common_nibbles = max(common_nibbles, common_from); + // let common_nibbles = min(common_nibbles, common_from); loop { match from.stack.prefix.len().cmp(&common_nibbles) { Ordering::Equal | Ordering::Less => break common_nibbles, @@ -769,6 +771,7 @@ pub fn record_query_plan< } } }; + prev_stacked_into = true; if let Some((_, hash_only)) = from.stack.iter_prefix.clone() { // statefull halted during iteration. let halt = from.iter_prefix(Some(&query), Some(db), hash_only, false)?; @@ -817,9 +820,14 @@ pub fn record_query_plan< Some(&mut slice_query), )? { TryStackChildResult::StackedFull => {}, - TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked => - break false, - TryStackChildResult::StackedAfter => break false, + TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked => { + prev_stacked_into = false; + break false + }, + TryStackChildResult::StackedAfter => { + prev_stacked_into = true; + break false + }, TryStackChildResult::StackedInto => { if query.as_prefix { let halt = diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index dd5022bb..e7830a40 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -231,6 +231,7 @@ where return Some(Ok(ReadProofItem::EndPrefix)) } if self.state == ReadProofState::SwitchQueryPlan || + self.state == ReadProofState::SwitchQueryPlanInto || self.state == ReadProofState::NotStarted { let query_plan = self.query_plan.as_mut().expect("Removed with state"); @@ -255,7 +256,8 @@ where let common_from = query_slice.common_prefix(&self.stack.prefix.as_leftnibbleslice()); let last_start_at = if self.stack.items.len() > 1 { - self.stack.items[self.stack.items.len() - 2].depth + self.stack.items[self.stack.items.len() - 2].depth + + if self.state == ReadProofState::SwitchQueryPlanInto { 0 } else { 1 } } else { 0 }; @@ -263,9 +265,10 @@ where // if common_from <= common_nibbles && common_from != 0 { self.current = Some(next); let current = self.current.as_ref().expect("current is set"); - return self.missing_switch_next(current.as_prefix, current.key) + return self.missing_switch_next(current.as_prefix, current.key, false) } + // let common_nibbles = min(common_nibbles, common_from); let r = self.stack.pop_until(common_nibbles, &self.expected_root, false); if let Err(e) = r { self.state = ReadProofState::Finished; @@ -386,7 +389,7 @@ where Ordering::Greater => { // two consecutive query in a node that hide them (two miss in a same proof // node). - return self.missing_switch_next(as_prefix, to_check.key) + return self.missing_switch_next(as_prefix, to_check.key, false) }, } @@ -398,7 +401,7 @@ where ); continue } - self.state = ReadProofState::SwitchQueryPlan; + self.state = ReadProofState::SwitchQueryPlanInto; match self.stack.access_value(&mut self.proof, check_hash, hash_only) { Ok((Some(value), None)) => return Some(Ok(ReadProofItem::Value(to_check.key.into(), value))), @@ -442,12 +445,13 @@ where ); continue } - self.state = ReadProofState::SwitchQueryPlan; + self.state = ReadProofState::SwitchQueryPlanInto; return Some(Ok(ReadProofItem::NoValue(to_check.key))) }, - TryStackChildResult::NotStackedBranch | - TryStackChildResult::NotStacked | - TryStackChildResult::StackedAfter => return self.missing_switch_next(as_prefix, to_check.key), + TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked => + return self.missing_switch_next(as_prefix, to_check.key, false), + TryStackChildResult::StackedAfter => + return self.missing_switch_next(as_prefix, to_check.key, true), TryStackChildResult::Halted => return self.halt(), } } @@ -475,8 +479,13 @@ where &mut self, as_prefix: bool, key: &'a [u8], + into: bool, ) -> Option, VerifyError, CError>>> { - self.state = ReadProofState::SwitchQueryPlan; + self.state = if into { + ReadProofState::SwitchQueryPlanInto + } else { + ReadProofState::SwitchQueryPlan + }; if as_prefix { self.send_enter_prefix = Some(key.to_vec()); return Some(Ok(ReadProofItem::EndPrefix)) diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index dcc343ed..422ddbeb 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -744,6 +744,14 @@ pub mod query_plan { fn fuzz_query_plan_1() { use reference_trie::{RefHasher, SubstrateV1}; let plans = [ + ArbitraryQueryPlan(vec![ + (false, ArbitraryKey::Indexed(17942346408707227648)), + (false, ArbitraryKey::Indexed(37833)), + ]), + ArbitraryQueryPlan(vec![ + (true, ArbitraryKey::Random(vec![131, 1, 11, 234, 137, 233, 233, 233, 180])), + (false, ArbitraryKey::Random(vec![137])), + ]), ArbitraryQueryPlan(vec![ (true, ArbitraryKey::Random(vec![76])), (true, ArbitraryKey::Random(vec![198, 198, 234, 35, 76, 76, 1])), @@ -761,10 +769,6 @@ pub mod query_plan { ArbitraryKey::Random(vec![252, 63, 149, 166, 164, 38]), )]), ArbitraryQueryPlan(vec![(false, ArbitraryKey::Indexed(459829968682))]), - ArbitraryQueryPlan(vec![ - (false, ArbitraryKey::Indexed(17942346408707227648)), - (false, ArbitraryKey::Indexed(37833)), - ]), ArbitraryQueryPlan(vec![(true, ArbitraryKey::Indexed(43218140957))]), ArbitraryQueryPlan(vec![]), ]; From 39f044beed421ccfb31973f85ec53a9292084339 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 28 Jun 2023 10:32:24 +0200 Subject: [PATCH 105/154] different approach --- trie-db/src/query_plan/record.rs | 77 +++++++++++++++++++------------- trie-db/src/query_plan/verify.rs | 45 +++++++++++++------ trie-db/test/src/fuzz.rs | 4 ++ 3 files changed, 81 insertions(+), 45 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 849c65d5..a0fab89e 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -695,7 +695,7 @@ pub fn record_query_plan< let mut prev_query: Option = None; let from_query = from.currently_query_item.take(); let mut from_query_ref = from_query.as_ref().map(|f| f.as_ref()); - let mut prev_stacked_into = true; + // let mut prev_stacked = true; while let Some(query) = from_query_ref.clone().or_else(|| query_plan.items.next()) { if stateless { // advance query plan @@ -716,8 +716,7 @@ pub fn record_query_plan< from.stack.seek = None; } } - let _common_nibbles = if let Some(slice_at) = statefull.take() { - slice_at + if let Some(slice_at) = statefull.take() { } else { let (ordered, common_nibbles) = prev_query.as_ref().map(|p| p.before(&query)).unwrap_or((true, 0)); @@ -732,30 +731,41 @@ pub fn record_query_plan< // TODO this could also be passed around from try stack result then // slice_query_len let common_from = query_slice.common_prefix(&from.stack.prefix.as_leftnibbleslice()); - let last_start_at = if from.stack.items.len() > 1 { - from.stack.items[from.stack.items.len() - 2].depth + - if prev_stacked_into { 0 } else { 1 } - } else { - 0 - }; - if common_from > last_start_at { - // if common_from <= common_nibbles && common_from != 0 { - /*if query.as_prefix { - let halt = - from.iter_prefix(Some(&query), Some(db), query.hash_only, true)?; - if halt { - return Ok(()) - } - }*/ - from_query_ref = None; - prev_query = Some(query); - continue - } - // let common_nibbles = min(common_nibbles, common_from); - loop { + /* + let last_start_at = if from.stack.items.len() > 1 { + from.stack.items[from.stack.items.len() - 2].depth + + if prev_stacked { 0 } else { 1 } + } else { + 0 + }; + if common_from > last_start_at { + // if common_from <= common_nibbles && common_from != 0 { + /*if query.as_prefix { + let halt = + from.iter_prefix(Some(&query), Some(db), query.hash_only, true)?; + if halt { + return Ok(()) + } + }*/ + from_query_ref = None; + prev_query = Some(query); + continue + } + */ + //let pop_to = min(common_nibbles, common_from); + let skip_query = loop { + let last_next_at = if from.stack.items.len() > 1 { + from.stack.items[from.stack.items.len() - 2].depth + } else { + 0 + }; + match from.stack.prefix.len().cmp(&common_nibbles) { - Ordering::Equal | Ordering::Less => break common_nibbles, + Ordering::Equal => break false, + Ordering::Less => break true, Ordering::Greater => { + // if !prev_stacked && common_nibbles > common_nibbles { + // } /* TODO these seems redundant with pop try_stack call if query_plan.kind.record_inline() { if from.stack.items.len() > 0 { @@ -763,15 +773,22 @@ pub fn record_query_plan< } } */ + //prev_stacked = true; if !from.pop() { from.finalize(); return Ok(()) } }, } + }; + if skip_query { + // will go down in same branch, skip query_plan + from_query_ref = None; + prev_query = Some(query); + continue } }; - prev_stacked_into = true; + //prev_stacked = true; if let Some((_, hash_only)) = from.stack.iter_prefix.clone() { // statefull halted during iteration. let halt = from.iter_prefix(Some(&query), Some(db), hash_only, false)?; @@ -821,13 +838,10 @@ pub fn record_query_plan< )? { TryStackChildResult::StackedFull => {}, TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked => { - prev_stacked_into = false; - break false - }, - TryStackChildResult::StackedAfter => { - prev_stacked_into = true; + //prev_stacked = false; break false }, + TryStackChildResult::StackedAfter => break false, TryStackChildResult::StackedInto => { if query.as_prefix { let halt = @@ -892,7 +906,6 @@ impl RecordStack { let child_handle = if let Some(item) = self.items.last_mut() { //if inline_only && item.accessed_children_node.at(child_index as usize) { if item.accessed_children_node.at(child_index as usize) { - // TODO may be unreachable (or remove some pre checks). // No reason to go twice in a same branch return Ok(TryStackChildResult::NotStackedBranch) } diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index e7830a40..175df903 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -57,6 +57,7 @@ struct ReadStack { start_items: usize, is_compact: bool, expect_value: bool, + accessed_root: bool, _ph: PhantomData, } @@ -69,6 +70,7 @@ impl Clone for ReadStack { iter_prefix: self.iter_prefix, is_compact: self.is_compact, expect_value: self.expect_value, + accessed_root: self.accessed_root, _ph: PhantomData, } } @@ -146,6 +148,7 @@ where is_compact: self.is_compact, expect_value: false, iter_prefix: None, + accessed_root: false, _ph: PhantomData, }, ); @@ -255,6 +258,7 @@ where // slice_query_len let common_from = query_slice.common_prefix(&self.stack.prefix.as_leftnibbleslice()); + /* let last_start_at = if self.stack.items.len() > 1 { self.stack.items[self.stack.items.len() - 2].depth + if self.state == ReadProofState::SwitchQueryPlanInto { 0 } else { 1 } @@ -268,11 +272,19 @@ where return self.missing_switch_next(current.as_prefix, current.key, false) } + */ // let common_nibbles = min(common_nibbles, common_from); - let r = self.stack.pop_until(common_nibbles, &self.expected_root, false); - if let Err(e) = r { - self.state = ReadProofState::Finished; - return Some(Err(e)) + match self.stack.pop_until(common_nibbles, &self.expected_root, false) { + Ok(true) => { + self.current = Some(next); + let current = self.current.as_ref().expect("current is set"); + return self.missing_switch_next(current.as_prefix, current.key, false) + }, + Err(e) => { + self.state = ReadProofState::Finished; + return Some(Err(e)) + }, + Ok(false) => (), } self.state = ReadProofState::Running; @@ -523,6 +535,7 @@ impl<'a, L: TrieLayout, C, D: SplitFirst> From> is_compact, expect_value: false, iter_prefix: None, + accessed_root: false, _ph: PhantomData, }, state: ReadProofState::NotStarted, @@ -559,6 +572,9 @@ impl ReadStack { }, } } else { + if self.accessed_root { + return Ok(TryStackChildResult::NotStacked) + } if self.is_compact { NodeHandle::Inline(&[]) } else { @@ -873,7 +889,7 @@ impl ReadStack { target: usize, expected_root: &Option>, check_only: bool, - ) -> Result<(), VerifyError, CError>> { + ) -> Result, CError>> { if self.is_compact && expected_root.is_some() { // TODO pop with check only, here unefficient implementation where we just restore @@ -888,10 +904,10 @@ impl ReadStack { Ordering::Greater => (), // depth should match. Ordering::Less => { - // TODO other error - return Err(VerifyError::ExtraneousNode) + // skip query plan + return Ok(true) }, - Ordering::Equal => return Ok(()), + Ordering::Equal => return Ok(false), } // one by one let _ = self.pop(expected_root)?; @@ -899,7 +915,7 @@ impl ReadStack { if let Some(old) = restore.take() { *self = old; - return Ok(()) + return Ok(false) } } loop { @@ -907,17 +923,20 @@ impl ReadStack { match last.depth.cmp(&target) { Ordering::Greater => (), // depth should match. - Ordering::Less => break, + Ordering::Less => { + // skip + return Ok(true) + }, Ordering::Equal => { self.prefix.drop_lasts(self.prefix.len() - last.depth); - return Ok(()) + return Ok(false) }, } } else { if target == 0 { - return Ok(()) + return Ok(false) } else { - break + return Ok(true) } } let _ = self.items.pop(); diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index 422ddbeb..41e7768c 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -744,6 +744,10 @@ pub mod query_plan { fn fuzz_query_plan_1() { use reference_trie::{RefHasher, SubstrateV1}; let plans = [ + ArbitraryQueryPlan(vec![ + (false, ArbitraryKey::Indexed(9137484785696899328)), + (false, ArbitraryKey::Indexed(393082)), + ]), ArbitraryQueryPlan(vec![ (false, ArbitraryKey::Indexed(17942346408707227648)), (false, ArbitraryKey::Indexed(37833)), From 9b50c3a1cd90494ec153a7baa9050eedbc0ac661 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 28 Jun 2023 10:36:19 +0200 Subject: [PATCH 106/154] clean --- trie-db/src/query_plan/record.rs | 58 +++----------------------------- trie-db/src/query_plan/verify.rs | 23 ------------- 2 files changed, 5 insertions(+), 76 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index a0fab89e..8eb6487f 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -695,7 +695,6 @@ pub fn record_query_plan< let mut prev_query: Option = None; let from_query = from.currently_query_item.take(); let mut from_query_ref = from_query.as_ref().map(|f| f.as_ref()); - // let mut prev_stacked = true; while let Some(query) = from_query_ref.clone().or_else(|| query_plan.items.next()) { if stateless { // advance query plan @@ -716,8 +715,7 @@ pub fn record_query_plan< from.stack.seek = None; } } - if let Some(slice_at) = statefull.take() { - } else { + if statefull.take().is_none() { let (ordered, common_nibbles) = prev_query.as_ref().map(|p| p.before(&query)).unwrap_or((true, 0)); if !ordered { @@ -727,58 +725,15 @@ pub fn record_query_plan< return Err(VerifyError::UnorderedKey(query.key.to_vec())) } } - let query_slice = LeftNibbleSlice::new(&query.key); - // TODO this could also be passed around from try stack result then - // slice_query_len - let common_from = query_slice.common_prefix(&from.stack.prefix.as_leftnibbleslice()); - /* - let last_start_at = if from.stack.items.len() > 1 { - from.stack.items[from.stack.items.len() - 2].depth + - if prev_stacked { 0 } else { 1 } - } else { - 0 - }; - if common_from > last_start_at { - // if common_from <= common_nibbles && common_from != 0 { - /*if query.as_prefix { - let halt = - from.iter_prefix(Some(&query), Some(db), query.hash_only, true)?; - if halt { - return Ok(()) - } - }*/ - from_query_ref = None; - prev_query = Some(query); - continue - } - */ - //let pop_to = min(common_nibbles, common_from); let skip_query = loop { - let last_next_at = if from.stack.items.len() > 1 { - from.stack.items[from.stack.items.len() - 2].depth - } else { - 0 - }; - match from.stack.prefix.len().cmp(&common_nibbles) { Ordering::Equal => break false, Ordering::Less => break true, - Ordering::Greater => { - // if !prev_stacked && common_nibbles > common_nibbles { - // } - /* TODO these seems redundant with pop try_stack call - if query_plan.kind.record_inline() { - if from.stack.items.len() > 0 { - from.try_stack_content_child(from.stack.items.len() - 1)?; - } - } - */ - //prev_stacked = true; + Ordering::Greater => if !from.pop() { from.finalize(); return Ok(()) - } - }, + }, } }; if skip_query { @@ -788,7 +743,6 @@ pub fn record_query_plan< continue } }; - //prev_stacked = true; if let Some((_, hash_only)) = from.stack.iter_prefix.clone() { // statefull halted during iteration. let halt = from.iter_prefix(Some(&query), Some(db), hash_only, false)?; @@ -837,10 +791,8 @@ pub fn record_query_plan< Some(&mut slice_query), )? { TryStackChildResult::StackedFull => {}, - TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked => { - //prev_stacked = false; - break false - }, + TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked => + break false, TryStackChildResult::StackedAfter => break false, TryStackChildResult::StackedInto => { if query.as_prefix { diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 175df903..8e90c6c6 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -253,27 +253,6 @@ where } } - let query_slice = LeftNibbleSlice::new(&next.key); - // TODO this could also be passed around from try stack result then - // slice_query_len - let common_from = - query_slice.common_prefix(&self.stack.prefix.as_leftnibbleslice()); - /* - let last_start_at = if self.stack.items.len() > 1 { - self.stack.items[self.stack.items.len() - 2].depth + - if self.state == ReadProofState::SwitchQueryPlanInto { 0 } else { 1 } - } else { - 0 - }; - if common_from > last_start_at { - // if common_from <= common_nibbles && common_from != 0 { - self.current = Some(next); - let current = self.current.as_ref().expect("current is set"); - return self.missing_switch_next(current.as_prefix, current.key, false) - } - - */ - // let common_nibbles = min(common_nibbles, common_from); match self.stack.pop_until(common_nibbles, &self.expected_root, false) { Ok(true) => { self.current = Some(next); @@ -941,7 +920,5 @@ impl ReadStack { } let _ = self.items.pop(); } - // TODO other error - Err(VerifyError::ExtraneousNode) } } From eb939e68f19fcf70128e7294ba619b08227511fd Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 29 Jun 2023 16:37:13 +0200 Subject: [PATCH 107/154] add fuzzers --- trie-db/fuzz/Cargo.toml | 16 ++++++++++ trie-db/fuzz/fuzz_targets/query_plan_1.rs | 3 +- trie-db/fuzz/fuzz_targets/query_plan_2.rs | 3 +- trie-db/fuzz/fuzz_targets/query_plan_3.rs | 18 +++++++++++ trie-db/fuzz/fuzz_targets/query_plan_4.rs | 38 +++++++++++++++++++++++ trie-db/fuzz/fuzz_targets/query_plan_5.rs | 18 +++++++++++ trie-db/fuzz/fuzz_targets/query_plan_6.rs | 38 +++++++++++++++++++++++ trie-db/src/query_plan/record.rs | 5 +-- 8 files changed, 133 insertions(+), 6 deletions(-) create mode 100644 trie-db/fuzz/fuzz_targets/query_plan_3.rs create mode 100644 trie-db/fuzz/fuzz_targets/query_plan_4.rs create mode 100644 trie-db/fuzz/fuzz_targets/query_plan_5.rs create mode 100644 trie-db/fuzz/fuzz_targets/query_plan_6.rs diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml index a5cbf2db..bcd9761b 100644 --- a/trie-db/fuzz/Cargo.toml +++ b/trie-db/fuzz/Cargo.toml @@ -79,3 +79,19 @@ path = "fuzz_targets/query_plan_1.rs" [[bin]] name = "query_plan_2" path = "fuzz_targets/query_plan_2.rs" + +[[bin]] +name = "query_plan_3" +path = "fuzz_targets/query_plan_3.rs" + +[[bin]] +name = "query_plan_4" +path = "fuzz_targets/query_plan_4.rs" + +[[bin]] +name = "query_plan_5" +path = "fuzz_targets/query_plan_5.rs" + +[[bin]] +name = "query_plan_6" +path = "fuzz_targets/query_plan_6.rs" diff --git a/trie-db/fuzz/fuzz_targets/query_plan_1.rs b/trie-db/fuzz/fuzz_targets/query_plan_1.rs index 0c11fa76..93b42dd5 100644 --- a/trie-db/fuzz/fuzz_targets/query_plan_1.rs +++ b/trie-db/fuzz/fuzz_targets/query_plan_1.rs @@ -2,9 +2,8 @@ use lazy_static::lazy_static; use libfuzzer_sys::fuzz_target; use reference_trie::{RefHasher, SubstrateV1}; -use trie_db::query_plan::ProofKind; use trie_db_test::fuzz::query_plan::{ - build_state, fuzz_query_plan, ArbitraryQueryPlan, Conf, FuzzContext, CONF1, + build_state, fuzz_query_plan, ArbitraryQueryPlan, FuzzContext, CONF1, }; lazy_static! { diff --git a/trie-db/fuzz/fuzz_targets/query_plan_2.rs b/trie-db/fuzz/fuzz_targets/query_plan_2.rs index fc549335..d06fab41 100644 --- a/trie-db/fuzz/fuzz_targets/query_plan_2.rs +++ b/trie-db/fuzz/fuzz_targets/query_plan_2.rs @@ -2,9 +2,8 @@ use lazy_static::lazy_static; use libfuzzer_sys::fuzz_target; use reference_trie::{RefHasher, SubstrateV1}; -use trie_db::query_plan::ProofKind; use trie_db_test::fuzz::query_plan::{ - build_state, fuzz_query_plan_conf, ArbitraryQueryPlan, Conf, FuzzContext, CONF1, + build_state, fuzz_query_plan_conf, ArbitraryQueryPlan, FuzzContext, CONF1, }; use arbitrary::Arbitrary; diff --git a/trie-db/fuzz/fuzz_targets/query_plan_3.rs b/trie-db/fuzz/fuzz_targets/query_plan_3.rs new file mode 100644 index 00000000..a10ad9ce --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/query_plan_3.rs @@ -0,0 +1,18 @@ +#![no_main] +use lazy_static::lazy_static; +use libfuzzer_sys::fuzz_target; +use reference_trie::{RefHasher, SubstrateV1}; +use trie_db::query_plan::ProofKind; +use trie_db_test::fuzz::query_plan::{ + build_state, fuzz_query_plan_conf, ArbitraryQueryPlan, FuzzContext, CONF1, +}; + +lazy_static! { + static ref CONTEXT: FuzzContext> = build_state(CONF1); +} + +fuzz_target!(|plan: ArbitraryQueryPlan| { + let mut conf = CONTEXT.conf.clone(); + conf.kind = ProofKind::CompactNodes; + fuzz_query_plan_conf::>(&CONTEXT, conf, plan); +}); diff --git a/trie-db/fuzz/fuzz_targets/query_plan_4.rs b/trie-db/fuzz/fuzz_targets/query_plan_4.rs new file mode 100644 index 00000000..05a20f2f --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/query_plan_4.rs @@ -0,0 +1,38 @@ +#![no_main] +use lazy_static::lazy_static; +use libfuzzer_sys::fuzz_target; +use reference_trie::{RefHasher, SubstrateV1}; +use trie_db::query_plan::ProofKind; +use trie_db_test::fuzz::query_plan::{ + build_state, fuzz_query_plan_conf, ArbitraryQueryPlan, FuzzContext, CONF1, +}; +use arbitrary::Arbitrary; + +lazy_static! { + static ref CONTEXT: FuzzContext> = build_state(CONF1); +} + +#[derive(Debug, Clone, Copy, Arbitrary)] +#[repr(usize)] +enum SplitSize { + One = 1, + Two = 2, + Three = 3, + More = 10, + MoreMore = 50, +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Arbitrary)] +enum SplitKind { + Stateless, + Stateful, +} + +fuzz_target!(|input: (ArbitraryQueryPlan, SplitSize, SplitKind)| { + let (plan, split_size, split_kind) = input; + let mut conf = CONTEXT.conf.clone(); + conf.kind = ProofKind::CompactNodes; + conf.limit = split_size as usize; + conf.proof_spawn_with_persistence = split_kind == SplitKind::Stateful; + fuzz_query_plan_conf::>(&CONTEXT, conf, plan); +}); diff --git a/trie-db/fuzz/fuzz_targets/query_plan_5.rs b/trie-db/fuzz/fuzz_targets/query_plan_5.rs new file mode 100644 index 00000000..155e0639 --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/query_plan_5.rs @@ -0,0 +1,18 @@ +#![no_main] +use lazy_static::lazy_static; +use libfuzzer_sys::fuzz_target; +use reference_trie::{RefHasher, SubstrateV1}; +use trie_db::query_plan::ProofKind; +use trie_db_test::fuzz::query_plan::{ + build_state, fuzz_query_plan_conf, ArbitraryQueryPlan, FuzzContext, CONF1, +}; + +lazy_static! { + static ref CONTEXT: FuzzContext> = build_state(CONF1); +} + +fuzz_target!(|plan: ArbitraryQueryPlan| { + let mut conf = CONTEXT.conf.clone(); + conf.kind = ProofKind::CompactContent; + fuzz_query_plan_conf::>(&CONTEXT, conf, plan); +}); diff --git a/trie-db/fuzz/fuzz_targets/query_plan_6.rs b/trie-db/fuzz/fuzz_targets/query_plan_6.rs new file mode 100644 index 00000000..ca2887e6 --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/query_plan_6.rs @@ -0,0 +1,38 @@ +#![no_main] +use lazy_static::lazy_static; +use libfuzzer_sys::fuzz_target; +use reference_trie::{RefHasher, SubstrateV1}; +use trie_db::query_plan::ProofKind; +use trie_db_test::fuzz::query_plan::{ + build_state, fuzz_query_plan_conf, ArbitraryQueryPlan, FuzzContext, CONF1, +}; +use arbitrary::Arbitrary; + +lazy_static! { + static ref CONTEXT: FuzzContext> = build_state(CONF1); +} + +#[derive(Debug, Clone, Copy, Arbitrary)] +#[repr(usize)] +enum SplitSize { + One = 1, + Two = 2, + Three = 3, + More = 10, + MoreMore = 50, +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Arbitrary)] +enum SplitKind { + Stateless, + Stateful, +} + +fuzz_target!(|input: (ArbitraryQueryPlan, SplitSize, SplitKind)| { + let (plan, split_size, split_kind) = input; + let mut conf = CONTEXT.conf.clone(); + conf.kind = ProofKind::CompactContent; + conf.limit = split_size as usize; + conf.proof_spawn_with_persistence = split_kind == SplitKind::Stateful; + fuzz_query_plan_conf::>(&CONTEXT, conf, plan); +}); diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 8eb6487f..2d1bc1ba 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -857,10 +857,11 @@ impl RecordStack { let mut from_branch = None; let child_handle = if let Some(item) = self.items.last_mut() { //if inline_only && item.accessed_children_node.at(child_index as usize) { - if item.accessed_children_node.at(child_index as usize) { + debug_assert!(!item.accessed_children_node.at(child_index as usize)); + /* if item.accessed_children_node.at(child_index as usize) { // No reason to go twice in a same branch return Ok(TryStackChildResult::NotStackedBranch) - } + }*/ let node_data = item.node.data(); From 3f5d83cddf6c6b5bcb624ef2e055d81380260ae8 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 29 Jun 2023 16:47:28 +0200 Subject: [PATCH 108/154] fix fuzzer. --- trie-db/fuzz/fuzz_targets/query_plan_3.rs | 9 ++-- trie-db/fuzz/fuzz_targets/query_plan_4.rs | 6 +-- trie-db/fuzz/fuzz_targets/query_plan_5.rs | 9 ++-- trie-db/fuzz/fuzz_targets/query_plan_6.rs | 6 +-- trie-db/test/src/fuzz.rs | 51 +++++++++++++++++++++++ 5 files changed, 61 insertions(+), 20 deletions(-) diff --git a/trie-db/fuzz/fuzz_targets/query_plan_3.rs b/trie-db/fuzz/fuzz_targets/query_plan_3.rs index a10ad9ce..2e9028bb 100644 --- a/trie-db/fuzz/fuzz_targets/query_plan_3.rs +++ b/trie-db/fuzz/fuzz_targets/query_plan_3.rs @@ -2,17 +2,14 @@ use lazy_static::lazy_static; use libfuzzer_sys::fuzz_target; use reference_trie::{RefHasher, SubstrateV1}; -use trie_db::query_plan::ProofKind; use trie_db_test::fuzz::query_plan::{ - build_state, fuzz_query_plan_conf, ArbitraryQueryPlan, FuzzContext, CONF1, + build_state, fuzz_query_plan, ArbitraryQueryPlan, FuzzContext, CONF2, }; lazy_static! { - static ref CONTEXT: FuzzContext> = build_state(CONF1); + static ref CONTEXT: FuzzContext> = build_state(CONF2); } fuzz_target!(|plan: ArbitraryQueryPlan| { - let mut conf = CONTEXT.conf.clone(); - conf.kind = ProofKind::CompactNodes; - fuzz_query_plan_conf::>(&CONTEXT, conf, plan); + fuzz_query_plan::>(&CONTEXT, plan); }); diff --git a/trie-db/fuzz/fuzz_targets/query_plan_4.rs b/trie-db/fuzz/fuzz_targets/query_plan_4.rs index 05a20f2f..29c4a563 100644 --- a/trie-db/fuzz/fuzz_targets/query_plan_4.rs +++ b/trie-db/fuzz/fuzz_targets/query_plan_4.rs @@ -2,14 +2,13 @@ use lazy_static::lazy_static; use libfuzzer_sys::fuzz_target; use reference_trie::{RefHasher, SubstrateV1}; -use trie_db::query_plan::ProofKind; use trie_db_test::fuzz::query_plan::{ - build_state, fuzz_query_plan_conf, ArbitraryQueryPlan, FuzzContext, CONF1, + build_state, fuzz_query_plan_conf, ArbitraryQueryPlan, FuzzContext, CONF2, }; use arbitrary::Arbitrary; lazy_static! { - static ref CONTEXT: FuzzContext> = build_state(CONF1); + static ref CONTEXT: FuzzContext> = build_state(CONF2); } #[derive(Debug, Clone, Copy, Arbitrary)] @@ -31,7 +30,6 @@ enum SplitKind { fuzz_target!(|input: (ArbitraryQueryPlan, SplitSize, SplitKind)| { let (plan, split_size, split_kind) = input; let mut conf = CONTEXT.conf.clone(); - conf.kind = ProofKind::CompactNodes; conf.limit = split_size as usize; conf.proof_spawn_with_persistence = split_kind == SplitKind::Stateful; fuzz_query_plan_conf::>(&CONTEXT, conf, plan); diff --git a/trie-db/fuzz/fuzz_targets/query_plan_5.rs b/trie-db/fuzz/fuzz_targets/query_plan_5.rs index 155e0639..8f40b560 100644 --- a/trie-db/fuzz/fuzz_targets/query_plan_5.rs +++ b/trie-db/fuzz/fuzz_targets/query_plan_5.rs @@ -2,17 +2,14 @@ use lazy_static::lazy_static; use libfuzzer_sys::fuzz_target; use reference_trie::{RefHasher, SubstrateV1}; -use trie_db::query_plan::ProofKind; use trie_db_test::fuzz::query_plan::{ - build_state, fuzz_query_plan_conf, ArbitraryQueryPlan, FuzzContext, CONF1, + build_state, fuzz_query_plan, ArbitraryQueryPlan, FuzzContext, CONF3, }; lazy_static! { - static ref CONTEXT: FuzzContext> = build_state(CONF1); + static ref CONTEXT: FuzzContext> = build_state(CONF3); } fuzz_target!(|plan: ArbitraryQueryPlan| { - let mut conf = CONTEXT.conf.clone(); - conf.kind = ProofKind::CompactContent; - fuzz_query_plan_conf::>(&CONTEXT, conf, plan); + fuzz_query_plan::>(&CONTEXT, plan); }); diff --git a/trie-db/fuzz/fuzz_targets/query_plan_6.rs b/trie-db/fuzz/fuzz_targets/query_plan_6.rs index ca2887e6..8d751c0c 100644 --- a/trie-db/fuzz/fuzz_targets/query_plan_6.rs +++ b/trie-db/fuzz/fuzz_targets/query_plan_6.rs @@ -2,14 +2,13 @@ use lazy_static::lazy_static; use libfuzzer_sys::fuzz_target; use reference_trie::{RefHasher, SubstrateV1}; -use trie_db::query_plan::ProofKind; use trie_db_test::fuzz::query_plan::{ - build_state, fuzz_query_plan_conf, ArbitraryQueryPlan, FuzzContext, CONF1, + build_state, fuzz_query_plan_conf, ArbitraryQueryPlan, FuzzContext, CONF3, }; use arbitrary::Arbitrary; lazy_static! { - static ref CONTEXT: FuzzContext> = build_state(CONF1); + static ref CONTEXT: FuzzContext> = build_state(CONF3); } #[derive(Debug, Clone, Copy, Arbitrary)] @@ -31,7 +30,6 @@ enum SplitKind { fuzz_target!(|input: (ArbitraryQueryPlan, SplitSize, SplitKind)| { let (plan, split_size, split_kind) = input; let mut conf = CONTEXT.conf.clone(); - conf.kind = ProofKind::CompactContent; conf.limit = split_size as usize; conf.proof_spawn_with_persistence = split_kind == SplitKind::Stateful; fuzz_query_plan_conf::>(&CONTEXT, conf, plan); diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index 41e7768c..2794991b 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -740,6 +740,30 @@ pub mod query_plan { proof_spawn_with_persistence: false, }; + /// Fuzzing conf 2. + pub const CONF2: Conf = Conf { + seed: 0u64, + kind: ProofKind::CompactNodes, + nb_key_value: 300, + nb_small_value_set: 5, + nb_big_value_set: 5, + hash_only: false, + limit: 0, // no limit + proof_spawn_with_persistence: false, + }; + + /// Fuzzing conf 3. + pub const CONF3: Conf = Conf { + seed: 0u64, + kind: ProofKind::CompactContent, + nb_key_value: 300, + nb_small_value_set: 5, + nb_big_value_set: 5, + hash_only: false, + limit: 0, // no limit + proof_spawn_with_persistence: false, + }; + #[test] fn fuzz_query_plan_1() { use reference_trie::{RefHasher, SubstrateV1}; @@ -810,4 +834,31 @@ pub mod query_plan { fuzz_query_plan_conf::>(&context, conf, plan.clone()); } } + + #[test] + fn fuzz_query_plan_3() { + use reference_trie::{RefHasher, SubstrateV1}; + let plans = [ + ArbitraryQueryPlan(vec![]), + ]; + let context: FuzzContext> = build_state(CONF2); + for plan in plans { + fuzz_query_plan::>(&context, plan.clone()); + } + } + + #[test] + fn fuzz_query_plan_4() { + use reference_trie::{RefHasher, SubstrateV1}; + let plans = [ + ArbitraryQueryPlan(vec![]), + ]; + let mut conf = CONF2.clone(); + let context: FuzzContext> = build_state(CONF2); + for plan in plans { + conf.limit = 2; + conf.proof_spawn_with_persistence = true; + fuzz_query_plan_conf::>(&context, conf, plan.clone()); + } + } } From 3e0b1eae1a1001896c0620d811d81c8a95fef2e4 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 29 Jun 2023 16:53:39 +0200 Subject: [PATCH 109/154] error --- trie-db/test/src/fuzz.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index 2794991b..0ea8d0ee 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -838,9 +838,7 @@ pub mod query_plan { #[test] fn fuzz_query_plan_3() { use reference_trie::{RefHasher, SubstrateV1}; - let plans = [ - ArbitraryQueryPlan(vec![]), - ]; + let plans = [ArbitraryQueryPlan(vec![])]; let context: FuzzContext> = build_state(CONF2); for plan in plans { fuzz_query_plan::>(&context, plan.clone()); @@ -850,14 +848,13 @@ pub mod query_plan { #[test] fn fuzz_query_plan_4() { use reference_trie::{RefHasher, SubstrateV1}; - let plans = [ - ArbitraryQueryPlan(vec![]), - ]; + let plans = + [(ArbitraryQueryPlan(vec![(false, ArbitraryKey::Random(vec![115]))]), 1, false)]; let mut conf = CONF2.clone(); let context: FuzzContext> = build_state(CONF2); - for plan in plans { - conf.limit = 2; - conf.proof_spawn_with_persistence = true; + for (plan, nb, statefull) in plans { + conf.limit = nb; + conf.proof_spawn_with_persistence = statefull; fuzz_query_plan_conf::>(&context, conf, plan.clone()); } } From 19d4c535c0bc0b86f1289d911ef536ea988c4bd4 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 30 Jun 2023 17:08:26 +0200 Subject: [PATCH 110/154] fix but root not checked --- trie-db/src/query_plan/verify.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 8e90c6c6..d9791f2f 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -128,7 +128,7 @@ where &mut self, ) -> Option, VerifyError, CError>>> { if self.is_compact { - let stack_to = 0; // TODO restart is different + let stack_to = 0; let r = self.stack.pop_until(stack_to, &self.expected_root, true); if let Err(e) = r { self.state = ReadProofState::Finished; @@ -139,7 +139,7 @@ where let query_plan = crate::rstd::mem::replace(&mut self.query_plan, None); let query_plan = query_plan.expect("Init with state"); let current = crate::rstd::mem::take(&mut self.current); - let mut stack = crate::rstd::mem::replace( + let mut stack = crate::rstd::mem::replace( // TODO impl default and use take &mut self.stack, ReadStack { items: Default::default(), @@ -534,6 +534,7 @@ impl ReadStack { mut slice_query: Option<&mut NibbleSlice>, ) -> Result, CError>> { let check_hash = expected_root.is_some(); + let items_len = self.items.len(); let child_handle = if let Some(node) = self.items.last_mut() { let node_data = node.data(); @@ -595,11 +596,14 @@ impl ReadStack { .into() }, NodeHandle::Hash(hash) => { - // TODO if is_compact allow only if restart bellow depth (otherwhise should be - // inline(0)) or means halted (if something in proof it is extraneous node) let Some(mut encoded_node) = proof.next() else { return Ok(TryStackChildResult::Halted); }; + if self.is_compact && items_len > self.start_items { + let mut error_hash = TrieHash::::default(); + error_hash.as_mut().copy_from_slice(hash); + return Err(VerifyError::ExtraneousHashReference(error_hash)); + } if self.is_compact && encoded_node.borrow().len() > 0 && Some(encoded_node.borrow()[0]) == @@ -890,6 +894,9 @@ impl ReadStack { } // one by one let _ = self.pop(expected_root)?; + if self.items.len() == self.start_items { + break; + } } if let Some(old) = restore.take() { From b0a26f504028c55672b94cd07ae466f9ccd51e6a Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 30 Jun 2023 17:19:59 +0200 Subject: [PATCH 111/154] fix check of root --- trie-db/src/query_plan/verify.rs | 63 +++++++++++++++++--------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index d9791f2f..72f4ecde 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -128,8 +128,7 @@ where &mut self, ) -> Option, VerifyError, CError>>> { if self.is_compact { - let stack_to = 0; - let r = self.stack.pop_until(stack_to, &self.expected_root, true); + let r = self.stack.pop_until(None, &self.expected_root, true); if let Err(e) = r { self.state = ReadProofState::Finished; return Some(Err(e)) @@ -139,7 +138,8 @@ where let query_plan = crate::rstd::mem::replace(&mut self.query_plan, None); let query_plan = query_plan.expect("Init with state"); let current = crate::rstd::mem::take(&mut self.current); - let mut stack = crate::rstd::mem::replace( // TODO impl default and use take + let mut stack = crate::rstd::mem::replace( + // TODO impl default and use take &mut self.stack, ReadStack { items: Default::default(), @@ -253,7 +253,7 @@ where } } - match self.stack.pop_until(common_nibbles, &self.expected_root, false) { + match self.stack.pop_until(Some(common_nibbles), &self.expected_root, false) { Ok(true) => { self.current = Some(next); let current = self.current.as_ref().expect("current is set"); @@ -449,9 +449,7 @@ where debug_assert!(self.state == ReadProofState::PlanConsumed); if self.is_compact { - let stack_to = 0; // TODO restart is different - // let r = self.stack.pop_until(common_nibbles, &self.expected_root); - let r = self.stack.pop_until(stack_to, &self.expected_root, false); + let r = self.stack.pop_until(None, &self.expected_root, false); if let Err(e) = r { self.state = ReadProofState::Finished; return Some(Err(e)) @@ -602,7 +600,7 @@ impl ReadStack { if self.is_compact && items_len > self.start_items { let mut error_hash = TrieHash::::default(); error_hash.as_mut().copy_from_slice(hash); - return Err(VerifyError::ExtraneousHashReference(error_hash)); + return Err(VerifyError::ExtraneousHashReference(error_hash)) } if self.is_compact && encoded_node.borrow().len() > 0 && @@ -869,7 +867,7 @@ impl ReadStack { fn pop_until( &mut self, - target: usize, + target: Option, expected_root: &Option>, check_only: bool, ) -> Result, CError>> { @@ -883,19 +881,21 @@ impl ReadStack { } // one by one while let Some(last) = self.items.last() { - match last.depth.cmp(&target) { - Ordering::Greater => (), - // depth should match. - Ordering::Less => { - // skip query plan - return Ok(true) - }, - Ordering::Equal => return Ok(false), + if let Some(target) = target.as_ref() { + match last.depth.cmp(&target) { + Ordering::Greater => (), + // depth should match. + Ordering::Less => { + // skip query plan + return Ok(true) + }, + Ordering::Equal => return Ok(false), + } } // one by one let _ = self.pop(expected_root)?; if self.items.len() == self.start_items { - break; + break } } @@ -904,22 +904,25 @@ impl ReadStack { return Ok(false) } } + // let target = target.unwrap_or(0); loop { if let Some(last) = self.items.last() { - match last.depth.cmp(&target) { - Ordering::Greater => (), - // depth should match. - Ordering::Less => { - // skip - return Ok(true) - }, - Ordering::Equal => { - self.prefix.drop_lasts(self.prefix.len() - last.depth); - return Ok(false) - }, + if let Some(target) = target.as_ref() { + match last.depth.cmp(&target) { + Ordering::Greater => (), + // depth should match. + Ordering::Less => { + // skip + return Ok(true) + }, + Ordering::Equal => { + self.prefix.drop_lasts(self.prefix.len() - last.depth); + return Ok(false) + }, + } } } else { - if target == 0 { + if target.unwrap_or(0) == 0 { return Ok(false) } else { return Ok(true) From 900c1a381f532079c44d8b892fac819964853089 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 30 Jun 2023 17:27:17 +0200 Subject: [PATCH 112/154] cache already use --- trie-db/src/query_plan/record.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 2d1bc1ba..9800bd4b 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -927,7 +927,6 @@ impl RecordStack { slice_query.as_mut().map(|s| s.advance(1)); prefix.push(child_index); } - // TODO handle cache first let child_node = if let Some(db) = db { db.get_raw_or_lookup_with_cache(parent_hash, child_handle, prefix.as_prefix(), false) .map_err(|_| VerifyError::IncompleteProof)? From 6636a69ae87e8817f17e9e4ad1d12489248505c3 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 30 Jun 2023 17:29:54 +0200 Subject: [PATCH 113/154] next issue --- trie-db/test/src/fuzz.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index 0ea8d0ee..35d5b4b3 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -848,8 +848,15 @@ pub mod query_plan { #[test] fn fuzz_query_plan_4() { use reference_trie::{RefHasher, SubstrateV1}; - let plans = - [(ArbitraryQueryPlan(vec![(false, ArbitraryKey::Random(vec![115]))]), 1, false)]; + let plans = [( + ArbitraryQueryPlan(vec![ + (true, ArbitraryKey::Random(vec![86])), + (false, ArbitraryKey::Random(vec![232])), + ]), + 3, + true, // TODO false + )]; + [(ArbitraryQueryPlan(vec![(false, ArbitraryKey::Random(vec![115]))]), 1, false)]; let mut conf = CONF2.clone(); let context: FuzzContext> = build_state(CONF2); for (plan, nb, statefull) in plans { From e1d4ec042b741178d72dddf3fe5e5f9bb2501ea7 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 30 Jun 2023 18:04:45 +0200 Subject: [PATCH 114/154] unstack missing start update. --- trie-db/src/query_plan/verify.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 72f4ecde..b1dd1a7f 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -929,6 +929,9 @@ impl ReadStack { } } let _ = self.items.pop(); + if self.items.len() < self.start_items { + self.start_items = self.items.len(); + } } } } From 8d992e9f76eb4a6a42635700bc868242ea174289 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 3 Jul 2023 10:38:45 +0200 Subject: [PATCH 115/154] cleaning --- trie-db/src/query_plan/mod.rs | 7 ++++ trie-db/src/query_plan/verify.rs | 42 +++++++++++++----------- trie-db/src/query_plan/verify_content.rs | 34 ++++++++++++------- trie-db/test/src/fuzz.rs | 12 +++++++ trie-db/test/src/query_plan.rs | 4 ++- 5 files changed, 68 insertions(+), 31 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index 7a4768aa..32388198 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -619,3 +619,10 @@ pub enum ReadProofItem<'a, L: TrieLayout, C, D: SplitFirst> { /// TODO unused implement EndPrefix, } + +#[derive(Clone)] +struct InPrefix { + start: usize, + send_value: bool, + hash_only: bool, +} diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index b1dd1a7f..087b6012 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -25,6 +25,10 @@ use crate::{ }; pub use record::{record_query_plan, HaltedStateRecord, Recorder}; + +/// Result of verify iterator. +type VerifyIteratorResult<'a, L, C, D> = Result, VerifyError, CError>>; + /// Proof reading iterator. pub struct ReadProofIterator<'a, L, C, D, P> where @@ -42,18 +46,17 @@ where current: Option>, current_offset: usize, state: ReadProofState, - stack: ReadStack, + stack: Stack, send_enter_prefix: Option>, send_exit_prefix: bool, - buffed_result: - Option, VerifyError, CError>>>>, + buffed_result: Option>>, } -struct ReadStack { +struct Stack { items: Vec>, prefix: NibbleVec, // limit and wether we return value and if hash only iteration. - iter_prefix: Option<(usize, bool, bool)>, + iter_prefix: Option, start_items: usize, is_compact: bool, expect_value: bool, @@ -61,13 +64,13 @@ struct ReadStack { _ph: PhantomData, } -impl Clone for ReadStack { +impl Clone for Stack { fn clone(&self) -> Self { - ReadStack { + Stack { items: self.items.clone(), prefix: self.prefix.clone(), start_items: self.start_items.clone(), - iter_prefix: self.iter_prefix, + iter_prefix: self.iter_prefix.clone(), is_compact: self.is_compact, expect_value: self.expect_value, accessed_root: self.accessed_root, @@ -141,7 +144,7 @@ where let mut stack = crate::rstd::mem::replace( // TODO impl default and use take &mut self.stack, - ReadStack { + Stack { items: Default::default(), start_items: 0, prefix: Default::default(), @@ -164,7 +167,7 @@ where fn enter_prefix_iter(&mut self, hash_only: bool, key: &[u8]) { self.send_enter_prefix = Some(key.to_vec()); - self.stack.iter_prefix = Some((self.stack.items.len(), false, hash_only)); + self.stack.iter_prefix = Some(InPrefix {start: self.stack.items.len(), send_value: false, hash_only}); } fn exit_prefix_iter(&mut self) { @@ -279,11 +282,12 @@ where } }; let did_prefix = self.stack.iter_prefix.is_some(); - while let Some((_, accessed_value_node, hash_only)) = self.stack.iter_prefix.clone() { + + while let Some(InPrefix {send_value, hash_only, ..}) = self.stack.iter_prefix.clone() { // prefix iteration - if !accessed_value_node { + if !send_value { self.stack.iter_prefix.as_mut().map(|s| { - s.1 = true; + s.send_value = true; }); match self.stack.access_value(&mut self.proof, check_hash, hash_only) { Ok((Some(value), None)) => @@ -329,7 +333,7 @@ where match r { TryStackChildResult::StackedFull => { self.stack.iter_prefix.as_mut().map(|p| { - p.1 = false; + p.send_value = false; }); break }, @@ -346,7 +350,7 @@ where }, } } - if self.stack.iter_prefix.as_ref().map(|p| p.1).unwrap_or_default() { + if self.stack.iter_prefix.as_ref().map(|p| p.send_value).unwrap_or_default() { if !match self.stack.pop(&self.expected_root) { Ok(r) => r, Err(e) => { @@ -489,7 +493,7 @@ where pub struct HaltedStateCheckNode<'a, L: TrieLayout, C, D: SplitFirst> { query_plan: QueryPlan<'a, C>, current: Option>, - stack: ReadStack, + stack: Stack, state: ReadProofState, restore_offset: usize, } @@ -505,7 +509,7 @@ impl<'a, L: TrieLayout, C, D: SplitFirst> From> }; HaltedStateCheckNode { - stack: ReadStack { + stack: Stack { items: Default::default(), start_items: 0, prefix: Default::default(), @@ -523,7 +527,7 @@ impl<'a, L: TrieLayout, C, D: SplitFirst> From> } } -impl ReadStack { +impl Stack { fn try_stack_child( &mut self, child_index: u8, @@ -789,7 +793,7 @@ impl ReadStack { &mut self, expected_root: &Option>, ) -> Result, CError>> { - if self.iter_prefix.as_ref().map(|p| p.0 == self.items.len()).unwrap_or(false) { + if self.iter_prefix.as_ref().map(|p| p.start == self.items.len()).unwrap_or(false) { return Ok(false) } if let Some(last) = self.items.pop() { diff --git a/trie-db/src/query_plan/verify_content.rs b/trie-db/src/query_plan/verify_content.rs index ac356c05..de0fb4ce 100644 --- a/trie-db/src/query_plan/verify_content.rs +++ b/trie-db/src/query_plan/verify_content.rs @@ -23,6 +23,9 @@ use crate::{ }; pub use record::{record_query_plan, HaltedStateRecord, Recorder}; +/// Result of verify iterator. +type VerifyIteratorResult<'a, L, C> = Result>, VerifyError, CError>>; + /// Proof reading iterator. pub struct ReadProofContentIterator<'a, L, C, P> where @@ -36,17 +39,20 @@ where proof: P, expected_root: Option>, current: Option>, + current_offset: usize, state: ReadProofState, - stack: ReadContentStack, + stack: Stack, buf_op: Option, Vec>>, + send_enter_prefix: Option>, + send_exit_prefix: bool, + buffed_result: Option>>, } -struct ReadContentStack { +struct Stack { items: Vec>, prefix: NibbleVec, // limit and wether we return value and if hash only iteration. - // TODO should be removable (just check current). - iter_prefix: Option<(usize, bool, bool)>, + iter_prefix: Option, start_items: usize, is_prev_hash_child: Option, expect_value: bool, @@ -56,13 +62,13 @@ struct ReadContentStack { _ph: PhantomData, } -impl Clone for ReadContentStack { +impl Clone for Stack { fn clone(&self) -> Self { - ReadContentStack { + Stack { items: self.items.clone(), prefix: self.prefix.clone(), start_items: self.start_items.clone(), - iter_prefix: self.iter_prefix, + iter_prefix: self.iter_prefix.clone(), expect_value: self.expect_value, is_prev_push_key: self.is_prev_push_key, is_prev_pop_key: self.is_prev_pop_key, @@ -90,7 +96,7 @@ where return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent }; - let HaltedStateCheckContent { query_plan, current, stack, state } = state; + let HaltedStateCheckContent { query_plan, current, restore_offset, stack, state } = state; match query_plan.kind { ProofKind::CompactContent => (), @@ -104,9 +110,13 @@ where proof, expected_root, current, + current_offset: restore_offset, state, stack, buf_op: None, + send_enter_prefix: None, + send_exit_prefix: false, + buffed_result: None, }) } @@ -406,14 +416,15 @@ where pub struct HaltedStateCheckContent<'a, L: TrieLayout, C> { query_plan: QueryPlan<'a, C>, current: Option>, - stack: ReadContentStack, + restore_offset: usize, + stack: Stack, state: ReadProofState, } impl<'a, L: TrieLayout, C> From> for HaltedStateCheckContent<'a, L, C> { fn from(query_plan: QueryPlan<'a, C>) -> Self { HaltedStateCheckContent { - stack: ReadContentStack { + stack: Stack { items: Default::default(), start_items: 0, prefix: Default::default(), @@ -427,12 +438,13 @@ impl<'a, L: TrieLayout, C> From> for HaltedStateCheckContent<'a }, state: ReadProofState::NotStarted, current: None, + restore_offset: 0, query_plan, } } } -impl ReadContentStack { +impl Stack { fn pop_until( &mut self, target: usize, diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index 35d5b4b3..2c612d9f 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -865,4 +865,16 @@ pub mod query_plan { fuzz_query_plan_conf::>(&context, conf, plan.clone()); } } + + #[test] + fn fuzz_query_plan_5() { + use reference_trie::{RefHasher, SubstrateV1}; + let plans = [ArbitraryQueryPlan(vec![ + (true, ArbitraryKey::Random(vec![])), + ])]; + let context: FuzzContext> = build_state(CONF3); + for plan in plans { + fuzz_query_plan::>(&context, plan.clone()); + } + } } diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index 9f3d1208..14a32a13 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -44,7 +44,7 @@ fn test_query_plan_compact_internal() { test_layouts!(test_query_plan_content, test_query_plan_content_internal); fn test_query_plan_content_internal() { test_query_plan_internal::(ProofKind::CompactContent, false); - test_query_plan_internal::(ProofKind::CompactNodes, true); + test_query_plan_internal::(ProofKind::CompactContent, true); } fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { @@ -79,6 +79,7 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { ignore_unordered: false, kind, }, + /* TODO restore InMemQueryPlan { items: vec![ InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), @@ -96,6 +97,7 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { ignore_unordered: false, kind, }, + */ ]; for (_nb_plan, query_plan) in query_plans.iter().enumerate() { for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] { From a29509fcc0b75d6f9da034e21b6fc29e0a190f04 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 3 Jul 2023 12:03:06 +0200 Subject: [PATCH 116/154] bad copy pasting, need rewrite --- trie-db/src/query_plan/mod.rs | 2 +- trie-db/src/query_plan/verify.rs | 18 +- trie-db/src/query_plan/verify_content.rs | 226 ++++++++++++++++------- trie-db/test/src/fuzz.rs | 4 +- 4 files changed, 173 insertions(+), 77 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index 32388198..e3c8854d 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -404,7 +404,7 @@ enum ReadProofState { /// Switch next item. SwitchQueryPlan, /// Switch next item, previous access was into the child node. - SwitchQueryPlanInto, + SwitchQueryPlanInto, // TODO check if still use /// Proof read. PlanConsumed, /// Proof read. diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 087b6012..dd7f988b 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -25,9 +25,9 @@ use crate::{ }; pub use record::{record_query_plan, HaltedStateRecord, Recorder}; - /// Result of verify iterator. -type VerifyIteratorResult<'a, L, C, D> = Result, VerifyError, CError>>; +type VerifyIteratorResult<'a, L, C, D> = + Result, VerifyError, CError>>; /// Proof reading iterator. pub struct ReadProofIterator<'a, L, C, D, P> @@ -167,7 +167,8 @@ where fn enter_prefix_iter(&mut self, hash_only: bool, key: &[u8]) { self.send_enter_prefix = Some(key.to_vec()); - self.stack.iter_prefix = Some(InPrefix {start: self.stack.items.len(), send_value: false, hash_only}); + self.stack.iter_prefix = + Some(InPrefix { start: self.stack.items.len(), send_value: false, hash_only }); } fn exit_prefix_iter(&mut self) { @@ -183,7 +184,7 @@ where P: Iterator, D: SplitFirst, { - type Item = Result, VerifyError, CError>>; + type Item = VerifyIteratorResult<'a, L, C, D>; fn next(&mut self) -> Option { debug_assert!(self.send_enter_prefix.is_none()); @@ -213,9 +214,7 @@ where P: Iterator, D: SplitFirst, { - fn next_inner( - &mut self, - ) -> Option, VerifyError, CError>>> { + fn next_inner(&mut self) -> Option> { if self.state == ReadProofState::Finished { return None } @@ -283,7 +282,8 @@ where }; let did_prefix = self.stack.iter_prefix.is_some(); - while let Some(InPrefix {send_value, hash_only, ..}) = self.stack.iter_prefix.clone() { + while let Some(InPrefix { send_value, hash_only, .. }) = self.stack.iter_prefix.clone() + { // prefix iteration if !send_value { self.stack.iter_prefix.as_mut().map(|s| { @@ -473,7 +473,7 @@ where as_prefix: bool, key: &'a [u8], into: bool, - ) -> Option, VerifyError, CError>>> { + ) -> Option> { self.state = if into { ReadProofState::SwitchQueryPlanInto } else { diff --git a/trie-db/src/query_plan/verify_content.rs b/trie-db/src/query_plan/verify_content.rs index de0fb4ce..ca4d939d 100644 --- a/trie-db/src/query_plan/verify_content.rs +++ b/trie-db/src/query_plan/verify_content.rs @@ -24,7 +24,8 @@ use crate::{ pub use record::{record_query_plan, HaltedStateRecord, Recorder}; /// Result of verify iterator. -type VerifyIteratorResult<'a, L, C> = Result>, VerifyError, CError>>; +type VerifyIteratorResult<'a, L, C> = + Result>, VerifyError, CError>>; /// Proof reading iterator. pub struct ReadProofContentIterator<'a, L, C, P> @@ -51,8 +52,6 @@ where struct Stack { items: Vec>, prefix: NibbleVec, - // limit and wether we return value and if hash only iteration. - iter_prefix: Option, start_items: usize, is_prev_hash_child: Option, expect_value: bool, @@ -68,7 +67,6 @@ impl Clone for Stack { items: self.items.clone(), prefix: self.prefix.clone(), start_items: self.start_items.clone(), - iter_prefix: self.iter_prefix.clone(), expect_value: self.expect_value, is_prev_push_key: self.is_prev_push_key, is_prev_pop_key: self.is_prev_pop_key, @@ -98,13 +96,6 @@ where let HaltedStateCheckContent { query_plan, current, restore_offset, stack, state } = state; - match query_plan.kind { - ProofKind::CompactContent => (), - _ => { - return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent - }, - }; - Ok(ReadProofContentIterator { query_plan: Some(query_plan), proof, @@ -126,18 +117,59 @@ where C: Iterator>, P: Iterator, Vec>>>, { - type Item = Result>, VerifyError, CError>>; + type Item = VerifyIteratorResult<'a, L, C>; fn next(&mut self) -> Option { + debug_assert!(self.send_enter_prefix.is_none()); + debug_assert!(!self.send_exit_prefix); + if let Some(r) = self.buffed_result.take() { + return r + } + let r = self.next_inner(); + if let Some(k) = self.send_enter_prefix.take() { + self.buffed_result = Some(r); + return Some(Ok(ReadProofItem::StartPrefix(k))) + } + if self.send_exit_prefix { + self.buffed_result = Some(r); + self.send_exit_prefix = false; + return Some(Ok(ReadProofItem::EndPrefix)) + } else { + r + } + } +} + +impl<'a, L, C, P> ReadProofContentIterator<'a, L, C, P> +where + L: TrieLayout, + C: Iterator>, + P: Iterator, Vec>>>, +{ + // TODO useless next_inner??? + fn next_inner(&mut self) -> Option> { if self.state == ReadProofState::Finished { return None } + let check_hash = self.expected_root.is_some(); if self.state == ReadProofState::Halted { self.state = ReadProofState::Running; } + let mut to_check_slice = self + .current + .as_ref() + .map(|n| NibbleSlice::new_offset(n.key, self.current_offset)); + // read proof loop { + if self.send_exit_prefix { + debug_assert!(self.send_enter_prefix.is_none()); + debug_assert!(self.buffed_result.is_none()); + self.send_exit_prefix = false; + return Some(Ok(ReadProofItem::EndPrefix)) + } if self.state == ReadProofState::SwitchQueryPlan || + self.state == ReadProofState::SwitchQueryPlanInto || self.state == ReadProofState::NotStarted { let query_plan = self.query_plan.as_mut().expect("Removed with state"); @@ -156,20 +188,30 @@ where } } - let r = self.stack.pop_until(common_nibbles, false); - if let Err(e) = r { - self.state = ReadProofState::Finished; - return Some(Err(e)) + match self.stack.pop_until(Some(common_nibbles), &self.expected_root, false) { + Ok(true) => { + self.current = Some(next); + let current = self.current.as_ref().expect("current is set"); + return self.missing_switch_next(current.as_prefix, current.key, false) + }, + Err(e) => { + self.state = ReadProofState::Finished; + return Some(Err(e)) + }, + Ok(false) => (), } + self.state = ReadProofState::Running; self.current = Some(next); + to_check_slice = self + .current + .as_ref() + .map(|n| NibbleSlice::new_offset(n.key, common_nibbles)); } else { self.state = ReadProofState::PlanConsumed; self.current = None; } }; - - // op sequence check let mut from_iter = false; while let Some(op) = self.buf_op.take().map(Option::Some).or_else(|| { from_iter = true; @@ -371,7 +413,12 @@ where Ok(()) }, Op::KeyPop(nb_nibble) => { - let r = self.stack.stack_pop(Some(nb_nibble as usize), &self.expected_root); + if nb_nibble as usize > self.stack.prefix.len() { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO better error + } + let target_depth = self.stack.prefix.len() - nb_nibble as usize; + let r = self.stack.stack_pop(Some(target_depth), &self.expected_root); self.stack.prefix.drop_lasts(nb_nibble.into()); r }, @@ -409,6 +456,25 @@ where } */ } + + fn missing_switch_next( + &mut self, + as_prefix: bool, + key: &'a [u8], + into: bool, + ) -> Option> { + self.state = if into { + ReadProofState::SwitchQueryPlanInto + } else { + ReadProofState::SwitchQueryPlan + }; + if as_prefix { + self.send_enter_prefix = Some(key.to_vec()); + return Some(Ok(ReadProofItem::EndPrefix)) + } else { + return Some(Ok(ReadProofItem::NoValue(key))) + } + } } /// When process is halted keep execution state @@ -429,7 +495,6 @@ impl<'a, L: TrieLayout, C> From> for HaltedStateCheckContent<'a start_items: 0, prefix: Default::default(), expect_value: false, - iter_prefix: None, is_prev_push_key: false, is_prev_pop_key: false, is_prev_hash_child: None, @@ -447,49 +512,71 @@ impl<'a, L: TrieLayout, C> From> for HaltedStateCheckContent<'a impl Stack { fn pop_until( &mut self, - target: usize, + target: Option, + expected_root: &Option>, check_only: bool, // TODO used? - ) -> Result<(), VerifyError, CError>> { - // TODO pop with check only, here unefficient implementation where we just restore + ) -> Result, CError>> { + if expected_root.is_some() { + // TODO pop with check only, here unefficient implementation where we just restore - let mut restore = None; - if check_only { - restore = Some(self.clone()); - self.iter_prefix = None; - } - // one by one - while let Some(last) = self.items.last() { - // depth should match. - match last.depth.cmp(&target) { - Ordering::Greater => { - // TODO could implicit pop here with a variant - // that do not write redundant pop. - return Err(VerifyError::ExtraneousNode) // TODO more precise error - }, - Ordering::Less => { - if self.first { - // allowed to have next at a upper level - } else { - return Err(VerifyError::ExtraneousNode) - } - }, - Ordering::Equal => return Ok(()), + let mut restore = None; + if check_only { + restore = Some(self.clone()); } - - // start_items update. - self.start_items = core::cmp::min(self.start_items, self.items.len()); // one by one - let _ = self.items.pop(); - } + while let Some(last) = self.items.last() { + if let Some(target) = target.as_ref() { + match last.depth.cmp(&target) { + Ordering::Greater => (), + // depth should match. + Ordering::Less => { + // skip query plan + return Ok(true) + }, + Ordering::Equal => return Ok(false), + } + } + // one by one + let target = self.items.get(self.items.len() - 2).map(|i| i.depth); + let _ = self.stack_pop(target, expected_root)?; + if self.items.len() == self.start_items { + break + } + } - if let Some(old) = restore.take() { - *self = old; - return Ok(()) + if let Some(old) = restore.take() { + *self = old; + return Ok(false) + } } - if self.items.is_empty() && target == 0 { - Ok(()) - } else { - Err(VerifyError::ExtraneousNode) + // let target = target.unwrap_or(0); + loop { + if let Some(last) = self.items.last() { + if let Some(target) = target.as_ref() { + match last.depth.cmp(&target) { + Ordering::Greater => (), + // depth should match. + Ordering::Less => { + // skip + return Ok(true) + }, + Ordering::Equal => { + self.prefix.drop_lasts(self.prefix.len() - last.depth); + return Ok(false) + }, + } + } + } else { + if target.unwrap_or(0) == 0 { + return Ok(false) + } else { + return Ok(true) + } + } + let _ = self.items.pop(); + if self.items.len() < self.start_items { + self.start_items = self.items.len(); + } } } @@ -513,10 +600,9 @@ impl Stack { #[inline(always)] fn stack_pop( &mut self, - nb_nibble: Option, + target_depth: Option, expected_root: &Option>, ) -> Result<(), VerifyError, CError>> { - let target_depth = nb_nibble.map(|n| self.prefix.len() - n); let mut first = true; while self .items @@ -547,16 +633,28 @@ impl Stack { self.items.push(item); // TODO this looks bad (pop then push, branch or leaf function should or should // not pop instead) // encode branch - self.standard_extension(depth, is_root, nkey, extension_only) + if expected_root.is_some() { + self.standard_extension(depth, is_root, nkey, extension_only) + } else { + ChildReference::Hash(TrieHash::::default()) + } } else { self.items.push(item); // TODO this looks bad (pop then push, branch or leaf function should or should // not pop instead) // encode branch - self.no_extension(depth, is_root, nkey) + if expected_root.is_some() { + self.no_extension(depth, is_root, nkey) + } else { + ChildReference::Hash(TrieHash::::default()) + } } } else { - // leaf with value - self.flush_value_change(from_depth + inc, item.depth, &item.value, is_root) + if expected_root.is_some() { + // leaf with value + self.flush_value_change(from_depth + inc, item.depth, &item.value, is_root) + } else { + ChildReference::Hash(TrieHash::::default()) + } }; if self.items.is_empty() && !is_root { @@ -579,7 +677,7 @@ impl Stack { item.children[child_ix as usize] = Some(child_reference); } else { if let Some(root) = expected_root.as_ref() { - if nb_nibble.is_none() { + if target_depth.is_none() { if root != child_reference.disp_hash() { return Err(VerifyError::RootMismatch(*child_reference.disp_hash())) } diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index 2c612d9f..6a2effe0 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -869,9 +869,7 @@ pub mod query_plan { #[test] fn fuzz_query_plan_5() { use reference_trie::{RefHasher, SubstrateV1}; - let plans = [ArbitraryQueryPlan(vec![ - (true, ArbitraryKey::Random(vec![])), - ])]; + let plans = [ArbitraryQueryPlan(vec![(true, ArbitraryKey::Random(vec![]))])]; let context: FuzzContext> = build_state(CONF3); for plan in plans { fuzz_query_plan::>(&context, plan.clone()); From 1f56d199f11343a99c463214cc8d1ffdfabd5bdd Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 3 Jul 2023 19:06:34 +0200 Subject: [PATCH 117/154] useless check, will be done on sequence and at value --- trie-db/src/query_plan/verify_content.rs | 227 +++++++++++++---------- trie-db/test/src/query_plan.rs | 10 +- 2 files changed, 142 insertions(+), 95 deletions(-) diff --git a/trie-db/src/query_plan/verify_content.rs b/trie-db/src/query_plan/verify_content.rs index ca4d939d..bda677ec 100644 --- a/trie-db/src/query_plan/verify_content.rs +++ b/trie-db/src/query_plan/verify_content.rs @@ -187,18 +187,19 @@ where return Some(Err(VerifyError::UnorderedKey(next.key.to_vec()))) } } - - match self.stack.pop_until(Some(common_nibbles), &self.expected_root, false) { - Ok(true) => { + match self.stack.stack_pop(Some(common_nibbles), &self.expected_root) { +// match self.stack.pop_until(Some(common_nibbles), &self.expected_root, false) { +/* Ok(true) => { self.current = Some(next); let current = self.current.as_ref().expect("current is set"); return self.missing_switch_next(current.as_prefix, current.key, false) - }, + },*/ Err(e) => { self.state = ReadProofState::Finished; return Some(Err(e)) }, - Ok(false) => (), + Ok(()) => (), + //Ok(false) => (), } self.state = ReadProofState::Running; @@ -212,24 +213,22 @@ where self.current = None; } }; - let mut from_iter = false; - while let Some(op) = self.buf_op.take().map(Option::Some).or_else(|| { - from_iter = true; - self.proof.next() - }) { + let mut in_iter = false; + while let Some(op) = self.buf_op.take().map(Option::Some).or_else(|| self.proof.next()) + { println!("read: {:?}", op); let Some(op) = op else { let r = self.stack.stack_pop(None, &self.expected_root); - self.state = ReadProofState::Finished; - if let Err(e) = r { - self.state = ReadProofState::Finished; - return Some(Err(e)) - } - if let Some(c) = self.current.as_ref() { + // TODO handle halt!! + self.state = ReadProofState::Finished; + if let Err(e) = r { + return Some(Err(e)) + } + if let Some(c) = self.current.as_ref() { if c.as_prefix { // end prefix switch to next self.state = ReadProofState::SwitchQueryPlan; - break; + return Some(Ok(ReadProofItem::EndPrefix)) } else { // missing value self.state = ReadProofState::SwitchQueryPlan; @@ -239,88 +238,83 @@ where return None; // finished } }; - if from_iter { - // check ordering logic - // TODO wrap in an error and put bools in a struct - match &op { - Op::KeyPush(..) => { - if self.stack.is_prev_push_key { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error - // TODO return - // Err(CompactDecoderError::ConsecutivePushKeys. - // into()) - } - self.stack.is_prev_push_key = true; - self.stack.is_prev_pop_key = false; - self.stack.is_prev_hash_child = None; - self.stack.first = false; - }, - Op::KeyPop(..) => { - if self.stack.is_prev_pop_key { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error - // return Err(CompactDecoderError::ConsecutivePopKeys. - // into()) - } - self.stack.is_prev_push_key = false; - self.stack.is_prev_pop_key = true; - self.stack.is_prev_hash_child = None; - self.stack.first = false; - }, - Op::HashChild(_, ix) => { - if let Some(prev_ix) = self.stack.is_prev_hash_child.as_ref() { - if prev_ix >= ix { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error - // return Err(CompactDecoderError::NotConsecutiveHash. - // into()) - } - } - // child ix on an existing content would be handle by iter_build. - self.stack.is_prev_push_key = false; - self.stack.is_prev_pop_key = false; - self.stack.is_prev_hash_child = Some(*ix); - }, - Op::Value(_) => { - // | Op::ValueForceInline(_) | Op::ValueForceHashed(_) => { - if !(self.stack.is_prev_push_key || self.stack.first) { + + // check ordering logic + // TODO wrap in an error and put bools in a struct TODO put in its own function + match &op { + Op::KeyPush(..) => { + if self.stack.is_prev_push_key { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error + // TODO return + // Err(CompactDecoderError::ConsecutivePushKeys. + // into()) + } + self.stack.is_prev_push_key = true; + self.stack.is_prev_pop_key = false; + self.stack.is_prev_hash_child = None; + self.stack.first = false; + }, + Op::KeyPop(..) => { + if self.stack.is_prev_pop_key { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error + // return Err(CompactDecoderError::ConsecutivePopKeys. + // into()) + } + self.stack.is_prev_push_key = false; + self.stack.is_prev_pop_key = true; + self.stack.is_prev_hash_child = None; + self.stack.first = false; + }, + Op::HashChild(_, ix) => { + if let Some(prev_ix) = self.stack.is_prev_hash_child.as_ref() { + if prev_ix >= ix { self.state = ReadProofState::Finished; return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error - // return Err(CompactDecoderError::ValueNotAfterPush. + // return Err(CompactDecoderError::NotConsecutiveHash. // into()) } - self.stack.is_prev_push_key = false; - self.stack.is_prev_pop_key = false; - self.stack.is_prev_hash_child = None; - self.stack.first = false; - }, - _ => { - self.stack.is_prev_push_key = false; - self.stack.is_prev_pop_key = false; - self.stack.is_prev_hash_child = None; - self.stack.first = false; - }, - } + } + // child ix on an existing content would be handle by iter_build. + self.stack.is_prev_push_key = false; + self.stack.is_prev_pop_key = false; + self.stack.is_prev_hash_child = Some(*ix); + }, + Op::Value(_) => { + // | Op::ValueForceInline(_) | Op::ValueForceHashed(_) => { + if !(self.stack.is_prev_push_key || self.stack.first) { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error + // return Err(CompactDecoderError::ValueNotAfterPush. + // into()) + } + self.stack.is_prev_push_key = false; + self.stack.is_prev_pop_key = false; + self.stack.is_prev_hash_child = None; + self.stack.first = false; + }, + _ => { + self.stack.is_prev_push_key = false; + self.stack.is_prev_pop_key = false; + self.stack.is_prev_hash_child = None; + self.stack.first = false; + }, + } - // debug TODO make it log and external function - match &op { - Op::HashChild(hash, child_ix) => { - println!( - "ChildHash {:?}, {:?}, {:?}", - self.stack.prefix, child_ix, hash - ); - }, - Op::HashValue(hash) => { - println!("ValueHash {:?}, {:?}", self.stack.prefix, hash); - }, - Op::Value(value) => { - println!("Value {:?}, {:?}", self.stack.prefix, value); - }, - _ => (), - } + // debug TODO make it log and external function + match &op { + Op::HashChild(hash, child_ix) => { + println!("ChildHash {:?}, {:?}, {:?}", self.stack.prefix, child_ix, hash); + }, + Op::HashValue(hash) => { + println!("ValueHash {:?}, {:?}", self.stack.prefix, hash); + }, + Op::Value(value) => { + println!("Value {:?}, {:?}", self.stack.prefix, value); + }, + _ => (), } - from_iter = false; // next let item = if match &op { @@ -406,10 +400,50 @@ where // act let r = match op { Op::KeyPush(partial, mask) => { + let slice = LeftNibbleSlice::new_with_mask(partial.as_slice(), mask); self.stack .prefix - .append_slice(LeftNibbleSlice::new_with_mask(partial.as_slice(), mask)); + .append_slice(slice); self.stack.stack_empty(self.stack.prefix.len()); + let Some(to_check_slice) = to_check_slice.as_mut() else { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) + }; + let slice = if mask == 255 { + NibbleSlice::new(&partial.as_slice()[..]) + } else { + NibbleSlice::new(&partial.as_slice()[..partial.as_slice().len() - 1]) + }; + let mut common = slice.common_prefix(to_check_slice); + to_check_slice.advance(common); + if common == slice.len() && mask != 255 && to_check_slice.len() > 0 { + if mask != 240 { + self.state = ReadProofState::Finished; + // TODO invalid key push + return Some(Err(VerifyError::ExtraneousNode)) + } + let nibble_key = nibble_ops::at_left(partial[partial.len() - 1], 0); + let nibble_plan = to_check_slice.at(0); + if nibble_key == nibble_plan { + to_check_slice.advance(1); + common += 1; + } + } + + match common.cmp(&slice.len()) { + Ordering::Less => { + }, + Ordering::Greater => { + unreachable!() + }, + Ordering::Equal => { + // setting at_value is not very useful as we expect a next value + // op and fail if it is not. + at_value = true; + }, + } + + Ok(()) }, Op::KeyPop(nb_nibble) => { @@ -604,6 +638,7 @@ impl Stack { expected_root: &Option>, ) -> Result<(), VerifyError, CError>> { let mut first = true; + let mut checked = false; while self .items .last() @@ -673,6 +708,7 @@ impl Stack { return Err(VerifyError::ExtraneousHashReference(*hash.disp_hash())) // return Err(CompactDecoderError::HashChildNotOmitted.into()) } + checked = true; } item.children[child_ix as usize] = Some(child_reference); } else { @@ -681,6 +717,7 @@ impl Stack { if root != child_reference.disp_hash() { return Err(VerifyError::RootMismatch(*child_reference.disp_hash())) } + checked = true; } } } @@ -688,6 +725,7 @@ impl Stack { // TODO can skip hash checks when above start_items. self.start_items = core::cmp::min(self.start_items, self.items.len()); } + debug_assert!(target_depth.is_some() || expected_root.is_none() || checked); Ok(()) } @@ -824,6 +862,7 @@ impl Stack { } #[inline(always)] + // TODO ret err on already set hash?? fn set_value_change(&mut self, change: ValueSet, Vec>) { if self.items.is_empty() { self.stack_empty(0); diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index 14a32a13..d3444713 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -117,6 +117,15 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { if !from.is_halted() { if kind == ProofKind::CompactContent { proofs.push(vec![from.finish().buffer]); + for proof in proofs.iter() { + let mut p = &proof[0][..]; + println!("proof start"); + while let Some((op, read)) = trie_db::content_proof::Op::, _>::decode(p).ok() { + println!("{:?}", op); + p = &p[read..]; + } + println!("proof end\n"); + } } else { proofs.push(from.finish().nodes); } @@ -130,7 +139,6 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { }; if kind == ProofKind::CompactContent { proofs.push(vec![rec.buffer]); - break // TODO remove } else { proofs.push(rec.nodes); } From 8970ae899710bf4c7d3ec4c1ac8d0d043b307912 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 3 Jul 2023 20:31:37 +0200 Subject: [PATCH 118/154] start, next fix hash only. --- trie-db/src/query_plan/verify_content.rs | 121 ++++++++++++----------- trie-db/test/src/query_plan.rs | 9 +- 2 files changed, 73 insertions(+), 57 deletions(-) diff --git a/trie-db/src/query_plan/verify_content.rs b/trie-db/src/query_plan/verify_content.rs index bda677ec..9e055138 100644 --- a/trie-db/src/query_plan/verify_content.rs +++ b/trie-db/src/query_plan/verify_content.rs @@ -44,6 +44,7 @@ where state: ReadProofState, stack: Stack, buf_op: Option, Vec>>, + in_prefix_depth: Option, send_enter_prefix: Option>, send_exit_prefix: bool, buffed_result: Option>>, @@ -105,6 +106,7 @@ where state, stack, buf_op: None, + in_prefix_depth: None, send_enter_prefix: None, send_exit_prefix: false, buffed_result: None, @@ -188,8 +190,9 @@ where } } match self.stack.stack_pop(Some(common_nibbles), &self.expected_root) { -// match self.stack.pop_until(Some(common_nibbles), &self.expected_root, false) { -/* Ok(true) => { + // match self.stack.pop_until(Some(common_nibbles), &self.expected_root, + // false) { + /* Ok(true) => { self.current = Some(next); let current = self.current.as_ref().expect("current is set"); return self.missing_switch_next(current.as_prefix, current.key, false) @@ -218,11 +221,13 @@ where { println!("read: {:?}", op); let Some(op) = op else { - let r = self.stack.stack_pop(None, &self.expected_root); - // TODO handle halt!! - self.state = ReadProofState::Finished; - if let Err(e) = r { - return Some(Err(e)) + if !self.stack.items.is_empty() { + let r = self.stack.stack_pop(None, &self.expected_root); + // TODO handle halt!! + self.state = ReadProofState::Finished; + if let Err(e) = r { + return Some(Err(e)) + } } if let Some(c) = self.current.as_ref() { if c.as_prefix { @@ -326,20 +331,33 @@ where if let Some(current) = self.current.as_ref() { let query_slice = LeftNibbleSlice::new(¤t.key); match self.stack.prefix.as_leftnibbleslice().cmp(&query_slice) { - Ordering::Equal => + Ordering::Equal => { + if current.as_prefix { + self.in_prefix_depth = Some(query_slice.len()); + self.send_enter_prefix = Some(current.key.to_vec()); + } if !self.stack.items.is_empty() { at_value = true; + } + }, + Ordering::Less => + if !self.stack.prefix.as_leftnibbleslice().starts_with(&query_slice) + { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO error backward pushed key }, - Ordering::Less => (), Ordering::Greater => if current.as_prefix { - let query_slice = LeftNibbleSlice::new(¤t.key); if self .stack .prefix .as_leftnibbleslice() .starts_with(&query_slice) { + if self.in_prefix_depth.is_none() { + self.in_prefix_depth = Some(query_slice.len()); + self.send_enter_prefix = Some(current.key.to_vec()); + } at_value = true; } else { next_query = true; @@ -352,7 +370,10 @@ where self.buf_op = Some(op); self.state = ReadProofState::SwitchQueryPlan; if current.as_prefix { - break + if self.in_prefix_depth.take().is_none() { + self.send_enter_prefix = Some(current.key.to_vec()); + } + return Some(Ok(ReadProofItem::EndPrefix)) } else { return Some(Ok(ReadProofItem::NoValue(¤t.key))) } @@ -369,13 +390,24 @@ where )) }, Op::HashValue(hash) => { + if let Some(current) = self.current.as_ref() { + if !current.as_prefix { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousHashReference( + hash.clone(), + ))) + } + } // TODO could get content from op with no clone. Some(ReadProofItem::Hash( self.stack.prefix.inner().to_vec().into(), hash.clone(), )) }, - _ => unreachable!(), + _ => { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO error unexpected op + }, } } else { match &op { @@ -401,49 +433,8 @@ where let r = match op { Op::KeyPush(partial, mask) => { let slice = LeftNibbleSlice::new_with_mask(partial.as_slice(), mask); - self.stack - .prefix - .append_slice(slice); + self.stack.prefix.append_slice(slice); self.stack.stack_empty(self.stack.prefix.len()); - let Some(to_check_slice) = to_check_slice.as_mut() else { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) - }; - let slice = if mask == 255 { - NibbleSlice::new(&partial.as_slice()[..]) - } else { - NibbleSlice::new(&partial.as_slice()[..partial.as_slice().len() - 1]) - }; - let mut common = slice.common_prefix(to_check_slice); - to_check_slice.advance(common); - if common == slice.len() && mask != 255 && to_check_slice.len() > 0 { - if mask != 240 { - self.state = ReadProofState::Finished; - // TODO invalid key push - return Some(Err(VerifyError::ExtraneousNode)) - } - let nibble_key = nibble_ops::at_left(partial[partial.len() - 1], 0); - let nibble_plan = to_check_slice.at(0); - if nibble_key == nibble_plan { - to_check_slice.advance(1); - common += 1; - } - } - - match common.cmp(&slice.len()) { - Ordering::Less => { - }, - Ordering::Greater => { - unreachable!() - }, - Ordering::Equal => { - // setting at_value is not very useful as we expect a next value - // op and fail if it is not. - at_value = true; - }, - } - - Ok(()) }, Op::KeyPop(nb_nibble) => { @@ -457,7 +448,27 @@ where r }, Op::EndProof => break, - Op::HashChild(hash, child_ix) => self.stack.set_branch_change(hash, child_ix), + Op::HashChild(hash, child_ix) => { + if self.in_prefix_depth.is_some() { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO better error missing query plan proof + } else { + // we did pop item before (see op sequence check), so we have + // stack prefix matching current plan. TODO debug assert plan starts + // with prefix TODO we could drop this check as we won t have the + // expected no value item in this case, but looks better to error here. + // TODO check, same for other proof: do a test. + if let Some(current) = self.current.as_ref() { + let query_slice = LeftNibbleSlice::new(¤t.key); + let at = self.stack.prefix.len(); + if query_slice.at(at) == Some(child_ix) { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO better error missing query plan proof + } + } + } + self.stack.set_branch_change(hash, child_ix) + }, op => { self.stack.set_value_change(op.into()); Ok(()) diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index d3444713..ed5bc5ed 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -100,7 +100,10 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { */ ]; for (_nb_plan, query_plan) in query_plans.iter().enumerate() { - for limit_conf in [(0, false), (1, false), (1, true), (2, false), (2, true), (3, true)] { + for limit_conf in [ + (0, false), /* TODO implement (1, false), (1, true), (2, false), (2, true), (3, + * true) */ + ] { let limit = limit_conf.0; let limit = (limit != 0).then(|| limit); let recorder = Recorder::new(kind, InMemoryRecorder::default(), limit, None); @@ -120,7 +123,9 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { for proof in proofs.iter() { let mut p = &proof[0][..]; println!("proof start"); - while let Some((op, read)) = trie_db::content_proof::Op::, _>::decode(p).ok() { + while let Some((op, read)) = + trie_db::content_proof::Op::, _>::decode(p).ok() + { println!("{:?}", op); p = &p[read..]; } From 11393a609340cf0868c16115d378af35e0d2a064 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 4 Jul 2023 10:51:36 +0200 Subject: [PATCH 119/154] it ok --- trie-db/src/content_proof.rs | 2 +- trie-db/src/query_plan/record.rs | 1 + trie-db/src/query_plan/verify_content.rs | 36 ++++++++++++++++++++---- trie-db/test/src/query_plan.rs | 2 +- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/trie-db/src/content_proof.rs b/trie-db/src/content_proof.rs index d63d9854..f7889ece 100644 --- a/trie-db/src/content_proof.rs +++ b/trie-db/src/content_proof.rs @@ -210,7 +210,7 @@ impl + AsMut<[u8]> + Default> Op> { 3 => { let mut hash = H::default(); let end = i + 1 + hash.as_ref().len(); - if end >= encoded.len() { + if end > encoded.len() { return Err(()) } hash.as_mut().copy_from_slice(&encoded[i + 1..end]); diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 9800bd4b..81308c38 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -524,6 +524,7 @@ impl HaltedStateRecord { return Ok(res); }; for i in 0..NIBBLE_LENGTH as u8 { + // TODO avoid the two consecutive iter on all children if !item.accessed_children_node.at(i as usize) { let node_data = item.node.data(); diff --git a/trie-db/src/query_plan/verify_content.rs b/trie-db/src/query_plan/verify_content.rs index 9e055138..73e88991 100644 --- a/trie-db/src/query_plan/verify_content.rs +++ b/trie-db/src/query_plan/verify_content.rs @@ -383,15 +383,39 @@ where if at_value { match &op { Op::Value(value) => { - // TODO could get content from op with no clone. - Some(ReadProofItem::Value( - self.stack.prefix.inner().to_vec().into(), - value.clone(), - )) + let mut hashed = None; + if let Some(current) = self.current.as_ref() { + if current.hash_only { + if L::MAX_INLINE_VALUE + .map(|max| max as usize <= value.len()) + .unwrap_or(false) + { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousValue( + value.clone(), + ))) + } else { + let hash = ::hash(value.as_slice()); + hashed = Some(ReadProofItem::Hash( + self.stack.prefix.inner().to_vec().into(), + hash, + )) + } + } + } + if hashed.is_some() { + hashed + } else { + // TODO could get content from op with no clone. + Some(ReadProofItem::Value( + self.stack.prefix.inner().to_vec().into(), + value.clone(), + )) + } }, Op::HashValue(hash) => { if let Some(current) = self.current.as_ref() { - if !current.as_prefix { + if !current.hash_only { self.state = ReadProofState::Finished; return Some(Err(VerifyError::ExtraneousHashReference( hash.clone(), diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index ed5bc5ed..96e3b26c 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -43,8 +43,8 @@ fn test_query_plan_compact_internal() { test_layouts!(test_query_plan_content, test_query_plan_content_internal); fn test_query_plan_content_internal() { - test_query_plan_internal::(ProofKind::CompactContent, false); test_query_plan_internal::(ProofKind::CompactContent, true); + test_query_plan_internal::(ProofKind::CompactContent, false); } fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { From 952b9dad5ddf1b90cdb3dac441f6a3a8fcfa240b Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 4 Jul 2023 12:47:33 +0200 Subject: [PATCH 120/154] more --- trie-db/src/query_plan/record.rs | 49 ++++++++++++++++++++++++++++++-- trie-db/test/src/query_plan.rs | 6 ++-- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 81308c38..442c0566 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -72,6 +72,21 @@ impl Recorder { Self { output, limits, start_at: None, _ph: PhantomData } } + #[must_use] + fn flush_pop_content(&mut self, items: &Vec) -> bool { + match &mut self.output { + RecorderStateInner::Content { output, stacked_push, stacked_pop } => + flush_compact_content_pop::( + output, + stacked_pop, + items, + None, + &mut self.limits, + ), + _ => false, + } + } + #[must_use] fn record_stacked_node( &mut self, @@ -116,6 +131,7 @@ impl Recorder { *stacked_push = Some(NibbleVec::new()); } if let Some(buff) = stacked_push.as_mut() { + // TODO should be doable to use the stack prefix. if !is_root { buff.push(parent_index); } @@ -402,8 +418,7 @@ impl HaltedStateRecord { RecorderStateInner::Content { output: _, stacked_push, stacked_pop: _ } => { // TODO protect existing stack as for compact assert!(stacked_push.is_none()); - // TODO could use function with &item and &[item] as param - // to skip this clone. + for i in (0..items.len()).rev() { let _ = self.record_popped_node(i); } @@ -459,7 +474,7 @@ impl HaltedStateRecord { }, } - let items = &self.stack.items[..at]; + let items = &self.stack.items[..at + 1]; match &mut self.stack.recorder.output { RecorderStateInner::Content { output, stacked_pop, .. } => { let item = &self.stack.items.get(at).expect("bounded iter"); @@ -492,6 +507,30 @@ impl HaltedStateRecord { at: usize, //upper: u8, ) -> Result, CError>> { + if self.stack.items.is_empty() { + return Ok(false) + } + if at < self.stack.items.len() - 1 { + // work on a copy of the stack + let saved_items = self.stack.items.clone(); + let saved_prefix = self.stack.prefix.clone(); + let saved_iter_prefix = self.stack.iter_prefix.clone(); + + while self.stack.items.len() != at + 1 { + self.stack.items.pop(); + } + let at = self.stack.items.last().map(|i| i.depth).unwrap_or(0); + self.stack.prefix.drop_lasts(self.stack.prefix.len() - at); + let result = self.try_stack_content_child(self.stack.items.len() - 1); + // restore state + self.stack.items = saved_items; + self.stack.prefix = saved_prefix; + self.stack.iter_prefix = saved_iter_prefix; + return result + } + + debug_assert!(at == self.stack.items.len() - 1); + let mut res = false; let dummy_parent_hash = TrieHash::::default(); for i in 0..NIBBLE_LENGTH as u8 { @@ -512,6 +551,8 @@ impl HaltedStateRecord { unreachable!() } else { self.pop(); + self.stack.halt |= + self.stack.recorder.flush_pop_content(&self.stack.items); } }, TryStackChildResult::NotStackedBranch => (), @@ -908,10 +949,12 @@ impl RecordStack { } */ if self.recorder.record_inline() { + /* TODO bad // ignore hash in inline call, but mark as accessed. if let Some(accessed_children_node) = from_branch { accessed_children_node.set(child_index as usize, true); } + */ } return Ok(TryStackChildResult::NotStackedBranch) } else if self.halt && from_branch.is_some() { diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index 96e3b26c..714fb794 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -88,16 +88,16 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { ignore_unordered: false, kind, }, + */ InMemQueryPlan { items: vec![ InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), - InMemQueryPlanItem::new(b"doge".to_vec(), hash_only, false), - InMemQueryPlanItem::new(b"horsey".to_vec(), hash_only, false), + // InMemQueryPlanItem::new(b"doge".to_vec(), hash_only, false), + // InMemQueryPlanItem::new(b"horsey".to_vec(), hash_only, false), ], ignore_unordered: false, kind, }, - */ ]; for (_nb_plan, query_plan) in query_plans.iter().enumerate() { for limit_conf in [ From 78dae9eab3d54602446c401bad06147969283064 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 4 Jul 2023 17:45:05 +0200 Subject: [PATCH 121/154] directio --- trie-db/src/query_plan/record.rs | 96 ++++++++++++++++++++------------ trie-db/test/src/query_plan.rs | 8 +-- 2 files changed, 64 insertions(+), 40 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 442c0566..3239be17 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -475,12 +475,13 @@ impl HaltedStateRecord { } let items = &self.stack.items[..at + 1]; + let mut staked_inline_last = false; match &mut self.stack.recorder.output { RecorderStateInner::Content { output, stacked_pop, .. } => { - let item = &self.stack.items.get(at).expect("bounded iter"); - if stacked_pop.is_none() { + //let item = &self.stack.items.get(at).expect("bounded iter"); + /*if stacked_pop.is_none() { *stacked_pop = Some(item.depth); - } + }*/ if has_hash_to_write { res |= flush_compact_content_pop::( @@ -497,6 +498,15 @@ impl HaltedStateRecord { }, _ => (), } + match &mut self.stack.recorder.output { + RecorderStateInner::Content { output, stacked_pop, .. } => { + let item = &self.stack.items.get(at).expect("bounded iter"); + if stacked_pop.is_none() { + *stacked_pop = Some(item.depth); + } + }, + _ => (), + } res } @@ -532,36 +542,8 @@ impl HaltedStateRecord { debug_assert!(at == self.stack.items.len() - 1); let mut res = false; - let dummy_parent_hash = TrieHash::::default(); - for i in 0..NIBBLE_LENGTH as u8 { - let Some(item) = self.stack.items.get(at) else { - return Ok(res); - }; - if !item.accessed_children_node.at(i as usize) { - match self.stack.try_stack_child(i, None, dummy_parent_hash, None)? { - // only expect a stacked full prefix or not stacked here - TryStackChildResult::StackedFull => { - let Some(item) = self.stack.items.get_mut(at) else { - return Ok(res); - }; - item.accessed_children_node.set(i as usize, true); - let halt = self.iter_prefix(None, None, false, true)?; - if halt { - // no halt on inline. - unreachable!() - } else { - self.pop(); - self.stack.halt |= - self.stack.recorder.flush_pop_content(&self.stack.items); - } - }, - TryStackChildResult::NotStackedBranch => (), - TryStackChildResult::NotStacked => break, - _ => unreachable!(), - } - } - } - let Some(item) = self.stack.items.get(at) else { + self.try_stack_content_inline_children(NIBBLE_LENGTH as u8)?; + let Some(item) = self.stack.items.last() else { return Ok(res); }; for i in 0..NIBBLE_LENGTH as u8 { @@ -577,6 +559,8 @@ impl HaltedStateRecord { if let Some(child) = &children[i as usize] { match child.build(node_data) { NodeHandle::Hash(hash) => { + // may need to flush an inline node + self.stack.recorder.flush_pop_content(&self.stack.items); res |= self.stack.recorder.touched_child_hash(&hash, i); }, _ => (), @@ -593,6 +577,43 @@ impl HaltedStateRecord { Ok(res) } + fn try_stack_content_inline_children( + &mut self, + child_ix_bound: u8, + ) -> Result<(), VerifyError, CError>> { + let Some(item) = self.stack.items.last_mut() else { + return Ok(()) + }; + let dummy_parent_hash = TrieHash::::default(); + for i in item.next_descended_child..child_ix_bound { + let Some(item) = self.stack.items.last_mut() else { + return Ok(()) + }; + if !item.accessed_children_node.at(i as usize) { + match self.stack.try_stack_child(i, None, dummy_parent_hash, None)? { + // only expect a stacked full prefix or not stacked here + TryStackChildResult::StackedFull => { + let halt = self.iter_prefix(None, None, false, true)?; + let Some(item) = self.stack.items.last_mut() else { + return Ok(()) + }; + item.accessed_children_node.set(i as usize, true); + // no halt on inline. + debug_assert!(!halt); + self.pop(); + // self.stack.halt |= + // self.stack.recorder.flush_pop_content(&self.stack.items); + }, + TryStackChildResult::NotStackedBranch => (), + TryStackChildResult::NotStacked => break, + _ => unreachable!(), + } + } + } + Ok(()) + } + + fn pop(&mut self) -> bool { if self .stack @@ -819,9 +840,9 @@ pub fn record_query_plan< } let child_index = if from.stack.items.is_empty() { 0 } else { slice_query.at(0) }; - /*if query_plan.kind.record_inline() { - from.try_stack_content_child(child_index)?; - }*/ + if query_plan.kind == ProofKind::CompactContent { + from.try_stack_content_inline_children(child_index)?; + } from.stack.items.last_mut().map(|i| { // TODO only needed for content but could be better to be always aligned i.next_descended_child = child_index + 1; @@ -1116,6 +1137,9 @@ fn flush_compact_content_pop( return false }; let pop_to = add_depth.unwrap_or_else(|| items.last().map(|i| i.depth).unwrap_or(0)); + if from == pop_to { + return false + } debug_assert!(from > pop_to); debug_assert!(from - pop_to <= u16::max_value() as usize); diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index 714fb794..b760a7e2 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -74,12 +74,12 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { return } let query_plans = [ + /* TODO restore InMemQueryPlan { items: vec![InMemQueryPlanItem::new(b"".to_vec(), hash_only, true)], ignore_unordered: false, kind, }, - /* TODO restore InMemQueryPlan { items: vec![ InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), @@ -91,9 +91,9 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { */ InMemQueryPlan { items: vec![ - InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), - // InMemQueryPlanItem::new(b"doge".to_vec(), hash_only, false), - // InMemQueryPlanItem::new(b"horsey".to_vec(), hash_only, false), + //InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), + //InMemQueryPlanItem::new(b"doge".to_vec(), hash_only, false), + InMemQueryPlanItem::new(b"horsey".to_vec(), hash_only, false), ], ignore_unordered: false, kind, From b5fff0354b1ffeeca874d328d58d6133fbf76678 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 4 Jul 2023 18:54:04 +0200 Subject: [PATCH 122/154] more --- trie-db/src/query_plan/record.rs | 5 ++--- trie-db/src/query_plan/verify_content.rs | 20 +++++++++++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 3239be17..b31e99ca 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -601,8 +601,8 @@ impl HaltedStateRecord { // no halt on inline. debug_assert!(!halt); self.pop(); - // self.stack.halt |= - // self.stack.recorder.flush_pop_content(&self.stack.items); + // self.stack.halt |= + // self.stack.recorder.flush_pop_content(&self.stack.items); }, TryStackChildResult::NotStackedBranch => (), TryStackChildResult::NotStacked => break, @@ -613,7 +613,6 @@ impl HaltedStateRecord { Ok(()) } - fn pop(&mut self) -> bool { if self .stack diff --git a/trie-db/src/query_plan/verify_content.rs b/trie-db/src/query_plan/verify_content.rs index 73e88991..377f7232 100644 --- a/trie-db/src/query_plan/verify_content.rs +++ b/trie-db/src/query_plan/verify_content.rs @@ -59,6 +59,7 @@ struct Stack { is_prev_push_key: bool, is_prev_pop_key: bool, first: bool, + expect_inline_child: bool, _ph: PhantomData, } @@ -72,6 +73,7 @@ impl Clone for Stack { is_prev_push_key: self.is_prev_push_key, is_prev_pop_key: self.is_prev_pop_key, is_prev_hash_child: self.is_prev_hash_child, + expect_inline_child: self.expect_inline_child, first: self.first, _ph: PhantomData, } @@ -286,7 +288,7 @@ where self.stack.is_prev_pop_key = false; self.stack.is_prev_hash_child = Some(*ix); }, - Op::Value(_) => { + Op::HashValue(_) | Op::Value(_) => { // | Op::ValueForceInline(_) | Op::ValueForceHashed(_) => { if !(self.stack.is_prev_push_key || self.stack.first) { self.state = ReadProofState::Finished; @@ -343,8 +345,10 @@ where Ordering::Less => if !self.stack.prefix.as_leftnibbleslice().starts_with(&query_slice) { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO error backward pushed key + self.stack.expect_inline_child = true; + // self.state = ReadProofState::Finished; + // return Some(Err(VerifyError::ExtraneousNode)) // TODO error + // backward pushed key }, Ordering::Greater => if current.as_prefix { @@ -368,6 +372,7 @@ where } if next_query { self.buf_op = Some(op); + self.stack.is_prev_push_key = true; self.state = ReadProofState::SwitchQueryPlan; if current.as_prefix { if self.in_prefix_depth.take().is_none() { @@ -567,6 +572,7 @@ impl<'a, L: TrieLayout, C> From> for HaltedStateCheckContent<'a is_prev_push_key: false, is_prev_pop_key: false, is_prev_hash_child: None, + expect_inline_child: false, first: true, _ph: PhantomData, }, @@ -731,6 +737,13 @@ impl Stack { self.stack_empty(from_depth); } + if self.expect_inline_child { + if !matches!(child_reference, ChildReference::Inline(..)) { + return Err(VerifyError::ExtraneousNode) + } + self.expect_inline_child = false; + } + let items_len = self.items.len(); if let Some(item) = self.items.last_mut() { let child_ix = self.prefix.at(item.depth); @@ -923,6 +936,7 @@ impl Stack { return Err(VerifyError::ExtraneousHashReference(*hash.disp_hash())) //return Err(CompactDecoderError::HashChildNotOmitted.into()) TODO } + item.children[i] = Some(ChildReference::Hash(branch_hash)); Ok(()) } From 74be877a6ac620b585656abca2d3d20774ff2f28 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 4 Jul 2023 19:44:06 +0200 Subject: [PATCH 123/154] pass full iter on content, next split --- trie-db/src/query_plan/record.rs | 7 +- trie-db/src/query_plan/verify_content.rs | 121 +++-------------------- trie-db/test/src/query_plan.rs | 6 +- 3 files changed, 21 insertions(+), 113 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index b31e99ca..ffc5ce73 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -75,7 +75,7 @@ impl Recorder { #[must_use] fn flush_pop_content(&mut self, items: &Vec) -> bool { match &mut self.output { - RecorderStateInner::Content { output, stacked_push, stacked_pop } => + RecorderStateInner::Content { output, stacked_pop, .. } => flush_compact_content_pop::( output, stacked_pop, @@ -475,7 +475,6 @@ impl HaltedStateRecord { } let items = &self.stack.items[..at + 1]; - let mut staked_inline_last = false; match &mut self.stack.recorder.output { RecorderStateInner::Content { output, stacked_pop, .. } => { //let item = &self.stack.items.get(at).expect("bounded iter"); @@ -499,7 +498,7 @@ impl HaltedStateRecord { _ => (), } match &mut self.stack.recorder.output { - RecorderStateInner::Content { output, stacked_pop, .. } => { + RecorderStateInner::Content { stacked_pop, .. } => { let item = &self.stack.items.get(at).expect("bounded iter"); if stacked_pop.is_none() { *stacked_pop = Some(item.depth); @@ -560,7 +559,7 @@ impl HaltedStateRecord { match child.build(node_data) { NodeHandle::Hash(hash) => { // may need to flush an inline node - self.stack.recorder.flush_pop_content(&self.stack.items); + res |= self.stack.recorder.flush_pop_content(&self.stack.items); res |= self.stack.recorder.touched_child_hash(&hash, i); }, _ => (), diff --git a/trie-db/src/query_plan/verify_content.rs b/trie-db/src/query_plan/verify_content.rs index 377f7232..cf1a4a2c 100644 --- a/trie-db/src/query_plan/verify_content.rs +++ b/trie-db/src/query_plan/verify_content.rs @@ -155,15 +155,9 @@ where if self.state == ReadProofState::Finished { return None } - let check_hash = self.expected_root.is_some(); if self.state == ReadProofState::Halted { self.state = ReadProofState::Running; } - let mut to_check_slice = self - .current - .as_ref() - .map(|n| NibbleSlice::new_offset(n.key, self.current_offset)); - // read proof loop { if self.send_exit_prefix { @@ -209,16 +203,11 @@ where self.state = ReadProofState::Running; self.current = Some(next); - to_check_slice = self - .current - .as_ref() - .map(|n| NibbleSlice::new_offset(n.key, common_nibbles)); } else { self.state = ReadProofState::PlanConsumed; self.current = None; } }; - let mut in_iter = false; while let Some(op) = self.buf_op.take().map(Option::Some).or_else(|| self.proof.next()) { println!("read: {:?}", op); @@ -331,8 +320,9 @@ where let mut at_value = false; let mut next_query = false; if let Some(current) = self.current.as_ref() { - let query_slice = LeftNibbleSlice::new(¤t.key); - match self.stack.prefix.as_leftnibbleslice().cmp(&query_slice) { + let left_query_slice = LeftNibbleSlice::new(¤t.key); + let query_slice = NibbleSlice::new(¤t.key); + match self.stack.prefix.as_leftnibbleslice().cmp(&left_query_slice) { Ordering::Equal => { if current.as_prefix { self.in_prefix_depth = Some(query_slice.len()); @@ -343,8 +333,7 @@ where } }, Ordering::Less => - if !self.stack.prefix.as_leftnibbleslice().starts_with(&query_slice) - { + if !query_slice.starts_with_vec(&self.stack.prefix) { self.stack.expect_inline_child = true; // self.state = ReadProofState::Finished; // return Some(Err(VerifyError::ExtraneousNode)) // TODO error @@ -356,7 +345,7 @@ where .stack .prefix .as_leftnibbleslice() - .starts_with(&query_slice) + .starts_with(&left_query_slice) { if self.in_prefix_depth.is_none() { self.in_prefix_depth = Some(query_slice.len()); @@ -474,6 +463,17 @@ where let target_depth = self.stack.prefix.len() - nb_nibble as usize; let r = self.stack.stack_pop(Some(target_depth), &self.expected_root); self.stack.prefix.drop_lasts(nb_nibble.into()); + if let Some(iter_depth) = self.in_prefix_depth.as_ref() { + if self + .stack + .items + .last() + .map(|i| iter_depth > &i.depth) + .unwrap_or(true) + { + self.in_prefix_depth = None; + } + } r }, Op::EndProof => break, @@ -530,25 +530,6 @@ where } */ } - - fn missing_switch_next( - &mut self, - as_prefix: bool, - key: &'a [u8], - into: bool, - ) -> Option> { - self.state = if into { - ReadProofState::SwitchQueryPlanInto - } else { - ReadProofState::SwitchQueryPlan - }; - if as_prefix { - self.send_enter_prefix = Some(key.to_vec()); - return Some(Ok(ReadProofItem::EndPrefix)) - } else { - return Some(Ok(ReadProofItem::NoValue(key))) - } - } } /// When process is halted keep execution state @@ -585,76 +566,6 @@ impl<'a, L: TrieLayout, C> From> for HaltedStateCheckContent<'a } impl Stack { - fn pop_until( - &mut self, - target: Option, - expected_root: &Option>, - check_only: bool, // TODO used? - ) -> Result, CError>> { - if expected_root.is_some() { - // TODO pop with check only, here unefficient implementation where we just restore - - let mut restore = None; - if check_only { - restore = Some(self.clone()); - } - // one by one - while let Some(last) = self.items.last() { - if let Some(target) = target.as_ref() { - match last.depth.cmp(&target) { - Ordering::Greater => (), - // depth should match. - Ordering::Less => { - // skip query plan - return Ok(true) - }, - Ordering::Equal => return Ok(false), - } - } - // one by one - let target = self.items.get(self.items.len() - 2).map(|i| i.depth); - let _ = self.stack_pop(target, expected_root)?; - if self.items.len() == self.start_items { - break - } - } - - if let Some(old) = restore.take() { - *self = old; - return Ok(false) - } - } - // let target = target.unwrap_or(0); - loop { - if let Some(last) = self.items.last() { - if let Some(target) = target.as_ref() { - match last.depth.cmp(&target) { - Ordering::Greater => (), - // depth should match. - Ordering::Less => { - // skip - return Ok(true) - }, - Ordering::Equal => { - self.prefix.drop_lasts(self.prefix.len() - last.depth); - return Ok(false) - }, - } - } - } else { - if target.unwrap_or(0) == 0 { - return Ok(false) - } else { - return Ok(true) - } - } - let _ = self.items.pop(); - if self.items.len() < self.start_items { - self.start_items = self.items.len(); - } - } - } - #[inline(always)] fn stack_empty(&mut self, depth: usize) { /* diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index b760a7e2..e168555f 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -74,7 +74,6 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { return } let query_plans = [ - /* TODO restore InMemQueryPlan { items: vec![InMemQueryPlanItem::new(b"".to_vec(), hash_only, true)], ignore_unordered: false, @@ -88,11 +87,10 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { ignore_unordered: false, kind, }, - */ InMemQueryPlan { items: vec![ - //InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), - //InMemQueryPlanItem::new(b"doge".to_vec(), hash_only, false), + InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), + InMemQueryPlanItem::new(b"doge".to_vec(), hash_only, false), InMemQueryPlanItem::new(b"horsey".to_vec(), hash_only, false), ], ignore_unordered: false, From 1d90e0d6cb1157da02af72784ae4c81b7666219c Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 5 Jul 2023 14:00:45 +0200 Subject: [PATCH 124/154] more --- trie-db/src/query_plan/record.rs | 87 ++++++++++++++++++++------------ trie-db/test/src/query_plan.rs | 6 +-- 2 files changed, 58 insertions(+), 35 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index ffc5ce73..c49f81bc 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -36,8 +36,18 @@ impl Recorder { /// Check and update start at record. /// When return true, do record. /// Else already was. - fn check_start_at(&mut self, depth: usize) -> bool { - if self.start_at.map(|s| s > depth).unwrap_or(false) { + fn check_start_at(&mut self, depth: usize, pop: bool) -> bool { + if matches!(self.output, RecorderStateInner::Content { .. }) { + if let Some(s) = self.start_at.as_mut() { + if &depth <= s { + if pop { + *s = depth; + } + return false + } + } + true + } else if self.start_at.map(|s| s >= depth).unwrap_or(false) { false } else { self.start_at = None; @@ -95,7 +105,7 @@ impl Recorder { parent_index: u8, items: &Vec, ) -> bool { - if !self.check_start_at(item.depth) { + if !self.check_start_at(item.depth, false) { return false } let mut res = false; @@ -156,7 +166,7 @@ impl Recorder { #[must_use] fn flush_compact_content_pushes(&mut self, depth: usize) -> bool { let mut res = false; - if !self.check_start_at(depth) { + if !self.check_start_at(depth, false) { // TODO actually should be unreachable return res } @@ -175,7 +185,7 @@ impl Recorder { #[must_use] fn record_value_node(&mut self, value: Vec, depth: usize) -> bool { - if !self.check_start_at(depth) { + if !self.check_start_at(depth, false) { return false } @@ -206,7 +216,7 @@ impl Recorder { #[must_use] fn record_value_inline(&mut self, value: &[u8], depth: usize) -> bool { let mut res = false; - if !self.check_start_at(depth) { + if !self.check_start_at(depth, false) { return res } if let RecorderStateInner::Content { .. } = &self.output { @@ -239,7 +249,7 @@ impl Recorder { return res } item.accessed_value_node = true; - if !self.check_start_at(item.depth) { + if !self.check_start_at(item.depth, false) { return res } let node_data = item.node.data(); @@ -383,7 +393,7 @@ impl HaltedStateRecord { self.stack.recorder.output() } - fn finalize(&mut self) { + fn finalize(&mut self, halt: bool) { let stack = &mut self.stack; let items = &stack.items; match &mut stack.recorder.output { @@ -419,8 +429,15 @@ impl HaltedStateRecord { // TODO protect existing stack as for compact assert!(stacked_push.is_none()); + if halt { + // next is a key push, do the pop + let _ = stack.recorder.flush_pop_content(items); + // let saved_iter_prefix = stack.iter_prefix.clone(); + } + for i in (0..items.len()).rev() { - let _ = self.record_popped_node(i); + // TODO if i > restart height + let _ = self.record_popped_node(i, halt); } }, } @@ -431,10 +448,10 @@ impl HaltedStateRecord { /// the stack and will not pop the node). /// Return true if should halt. #[must_use] - fn record_popped_node(&mut self, at: usize) -> bool { + fn record_popped_node(&mut self, at: usize, halt: bool) -> bool { let item = self.stack.items.get(at).expect("bounded check call"); let mut res = false; - if !self.stack.recorder.check_start_at(item.depth) { + if !self.stack.recorder.check_start_at(item.depth, true) { return res } /* TODO rem Incorrect, if first child is inline, we would push more: push @@ -492,7 +509,8 @@ impl HaltedStateRecord { ); } if process_children { - res |= self.try_stack_content_child(at).expect("stack inline do not fetch"); + res |= + self.try_stack_content_child(at, halt).expect("stack inline do not fetch"); } }, _ => (), @@ -500,7 +518,7 @@ impl HaltedStateRecord { match &mut self.stack.recorder.output { RecorderStateInner::Content { stacked_pop, .. } => { let item = &self.stack.items.get(at).expect("bounded iter"); - if stacked_pop.is_none() { + if !halt && stacked_pop.is_none() { *stacked_pop = Some(item.depth); } }, @@ -514,12 +532,12 @@ impl HaltedStateRecord { fn try_stack_content_child( &mut self, at: usize, - //upper: u8, + halt: bool, ) -> Result, CError>> { if self.stack.items.is_empty() { return Ok(false) } - if at < self.stack.items.len() - 1 { + if halt || at < self.stack.items.len() - 1 { // work on a copy of the stack let saved_items = self.stack.items.clone(); let saved_prefix = self.stack.prefix.clone(); @@ -530,7 +548,7 @@ impl HaltedStateRecord { } let at = self.stack.items.last().map(|i| i.depth).unwrap_or(0); self.stack.prefix.drop_lasts(self.stack.prefix.len() - at); - let result = self.try_stack_content_child(self.stack.items.len() - 1); + let result = self.try_stack_content_child(self.stack.items.len() - 1, false); // restore state self.stack.items = saved_items; self.stack.prefix = saved_prefix; @@ -623,7 +641,7 @@ impl HaltedStateRecord { } let at = self.stack.items.len(); if at > 0 { - self.stack.halt |= self.record_popped_node(at - 1); + self.stack.halt |= self.record_popped_node(at - 1, false); } if let Some(item) = self.stack.items.pop() { let depth = self.stack.items.last().map(|i| i.depth).unwrap_or(0); @@ -690,7 +708,8 @@ impl HaltedStateRecord { (self.stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, )); self.stack.prefix.pop(); - self.finalize(); + self.finalize(true); + self.stack.halt = false; self.from = dest_from; self.currently_query_item = prev_query.map(|q| q.to_owned()); return Ok(true) @@ -743,12 +762,13 @@ pub fn record_query_plan< if lower_bound.1 { bound.pop(); } - from.stack.recorder.start_at = Some(bound.len()); + from.stack.recorder.start_at = Some(bound.len() - 1); from.stack.seek = Some(bound); } else { // statefull case let bound_len = lower_bound.0.len() * nibble_ops::NIBBLE_PER_BYTE - if lower_bound.1 { 2 } else { 1 }; + from.stack.recorder.start_at = Some(bound_len); statefull = Some(bound_len); } } @@ -792,7 +812,7 @@ pub fn record_query_plan< Ordering::Less => break true, Ordering::Greater => if !from.pop() { - from.finalize(); + from.finalize(false); return Ok(()) }, } @@ -874,7 +894,7 @@ pub fn record_query_plan< )); from.stack.prefix.pop(); from.currently_query_item = Some(query.to_owned()); - from.finalize(); + from.finalize(true); return Ok(()) }, } @@ -899,7 +919,7 @@ pub fn record_query_plan< } } */ - from.finalize(); + from.finalize(false); Ok(()) } @@ -916,7 +936,9 @@ impl RecordStack { let prefix = &mut self.prefix; let mut stack_extension = false; let mut from_branch = None; + let mut item_depth = 0; let child_handle = if let Some(item) = self.items.last_mut() { + item_depth = item.depth; //if inline_only && item.accessed_children_node.at(child_index as usize) { debug_assert!(!item.accessed_children_node.at(child_index as usize)); /* if item.accessed_children_node.at(child_index as usize) { @@ -958,6 +980,12 @@ impl RecordStack { NodeHandle::Inline(_) => { // TODO consider not going into inline for all proof but content. // Returning NotStacked here sounds safe, then the is_inline field is not needed. + if self.recorder.record_inline() { + if !self.recorder.check_start_at(item_depth, false) { + // inline in a previous proof + return Ok(TryStackChildResult::NotStackedBranch) + } + } is_inline = true; }, NodeHandle::Hash(_) => { @@ -1098,18 +1126,13 @@ impl RecordStack { let Some(value) = db.expect("non inline").db().get(&hash, self.prefix.as_prefix()) else { return Err(VerifyError::IncompleteProof); }; - if self.recorder.record_value_node(value, self.prefix.len()) { - self.halt = true; - } + self.halt |= self.recorder.record_value_node(value, self.prefix.len()); } else { - if self.recorder.record_skip_value(&mut self.items) { - self.halt = true; - } - }, - Value::Inline(value) => - if self.recorder.record_value_inline(value, self.prefix.len()) { - self.halt = true; + self.halt |= self.recorder.record_skip_value(&mut self.items); }, + Value::Inline(value) => { + self.halt |= self.recorder.record_value_inline(value, self.prefix.len()); + }, } Ok(true) } diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index e168555f..e91f52da 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -43,8 +43,8 @@ fn test_query_plan_compact_internal() { test_layouts!(test_query_plan_content, test_query_plan_content_internal); fn test_query_plan_content_internal() { - test_query_plan_internal::(ProofKind::CompactContent, true); test_query_plan_internal::(ProofKind::CompactContent, false); + test_query_plan_internal::(ProofKind::CompactContent, true); } fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { @@ -99,8 +99,8 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { ]; for (_nb_plan, query_plan) in query_plans.iter().enumerate() { for limit_conf in [ - (0, false), /* TODO implement (1, false), (1, true), (2, false), (2, true), (3, - * true) */ + (1, true), /* TODO uncomment (0, false), (1, false), (1, true), (2, false), (2, + * true), (3, true) */ ] { let limit = limit_conf.0; let limit = (limit != 0).then(|| limit); From b57a3789c0fd7547fb2635966bac55aaaabde6e1 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 5 Jul 2023 17:09:20 +0200 Subject: [PATCH 125/154] cannot find if halted due to inline nodes. -> maybe halted op? --- trie-db/src/query_plan/verify.rs | 14 ++-- trie-db/src/query_plan/verify_content.rs | 98 +++++++++++++++++++----- trie-db/test/src/query_plan.rs | 4 +- 3 files changed, 87 insertions(+), 29 deletions(-) diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index dd7f988b..72051d27 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -127,14 +127,12 @@ where P: Iterator, D: SplitFirst, { - fn halt( - &mut self, - ) -> Option, VerifyError, CError>>> { + fn halt(&mut self) -> VerifyIteratorResult<'a, L, C, D> { if self.is_compact { let r = self.stack.pop_until(None, &self.expected_root, true); if let Err(e) = r { self.state = ReadProofState::Finished; - return Some(Err(e)) + return Err(e) } } self.state = ReadProofState::Finished; @@ -156,13 +154,13 @@ where }, ); stack.start_items = stack.items.len(); - Some(Ok(ReadProofItem::Halted(Box::new(HaltedStateCheck::Node(HaltedStateCheckNode { + Ok(ReadProofItem::Halted(Box::new(HaltedStateCheck::Node(HaltedStateCheckNode { query_plan, current, restore_offset: self.current_offset, stack, state: ReadProofState::Halted, - }))))) + })))) } fn enter_prefix_iter(&mut self, hash_only: bool, key: &[u8]) { @@ -346,7 +344,7 @@ where if let Some(last) = self.stack.items.last_mut() { last.next_descended_child -= 1; } - return self.halt() + return Some(self.halt()) }, } } @@ -447,7 +445,7 @@ where return self.missing_switch_next(as_prefix, to_check.key, false), TryStackChildResult::StackedAfter => return self.missing_switch_next(as_prefix, to_check.key, true), - TryStackChildResult::Halted => return self.halt(), + TryStackChildResult::Halted => return Some(self.halt()), } } diff --git a/trie-db/src/query_plan/verify_content.rs b/trie-db/src/query_plan/verify_content.rs index cf1a4a2c..356e7e52 100644 --- a/trie-db/src/query_plan/verify_content.rs +++ b/trie-db/src/query_plan/verify_content.rs @@ -59,6 +59,7 @@ struct Stack { is_prev_push_key: bool, is_prev_pop_key: bool, first: bool, + halting: bool, expect_inline_child: bool, _ph: PhantomData, } @@ -75,6 +76,7 @@ impl Clone for Stack { is_prev_hash_child: self.is_prev_hash_child, expect_inline_child: self.expect_inline_child, first: self.first, + halting: self.halting, _ph: PhantomData, } } @@ -220,6 +222,9 @@ where return Some(Err(e)) } } + if self.stack.halting { + return Some(self.halt()); + } if let Some(c) = self.current.as_ref() { if c.as_prefix { // end prefix switch to next @@ -239,7 +244,9 @@ where // TODO wrap in an error and put bools in a struct TODO put in its own function match &op { Op::KeyPush(..) => { - if self.stack.is_prev_push_key { + if self.stack.is_prev_push_key || + (self.stack.halting && self.in_prefix_depth.is_none()) + { self.state = ReadProofState::Finished; return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error // TODO return @@ -252,7 +259,9 @@ where self.stack.first = false; }, Op::KeyPop(..) => { - if self.stack.is_prev_pop_key { + if self.stack.is_prev_pop_key || + (self.stack.halting && self.in_prefix_depth.is_none()) + { self.state = ReadProofState::Finished; return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error // return Err(CompactDecoderError::ConsecutivePopKeys. @@ -479,8 +488,11 @@ where Op::EndProof => break, Op::HashChild(hash, child_ix) => { if self.in_prefix_depth.is_some() { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO better error missing query plan proof + // consider it halted. + self.stack.halting = true; + //self.state = ReadProofState::Finished; + //return Some(Err(VerifyError::ExtraneousNode)) // TODO better error + // missing query plan proof } else { // we did pop item before (see op sequence check), so we have // stack prefix matching current plan. TODO debug assert plan starts @@ -530,6 +542,45 @@ where } */ } + + fn halt(&mut self) -> VerifyIteratorResult<'a, L, C> { + // check proof + let r = self.stack.stack_pop(None, &self.expected_root); + if let Err(e) = r { + self.state = ReadProofState::Finished; + return Err(e) + } + self.state = ReadProofState::Finished; + let query_plan = crate::rstd::mem::replace(&mut self.query_plan, None); + let query_plan = query_plan.expect("Init with state"); + let current = crate::rstd::mem::take(&mut self.current); + let mut stack = crate::rstd::mem::replace( + // TODO impl default and use take + &mut self.stack, + Stack { + items: Default::default(), + start_items: 0, + prefix: Default::default(), + first: false, + is_prev_hash_child: None, + expect_inline_child: false, + is_prev_push_key: false, + is_prev_pop_key: false, + expect_value: false, + halting: false, + _ph: PhantomData, + }, + ); + stack.start_items = stack.items.len(); + stack.halting = false; + Ok(ReadProofItem::Halted(Box::new(HaltedStateCheck::Content(HaltedStateCheckContent { + query_plan, + current, + restore_offset: self.current_offset, + stack, + state: ReadProofState::Halted, + })))) + } } /// When process is halted keep execution state @@ -555,6 +606,7 @@ impl<'a, L: TrieLayout, C> From> for HaltedStateCheckContent<'a is_prev_hash_child: None, expect_inline_child: false, first: true, + halting: false, _ph: PhantomData, }, state: ReadProofState::NotStarted, @@ -589,6 +641,7 @@ impl Stack { target_depth: Option, expected_root: &Option>, ) -> Result<(), VerifyError, CError>> { + let mut halting = if self.halting { Some((Vec::new(), self.prefix.clone())) } else { None }; let mut first = true; let mut checked = false; while self @@ -617,20 +670,18 @@ impl Stack { let extension_only = first && matches!(&item.value, &ValueSet::None) && item.children.iter().filter(|child| child.is_some()).count() == 1; - self.items.push(item); // TODO this looks bad (pop then push, branch or leaf function should or should - // not pop instead) - // encode branch + // not pop instead) + // encode branch if expected_root.is_some() { - self.standard_extension(depth, is_root, nkey, extension_only) + self.standard_extension(&item, depth, is_root, nkey, extension_only) } else { ChildReference::Hash(TrieHash::::default()) } } else { - self.items.push(item); // TODO this looks bad (pop then push, branch or leaf function should or should - // not pop instead) - // encode branch + // not pop instead) + // encode branch if expected_root.is_some() { - self.no_extension(depth, is_root, nkey) + self.no_extension(&item, depth, is_root, nkey) } else { ChildReference::Hash(TrieHash::::default()) } @@ -681,10 +732,17 @@ impl Stack { } } first = false; + halting.as_mut().map(|(pop_items, _)| { + pop_items.push(item); + }); // TODO can skip hash checks when above start_items. self.start_items = core::cmp::min(self.start_items, self.items.len()); } debug_assert!(target_depth.is_some() || expected_root.is_none() || checked); + halting.map(|(pop_items, saved_prefix)| { + self.items.extend(pop_items.into_iter().rev()); + self.prefix = saved_prefix; + }); Ok(()) } @@ -702,7 +760,8 @@ impl Stack { // TODO factor with iter_build (reuse cacheaccum here). #[inline(always)] fn standard_extension( - &mut self, + &self, + item: &ItemContentStack, branch_d: usize, is_root: bool, nkey: Option<(usize, usize)>, @@ -712,9 +771,9 @@ impl Stack { let last = self.items.len() - 1; assert_eq!(self.items[last].depth, branch_d); - let ItemContentStack { children, value: v, depth, .. } = self.items.pop().expect("checked"); + let ItemContentStack { children, value: v, depth, .. } = item; - debug_assert!(branch_d == depth); + debug_assert!(&branch_d == depth); let hashed; let value = if let Some(v) = v.as_ref() { @@ -752,15 +811,16 @@ impl Stack { #[inline(always)] fn no_extension( - &mut self, + &self, + item: &ItemContentStack, branch_d: usize, is_root: bool, nkey: Option<(usize, usize)>, ) -> ChildReference> { let key_branch = &self.prefix.inner().as_ref()[..]; - let ItemContentStack { children, value: v, depth, .. } = self.items.pop().expect("checked"); + let ItemContentStack { children, value: v, depth, .. } = item; - debug_assert!(branch_d == depth); + debug_assert!(&branch_d == depth); // encode branch let nkeyix = nkey.unwrap_or((branch_d, 0)); let pr = NibbleSlice::new_offset(&key_branch, nkeyix.0); @@ -792,7 +852,7 @@ impl Stack { } fn flush_value_change<'a>( - &mut self, + &self, from_depth: usize, to_depth: usize, value: &ValueSet, Vec>, diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index e91f52da..c3d6c42e 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -99,8 +99,8 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { ]; for (_nb_plan, query_plan) in query_plans.iter().enumerate() { for limit_conf in [ - (1, true), /* TODO uncomment (0, false), (1, false), (1, true), (2, false), (2, - * true), (3, true) */ + (1, false), /* TODO uncomment (0, false), (1, false), (1, true), (2, false), (2, + * true), (3, true) */ ] { let limit = limit_conf.0; let limit = (limit != 0).then(|| limit); From cf55be917bb9dc697f84f36491cfeda622336ed6 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 5 Jul 2023 17:33:01 +0200 Subject: [PATCH 126/154] suspend proof op --- trie-db/src/content_proof.rs | 12 ++++++++++++ trie-db/src/query_plan/record.rs | 15 +++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/trie-db/src/content_proof.rs b/trie-db/src/content_proof.rs index f7889ece..01ead472 100644 --- a/trie-db/src/content_proof.rs +++ b/trie-db/src/content_proof.rs @@ -45,6 +45,13 @@ pub enum Op { // This is not strictly necessary, only if the proof is not sized, otherwhise if we know // the stream will end it can be skipped. EndProof, + // Indicate the proof is incomplete and next elements from the proof are + // only needed to check the validity. + // This is needed for query plan where the inline content cannot be distinguished + // from sequenced content until seing child hashes. + // If no inline or more sequenced proof this would not be necessary. + // Nevertheless makes things less error prone. + SuspendProof, } // Limiting size to u32 (could also just use a terminal character). @@ -133,6 +140,7 @@ impl, V: AsRef<[u8]>> Op { len += value.as_ref().len(); }, Op::EndProof => (), + Op::SuspendProof => (), } len } @@ -168,6 +176,9 @@ impl, V: AsRef<[u8]>> Op { Op::EndProof => { out.write_bytes(&[5]); }, + Op::SuspendProof => { + out.write_bytes(&[6]); + }, } } } @@ -226,6 +237,7 @@ impl + AsMut<[u8]> + Default> Op> { (Op::Value(value.to_vec()), i + len.0 as usize) }, 5 => (Op::EndProof, 1), + 6 => (Op::SuspendProof, 1), _ => return Err(()), }) } diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index c49f81bc..389eac2e 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -82,6 +82,20 @@ impl Recorder { Self { output, limits, start_at: None, _ph: PhantomData } } + fn record_halt(&mut self) { + let mut written = 0; + match &mut self.output { + RecorderStateInner::Content { output, stacked_pop, .. } => { + let op = Op::, Vec>::SuspendProof; + let init_len = output.buf_len(); + op.encode_into(output); + let written = output.buf_len() - init_len; + }, + _ => return, + } + self.limits.add_node(written, 0, false); + } + #[must_use] fn flush_pop_content(&mut self, items: &Vec) -> bool { match &mut self.output { @@ -432,6 +446,7 @@ impl HaltedStateRecord { if halt { // next is a key push, do the pop let _ = stack.recorder.flush_pop_content(items); + stack.recorder.record_halt(); // let saved_iter_prefix = stack.iter_prefix.clone(); } From 2ca04b8db83eeb3f1eb3f60174dac758a05e19e2 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 7 Jul 2023 16:59:25 +0200 Subject: [PATCH 127/154] store inline children to return. --- trie-db/src/query_plan/verify_content.rs | 287 ++++++++++++++--------- 1 file changed, 182 insertions(+), 105 deletions(-) diff --git a/trie-db/src/query_plan/verify_content.rs b/trie-db/src/query_plan/verify_content.rs index 356e7e52..3794a268 100644 --- a/trie-db/src/query_plan/verify_content.rs +++ b/trie-db/src/query_plan/verify_content.rs @@ -40,11 +40,10 @@ where proof: P, expected_root: Option>, current: Option>, - current_offset: usize, + current_offset: usize, // TODO remove seems unused state: ReadProofState, stack: Stack, buf_op: Option, Vec>>, - in_prefix_depth: Option, send_enter_prefix: Option>, send_exit_prefix: bool, buffed_result: Option>>, @@ -59,8 +58,12 @@ struct Stack { is_prev_push_key: bool, is_prev_pop_key: bool, first: bool, - halting: bool, - expect_inline_child: bool, + in_prefix_depth: Option, + // when exititng keep nibble vec and items to restore and current prefix iter. + halting: Option<(Vec>, NibbleVec, usize, Option)>, + // depth of node containing inline hash + expect_inline_child: Option, + process_inline_children: Option<(NibbleVec, Vec<(u8, usize, TrieHash, usize)>)>, _ph: PhantomData, } @@ -76,7 +79,9 @@ impl Clone for Stack { is_prev_hash_child: self.is_prev_hash_child, expect_inline_child: self.expect_inline_child, first: self.first, - halting: self.halting, + halting: self.halting.clone(), + process_inline_children: self.process_inline_children.clone(), + in_prefix_depth: self.in_prefix_depth.clone(), _ph: PhantomData, } } @@ -110,7 +115,6 @@ where state, stack, buf_op: None, - in_prefix_depth: None, send_enter_prefix: None, send_exit_prefix: false, buffed_result: None, @@ -222,7 +226,7 @@ where return Some(Err(e)) } } - if self.stack.halting { + if self.stack.halting.is_some() { return Some(self.halt()); } if let Some(c) = self.current.as_ref() { @@ -244,15 +248,18 @@ where // TODO wrap in an error and put bools in a struct TODO put in its own function match &op { Op::KeyPush(..) => { - if self.stack.is_prev_push_key || - (self.stack.halting && self.in_prefix_depth.is_none()) - { + if self.stack.is_prev_push_key { self.state = ReadProofState::Finished; return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error // TODO return // Err(CompactDecoderError::ConsecutivePushKeys. // into()) } + if self.stack.halting.is_some() { + if self.stack.expect_inline_child.is_none() { + self.stack.expect_inline_child = Some(self.stack.prefix.len()); + } + } self.stack.is_prev_push_key = true; self.stack.is_prev_pop_key = false; self.stack.is_prev_hash_child = None; @@ -260,7 +267,8 @@ where }, Op::KeyPop(..) => { if self.stack.is_prev_pop_key || - (self.stack.halting && self.in_prefix_depth.is_none()) + (self.stack.halting.is_some() && + self.stack.in_prefix_depth.is_none()) { self.state = ReadProofState::Finished; return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error @@ -299,6 +307,7 @@ where self.stack.is_prev_hash_child = None; self.stack.first = false; }, + Op::SuspendProof => {}, _ => { self.stack.is_prev_push_key = false; self.stack.is_prev_pop_key = false; @@ -334,7 +343,7 @@ where match self.stack.prefix.as_leftnibbleslice().cmp(&left_query_slice) { Ordering::Equal => { if current.as_prefix { - self.in_prefix_depth = Some(query_slice.len()); + self.stack.in_prefix_depth = Some(query_slice.len()); self.send_enter_prefix = Some(current.key.to_vec()); } if !self.stack.items.is_empty() { @@ -343,10 +352,13 @@ where }, Ordering::Less => if !query_slice.starts_with_vec(&self.stack.prefix) { - self.stack.expect_inline_child = true; - // self.state = ReadProofState::Finished; - // return Some(Err(VerifyError::ExtraneousNode)) // TODO error - // backward pushed key + if self.stack.expect_inline_child.is_none() { + self.stack.expect_inline_child = + Some(self.stack.prefix.len()); + } // TODO else debug assert prefix len > expect inline child + // self.state = ReadProofState::Finished; + // return Some(Err(VerifyError::ExtraneousNode)) // TODO error + // backward pushed key }, Ordering::Greater => if current.as_prefix { @@ -356,8 +368,8 @@ where .as_leftnibbleslice() .starts_with(&left_query_slice) { - if self.in_prefix_depth.is_none() { - self.in_prefix_depth = Some(query_slice.len()); + if self.stack.in_prefix_depth.is_none() { + self.stack.in_prefix_depth = Some(query_slice.len()); self.send_enter_prefix = Some(current.key.to_vec()); } at_value = true; @@ -368,12 +380,23 @@ where next_query = true; }, } + if current.hash_only || !at_value || self.stack.halting.is_some() { + if let Op::Value(value) = &op { + if L::MAX_INLINE_VALUE + .map(|max| max as usize <= value.len()) + .unwrap_or(false) + { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousValue(value.clone()))) + } + } + } if next_query { self.buf_op = Some(op); self.stack.is_prev_push_key = true; self.state = ReadProofState::SwitchQueryPlan; if current.as_prefix { - if self.in_prefix_depth.take().is_none() { + if self.stack.in_prefix_depth.take().is_none() { self.send_enter_prefix = Some(current.key.to_vec()); } return Some(Ok(ReadProofItem::EndPrefix)) @@ -389,31 +412,25 @@ where let mut hashed = None; if let Some(current) = self.current.as_ref() { if current.hash_only { - if L::MAX_INLINE_VALUE - .map(|max| max as usize <= value.len()) - .unwrap_or(false) - { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousValue( - value.clone(), - ))) - } else { - let hash = ::hash(value.as_slice()); - hashed = Some(ReadProofItem::Hash( - self.stack.prefix.inner().to_vec().into(), - hash, - )) - } + let hash = ::hash(value.as_slice()); + hashed = Some(ReadProofItem::Hash( + self.stack.prefix.inner().to_vec().into(), + hash, + )) } } if hashed.is_some() { hashed } else { - // TODO could get content from op with no clone. - Some(ReadProofItem::Value( - self.stack.prefix.inner().to_vec().into(), - value.clone(), - )) + if self.stack.halting.is_some() { + None + } else { + // TODO could get content from op with no clone. + Some(ReadProofItem::Value( + self.stack.prefix.inner().to_vec().into(), + value.clone(), + )) + } } }, Op::HashValue(hash) => { @@ -425,11 +442,15 @@ where ))) } } - // TODO could get content from op with no clone. - Some(ReadProofItem::Hash( - self.stack.prefix.inner().to_vec().into(), - hash.clone(), - )) + if self.stack.halting.is_some() { + None + } else { + // TODO could get content from op with no clone. + Some(ReadProofItem::Hash( + self.stack.prefix.inner().to_vec().into(), + hash.clone(), + )) + } }, _ => { self.state = ReadProofState::Finished; @@ -437,19 +458,6 @@ where }, } } else { - match &op { - Op::Value(value) => { - // hash value here not value - if L::MAX_INLINE_VALUE - .map(|max| max as usize <= value.len()) - .unwrap_or(false) - { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousValue(value.clone()))) - } - }, - _ => (), - } None } } else { @@ -472,7 +480,7 @@ where let target_depth = self.stack.prefix.len() - nb_nibble as usize; let r = self.stack.stack_pop(Some(target_depth), &self.expected_root); self.stack.prefix.drop_lasts(nb_nibble.into()); - if let Some(iter_depth) = self.in_prefix_depth.as_ref() { + if let Some(iter_depth) = self.stack.in_prefix_depth.as_ref() { if self .stack .items @@ -480,19 +488,33 @@ where .map(|i| iter_depth > &i.depth) .unwrap_or(true) { - self.in_prefix_depth = None; + self.stack.in_prefix_depth = None; } } r }, + Op::SuspendProof => { + if self.stack.halting.is_some() { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO better error + // duplicate halting + } + self.stack.halting = Some(( + Vec::with_capacity(self.stack.items.len()), + self.stack.prefix.clone(), + self.current_offset, + self.stack.in_prefix_depth.clone(), + )); + continue + }, Op::EndProof => break, Op::HashChild(hash, child_ix) => { - if self.in_prefix_depth.is_some() { - // consider it halted. - self.stack.halting = true; - //self.state = ReadProofState::Finished; - //return Some(Err(VerifyError::ExtraneousNode)) // TODO better error - // missing query plan proof + if self.stack.in_prefix_depth.is_some() { + if self.stack.halting.is_none() { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO better error + // missing query plan proof + } } else { // we did pop item before (see op sequence check), so we have // stack prefix matching current plan. TODO debug assert plan starts @@ -544,12 +566,14 @@ where } fn halt(&mut self) -> VerifyIteratorResult<'a, L, C> { - // check proof + // proof already checked + /* let r = self.stack.stack_pop(None, &self.expected_root); if let Err(e) = r { self.state = ReadProofState::Finished; return Err(e) } + */ self.state = ReadProofState::Finished; let query_plan = crate::rstd::mem::replace(&mut self.query_plan, None); let query_plan = query_plan.expect("Init with state"); @@ -563,20 +587,30 @@ where prefix: Default::default(), first: false, is_prev_hash_child: None, - expect_inline_child: false, + expect_inline_child: None, is_prev_push_key: false, is_prev_pop_key: false, expect_value: false, - halting: false, + halting: None, + process_inline_children: None, + in_prefix_depth: None, _ph: PhantomData, }, ); + let Some((items, prefix, restore_offset, in_prefix_depth)) = stack.halting.take() else { + unreachable!("Halt called without halting state") + }; + stack.items.extend(items.into_iter().rev()); + stack.prefix = prefix; stack.start_items = stack.items.len(); - stack.halting = false; + stack.in_prefix_depth = in_prefix_depth; + // following proof start at key push (we halt on key push), forcing + // precondition check. + stack.is_prev_push_key = false; Ok(ReadProofItem::Halted(Box::new(HaltedStateCheck::Content(HaltedStateCheckContent { query_plan, current, - restore_offset: self.current_offset, + restore_offset, stack, state: ReadProofState::Halted, })))) @@ -604,9 +638,11 @@ impl<'a, L: TrieLayout, C> From> for HaltedStateCheckContent<'a is_prev_push_key: false, is_prev_pop_key: false, is_prev_hash_child: None, - expect_inline_child: false, + expect_inline_child: None, first: true, - halting: false, + halting: None, + process_inline_children: None, + in_prefix_depth: None, _ph: PhantomData, }, state: ReadProofState::NotStarted, @@ -641,9 +677,9 @@ impl Stack { target_depth: Option, expected_root: &Option>, ) -> Result<(), VerifyError, CError>> { - let mut halting = if self.halting { Some((Vec::new(), self.prefix.clone())) } else { None }; let mut first = true; - let mut checked = false; + let mut no_inline = false; + let mut checked = expected_root.is_none(); while self .items .last() @@ -672,7 +708,7 @@ impl Stack { item.children.iter().filter(|child| child.is_some()).count() == 1; // not pop instead) // encode branch - if expected_root.is_some() { + if !checked { self.standard_extension(&item, depth, is_root, nkey, extension_only) } else { ChildReference::Hash(TrieHash::::default()) @@ -680,14 +716,14 @@ impl Stack { } else { // not pop instead) // encode branch - if expected_root.is_some() { + if !checked { self.no_extension(&item, depth, is_root, nkey) } else { ChildReference::Hash(TrieHash::::default()) } } } else { - if expected_root.is_some() { + if !checked { // leaf with value self.flush_value_change(from_depth + inc, item.depth, &item.value, is_root) } else { @@ -699,50 +735,91 @@ impl Stack { self.stack_empty(from_depth); } - if self.expect_inline_child { + if self.expect_inline_child.is_some() { if !matches!(child_reference, ChildReference::Inline(..)) { return Err(VerifyError::ExtraneousNode) } - self.expect_inline_child = false; } let items_len = self.items.len(); - if let Some(item) = self.items.last_mut() { - let child_ix = self.prefix.at(item.depth); - if let Some(hash) = item.children[child_ix as usize].as_ref() { - if items_len == self.start_items + 1 { - if expected_root.is_some() && hash != &child_reference { - return Err(VerifyError::HashMismatch(*child_reference.disp_hash())) + if !checked { + if let Some(item) = self.items.last_mut() { + let child_ix = self.prefix.at(item.depth); + if let Some(hash) = item.children[child_ix as usize].as_ref() { + if items_len == self.start_items { + if hash != &child_reference { + return Err(VerifyError::HashMismatch(*child_reference.disp_hash())) + } + } else { + return Err(VerifyError::ExtraneousHashReference(*hash.disp_hash())) + // return Err(CompactDecoderError::HashChildNotOmitted.into()) } - } else { - return Err(VerifyError::ExtraneousHashReference(*hash.disp_hash())) - // return Err(CompactDecoderError::HashChildNotOmitted.into()) + checked = true; } - checked = true; - } - item.children[child_ix as usize] = Some(child_reference); - } else { - if let Some(root) = expected_root.as_ref() { - if target_depth.is_none() { - if root != child_reference.disp_hash() { - return Err(VerifyError::RootMismatch(*child_reference.disp_hash())) + item.children[child_ix as usize] = Some(child_reference); + } else { + if let Some(root) = expected_root.as_ref() { + if target_depth.is_none() { + if root != child_reference.disp_hash() { + return Err(VerifyError::RootMismatch(*child_reference.disp_hash())) + } + checked = true; } - checked = true; } } } first = false; - halting.as_mut().map(|(pop_items, _)| { - pop_items.push(item); - }); // TODO can skip hash checks when above start_items. - self.start_items = core::cmp::min(self.start_items, self.items.len()); + if self.items.len() <= self.start_items { + self.start_items = core::cmp::min(self.start_items, self.items.len()); + + // if doing iteration, iterate on all next inline content. + if let Some(d) = self.in_prefix_depth.as_ref() { + if let Some(last) = self.items.last_mut() { + if &last.depth >= d && !no_inline { + let child_ix = self.prefix.at(last.depth); + for i in (child_ix + 1)..NIBBLE_LENGTH as u8 { + if let Some(hash) = last.children[i as usize].as_ref() { + match hash { + ChildReference::Inline(hash, len) => { + if self.process_inline_children.is_none() { + self.process_inline_children = + Some((self.prefix.clone(), Vec::new())); + } + if let Some((_, inlines)) = + self.process_inline_children.as_mut() + { + inlines.push((i, *d, hash.clone(), *len)); + } + }, + // will get content for this in next proofs. + ChildReference::Hash(..) => { + no_inline = true; + break + }, + } + } + } + } + } + } + } + + if let Some((pop_items, ..)) = self.halting.as_mut() { + if !self.expect_inline_child.is_some() { + pop_items.push(item) + }; + } + if let Some(inline_depth) = self.expect_inline_child.clone() { + if self.items.last().map(|i| inline_depth >= i.depth).unwrap_or(true) { + self.expect_inline_child = None; + } + } + } + if let Some((_, inlines)) = self.process_inline_children.as_mut() { + inlines.reverse(); } debug_assert!(target_depth.is_some() || expected_root.is_none() || checked); - halting.map(|(pop_items, saved_prefix)| { - self.items.extend(pop_items.into_iter().rev()); - self.prefix = saved_prefix; - }); Ok(()) } From 363cd1b6ecebbdfe55ba70b197652c455629616a Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 7 Jul 2023 17:25:16 +0200 Subject: [PATCH 128/154] plug process inline unimplemented call --- trie-db/src/query_plan/verify_content.rs | 22 ++++++++++++++++++++++ trie-db/test/src/query_plan.rs | 1 + 2 files changed, 23 insertions(+) diff --git a/trie-db/src/query_plan/verify_content.rs b/trie-db/src/query_plan/verify_content.rs index 3794a268..180a0a5f 100644 --- a/trie-db/src/query_plan/verify_content.rs +++ b/trie-db/src/query_plan/verify_content.rs @@ -136,6 +136,13 @@ where return r } let r = self.next_inner(); + if self.stack.process_inline_children.is_some() { + unimplemented!(); + // TODO process all + // TODO reset prefix to last process depth and with actual prefix + // TODO test against send_exit prefix, potentially can send exit in middle, + // so just set to false and test during iteration. + } if let Some(k) = self.send_enter_prefix.take() { self.buffed_result = Some(r); return Some(Ok(ReadProofItem::StartPrefix(k))) @@ -209,6 +216,10 @@ where self.state = ReadProofState::Running; self.current = Some(next); + if self.stack.process_inline_children.is_some() { + // iterate children first. + return None + } } else { self.state = ReadProofState::PlanConsumed; self.current = None; @@ -220,6 +231,11 @@ where let Some(op) = op else { if !self.stack.items.is_empty() { let r = self.stack.stack_pop(None, &self.expected_root); + if self.stack.process_inline_children.is_some() { + // iterate children first. + return None + } + // TODO handle halt!! self.state = ReadProofState::Finished; if let Err(e) = r { @@ -488,6 +504,7 @@ where .map(|i| iter_depth > &i.depth) .unwrap_or(true) { + self.send_exit_prefix = true; self.stack.in_prefix_depth = None; } } @@ -541,6 +558,11 @@ where self.state = ReadProofState::Finished; return Some(Err(e)) } + if self.stack.process_inline_children.is_some() { + debug_assert!(item.is_none()); + // iterate children first. + return None + } if let Some(r) = item { if self.current.as_ref().map(|c| !c.as_prefix).unwrap_or(true) { self.state = ReadProofState::SwitchQueryPlan; // TODO this is same as content NOne? diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index c3d6c42e..6faa4024 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -631,4 +631,5 @@ pub fn check_proofs( if !has_run_full { panic!("did not run full proof") } + assert!(!in_prefix); } From 589157fdb5bc0f774584d1fc5e79c9dca8f6ca38 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 7 Jul 2023 18:11:58 +0200 Subject: [PATCH 129/154] inline iterator test --- trie-db/src/iterator.rs | 16 ++++++-- trie-db/src/query_plan/verify_content.rs | 51 +++++++++++++++++++++--- 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/trie-db/src/iterator.rs b/trie-db/src/iterator.rs index ca382b70..54b34a5b 100644 --- a/trie-db/src/iterator.rs +++ b/trie-db/src/iterator.rs @@ -34,7 +34,7 @@ enum Status { #[cfg_attr(feature = "std", derive(Debug))] #[derive(Eq, PartialEq)] -struct Crumb { +pub(crate) struct Crumb { // TODO rem pub(crate) by having builder over nibllevec hash: Option, node: Arc>, status: Status, @@ -60,8 +60,8 @@ impl Crumb { /// Iterator for going through all nodes in the trie in pre-order traversal order. pub struct TrieDBRawIterator { - trail: Vec>, - key_nibbles: NibbleVec, + pub(crate) trail: Vec>, + pub(crate) key_nibbles: NibbleVec, } impl TrieDBRawIterator { @@ -105,6 +105,16 @@ impl TrieDBRawIterator { Ok(iter) } + pub(crate) fn init_from_inline(&mut self, node: &[u8], db: &TrieDB) { + let node = db.get_raw_or_lookup( + >::default(), + NodeHandle::Inline(node), + EMPTY_PREFIX, + false, + ).expect("inline node is always in db; qed").0; + self.descend(node, None); + } + /// Descend into a payload. fn descend(&mut self, node: OwnedNode, node_hash: Option>) { self.trail diff --git a/trie-db/src/query_plan/verify_content.rs b/trie-db/src/query_plan/verify_content.rs index 180a0a5f..c190857b 100644 --- a/trie-db/src/query_plan/verify_content.rs +++ b/trie-db/src/query_plan/verify_content.rs @@ -130,6 +130,9 @@ where type Item = VerifyIteratorResult<'a, L, C>; fn next(&mut self) -> Option { + if self.stack.process_inline_children.is_some() { + return Some(self.do_process_inline_children()) + } debug_assert!(self.send_enter_prefix.is_none()); debug_assert!(!self.send_exit_prefix); if let Some(r) = self.buffed_result.take() { @@ -137,11 +140,8 @@ where } let r = self.next_inner(); if self.stack.process_inline_children.is_some() { - unimplemented!(); - // TODO process all - // TODO reset prefix to last process depth and with actual prefix - // TODO test against send_exit prefix, potentially can send exit in middle, - // so just set to false and test during iteration. + self.send_exit_prefix = false; + return Some(self.do_process_inline_children()) } if let Some(k) = self.send_enter_prefix.take() { self.buffed_result = Some(r); @@ -157,12 +157,53 @@ where } } +struct DummyDB; + +impl hash_db::HashDBRef for DummyDB { + fn get(&self, _key: &H::Out, _prefix: hash_db::Prefix) -> Option { + None + } + + fn contains(&self, _key: &H::Out, _prefix: hash_db::Prefix) -> bool { + false + } +} + impl<'a, L, C, P> ReadProofContentIterator<'a, L, C, P> where L: TrieLayout, C: Iterator>, P: Iterator, Vec>>>, { + fn do_process_inline_children(&mut self) -> VerifyIteratorResult<'a, L, C> { + let (prefix, inlines) = self.stack.process_inline_children.as_mut().expect("checked call"); + // TODO process all + // TODO reset prefix to last process depth and with actual prefix + // TODO test against send_exit prefix, potentially can send exit in middle, + // so just set to false and test during iteration. + + let (child_ix, depth, inline, inline_len) = inlines.pop().expect("checked"); + let dummy_root = TrieHash::::default(); + let dummy_db = DummyDB; + let dummy_trie_db = crate::TrieDBBuilder::new(&dummy_db, &dummy_root).build(); + let mut raw_iter = crate::iterator::TrieDBRawIterator:: { + trail: Vec::new(), + key_nibbles: prefix.clone(), + }; + + raw_iter.init_from_inline(&inline.as_ref()[..inline_len], &dummy_trie_db); + if let Some(item) = raw_iter.next_item(&dummy_trie_db) { + println!("accessing"); + } + + unimplemented!(); + + /* + if inlines.is_empty() { + self.stack.process_inline_children = None; + } + */ + } // TODO useless next_inner??? fn next_inner(&mut self) -> Option> { if self.state == ReadProofState::Finished { From 361c7427a0963f7c4288c35532c8c8cac1a2e2d5 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 31 Jul 2023 15:33:05 +0200 Subject: [PATCH 130/154] fix a bit --- trie-db/src/iterator.rs | 30 ++++-- trie-db/src/query_plan/record.rs | 34 +++--- trie-db/src/query_plan/verify.rs | 10 +- trie-db/src/query_plan/verify_content.rs | 130 ++++++++++++++++------- trie-db/test/src/query_plan.rs | 3 +- 5 files changed, 133 insertions(+), 74 deletions(-) diff --git a/trie-db/src/iterator.rs b/trie-db/src/iterator.rs index 54b34a5b..eee7d24c 100644 --- a/trie-db/src/iterator.rs +++ b/trie-db/src/iterator.rs @@ -34,12 +34,19 @@ enum Status { #[cfg_attr(feature = "std", derive(Debug))] #[derive(Eq, PartialEq)] -pub(crate) struct Crumb { // TODO rem pub(crate) by having builder over nibllevec +pub(crate) struct Crumb { + // TODO rem pub(crate) by having builder over nibllevec hash: Option, node: Arc>, status: Status, } +impl Clone for Crumb { + fn clone(&self) -> Self { + Self { hash: self.hash.clone(), node: self.node.clone(), status: self.status.clone() } + } +} + impl Crumb { /// Move on to next status in the node's sequence. fn increment(&mut self) { @@ -64,6 +71,12 @@ pub struct TrieDBRawIterator { pub(crate) key_nibbles: NibbleVec, } +impl Clone for TrieDBRawIterator { + fn clone(&self) -> Self { + Self { trail: self.trail.clone(), key_nibbles: self.key_nibbles.clone() } + } +} + impl TrieDBRawIterator { /// Create a new empty iterator. pub fn empty() -> Self { @@ -106,12 +119,15 @@ impl TrieDBRawIterator { } pub(crate) fn init_from_inline(&mut self, node: &[u8], db: &TrieDB) { - let node = db.get_raw_or_lookup( - >::default(), - NodeHandle::Inline(node), - EMPTY_PREFIX, - false, - ).expect("inline node is always in db; qed").0; + let node = db + .get_raw_or_lookup( + >::default(), + NodeHandle::Inline(node), + EMPTY_PREFIX, + false, + ) + .expect("inline node is always in db; qed") + .0; self.descend(node, None); } diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 389eac2e..8da425a1 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -575,9 +575,7 @@ impl HaltedStateRecord { let mut res = false; self.try_stack_content_inline_children(NIBBLE_LENGTH as u8)?; - let Some(item) = self.stack.items.last() else { - return Ok(res); - }; + let Some(item) = self.stack.items.last() else { return Ok(res) }; for i in 0..NIBBLE_LENGTH as u8 { // TODO avoid the two consecutive iter on all children if !item.accessed_children_node.at(i as usize) { @@ -613,22 +611,16 @@ impl HaltedStateRecord { &mut self, child_ix_bound: u8, ) -> Result<(), VerifyError, CError>> { - let Some(item) = self.stack.items.last_mut() else { - return Ok(()) - }; + let Some(item) = self.stack.items.last_mut() else { return Ok(()) }; let dummy_parent_hash = TrieHash::::default(); for i in item.next_descended_child..child_ix_bound { - let Some(item) = self.stack.items.last_mut() else { - return Ok(()) - }; + let Some(item) = self.stack.items.last_mut() else { return Ok(()) }; if !item.accessed_children_node.at(i as usize) { match self.stack.try_stack_child(i, None, dummy_parent_hash, None)? { // only expect a stacked full prefix or not stacked here TryStackChildResult::StackedFull => { let halt = self.iter_prefix(None, None, false, true)?; - let Some(item) = self.stack.items.last_mut() else { - return Ok(()) - }; + let Some(item) = self.stack.items.last_mut() else { return Ok(()) }; item.accessed_children_node.set(i as usize, true); // no halt on inline. debug_assert!(!halt); @@ -1104,7 +1096,9 @@ impl RecordStack { if stack_extension { let sbranch = self.try_stack_child(0, db, parent_hash, slice_query)?; let TryStackChildResult::StackedFull = sbranch else { - return Err(VerifyError::InvalidChildReference(b"branch in db should follow extension".to_vec())); + return Err(VerifyError::InvalidChildReference( + b"branch in db should follow extension".to_vec(), + )) }; } @@ -1116,9 +1110,7 @@ impl RecordStack { db: Option<&TrieDB>, hash_only: bool, ) -> Result, CError>> { - let Some(item) = self.items.last_mut() else { - return Ok(false) - }; + let Some(item) = self.items.last_mut() else { return Ok(false) }; let node_data = item.node.data(); let value = match item.node.node_plan() { @@ -1138,8 +1130,10 @@ impl RecordStack { item.accessed_value_node = true; let mut hash = TrieHash::::default(); hash.as_mut().copy_from_slice(hash_slice); - let Some(value) = db.expect("non inline").db().get(&hash, self.prefix.as_prefix()) else { - return Err(VerifyError::IncompleteProof); + let Some(value) = + db.expect("non inline").db().get(&hash, self.prefix.as_prefix()) + else { + return Err(VerifyError::IncompleteProof) }; self.halt |= self.recorder.record_value_node(value, self.prefix.len()); } else { @@ -1169,9 +1163,7 @@ fn flush_compact_content_pop( add_depth: Option, limits: &mut Limits, ) -> bool { - let Some(from) = stacked_from.take() else { - return false - }; + let Some(from) = stacked_from.take() else { return false }; let pop_to = add_depth.unwrap_or_else(|| items.last().map(|i| i.depth).unwrap_or(0)); if from == pop_to { return false diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 72051d27..3c56f573 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -567,7 +567,7 @@ impl Stack { // ommitted hash let Some(mut encoded_node) = proof.next() else { // halt happens with a hash, this is not. - return Err(VerifyError::IncompleteProof); + return Err(VerifyError::IncompleteProof) }; if self.is_compact && encoded_node.borrow().len() > 0 && @@ -597,7 +597,7 @@ impl Stack { }, NodeHandle::Hash(hash) => { let Some(mut encoded_node) = proof.next() else { - return Ok(TryStackChildResult::Halted); + return Ok(TryStackChildResult::Halted) }; if self.is_compact && items_len > self.start_items { let mut error_hash = TrieHash::::default(); @@ -665,7 +665,7 @@ impl Stack { NodeHandle::Hash(hash) => { let Some(encoded_branch) = proof.next() else { // No halt on extension node (restart over a child index). - return Err(VerifyError::IncompleteProof); + return Err(VerifyError::IncompleteProof) }; if self.is_compact { let mut error_hash = TrieHash::::default(); @@ -741,7 +741,7 @@ impl Stack { } let Some(value) = proof.next() else { - return Err(VerifyError::IncompleteProof); + return Err(VerifyError::IncompleteProof) }; if check_hash { let hash = L::Hash::hash(value.borrow()); @@ -771,7 +771,7 @@ impl Stack { return Ok((None, Some(result_hash))) } let Some(value) = proof.next() else { - return Err(VerifyError::IncompleteProof); + return Err(VerifyError::IncompleteProof) }; if check_hash { verify_hash::(value.borrow(), hash)?; diff --git a/trie-db/src/query_plan/verify_content.rs b/trie-db/src/query_plan/verify_content.rs index c190857b..eae495b7 100644 --- a/trie-db/src/query_plan/verify_content.rs +++ b/trie-db/src/query_plan/verify_content.rs @@ -63,7 +63,12 @@ struct Stack { halting: Option<(Vec>, NibbleVec, usize, Option)>, // depth of node containing inline hash expect_inline_child: Option, - process_inline_children: Option<(NibbleVec, Vec<(u8, usize, TrieHash, usize)>)>, + process_inline_children: Option<( + NibbleVec, + Vec<(u8, usize, TrieHash, usize)>, + Option>, + bool, + )>, _ph: PhantomData, } @@ -131,7 +136,9 @@ where fn next(&mut self) -> Option { if self.stack.process_inline_children.is_some() { - return Some(self.do_process_inline_children()) + if let Some(r) = self.do_process_inline_children() { + return Some(r) + } } debug_assert!(self.send_enter_prefix.is_none()); debug_assert!(!self.send_exit_prefix); @@ -141,7 +148,9 @@ where let r = self.next_inner(); if self.stack.process_inline_children.is_some() { self.send_exit_prefix = false; - return Some(self.do_process_inline_children()) + if let Some(r) = self.do_process_inline_children() { + return Some(r) + } } if let Some(k) = self.send_enter_prefix.take() { self.buffed_result = Some(r); @@ -175,35 +184,56 @@ where C: Iterator>, P: Iterator, Vec>>>, { - fn do_process_inline_children(&mut self) -> VerifyIteratorResult<'a, L, C> { - let (prefix, inlines) = self.stack.process_inline_children.as_mut().expect("checked call"); + // this is strictly prefix iteration processing + fn do_process_inline_children(&mut self) -> Option> { + let (prefix, inlines, current, hash_only) = + self.stack.process_inline_children.as_mut().expect("checked call"); + debug_assert!(current.is_none()); // TODO process all // TODO reset prefix to last process depth and with actual prefix // TODO test against send_exit prefix, potentially can send exit in middle, // so just set to false and test during iteration. - let (child_ix, depth, inline, inline_len) = inlines.pop().expect("checked"); - let dummy_root = TrieHash::::default(); - let dummy_db = DummyDB; - let dummy_trie_db = crate::TrieDBBuilder::new(&dummy_db, &dummy_root).build(); - let mut raw_iter = crate::iterator::TrieDBRawIterator:: { - trail: Vec::new(), - key_nibbles: prefix.clone(), - }; - - raw_iter.init_from_inline(&inline.as_ref()[..inline_len], &dummy_trie_db); - if let Some(item) = raw_iter.next_item(&dummy_trie_db) { - println!("accessing"); - } + loop { + let dummy_root = TrieHash::::default(); + let dummy_db = DummyDB; + let dummy_trie_db = crate::TrieDBBuilder::new(&dummy_db, &dummy_root).build(); + if current.is_none() { + if inlines.is_empty() { + self.stack.process_inline_children = None; + return None + } + let (child_ix, depth, inline, inline_len) = inlines.pop().expect("checked"); + let mut raw_iter = crate::iterator::TrieDBRawIterator:: { + trail: Vec::new(), + key_nibbles: prefix.clone(), + }; - unimplemented!(); + raw_iter.init_from_inline(&inline.as_ref()[..inline_len], &dummy_trie_db); + *current = Some(raw_iter); + } - /* - if inlines.is_empty() { - self.stack.process_inline_children = None; + let iter = current.as_mut().expect("init above"); + if let Some(r) = iter.next_item(&dummy_trie_db) { + match r { + Ok((key, value)) => + if *hash_only { + let hash = ::hash(value.as_slice()); + return Some(Ok(ReadProofItem::Hash(key.to_vec().into(), hash))) + } else { + return Some(Ok(ReadProofItem::Value( + key.to_vec().into(), + value.clone(), + ))) + }, + // TODO unreachable for inline? + Err(e) => return Some(Err(VerifyError::IncompleteProof)), /* TODO right error + * return */ + } + } } - */ } + // TODO useless next_inner??? fn next_inner(&mut self) -> Option> { if self.state == ReadProofState::Finished { @@ -226,11 +256,12 @@ where { let query_plan = self.query_plan.as_mut().expect("Removed with state"); if let Some(next) = query_plan.items.next() { - let (ordered, common_nibbles) = if let Some(old) = self.current.as_ref() { - old.before(&next) - } else { - (true, 0) - }; + let ((ordered, common_nibbles), hash_only) = + if let Some(old) = self.current.as_ref() { + (old.before(&next), old.hash_only) + } else { + ((true, 0), false) + }; if !ordered { if query_plan.ignore_unordered { continue @@ -239,7 +270,8 @@ where return Some(Err(VerifyError::UnorderedKey(next.key.to_vec()))) } } - match self.stack.stack_pop(Some(common_nibbles), &self.expected_root) { + match self.stack.stack_pop(Some(common_nibbles), &self.expected_root, hash_only) + { // match self.stack.pop_until(Some(common_nibbles), &self.expected_root, // false) { /* Ok(true) => { @@ -271,7 +303,12 @@ where println!("read: {:?}", op); let Some(op) = op else { if !self.stack.items.is_empty() { - let r = self.stack.stack_pop(None, &self.expected_root); + let hash_only = if let Some(old) = self.current.as_ref() { + old.hash_only + } else { + false + }; + let r = self.stack.stack_pop(None, &self.expected_root, hash_only); if self.stack.process_inline_children.is_some() { // iterate children first. return None @@ -284,7 +321,7 @@ where } } if self.stack.halting.is_some() { - return Some(self.halt()); + return Some(self.halt()) } if let Some(c) = self.current.as_ref() { if c.as_prefix { @@ -294,10 +331,10 @@ where } else { // missing value self.state = ReadProofState::SwitchQueryPlan; - return Some(Ok(ReadProofItem::NoValue(&c.key))); + return Some(Ok(ReadProofItem::NoValue(&c.key))) } } else { - return None; // finished + return None // finished } }; @@ -535,7 +572,16 @@ where return Some(Err(VerifyError::ExtraneousNode)) // TODO better error } let target_depth = self.stack.prefix.len() - nb_nibble as usize; - let r = self.stack.stack_pop(Some(target_depth), &self.expected_root); + let hash_only = if let Some(old) = self.current.as_ref() { + old.hash_only + } else { + false + }; + let r = self.stack.stack_pop( + Some(target_depth), + &self.expected_root, + hash_only, + ); self.stack.prefix.drop_lasts(nb_nibble.into()); if let Some(iter_depth) = self.stack.in_prefix_depth.as_ref() { if self @@ -739,6 +785,7 @@ impl Stack { &mut self, target_depth: Option, expected_root: &Option>, + hash_only: bool, ) -> Result<(), VerifyError, CError>> { let mut first = true; let mut no_inline = false; @@ -832,8 +879,7 @@ impl Stack { } } first = false; - // TODO can skip hash checks when above start_items. - if self.items.len() <= self.start_items { + if !checked && self.items.len() <= self.start_items { self.start_items = core::cmp::min(self.start_items, self.items.len()); // if doing iteration, iterate on all next inline content. @@ -846,10 +892,14 @@ impl Stack { match hash { ChildReference::Inline(hash, len) => { if self.process_inline_children.is_none() { - self.process_inline_children = - Some((self.prefix.clone(), Vec::new())); + self.process_inline_children = Some(( + self.prefix.clone(), + Vec::new(), + None, + hash_only, + )); } - if let Some((_, inlines)) = + if let Some((_, inlines, _, _)) = self.process_inline_children.as_mut() { inlines.push((i, *d, hash.clone(), *len)); @@ -879,7 +929,7 @@ impl Stack { } } } - if let Some((_, inlines)) = self.process_inline_children.as_mut() { + if let Some((_, inlines, _, _)) = self.process_inline_children.as_mut() { inlines.reverse(); } debug_assert!(target_depth.is_some() || expected_root.is_none() || checked); diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index 6faa4024..316e2be1 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -79,6 +79,7 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { ignore_unordered: false, kind, }, + /* InMemQueryPlan { items: vec![ InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), @@ -95,7 +96,7 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { ], ignore_unordered: false, kind, - }, + },*/ ]; for (_nb_plan, query_plan) in query_plans.iter().enumerate() { for limit_conf in [ From debdb305dc53fa2a46219c11dceba2dc3e40a9fd Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 31 Jul 2023 18:06:29 +0200 Subject: [PATCH 131/154] verify content need rewrite: way too many state variable, also: - inline node during iteration needing specific suspend feels rather bad and case hard to catch (hash before and inline node but hash are only emmited after). -> generally probably better to switch format to always respect child index order. --- trie-db/src/lookup.rs | 3 - trie-db/src/query_plan/record.rs | 12 +- trie-db/src/query_plan/verify_content.rs | 146 ++++++++++++----------- trie-db/src/triedbmut.rs | 1 - trie-db/test/src/query_plan.rs | 8 +- 5 files changed, 87 insertions(+), 83 deletions(-) diff --git a/trie-db/src/lookup.rs b/trie-db/src/lookup.rs index 5e34055f..90bdcb5e 100644 --- a/trie-db/src/lookup.rs +++ b/trie-db/src/lookup.rs @@ -367,7 +367,6 @@ where NodeOwned::Leaf(slice, value) => return if partial == *slice { let value = (*value).clone(); - drop(node); load_value_owned( value, nibble_key.original_data_as_prefix(), @@ -395,7 +394,6 @@ where NodeOwned::Branch(children, value) => if partial.is_empty() { return if let Some(value) = value.clone() { - drop(node); load_value_owned( value, nibble_key.original_data_as_prefix(), @@ -433,7 +431,6 @@ where if partial.len() == slice.len() { return if let Some(value) = value.clone() { - drop(node); load_value_owned( value, nibble_key.original_data_as_prefix(), diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 8da425a1..d62649da 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -83,17 +83,17 @@ impl Recorder { } fn record_halt(&mut self) { - let mut written = 0; + let written; match &mut self.output { - RecorderStateInner::Content { output, stacked_pop, .. } => { + RecorderStateInner::Content { output, .. } => { let op = Op::, Vec>::SuspendProof; let init_len = output.buf_len(); op.encode_into(output); - let written = output.buf_len() - init_len; + written = output.buf_len() - init_len; }, _ => return, } - self.limits.add_node(written, 0, false); + let _ = self.limits.add_node(written, 0, false); } #[must_use] @@ -686,7 +686,7 @@ impl HaltedStateRecord { stacked = false; } - let child_index = if let Some(mut item) = self.stack.items.last_mut() { + let child_index = if let Some(item) = self.stack.items.last_mut() { if item.next_descended_child as usize >= NIBBLE_LENGTH { break } @@ -705,7 +705,7 @@ impl HaltedStateRecord { TryStackChildResult::NotStackedBranch => (), TryStackChildResult::NotStacked => break, TryStackChildResult::Halted => { - if let Some(mut item) = self.stack.items.last_mut() { + if let Some(item) = self.stack.items.last_mut() { item.next_descended_child -= 1; } self.stack.halt = false; diff --git a/trie-db/src/query_plan/verify_content.rs b/trie-db/src/query_plan/verify_content.rs index eae495b7..cdf0ff31 100644 --- a/trie-db/src/query_plan/verify_content.rs +++ b/trie-db/src/query_plan/verify_content.rs @@ -360,10 +360,7 @@ where self.stack.first = false; }, Op::KeyPop(..) => { - if self.stack.is_prev_pop_key || - (self.stack.halting.is_some() && - self.stack.in_prefix_depth.is_none()) - { + if self.stack.is_prev_pop_key { self.state = ReadProofState::Finished; return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error // return Err(CompactDecoderError::ConsecutivePopKeys. @@ -434,45 +431,53 @@ where if let Some(current) = self.current.as_ref() { let left_query_slice = LeftNibbleSlice::new(¤t.key); let query_slice = NibbleSlice::new(¤t.key); - match self.stack.prefix.as_leftnibbleslice().cmp(&left_query_slice) { - Ordering::Equal => { - if current.as_prefix { - self.stack.in_prefix_depth = Some(query_slice.len()); - self.send_enter_prefix = Some(current.key.to_vec()); - } - if !self.stack.items.is_empty() { - at_value = true; - } - }, - Ordering::Less => - if !query_slice.starts_with_vec(&self.stack.prefix) { - if self.stack.expect_inline_child.is_none() { + if self.stack.expect_inline_child.is_some() { + // TODO could check ordering of key on push, but we do not have current + // generally there is way to create proof with lot of inline content + // child so need to count current inline content size (or at least an + // approximate). + at_value = true; + } else { + match self.stack.prefix.as_leftnibbleslice().cmp(&left_query_slice) { + Ordering::Equal => { + if current.as_prefix { + self.stack.in_prefix_depth = Some(query_slice.len()); + self.send_enter_prefix = Some(current.key.to_vec()); + } + if !self.stack.items.is_empty() { + at_value = true; + } + }, + Ordering::Less => + if !query_slice.starts_with_vec(&self.stack.prefix) { self.stack.expect_inline_child = Some(self.stack.prefix.len()); - } // TODO else debug assert prefix len > expect inline child - // self.state = ReadProofState::Finished; - // return Some(Err(VerifyError::ExtraneousNode)) // TODO error - // backward pushed key - }, - Ordering::Greater => - if current.as_prefix { - if self - .stack - .prefix - .as_leftnibbleslice() - .starts_with(&left_query_slice) - { - if self.stack.in_prefix_depth.is_none() { - self.stack.in_prefix_depth = Some(query_slice.len()); - self.send_enter_prefix = Some(current.key.to_vec()); + // TODO else debug assert prefix len > expect inline child + // self.state = ReadProofState::Finished; + // return Some(Err(VerifyError::ExtraneousNode)) // TODO + // error backward pushed key + }, + Ordering::Greater => + if current.as_prefix { + if self + .stack + .prefix + .as_leftnibbleslice() + .starts_with(&left_query_slice) + { + if self.stack.in_prefix_depth.is_none() { + self.stack.in_prefix_depth = + Some(query_slice.len()); + self.send_enter_prefix = Some(current.key.to_vec()); + } + at_value = true; + } else { + next_query = true; } - at_value = true; } else { next_query = true; - } - } else { - next_query = true; - }, + }, + } } if current.hash_only || !at_value || self.stack.halting.is_some() { if let Op::Value(value) = &op { @@ -490,9 +495,11 @@ where self.stack.is_prev_push_key = true; self.state = ReadProofState::SwitchQueryPlan; if current.as_prefix { - if self.stack.in_prefix_depth.take().is_none() { + // empty prefix carse + if self.stack.in_prefix_depth.take().is_none() && !self.send_exit_prefix { self.send_enter_prefix = Some(current.key.to_vec()); } + self.send_exit_prefix = false; return Some(Ok(ReadProofItem::EndPrefix)) } else { return Some(Ok(ReadProofItem::NoValue(¤t.key))) @@ -503,21 +510,21 @@ where if at_value { match &op { Op::Value(value) => { - let mut hashed = None; - if let Some(current) = self.current.as_ref() { - if current.hash_only { - let hash = ::hash(value.as_slice()); - hashed = Some(ReadProofItem::Hash( - self.stack.prefix.inner().to_vec().into(), - hash, - )) - } - } - if hashed.is_some() { - hashed + if self.stack.halting.is_some() { + None } else { - if self.stack.halting.is_some() { - None + let mut hashed = None; + if let Some(current) = self.current.as_ref() { + if current.hash_only { + let hash = ::hash(value.as_slice()); + hashed = Some(ReadProofItem::Hash( + self.stack.prefix.inner().to_vec().into(), + hash, + )) + } + } + if hashed.is_some() { + hashed } else { // TODO could get content from op with no clone. Some(ReadProofItem::Value( @@ -582,7 +589,7 @@ where &self.expected_root, hash_only, ); - self.stack.prefix.drop_lasts(nb_nibble.into()); + self.stack.prefix.drop_lasts(nb_nibble.into()); // TODO should drop only until exit prefix... if let Some(iter_depth) = self.stack.in_prefix_depth.as_ref() { if self .stack @@ -613,24 +620,24 @@ where }, Op::EndProof => break, Op::HashChild(hash, child_ix) => { - if self.stack.in_prefix_depth.is_some() { - if self.stack.halting.is_none() { + if self.stack.halting.is_none() { + if self.stack.in_prefix_depth.is_some() { self.state = ReadProofState::Finished; return Some(Err(VerifyError::ExtraneousNode)) // TODO better error // missing query plan proof - } - } else { - // we did pop item before (see op sequence check), so we have - // stack prefix matching current plan. TODO debug assert plan starts - // with prefix TODO we could drop this check as we won t have the - // expected no value item in this case, but looks better to error here. - // TODO check, same for other proof: do a test. - if let Some(current) = self.current.as_ref() { - let query_slice = LeftNibbleSlice::new(¤t.key); - let at = self.stack.prefix.len(); - if query_slice.at(at) == Some(child_ix) { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO better error missing query plan proof + } else { + // we did pop item before (see op sequence check), so we have + // stack prefix matching current plan. TODO debug assert plan starts + // with prefix TODO we could drop this check as we won t have the + // expected no value item in this case, but looks better to error + // here. TODO check, same for other proof: do a test. + if let Some(current) = self.current.as_ref() { + let query_slice = LeftNibbleSlice::new(¤t.key); + let at = self.stack.prefix.len(); + if query_slice.at(at) == Some(child_ix) { + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::ExtraneousNode)) // TODO better error missing query plan proof + } } } } @@ -879,6 +886,7 @@ impl Stack { } } first = false; + if !checked && self.items.len() <= self.start_items { self.start_items = core::cmp::min(self.start_items, self.items.len()); diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 43559677..96da9d3c 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1921,7 +1921,6 @@ where cache_child_values::(&node, &mut values_to_cache, full_key.clone()); } - drop(node); values_to_cache.into_iter().for_each(|(k, v)| cache.cache_value_for_key(&k, v)); } } diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index 316e2be1..688ca5c5 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -79,7 +79,6 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { ignore_unordered: false, kind, }, - /* InMemQueryPlan { items: vec![ InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), @@ -96,12 +95,12 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { ], ignore_unordered: false, kind, - },*/ + }, ]; for (_nb_plan, query_plan) in query_plans.iter().enumerate() { for limit_conf in [ - (1, false), /* TODO uncomment (0, false), (1, false), (1, true), (2, false), (2, - * true), (3, true) */ + (0, false), /* TODO uncomment (0, false), (1, false), (1, true), (2, false), (2, + * true), (3, true) */ ] { let limit = limit_conf.0; let limit = (limit != 0).then(|| limit); @@ -580,6 +579,7 @@ pub fn check_proofs( current_plan = query_plan_iter.items.next(); }, ReadProofItem::StartPrefix(prefix) => { + assert!(!in_prefix); in_prefix = true; if hash_only { assert_eq!( From fff5dde123f6fd96aed2db2ce4657a19c9518e73 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 10:16:42 +0200 Subject: [PATCH 132/154] Removing content proof code. --- trie-db/fuzz/Cargo.toml | 8 - trie-db/fuzz/fuzz_targets/query_plan_5.rs | 15 - trie-db/fuzz/fuzz_targets/query_plan_6.rs | 36 - trie-db/src/content_proof.rs | 271 ----- trie-db/src/iterator.rs | 13 - trie-db/src/lib.rs | 1 - trie-db/src/query_plan/mod.rs | 118 +-- trie-db/src/query_plan/record.rs | 457 +-------- trie-db/src/query_plan/verify.rs | 26 +- trie-db/src/query_plan/verify_content.rs | 1112 --------------------- trie-db/test/src/fuzz.rs | 34 +- trie-db/test/src/query_plan.rs | 94 +- 12 files changed, 45 insertions(+), 2140 deletions(-) delete mode 100644 trie-db/fuzz/fuzz_targets/query_plan_5.rs delete mode 100644 trie-db/fuzz/fuzz_targets/query_plan_6.rs delete mode 100644 trie-db/src/content_proof.rs delete mode 100644 trie-db/src/query_plan/verify_content.rs diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml index bcd9761b..3b4c9990 100644 --- a/trie-db/fuzz/Cargo.toml +++ b/trie-db/fuzz/Cargo.toml @@ -87,11 +87,3 @@ path = "fuzz_targets/query_plan_3.rs" [[bin]] name = "query_plan_4" path = "fuzz_targets/query_plan_4.rs" - -[[bin]] -name = "query_plan_5" -path = "fuzz_targets/query_plan_5.rs" - -[[bin]] -name = "query_plan_6" -path = "fuzz_targets/query_plan_6.rs" diff --git a/trie-db/fuzz/fuzz_targets/query_plan_5.rs b/trie-db/fuzz/fuzz_targets/query_plan_5.rs deleted file mode 100644 index 8f40b560..00000000 --- a/trie-db/fuzz/fuzz_targets/query_plan_5.rs +++ /dev/null @@ -1,15 +0,0 @@ -#![no_main] -use lazy_static::lazy_static; -use libfuzzer_sys::fuzz_target; -use reference_trie::{RefHasher, SubstrateV1}; -use trie_db_test::fuzz::query_plan::{ - build_state, fuzz_query_plan, ArbitraryQueryPlan, FuzzContext, CONF3, -}; - -lazy_static! { - static ref CONTEXT: FuzzContext> = build_state(CONF3); -} - -fuzz_target!(|plan: ArbitraryQueryPlan| { - fuzz_query_plan::>(&CONTEXT, plan); -}); diff --git a/trie-db/fuzz/fuzz_targets/query_plan_6.rs b/trie-db/fuzz/fuzz_targets/query_plan_6.rs deleted file mode 100644 index 8d751c0c..00000000 --- a/trie-db/fuzz/fuzz_targets/query_plan_6.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![no_main] -use lazy_static::lazy_static; -use libfuzzer_sys::fuzz_target; -use reference_trie::{RefHasher, SubstrateV1}; -use trie_db_test::fuzz::query_plan::{ - build_state, fuzz_query_plan_conf, ArbitraryQueryPlan, FuzzContext, CONF3, -}; -use arbitrary::Arbitrary; - -lazy_static! { - static ref CONTEXT: FuzzContext> = build_state(CONF3); -} - -#[derive(Debug, Clone, Copy, Arbitrary)] -#[repr(usize)] -enum SplitSize { - One = 1, - Two = 2, - Three = 3, - More = 10, - MoreMore = 50, -} - -#[derive(Debug, Clone, Copy, Eq, PartialEq, Arbitrary)] -enum SplitKind { - Stateless, - Stateful, -} - -fuzz_target!(|input: (ArbitraryQueryPlan, SplitSize, SplitKind)| { - let (plan, split_size, split_kind) = input; - let mut conf = CONTEXT.conf.clone(); - conf.limit = split_size as usize; - conf.proof_spawn_with_persistence = split_kind == SplitKind::Stateful; - fuzz_query_plan_conf::>(&CONTEXT, conf, plan); -}); diff --git a/trie-db/src/content_proof.rs b/trie-db/src/content_proof.rs deleted file mode 100644 index 01ead472..00000000 --- a/trie-db/src/content_proof.rs +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright 2023, 2023 Parity Technologies -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Compact content proof, a sequence of content as met in the trie. -//! Exception for hashes that are only encoded when node is popped, allowing to merge a few more key -//! manipulation op. Values are return as seen to avoid the need to keep them all (warning a return -//! value may be from an invalid proof and action on those value usually need to be revertible). -//! Proof validity as for compact proof is only possible to check at the end of the proof reading. - -use crate::query_plan::RecorderOutput; -/// Representation of each encoded action -/// for building the proof. -/// TODO ref variant for encoding ?? or key using V and use Op<&H, &[u8]>. -use core::marker::PhantomData; - -#[derive(Debug)] -pub enum Op { - // key content followed by a mask for last byte. - // If mask erase some content the content need to - // be set at 0 (or error). - // Two consecutive `KeyPush` are invalid. - KeyPush(Vec, u8), /* TODO could use BackingByteVec (but Vec for new as it scale - * encode) */ - // Last call to pop is implicit (up to root), defining - // one will result in an error. - // Two consecutive `KeyPop` are invalid. - // TODO should be compact encoding of number. - KeyPop(u16), - // u8 is child index, shorthand for key push one nibble followed by key pop. - HashChild(H, u8), - // All value variant are only after a `KeyPush` or at first position. - HashValue(H), - Value(V), - // This is not strictly necessary, only if the proof is not sized, otherwhise if we know - // the stream will end it can be skipped. - EndProof, - // Indicate the proof is incomplete and next elements from the proof are - // only needed to check the validity. - // This is needed for query plan where the inline content cannot be distinguished - // from sequenced content until seing child hashes. - // If no inline or more sequenced proof this would not be necessary. - // Nevertheless makes things less error prone. - SuspendProof, -} - -// Limiting size to u32 (could also just use a terminal character). -#[derive(Debug, PartialEq, Eq)] -#[repr(transparent)] -struct VarInt(u32); - -impl VarInt { - fn encoded_len(&self) -> usize { - if self.0 == 0 { - return 1 - } - let len = 32 - self.0.leading_zeros() as usize; - if len % 7 == 0 { - len / 7 - } else { - len / 7 + 1 - } - /* - match self.0 { - l if l < 2 ^ 7 => 1, // leading 0: 25 - l if l < 2 ^ 14 => 2, // leading 0: 18 - - l if l < 2 ^ 21 => 3, // 11 - l if l < 2 ^ 28 => 4, // 4 - _ => 5, - } - */ - } - - fn encode_into(&self, out: &mut impl RecorderOutput) { - let mut to_encode = self.0; - for _ in 0..self.encoded_len() - 1 { - out.write_bytes(&[0b1000_0000 | to_encode as u8]); - to_encode >>= 7; - } - out.write_bytes(&[to_encode as u8]); - } - - fn decode(encoded: &[u8]) -> Result<(Self, usize), ()> { - let mut value = 0u32; - for (i, byte) in encoded.iter().enumerate() { - let last = byte & 0b1000_0000 == 0; - value |= ((byte & 0b0111_1111) as u32) << (i * 7); - if last { - return Ok((VarInt(value), i + 1)) - } - } - Err(()) - } -} - -#[test] -fn varint_encode_decode() { - let mut buf = crate::query_plan::InMemoryRecorder::default(); - for i in 0..u16::MAX as u32 + 1 { - VarInt(i).encode_into(&mut buf); - assert_eq!(buf.buffer.len(), VarInt(i).encoded_len()); - assert_eq!(Ok((VarInt(i), buf.buffer.len())), VarInt::decode(&buf.buffer)); - buf.buffer.clear(); - } -} - -impl, V: AsRef<[u8]>> Op { - /// Calculate encoded len. - pub fn encoded_len(&self) -> usize { - let mut len = 1; - match self { - Op::KeyPush(key, _mask) => { - len += VarInt(key.len() as u32).encoded_len(); - len += key.len(); - len += 1; - }, - Op::KeyPop(nb) => { - len += VarInt(*nb as u32).encoded_len(); - }, - Op::HashChild(hash, _at) => { - len += hash.as_ref().len(); - len += 1; - }, - Op::HashValue(hash) => { - len += hash.as_ref().len(); - }, - Op::Value(value) => { - len += VarInt(value.as_ref().len() as u32).encoded_len(); - len += value.as_ref().len(); - }, - Op::EndProof => (), - Op::SuspendProof => (), - } - len - } - - /// Write op. - pub fn encode_into(&self, out: &mut impl RecorderOutput) { - match self { - Op::KeyPush(key, mask) => { - out.write_bytes(&[0]); - VarInt(key.len() as u32).encode_into(out); - out.write_bytes(&key); - out.write_bytes(&[*mask]); - }, - Op::KeyPop(nb) => { - out.write_bytes(&[1]); - VarInt(*nb as u32).encode_into(out); - }, - Op::HashChild(hash, at) => { - out.write_bytes(&[2]); - out.write_bytes(hash.as_ref()); - out.write_bytes(&[*at]); - }, - Op::HashValue(hash) => { - out.write_bytes(&[3]); - out.write_bytes(hash.as_ref()); - }, - Op::Value(value) => { - out.write_bytes(&[4]); - let value = value.as_ref(); - VarInt(value.len() as u32).encode_into(out); - out.write_bytes(&value); - }, - Op::EndProof => { - out.write_bytes(&[5]); - }, - Op::SuspendProof => { - out.write_bytes(&[6]); - }, - } - } -} - -impl + AsMut<[u8]> + Default> Op> { - /// Read an op, return op and number byte read. Or error if invalid encoded. - pub fn decode(encoded: &[u8]) -> Result<(Self, usize), ()> { - let mut i = 0; - if i >= encoded.len() { - return Err(()) - } - Ok(match encoded[i] { - 0 => { - let (len, offset) = VarInt::decode(&encoded[i + 1..])?; - i += 1 + offset; - if i + len.0 as usize >= encoded.len() { - return Err(()) - } - let key = &encoded[i..i + len.0 as usize]; - let mask = encoded[i + len.0 as usize]; - (Op::KeyPush(key.to_vec(), mask), i + len.0 as usize + 1) - }, - 1 => { - let (len, offset) = VarInt::decode(&encoded[i + 1..])?; - if len.0 > u16::MAX as u32 { - return Err(()) - } - (Op::KeyPop(len.0 as u16), i + 1 + offset) - }, - 2 => { - let mut hash = H::default(); - let end = i + 1 + hash.as_ref().len(); - if end >= encoded.len() { - return Err(()) - } - hash.as_mut().copy_from_slice(&encoded[i + 1..end]); - let mask = encoded[end]; - (Op::HashChild(hash, mask), end + 1) - }, - 3 => { - let mut hash = H::default(); - let end = i + 1 + hash.as_ref().len(); - if end > encoded.len() { - return Err(()) - } - hash.as_mut().copy_from_slice(&encoded[i + 1..end]); - (Op::HashValue(hash), end) - }, - 4 => { - let (len, offset) = VarInt::decode(&encoded[i + 1..])?; - i += 1 + offset; - if i + len.0 as usize > encoded.len() { - return Err(()) - } - let value = &encoded[i..i + len.0 as usize]; - (Op::Value(value.to_vec()), i + len.0 as usize) - }, - 5 => (Op::EndProof, 1), - 6 => (Op::SuspendProof, 1), - _ => return Err(()), - }) - } -} - -/// Iterator on op from a in memory encoded proof. -pub struct IterOpProof + AsMut<[u8]> + Default, B: AsRef<[u8]>>( - B, - usize, - PhantomData, -); - -impl + AsMut<[u8]> + Default, B: AsRef<[u8]>> From for IterOpProof { - fn from(b: B) -> Self { - Self(b, 0, PhantomData) - } -} - -impl + AsMut<[u8]> + Default, B: AsRef<[u8]>> Iterator for IterOpProof { - type Item = Option>>; - - fn next(&mut self) -> Option { - match Op::decode(&self.0.as_ref()[self.1..]) { - Ok((op, len)) => { - self.1 += len; - Some(Some(op)) - }, - Err(_) => Some(None), - } - } -} diff --git a/trie-db/src/iterator.rs b/trie-db/src/iterator.rs index eee7d24c..150bf286 100644 --- a/trie-db/src/iterator.rs +++ b/trie-db/src/iterator.rs @@ -118,19 +118,6 @@ impl TrieDBRawIterator { Ok(iter) } - pub(crate) fn init_from_inline(&mut self, node: &[u8], db: &TrieDB) { - let node = db - .get_raw_or_lookup( - >::default(), - NodeHandle::Inline(node), - EMPTY_PREFIX, - false, - ) - .expect("inline node is always in db; qed") - .0; - self.descend(node, None); - } - /// Descend into a payload. fn descend(&mut self, node: OwnedNode, node_hash: Option>) { self.trail diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 63c03acc..590ad4fa 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -41,7 +41,6 @@ use self::rstd::{boxed::Box, vec::Vec}; use hash_db::MaybeDebug; use node::NodeOwned; -pub mod content_proof; pub mod node; pub mod proof; pub mod query_plan; diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index e3c8854d..4ec55f75 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -24,7 +24,6 @@ use core::marker::PhantomData; use crate::{ - content_proof::Op, nibble::{nibble_ops, nibble_ops::NIBBLE_LENGTH, LeftNibbleSlice, NibbleSlice}, node::{NodeHandle, NodePlan, OwnedNode, Value}, node_codec::NodeCodec, @@ -39,21 +38,16 @@ use crate::{ }; use hash_db::Hasher; pub use record::{record_query_plan, HaltedStateRecord, Recorder}; -pub use verify::verify_query_plan_iter; -use verify::HaltedStateCheckNode; -pub use verify_content::verify_query_plan_iter_content; -use verify_content::HaltedStateCheckContent; +pub use verify::{verify_query_plan_iter, HaltedStateCheck}; mod record; mod verify; -mod verify_content; /// Item to query, in memory. #[derive(Default, Clone, Debug)] pub struct InMemQueryPlanItem { key: Vec, hash_only: bool, - // hash_only: bool, TODO implement as_prefix: bool, } @@ -166,26 +160,7 @@ pub enum ProofKind { /// contains hashes that would not be needed if creating the /// proof at once. CompactNodes, - - /// Content oriented proof, no nodes are written, just a - /// sequence of accessed by lexicographical order as described - /// in content_proof::Op. - /// As with compact node, checking validity of proof need to - /// load the full proof or up to the halt point. - CompactContent, -} - -/* -impl ProofKind { - // Do we need to record child hash and inline value individually. - fn record_inline(&self) -> bool { - match self { - ProofKind::FullNodes | ProofKind::CompactNodes => false, - ProofKind::CompactContent => true, - } - } } -*/ #[derive(Default, Clone, Copy)] struct Bitmap(u16); @@ -234,14 +209,9 @@ struct StackedNodeRecord { is_inline: bool, } +// TODO try rem /// Allows sending proof recording as it is produced. pub trait RecorderOutput { - /// Append bytes. - fn write_bytes(&mut self, bytes: &[u8]); - - /// Bytes buf len. - fn buf_len(&self) -> usize; - /// Append a delimited sequence of bytes (usually a node). fn write_entry(&mut self, bytes: Cow<[u8]>); } @@ -258,14 +228,6 @@ pub struct InMemoryRecorder { } impl RecorderOutput for InMemoryRecorder { - fn write_bytes(&mut self, bytes: &[u8]) { - self.buffer.extend_from_slice(bytes) - } - - fn buf_len(&self) -> usize { - self.buffer.len() - } - fn write_entry(&mut self, bytes: Cow<[u8]>) { if !self.buffer.is_empty() { self.nodes.push(core::mem::take(&mut self.buffer)); @@ -310,25 +272,6 @@ impl Limits { } } }, - ProofKind::CompactContent => { - // everything is counted as a node. - if let Some(rem_size) = self.remaining_size.as_mut() { - if *rem_size >= size { - *rem_size -= size; - } else { - *rem_size = 0; - res = true; - } - } - if let Some(rem_node) = self.remaining_node.as_mut() { - if *rem_node > 1 { - *rem_node -= 1; - } else { - *rem_node = 0; - res = true; - } - } - }, } res } @@ -359,21 +302,11 @@ impl Limits { } } }, - ProofKind::CompactContent => { - unreachable!() - }, } res } } -/// When process is halted keep execution state -/// to restore later. -pub enum HaltedStateCheck<'a, L: TrieLayout, C, D: SplitFirst> { - Node(HaltedStateCheckNode<'a, L, C, D>), - Content(HaltedStateCheckContent<'a, L, C>), -} - #[derive(Eq, PartialEq)] enum TryStackChildResult { /// If there is no child to stack. @@ -421,43 +354,6 @@ struct StackedNodeCheck { next_descended_child: u8, } -#[derive(Clone)] -enum ValueSet { - None, - Standard(V), - HashOnly(H), - // ForceInline(V), - // ForceHashed(V), -} - -impl ValueSet { - fn as_ref(&self) -> Option<&V> { - match self { - ValueSet::Standard(v) => Some(v), - //ValueSet::ForceInline(v) | ValueSet::ForceHashed(v) => Some(v), - ValueSet::HashOnly(..) | ValueSet::None => None, - } - } -} - -impl From> for ValueSet { - fn from(op: Op) -> Self { - match op { - Op::HashValue(hash) => ValueSet::HashOnly(hash), - Op::Value(value) => ValueSet::Standard(value), - //Op::ValueForceInline(value) => ValueSet::ForceInline(value), - //Op::ValueForceHashed(value) => ValueSet::ForceHashed(value), - _ => ValueSet::None, - } - } -} - -struct ItemContentStack { - children: Vec>>>, - value: ValueSet, Vec>, - depth: usize, -} - impl Clone for StackedNodeCheck { fn clone(&self) -> Self { StackedNodeCheck { @@ -470,16 +366,6 @@ impl Clone for StackedNodeCheck { } } -impl Clone for ItemContentStack { - fn clone(&self) -> Self { - ItemContentStack { - children: self.children.clone(), - value: self.value.clone(), - depth: self.depth, - } - } -} - #[derive(Clone)] enum ItemStackNode { Inline(OwnedNode>), diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index d62649da..b960c714 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -26,28 +26,16 @@ pub struct Recorder { } impl Recorder { + // TODO rem fn record_inline(&self) -> bool { - match &self.output { - RecorderStateInner::Content { .. } => true, - _ => false, - } + false } /// Check and update start at record. /// When return true, do record. /// Else already was. - fn check_start_at(&mut self, depth: usize, pop: bool) -> bool { - if matches!(self.output, RecorderStateInner::Content { .. }) { - if let Some(s) = self.start_at.as_mut() { - if &depth <= s { - if pop { - *s = depth; - } - return false - } - } - true - } else if self.start_at.map(|s| s >= depth).unwrap_or(false) { + fn check_start_at(&mut self, depth: usize) -> bool { + if self.start_at.map(|s| s >= depth).unwrap_or(false) { false } else { self.start_at = None; @@ -58,9 +46,8 @@ impl Recorder { /// Get back output handle from a recorder. pub fn output(self) -> O { match self.output { - RecorderStateInner::Stream(output) | - RecorderStateInner::Compact { output, .. } | - RecorderStateInner::Content { output, .. } => output, + RecorderStateInner::Stream(output) | RecorderStateInner::Compact { output, .. } => + output, } } @@ -75,51 +62,14 @@ impl Recorder { ProofKind::FullNodes => RecorderStateInner::Stream(output), ProofKind::CompactNodes => RecorderStateInner::Compact { output, proof: Vec::new(), stacked_pos: Vec::new() }, - ProofKind::CompactContent => - RecorderStateInner::Content { output, stacked_push: None, stacked_pop: None }, }; let limits = Limits { remaining_node: limit_node, remaining_size: limit_size, kind }; Self { output, limits, start_at: None, _ph: PhantomData } } - fn record_halt(&mut self) { - let written; - match &mut self.output { - RecorderStateInner::Content { output, .. } => { - let op = Op::, Vec>::SuspendProof; - let init_len = output.buf_len(); - op.encode_into(output); - written = output.buf_len() - init_len; - }, - _ => return, - } - let _ = self.limits.add_node(written, 0, false); - } - - #[must_use] - fn flush_pop_content(&mut self, items: &Vec) -> bool { - match &mut self.output { - RecorderStateInner::Content { output, stacked_pop, .. } => - flush_compact_content_pop::( - output, - stacked_pop, - items, - None, - &mut self.limits, - ), - _ => false, - } - } - #[must_use] - fn record_stacked_node( - &mut self, - item: &StackedNodeRecord, - is_root: bool, - parent_index: u8, - items: &Vec, - ) -> bool { - if !self.check_start_at(item.depth, false) { + fn record_stacked_node(&mut self, item: &StackedNodeRecord, is_root: bool) -> bool { + if !self.check_start_at(item.depth) { return false } let mut res = false; @@ -143,70 +93,17 @@ impl Recorder { stacked_pos.push(proof.len()); proof.push(Vec::new()); }, - RecorderStateInner::Content { output, stacked_push, stacked_pop } => { - res |= flush_compact_content_pop::( - output, - stacked_pop, - items, - None, - &mut self.limits, - ); - if stacked_push.is_none() { - *stacked_push = Some(NibbleVec::new()); - } - if let Some(buff) = stacked_push.as_mut() { - // TODO should be doable to use the stack prefix. - if !is_root { - buff.push(parent_index); - } - let node_data = item.node.data(); - - match item.node.node_plan() { - NodePlan::Branch { .. } => (), - | NodePlan::Empty => (), - NodePlan::Leaf { partial, .. } | - NodePlan::NibbledBranch { partial, .. } | - NodePlan::Extension { partial, .. } => { - let partial = partial.build(node_data); - buff.append_optional_slice_and_nibble(Some(&partial), None); - }, - } - } - }, - } - res - } - - #[must_use] - fn flush_compact_content_pushes(&mut self, depth: usize) -> bool { - let mut res = false; - if !self.check_start_at(depth, false) { - // TODO actually should be unreachable - return res - } - if let RecorderStateInner::Content { output, stacked_push, .. } = &mut self.output { - if let Some(buff) = stacked_push.take() { - let mask = if buff.len() % 2 == 0 { 0xff } else { 0xf0 }; - let op = Op::, Vec>::KeyPush(buff.inner().to_vec(), mask); - let init_len = output.buf_len(); - op.encode_into(output); - let written = output.buf_len() - init_len; - res |= self.limits.add_node(written, 0, false); - } } res } #[must_use] fn record_value_node(&mut self, value: Vec, depth: usize) -> bool { - if !self.check_start_at(depth, false) { + if !self.check_start_at(depth) { return false } let mut res = false; - if let RecorderStateInner::Content { .. } = &self.output { - res |= self.flush_compact_content_pushes(depth); - } match &mut self.output { RecorderStateInner::Stream(output) => { res |= self.limits.add_value(value.len(), L::Codec::DELTA_COMPACT_OMITTED_VALUE); @@ -216,107 +113,6 @@ impl Recorder { res |= self.limits.add_value(value.len(), L::Codec::DELTA_COMPACT_OMITTED_VALUE); proof.push(value.into()); }, - RecorderStateInner::Content { output, .. } => { - let op = Op::, Vec>::Value(value); - let init_len = output.buf_len(); - op.encode_into(output); - let written = output.buf_len() - init_len; - res |= self.limits.add_node(written, 0, false) - }, - } - res - } - - #[must_use] - fn record_value_inline(&mut self, value: &[u8], depth: usize) -> bool { - let mut res = false; - if !self.check_start_at(depth, false) { - return res - } - if let RecorderStateInner::Content { .. } = &self.output { - res |= self.flush_compact_content_pushes(depth); - } - - match &mut self.output { - RecorderStateInner::Compact { .. } | RecorderStateInner::Stream(_) => { - // not writing inline value (already - // in parent node). - }, - RecorderStateInner::Content { output, .. } => { - let op = Op::, &[u8]>::Value(value); - let init_len = output.buf_len(); - op.encode_into(output); - let written = output.buf_len() - init_len; - res |= self.limits.add_node(written, 0, false); - }, - } - res - } - - #[must_use] - fn record_skip_value(&mut self, items: &mut Vec) -> bool { - let mut res = false; - let mut op = None; - if let RecorderStateInner::Content { .. } = &self.output { - if let Some(item) = items.last_mut() { - if item.accessed_value_node { - return res - } - item.accessed_value_node = true; - if !self.check_start_at(item.depth, false) { - return res - } - let node_data = item.node.data(); - - match item.node.node_plan() { - NodePlan::Leaf { value, .. } | - NodePlan::Branch { value: Some(value), .. } | - NodePlan::NibbledBranch { value: Some(value), .. } => { - op = Some(match value.build(node_data) { - Value::Node(hash_slice) => { - let mut hash = TrieHash::::default(); - hash.as_mut().copy_from_slice(hash_slice); - Op::<_, Vec>::HashValue(hash) - }, - Value::Inline(value) => - Op::, Vec>::Value(value.to_vec()), - }); - }, - _ => return res, - } - - res |= self.flush_compact_content_pushes(item.depth); - } - } - - if let Some(op) = op { - match &mut self.output { - RecorderStateInner::Content { output, .. } => { - let init_len = output.buf_len(); - op.encode_into(output); - let written = output.buf_len() - init_len; - res |= self.limits.add_node(written, 0, false); - }, - _ => (), - } - } - res - } - - #[must_use] - fn touched_child_hash(&mut self, hash_slice: &[u8], i: u8) -> bool { - let mut res = false; - match &mut self.output { - RecorderStateInner::Content { output, .. } => { - let mut hash = TrieHash::::default(); - hash.as_mut().copy_from_slice(hash_slice); - let op = Op::, Vec>::HashChild(hash, i as u8); - let init_len = output.buf_len(); - op.encode_into(output); - let written = output.buf_len() - init_len; - res |= self.limits.add_node(written, 0, false); - }, - _ => (), } res } @@ -333,14 +129,6 @@ enum RecorderStateInner { /// when information got accessed. stacked_pos: Vec, }, - /// For FullNodes proofs, just send node to this stream. - Content { - output: O, - // push current todos. - stacked_push: Option, - // pop from depth. - stacked_pop: Option, - }, } /// When process is halted keep execution state @@ -407,7 +195,7 @@ impl HaltedStateRecord { self.stack.recorder.output() } - fn finalize(&mut self, halt: bool) { + fn finalize(&mut self) { let stack = &mut self.stack; let items = &stack.items; match &mut stack.recorder.output { @@ -439,35 +227,16 @@ impl HaltedStateRecord { RecorderStateInner::Stream(_output) => { // all written on access }, - RecorderStateInner::Content { output: _, stacked_push, stacked_pop: _ } => { - // TODO protect existing stack as for compact - assert!(stacked_push.is_none()); - - if halt { - // next is a key push, do the pop - let _ = stack.recorder.flush_pop_content(items); - stack.recorder.record_halt(); - // let saved_iter_prefix = stack.iter_prefix.clone(); - } - - for i in (0..items.len()).rev() { - // TODO if i > restart height - let _ = self.record_popped_node(i, halt); - } - }, } } /// Callback on node before a node in the stack. /// `at` is the the position in the stack (in some case we keep /// the stack and will not pop the node). - /// Return true if should halt. - #[must_use] - fn record_popped_node(&mut self, at: usize, halt: bool) -> bool { + fn record_popped_node(&mut self, at: usize) { let item = self.stack.items.get(at).expect("bounded check call"); - let mut res = false; - if !self.stack.recorder.check_start_at(item.depth, true) { - return res + if !self.stack.recorder.check_start_at(item.depth) { + return } /* TODO rem Incorrect, if first child is inline, we would push more: push * should only be flushed on first pop flush here. @@ -477,8 +246,6 @@ impl HaltedStateRecord { } */ - let mut process_children = false; - let mut has_hash_to_write = false; match &mut self.stack.recorder.output { RecorderStateInner::Stream(_) => (), RecorderStateInner::Compact { proof, stacked_pos, .. } => @@ -492,149 +259,7 @@ impl HaltedStateRecord { .expect("TODO error handling, can it actually fail?"); } // else when restarting record, this is not to be recorded }, - RecorderStateInner::Content { .. } => match item.node.node_plan() { - NodePlan::Branch { children, .. } | NodePlan::NibbledBranch { children, .. } => { - process_children = true; - for i in 0..children.len() { - if children[i].is_some() && !item.accessed_children_node.at(i) { - has_hash_to_write = true; - break - } - } - }, - _ => (), - }, - } - - let items = &self.stack.items[..at + 1]; - match &mut self.stack.recorder.output { - RecorderStateInner::Content { output, stacked_pop, .. } => { - //let item = &self.stack.items.get(at).expect("bounded iter"); - /*if stacked_pop.is_none() { - *stacked_pop = Some(item.depth); - }*/ - - if has_hash_to_write { - res |= flush_compact_content_pop::( - output, - stacked_pop, - items, - None, - &mut self.stack.recorder.limits, - ); - } - if process_children { - res |= - self.try_stack_content_child(at, halt).expect("stack inline do not fetch"); - } - }, - _ => (), - } - match &mut self.stack.recorder.output { - RecorderStateInner::Content { stacked_pop, .. } => { - let item = &self.stack.items.get(at).expect("bounded iter"); - if !halt && stacked_pop.is_none() { - *stacked_pop = Some(item.depth); - } - }, - _ => (), - } - - res - } - - // Add child for content - fn try_stack_content_child( - &mut self, - at: usize, - halt: bool, - ) -> Result, CError>> { - if self.stack.items.is_empty() { - return Ok(false) - } - if halt || at < self.stack.items.len() - 1 { - // work on a copy of the stack - let saved_items = self.stack.items.clone(); - let saved_prefix = self.stack.prefix.clone(); - let saved_iter_prefix = self.stack.iter_prefix.clone(); - - while self.stack.items.len() != at + 1 { - self.stack.items.pop(); - } - let at = self.stack.items.last().map(|i| i.depth).unwrap_or(0); - self.stack.prefix.drop_lasts(self.stack.prefix.len() - at); - let result = self.try_stack_content_child(self.stack.items.len() - 1, false); - // restore state - self.stack.items = saved_items; - self.stack.prefix = saved_prefix; - self.stack.iter_prefix = saved_iter_prefix; - return result - } - - debug_assert!(at == self.stack.items.len() - 1); - - let mut res = false; - self.try_stack_content_inline_children(NIBBLE_LENGTH as u8)?; - let Some(item) = self.stack.items.last() else { return Ok(res) }; - for i in 0..NIBBLE_LENGTH as u8 { - // TODO avoid the two consecutive iter on all children - if !item.accessed_children_node.at(i as usize) { - let node_data = item.node.data(); - - match item.node.node_plan() { - NodePlan::Empty | NodePlan::Leaf { .. } => (), - NodePlan::Extension { .. } => (), - NodePlan::NibbledBranch { children, .. } | - NodePlan::Branch { children, .. } => - if let Some(child) = &children[i as usize] { - match child.build(node_data) { - NodeHandle::Hash(hash) => { - // may need to flush an inline node - res |= self.stack.recorder.flush_pop_content(&self.stack.items); - res |= self.stack.recorder.touched_child_hash(&hash, i); - }, - _ => (), - } - }, - } - } - } - - self.stack - .items - .last_mut() - .map(|i| i.next_descended_child = NIBBLE_LENGTH as u8); - Ok(res) - } - - fn try_stack_content_inline_children( - &mut self, - child_ix_bound: u8, - ) -> Result<(), VerifyError, CError>> { - let Some(item) = self.stack.items.last_mut() else { return Ok(()) }; - let dummy_parent_hash = TrieHash::::default(); - for i in item.next_descended_child..child_ix_bound { - let Some(item) = self.stack.items.last_mut() else { return Ok(()) }; - if !item.accessed_children_node.at(i as usize) { - match self.stack.try_stack_child(i, None, dummy_parent_hash, None)? { - // only expect a stacked full prefix or not stacked here - TryStackChildResult::StackedFull => { - let halt = self.iter_prefix(None, None, false, true)?; - let Some(item) = self.stack.items.last_mut() else { return Ok(()) }; - item.accessed_children_node.set(i as usize, true); - // no halt on inline. - debug_assert!(!halt); - self.pop(); - // self.stack.halt |= - // self.stack.recorder.flush_pop_content(&self.stack.items); - }, - TryStackChildResult::NotStackedBranch => (), - TryStackChildResult::NotStacked => break, - _ => unreachable!(), - } - } } - Ok(()) } fn pop(&mut self) -> bool { @@ -648,7 +273,7 @@ impl HaltedStateRecord { } let at = self.stack.items.len(); if at > 0 { - self.stack.halt |= self.record_popped_node(at - 1, false); + self.record_popped_node(at - 1); } if let Some(item) = self.stack.items.pop() { let depth = self.stack.items.last().map(|i| i.depth).unwrap_or(0); @@ -715,7 +340,7 @@ impl HaltedStateRecord { (self.stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, )); self.stack.prefix.pop(); - self.finalize(true); + self.finalize(); self.stack.halt = false; self.from = dest_from; self.currently_query_item = prev_query.map(|q| q.to_owned()); @@ -819,7 +444,7 @@ pub fn record_query_plan< Ordering::Less => break true, Ordering::Greater => if !from.pop() { - from.finalize(false); + from.finalize(); return Ok(()) }, } @@ -857,17 +482,10 @@ pub fn record_query_plan< } else { break true } - } else { - if from.stack.recorder.record_skip_value(&mut from.stack.items) { - from.stack.halt = true; - } } } let child_index = if from.stack.items.is_empty() { 0 } else { slice_query.at(0) }; - if query_plan.kind == ProofKind::CompactContent { - from.try_stack_content_inline_children(child_index)?; - } from.stack.items.last_mut().map(|i| { // TODO only needed for content but could be better to be always aligned i.next_descended_child = child_index + 1; @@ -901,7 +519,7 @@ pub fn record_query_plan< )); from.stack.prefix.pop(); from.currently_query_item = Some(query.to_owned()); - from.finalize(true); + from.finalize(); return Ok(()) }, } @@ -926,7 +544,7 @@ pub fn record_query_plan< } } */ - from.finalize(false); + from.finalize(); Ok(()) } @@ -988,7 +606,7 @@ impl RecordStack { // TODO consider not going into inline for all proof but content. // Returning NotStacked here sounds safe, then the is_inline field is not needed. if self.recorder.record_inline() { - if !self.recorder.check_start_at(item_depth, false) { + if !self.recorder.check_start_at(item_depth) { // inline in a previous proof return Ok(TryStackChildResult::NotStackedBranch) } @@ -1086,12 +704,7 @@ impl RecordStack { next_descended_child, is_inline, }; - self.halt |= self.recorder.record_stacked_node( - &infos, - self.items.is_empty(), - child_index, - &self.items, - ); + self.halt |= self.recorder.record_stacked_node(&infos, self.items.is_empty()); self.items.push(infos); if stack_extension { let sbranch = self.try_stack_child(0, db, parent_hash, slice_query)?; @@ -1136,12 +749,8 @@ impl RecordStack { return Err(VerifyError::IncompleteProof) }; self.halt |= self.recorder.record_value_node(value, self.prefix.len()); - } else { - self.halt |= self.recorder.record_skip_value(&mut self.items); }, - Value::Inline(value) => { - self.halt |= self.recorder.record_value_inline(value, self.prefix.len()); - }, + Value::Inline(_) => (), } Ok(true) } @@ -1154,27 +763,3 @@ impl RecordStack { self.iter_prefix = None } } - -#[must_use] -fn flush_compact_content_pop( - out: &mut O, - stacked_from: &mut Option, - items: &[StackedNodeRecord], - add_depth: Option, - limits: &mut Limits, -) -> bool { - let Some(from) = stacked_from.take() else { return false }; - let pop_to = add_depth.unwrap_or_else(|| items.last().map(|i| i.depth).unwrap_or(0)); - if from == pop_to { - return false - } - debug_assert!(from > pop_to); - - debug_assert!(from - pop_to <= u16::max_value() as usize); - // Warning this implies key size limit of u16::max - let op = Op::, Vec>::KeyPop((from - pop_to) as u16); - let init_len = out.buf_len(); - op.encode_into(out); - let written = out.buf_len() - init_len; - limits.add_node(written, 0, false) -} diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 3c56f573..6d0183d7 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -93,17 +93,7 @@ where P: Iterator, D: SplitFirst, { - let HaltedStateCheck::Node(state) = state else { - return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent - }; - let HaltedStateCheckNode { query_plan, current, stack, state, restore_offset } = state; - - match query_plan.kind { - ProofKind::CompactContent => { - return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent - }, - _ => (), - }; + let HaltedStateCheck { query_plan, current, stack, state, restore_offset } = state; Ok(ReadProofIterator { query_plan: Some(query_plan), @@ -154,13 +144,13 @@ where }, ); stack.start_items = stack.items.len(); - Ok(ReadProofItem::Halted(Box::new(HaltedStateCheck::Node(HaltedStateCheckNode { + Ok(ReadProofItem::Halted(Box::new(HaltedStateCheck { query_plan, current, restore_offset: self.current_offset, stack, state: ReadProofState::Halted, - })))) + }))) } fn enter_prefix_iter(&mut self, hash_only: bool, key: &[u8]) { @@ -488,7 +478,7 @@ where /// When process is halted keep execution state /// to restore later. -pub struct HaltedStateCheckNode<'a, L: TrieLayout, C, D: SplitFirst> { +pub struct HaltedStateCheck<'a, L: TrieLayout, C, D: SplitFirst> { query_plan: QueryPlan<'a, C>, current: Option>, stack: Stack, @@ -496,17 +486,15 @@ pub struct HaltedStateCheckNode<'a, L: TrieLayout, C, D: SplitFirst> { restore_offset: usize, } -impl<'a, L: TrieLayout, C, D: SplitFirst> From> - for HaltedStateCheckNode<'a, L, C, D> -{ +impl<'a, L: TrieLayout, C, D: SplitFirst> From> for HaltedStateCheck<'a, L, C, D> { fn from(query_plan: QueryPlan<'a, C>) -> Self { + // TODO a method in kind let is_compact = match query_plan.kind { ProofKind::FullNodes => false, ProofKind::CompactNodes => true, - _ => false, }; - HaltedStateCheckNode { + HaltedStateCheck { stack: Stack { items: Default::default(), start_items: 0, diff --git a/trie-db/src/query_plan/verify_content.rs b/trie-db/src/query_plan/verify_content.rs deleted file mode 100644 index cdf0ff31..00000000 --- a/trie-db/src/query_plan/verify_content.rs +++ /dev/null @@ -1,1112 +0,0 @@ -// Copyright 2023, 2023 Parity Technologies -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Verify query plan proof for content proofs. - -use super::*; -use core::marker::PhantomData; - -use crate::{ - content_proof::Op, nibble::LeftNibbleSlice, proof::VerifyError, rstd::result::Result, CError, - TrieHash, TrieLayout, -}; -pub use record::{record_query_plan, HaltedStateRecord, Recorder}; - -/// Result of verify iterator. -type VerifyIteratorResult<'a, L, C> = - Result>, VerifyError, CError>>; - -/// Proof reading iterator. -pub struct ReadProofContentIterator<'a, L, C, P> -where - L: TrieLayout, - C: Iterator>, - P: Iterator, Vec>>>, -{ - // always needed, this is option only - // to avoid unsafe code when halting. - query_plan: Option>, - proof: P, - expected_root: Option>, - current: Option>, - current_offset: usize, // TODO remove seems unused - state: ReadProofState, - stack: Stack, - buf_op: Option, Vec>>, - send_enter_prefix: Option>, - send_exit_prefix: bool, - buffed_result: Option>>, -} - -struct Stack { - items: Vec>, - prefix: NibbleVec, - start_items: usize, - is_prev_hash_child: Option, - expect_value: bool, - is_prev_push_key: bool, - is_prev_pop_key: bool, - first: bool, - in_prefix_depth: Option, - // when exititng keep nibble vec and items to restore and current prefix iter. - halting: Option<(Vec>, NibbleVec, usize, Option)>, - // depth of node containing inline hash - expect_inline_child: Option, - process_inline_children: Option<( - NibbleVec, - Vec<(u8, usize, TrieHash, usize)>, - Option>, - bool, - )>, - _ph: PhantomData, -} - -impl Clone for Stack { - fn clone(&self) -> Self { - Stack { - items: self.items.clone(), - prefix: self.prefix.clone(), - start_items: self.start_items.clone(), - expect_value: self.expect_value, - is_prev_push_key: self.is_prev_push_key, - is_prev_pop_key: self.is_prev_pop_key, - is_prev_hash_child: self.is_prev_hash_child, - expect_inline_child: self.expect_inline_child, - first: self.first, - halting: self.halting.clone(), - process_inline_children: self.process_inline_children.clone(), - in_prefix_depth: self.in_prefix_depth.clone(), - _ph: PhantomData, - } - } -} - -/// Read the proof. -/// -/// If expected root is None, then we do not check hashes at all. -pub fn verify_query_plan_iter_content<'a, L, C, P>( - state: HaltedStateCheck<'a, L, C, Vec>, - proof: P, - expected_root: Option>, -) -> Result, VerifyError, CError>> -where - L: TrieLayout, - C: Iterator>, - P: Iterator, Vec>>>, -{ - let HaltedStateCheck::Content(state) = state else { - return Err(VerifyError::IncompleteProof) // TODO not kind as param if keeping CompactContent - }; - - let HaltedStateCheckContent { query_plan, current, restore_offset, stack, state } = state; - - Ok(ReadProofContentIterator { - query_plan: Some(query_plan), - proof, - expected_root, - current, - current_offset: restore_offset, - state, - stack, - buf_op: None, - send_enter_prefix: None, - send_exit_prefix: false, - buffed_result: None, - }) -} - -impl<'a, L, C, P> Iterator for ReadProofContentIterator<'a, L, C, P> -where - L: TrieLayout, - C: Iterator>, - P: Iterator, Vec>>>, -{ - type Item = VerifyIteratorResult<'a, L, C>; - - fn next(&mut self) -> Option { - if self.stack.process_inline_children.is_some() { - if let Some(r) = self.do_process_inline_children() { - return Some(r) - } - } - debug_assert!(self.send_enter_prefix.is_none()); - debug_assert!(!self.send_exit_prefix); - if let Some(r) = self.buffed_result.take() { - return r - } - let r = self.next_inner(); - if self.stack.process_inline_children.is_some() { - self.send_exit_prefix = false; - if let Some(r) = self.do_process_inline_children() { - return Some(r) - } - } - if let Some(k) = self.send_enter_prefix.take() { - self.buffed_result = Some(r); - return Some(Ok(ReadProofItem::StartPrefix(k))) - } - if self.send_exit_prefix { - self.buffed_result = Some(r); - self.send_exit_prefix = false; - return Some(Ok(ReadProofItem::EndPrefix)) - } else { - r - } - } -} - -struct DummyDB; - -impl hash_db::HashDBRef for DummyDB { - fn get(&self, _key: &H::Out, _prefix: hash_db::Prefix) -> Option { - None - } - - fn contains(&self, _key: &H::Out, _prefix: hash_db::Prefix) -> bool { - false - } -} - -impl<'a, L, C, P> ReadProofContentIterator<'a, L, C, P> -where - L: TrieLayout, - C: Iterator>, - P: Iterator, Vec>>>, -{ - // this is strictly prefix iteration processing - fn do_process_inline_children(&mut self) -> Option> { - let (prefix, inlines, current, hash_only) = - self.stack.process_inline_children.as_mut().expect("checked call"); - debug_assert!(current.is_none()); - // TODO process all - // TODO reset prefix to last process depth and with actual prefix - // TODO test against send_exit prefix, potentially can send exit in middle, - // so just set to false and test during iteration. - - loop { - let dummy_root = TrieHash::::default(); - let dummy_db = DummyDB; - let dummy_trie_db = crate::TrieDBBuilder::new(&dummy_db, &dummy_root).build(); - if current.is_none() { - if inlines.is_empty() { - self.stack.process_inline_children = None; - return None - } - let (child_ix, depth, inline, inline_len) = inlines.pop().expect("checked"); - let mut raw_iter = crate::iterator::TrieDBRawIterator:: { - trail: Vec::new(), - key_nibbles: prefix.clone(), - }; - - raw_iter.init_from_inline(&inline.as_ref()[..inline_len], &dummy_trie_db); - *current = Some(raw_iter); - } - - let iter = current.as_mut().expect("init above"); - if let Some(r) = iter.next_item(&dummy_trie_db) { - match r { - Ok((key, value)) => - if *hash_only { - let hash = ::hash(value.as_slice()); - return Some(Ok(ReadProofItem::Hash(key.to_vec().into(), hash))) - } else { - return Some(Ok(ReadProofItem::Value( - key.to_vec().into(), - value.clone(), - ))) - }, - // TODO unreachable for inline? - Err(e) => return Some(Err(VerifyError::IncompleteProof)), /* TODO right error - * return */ - } - } - } - } - - // TODO useless next_inner??? - fn next_inner(&mut self) -> Option> { - if self.state == ReadProofState::Finished { - return None - } - if self.state == ReadProofState::Halted { - self.state = ReadProofState::Running; - } - // read proof - loop { - if self.send_exit_prefix { - debug_assert!(self.send_enter_prefix.is_none()); - debug_assert!(self.buffed_result.is_none()); - self.send_exit_prefix = false; - return Some(Ok(ReadProofItem::EndPrefix)) - } - if self.state == ReadProofState::SwitchQueryPlan || - self.state == ReadProofState::SwitchQueryPlanInto || - self.state == ReadProofState::NotStarted - { - let query_plan = self.query_plan.as_mut().expect("Removed with state"); - if let Some(next) = query_plan.items.next() { - let ((ordered, common_nibbles), hash_only) = - if let Some(old) = self.current.as_ref() { - (old.before(&next), old.hash_only) - } else { - ((true, 0), false) - }; - if !ordered { - if query_plan.ignore_unordered { - continue - } else { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::UnorderedKey(next.key.to_vec()))) - } - } - match self.stack.stack_pop(Some(common_nibbles), &self.expected_root, hash_only) - { - // match self.stack.pop_until(Some(common_nibbles), &self.expected_root, - // false) { - /* Ok(true) => { - self.current = Some(next); - let current = self.current.as_ref().expect("current is set"); - return self.missing_switch_next(current.as_prefix, current.key, false) - },*/ - Err(e) => { - self.state = ReadProofState::Finished; - return Some(Err(e)) - }, - Ok(()) => (), - //Ok(false) => (), - } - - self.state = ReadProofState::Running; - self.current = Some(next); - if self.stack.process_inline_children.is_some() { - // iterate children first. - return None - } - } else { - self.state = ReadProofState::PlanConsumed; - self.current = None; - } - }; - while let Some(op) = self.buf_op.take().map(Option::Some).or_else(|| self.proof.next()) - { - println!("read: {:?}", op); - let Some(op) = op else { - if !self.stack.items.is_empty() { - let hash_only = if let Some(old) = self.current.as_ref() { - old.hash_only - } else { - false - }; - let r = self.stack.stack_pop(None, &self.expected_root, hash_only); - if self.stack.process_inline_children.is_some() { - // iterate children first. - return None - } - - // TODO handle halt!! - self.state = ReadProofState::Finished; - if let Err(e) = r { - return Some(Err(e)) - } - } - if self.stack.halting.is_some() { - return Some(self.halt()) - } - if let Some(c) = self.current.as_ref() { - if c.as_prefix { - // end prefix switch to next - self.state = ReadProofState::SwitchQueryPlan; - return Some(Ok(ReadProofItem::EndPrefix)) - } else { - // missing value - self.state = ReadProofState::SwitchQueryPlan; - return Some(Ok(ReadProofItem::NoValue(&c.key))) - } - } else { - return None // finished - } - }; - - // check ordering logic - // TODO wrap in an error and put bools in a struct TODO put in its own function - match &op { - Op::KeyPush(..) => { - if self.stack.is_prev_push_key { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error - // TODO return - // Err(CompactDecoderError::ConsecutivePushKeys. - // into()) - } - if self.stack.halting.is_some() { - if self.stack.expect_inline_child.is_none() { - self.stack.expect_inline_child = Some(self.stack.prefix.len()); - } - } - self.stack.is_prev_push_key = true; - self.stack.is_prev_pop_key = false; - self.stack.is_prev_hash_child = None; - self.stack.first = false; - }, - Op::KeyPop(..) => { - if self.stack.is_prev_pop_key { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error - // return Err(CompactDecoderError::ConsecutivePopKeys. - // into()) - } - self.stack.is_prev_push_key = false; - self.stack.is_prev_pop_key = true; - self.stack.is_prev_hash_child = None; - self.stack.first = false; - }, - Op::HashChild(_, ix) => { - if let Some(prev_ix) = self.stack.is_prev_hash_child.as_ref() { - if prev_ix >= ix { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error - // return Err(CompactDecoderError::NotConsecutiveHash. - // into()) - } - } - // child ix on an existing content would be handle by iter_build. - self.stack.is_prev_push_key = false; - self.stack.is_prev_pop_key = false; - self.stack.is_prev_hash_child = Some(*ix); - }, - Op::HashValue(_) | Op::Value(_) => { - // | Op::ValueForceInline(_) | Op::ValueForceHashed(_) => { - if !(self.stack.is_prev_push_key || self.stack.first) { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO a decode op error - // return Err(CompactDecoderError::ValueNotAfterPush. - // into()) - } - self.stack.is_prev_push_key = false; - self.stack.is_prev_pop_key = false; - self.stack.is_prev_hash_child = None; - self.stack.first = false; - }, - Op::SuspendProof => {}, - _ => { - self.stack.is_prev_push_key = false; - self.stack.is_prev_pop_key = false; - self.stack.is_prev_hash_child = None; - self.stack.first = false; - }, - } - - // debug TODO make it log and external function - match &op { - Op::HashChild(hash, child_ix) => { - println!("ChildHash {:?}, {:?}, {:?}", self.stack.prefix, child_ix, hash); - }, - Op::HashValue(hash) => { - println!("ValueHash {:?}, {:?}", self.stack.prefix, hash); - }, - Op::Value(value) => { - println!("Value {:?}, {:?}", self.stack.prefix, value); - }, - _ => (), - } - - // next - let item = if match &op { - Op::Value(..) | Op::HashValue(..) => true, - _ => false, - } { - let mut at_value = false; - let mut next_query = false; - if let Some(current) = self.current.as_ref() { - let left_query_slice = LeftNibbleSlice::new(¤t.key); - let query_slice = NibbleSlice::new(¤t.key); - if self.stack.expect_inline_child.is_some() { - // TODO could check ordering of key on push, but we do not have current - // generally there is way to create proof with lot of inline content - // child so need to count current inline content size (or at least an - // approximate). - at_value = true; - } else { - match self.stack.prefix.as_leftnibbleslice().cmp(&left_query_slice) { - Ordering::Equal => { - if current.as_prefix { - self.stack.in_prefix_depth = Some(query_slice.len()); - self.send_enter_prefix = Some(current.key.to_vec()); - } - if !self.stack.items.is_empty() { - at_value = true; - } - }, - Ordering::Less => - if !query_slice.starts_with_vec(&self.stack.prefix) { - self.stack.expect_inline_child = - Some(self.stack.prefix.len()); - // TODO else debug assert prefix len > expect inline child - // self.state = ReadProofState::Finished; - // return Some(Err(VerifyError::ExtraneousNode)) // TODO - // error backward pushed key - }, - Ordering::Greater => - if current.as_prefix { - if self - .stack - .prefix - .as_leftnibbleslice() - .starts_with(&left_query_slice) - { - if self.stack.in_prefix_depth.is_none() { - self.stack.in_prefix_depth = - Some(query_slice.len()); - self.send_enter_prefix = Some(current.key.to_vec()); - } - at_value = true; - } else { - next_query = true; - } - } else { - next_query = true; - }, - } - } - if current.hash_only || !at_value || self.stack.halting.is_some() { - if let Op::Value(value) = &op { - if L::MAX_INLINE_VALUE - .map(|max| max as usize <= value.len()) - .unwrap_or(false) - { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousValue(value.clone()))) - } - } - } - if next_query { - self.buf_op = Some(op); - self.stack.is_prev_push_key = true; - self.state = ReadProofState::SwitchQueryPlan; - if current.as_prefix { - // empty prefix carse - if self.stack.in_prefix_depth.take().is_none() && !self.send_exit_prefix { - self.send_enter_prefix = Some(current.key.to_vec()); - } - self.send_exit_prefix = false; - return Some(Ok(ReadProofItem::EndPrefix)) - } else { - return Some(Ok(ReadProofItem::NoValue(¤t.key))) - } - } - } - - if at_value { - match &op { - Op::Value(value) => { - if self.stack.halting.is_some() { - None - } else { - let mut hashed = None; - if let Some(current) = self.current.as_ref() { - if current.hash_only { - let hash = ::hash(value.as_slice()); - hashed = Some(ReadProofItem::Hash( - self.stack.prefix.inner().to_vec().into(), - hash, - )) - } - } - if hashed.is_some() { - hashed - } else { - // TODO could get content from op with no clone. - Some(ReadProofItem::Value( - self.stack.prefix.inner().to_vec().into(), - value.clone(), - )) - } - } - }, - Op::HashValue(hash) => { - if let Some(current) = self.current.as_ref() { - if !current.hash_only { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousHashReference( - hash.clone(), - ))) - } - } - if self.stack.halting.is_some() { - None - } else { - // TODO could get content from op with no clone. - Some(ReadProofItem::Hash( - self.stack.prefix.inner().to_vec().into(), - hash.clone(), - )) - } - }, - _ => { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO error unexpected op - }, - } - } else { - None - } - } else { - None - }; - - // act - let r = match op { - Op::KeyPush(partial, mask) => { - let slice = LeftNibbleSlice::new_with_mask(partial.as_slice(), mask); - self.stack.prefix.append_slice(slice); - self.stack.stack_empty(self.stack.prefix.len()); - Ok(()) - }, - Op::KeyPop(nb_nibble) => { - if nb_nibble as usize > self.stack.prefix.len() { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO better error - } - let target_depth = self.stack.prefix.len() - nb_nibble as usize; - let hash_only = if let Some(old) = self.current.as_ref() { - old.hash_only - } else { - false - }; - let r = self.stack.stack_pop( - Some(target_depth), - &self.expected_root, - hash_only, - ); - self.stack.prefix.drop_lasts(nb_nibble.into()); // TODO should drop only until exit prefix... - if let Some(iter_depth) = self.stack.in_prefix_depth.as_ref() { - if self - .stack - .items - .last() - .map(|i| iter_depth > &i.depth) - .unwrap_or(true) - { - self.send_exit_prefix = true; - self.stack.in_prefix_depth = None; - } - } - r - }, - Op::SuspendProof => { - if self.stack.halting.is_some() { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO better error - // duplicate halting - } - self.stack.halting = Some(( - Vec::with_capacity(self.stack.items.len()), - self.stack.prefix.clone(), - self.current_offset, - self.stack.in_prefix_depth.clone(), - )); - continue - }, - Op::EndProof => break, - Op::HashChild(hash, child_ix) => { - if self.stack.halting.is_none() { - if self.stack.in_prefix_depth.is_some() { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO better error - // missing query plan proof - } else { - // we did pop item before (see op sequence check), so we have - // stack prefix matching current plan. TODO debug assert plan starts - // with prefix TODO we could drop this check as we won t have the - // expected no value item in this case, but looks better to error - // here. TODO check, same for other proof: do a test. - if let Some(current) = self.current.as_ref() { - let query_slice = LeftNibbleSlice::new(¤t.key); - let at = self.stack.prefix.len(); - if query_slice.at(at) == Some(child_ix) { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) // TODO better error missing query plan proof - } - } - } - } - self.stack.set_branch_change(hash, child_ix) - }, - op => { - self.stack.set_value_change(op.into()); - Ok(()) - }, - }; - if let Err(e) = r { - self.state = ReadProofState::Finished; - return Some(Err(e)) - } - if self.stack.process_inline_children.is_some() { - debug_assert!(item.is_none()); - // iterate children first. - return None - } - if let Some(r) = item { - if self.current.as_ref().map(|c| !c.as_prefix).unwrap_or(true) { - self.state = ReadProofState::SwitchQueryPlan; // TODO this is same as content NOne? - } - return Some(Ok(r)) - } - } - if self.state != ReadProofState::SwitchQueryPlan && self.current.is_some() { - // TODO return halt instead - return Some(Err(VerifyError::IncompleteProof)) - } - self.state = ReadProofState::SwitchQueryPlan; - } - - /* - self.state = ReadProofState::Finished; - if self.proof.next().is_some() { - return Some(Err(VerifyError::ExtraneousNode)) - } else { - return None - } - */ - } - - fn halt(&mut self) -> VerifyIteratorResult<'a, L, C> { - // proof already checked - /* - let r = self.stack.stack_pop(None, &self.expected_root); - if let Err(e) = r { - self.state = ReadProofState::Finished; - return Err(e) - } - */ - self.state = ReadProofState::Finished; - let query_plan = crate::rstd::mem::replace(&mut self.query_plan, None); - let query_plan = query_plan.expect("Init with state"); - let current = crate::rstd::mem::take(&mut self.current); - let mut stack = crate::rstd::mem::replace( - // TODO impl default and use take - &mut self.stack, - Stack { - items: Default::default(), - start_items: 0, - prefix: Default::default(), - first: false, - is_prev_hash_child: None, - expect_inline_child: None, - is_prev_push_key: false, - is_prev_pop_key: false, - expect_value: false, - halting: None, - process_inline_children: None, - in_prefix_depth: None, - _ph: PhantomData, - }, - ); - let Some((items, prefix, restore_offset, in_prefix_depth)) = stack.halting.take() else { - unreachable!("Halt called without halting state") - }; - stack.items.extend(items.into_iter().rev()); - stack.prefix = prefix; - stack.start_items = stack.items.len(); - stack.in_prefix_depth = in_prefix_depth; - // following proof start at key push (we halt on key push), forcing - // precondition check. - stack.is_prev_push_key = false; - Ok(ReadProofItem::Halted(Box::new(HaltedStateCheck::Content(HaltedStateCheckContent { - query_plan, - current, - restore_offset, - stack, - state: ReadProofState::Halted, - })))) - } -} - -/// When process is halted keep execution state -/// to restore later. -pub struct HaltedStateCheckContent<'a, L: TrieLayout, C> { - query_plan: QueryPlan<'a, C>, - current: Option>, - restore_offset: usize, - stack: Stack, - state: ReadProofState, -} - -impl<'a, L: TrieLayout, C> From> for HaltedStateCheckContent<'a, L, C> { - fn from(query_plan: QueryPlan<'a, C>) -> Self { - HaltedStateCheckContent { - stack: Stack { - items: Default::default(), - start_items: 0, - prefix: Default::default(), - expect_value: false, - is_prev_push_key: false, - is_prev_pop_key: false, - is_prev_hash_child: None, - expect_inline_child: None, - first: true, - halting: None, - process_inline_children: None, - in_prefix_depth: None, - _ph: PhantomData, - }, - state: ReadProofState::NotStarted, - current: None, - restore_offset: 0, - query_plan, - } - } -} - -impl Stack { - #[inline(always)] - fn stack_empty(&mut self, depth: usize) { - /* - items: Vec>, - prefix: NibbleVec, - // limit and wether we return value and if hash only iteration. - iter_prefix: Option<(usize, bool, bool)>, - start_items: usize, - */ - - self.items.push(ItemContentStack { - children: vec![None; NIBBLE_LENGTH], - value: ValueSet::None, - depth, - }) - } - - #[inline(always)] - fn stack_pop( - &mut self, - target_depth: Option, - expected_root: &Option>, - hash_only: bool, - ) -> Result<(), VerifyError, CError>> { - let mut first = true; - let mut no_inline = false; - let mut checked = expected_root.is_none(); - while self - .items - .last() - .map(|item| target_depth.map(|target| item.depth > target).unwrap_or(true)) - .unwrap_or(false) - { - let item = self.items.pop().expect("Checked"); - let mut from_depth = - self.items.last().map(|item| item.depth).unwrap_or(target_depth.unwrap_or(0)); - if let Some(from) = target_depth { - if from > from_depth { - self.stack_empty(from); - from_depth = from; - } - } - let depth = item.depth; - let is_root = target_depth.is_none() && self.items.is_empty(); - let inc = if is_root { 0 } else { 1 }; - - let child_reference = if item.children.iter().any(|child| child.is_some()) { - let nkey = (depth > (from_depth + inc)) - .then(|| (from_depth + inc, depth - from_depth - inc)); - if L::USE_EXTENSION { - let extension_only = first && - matches!(&item.value, &ValueSet::None) && - item.children.iter().filter(|child| child.is_some()).count() == 1; - // not pop instead) - // encode branch - if !checked { - self.standard_extension(&item, depth, is_root, nkey, extension_only) - } else { - ChildReference::Hash(TrieHash::::default()) - } - } else { - // not pop instead) - // encode branch - if !checked { - self.no_extension(&item, depth, is_root, nkey) - } else { - ChildReference::Hash(TrieHash::::default()) - } - } - } else { - if !checked { - // leaf with value - self.flush_value_change(from_depth + inc, item.depth, &item.value, is_root) - } else { - ChildReference::Hash(TrieHash::::default()) - } - }; - - if self.items.is_empty() && !is_root { - self.stack_empty(from_depth); - } - - if self.expect_inline_child.is_some() { - if !matches!(child_reference, ChildReference::Inline(..)) { - return Err(VerifyError::ExtraneousNode) - } - } - - let items_len = self.items.len(); - if !checked { - if let Some(item) = self.items.last_mut() { - let child_ix = self.prefix.at(item.depth); - if let Some(hash) = item.children[child_ix as usize].as_ref() { - if items_len == self.start_items { - if hash != &child_reference { - return Err(VerifyError::HashMismatch(*child_reference.disp_hash())) - } - } else { - return Err(VerifyError::ExtraneousHashReference(*hash.disp_hash())) - // return Err(CompactDecoderError::HashChildNotOmitted.into()) - } - checked = true; - } - item.children[child_ix as usize] = Some(child_reference); - } else { - if let Some(root) = expected_root.as_ref() { - if target_depth.is_none() { - if root != child_reference.disp_hash() { - return Err(VerifyError::RootMismatch(*child_reference.disp_hash())) - } - checked = true; - } - } - } - } - first = false; - - if !checked && self.items.len() <= self.start_items { - self.start_items = core::cmp::min(self.start_items, self.items.len()); - - // if doing iteration, iterate on all next inline content. - if let Some(d) = self.in_prefix_depth.as_ref() { - if let Some(last) = self.items.last_mut() { - if &last.depth >= d && !no_inline { - let child_ix = self.prefix.at(last.depth); - for i in (child_ix + 1)..NIBBLE_LENGTH as u8 { - if let Some(hash) = last.children[i as usize].as_ref() { - match hash { - ChildReference::Inline(hash, len) => { - if self.process_inline_children.is_none() { - self.process_inline_children = Some(( - self.prefix.clone(), - Vec::new(), - None, - hash_only, - )); - } - if let Some((_, inlines, _, _)) = - self.process_inline_children.as_mut() - { - inlines.push((i, *d, hash.clone(), *len)); - } - }, - // will get content for this in next proofs. - ChildReference::Hash(..) => { - no_inline = true; - break - }, - } - } - } - } - } - } - } - - if let Some((pop_items, ..)) = self.halting.as_mut() { - if !self.expect_inline_child.is_some() { - pop_items.push(item) - }; - } - if let Some(inline_depth) = self.expect_inline_child.clone() { - if self.items.last().map(|i| inline_depth >= i.depth).unwrap_or(true) { - self.expect_inline_child = None; - } - } - } - if let Some((_, inlines, _, _)) = self.process_inline_children.as_mut() { - inlines.reverse(); - } - debug_assert!(target_depth.is_some() || expected_root.is_none() || checked); - Ok(()) - } - - fn process(encoded_node: Vec, is_root: bool) -> ChildReference> { - let len = encoded_node.len(); - if !is_root && len < ::LENGTH { - let mut h = <::Out as Default>::default(); - h.as_mut()[..len].copy_from_slice(&encoded_node[..len]); - return ChildReference::Inline(h, len) - } - let hash = ::hash(encoded_node.as_slice()); - ChildReference::Hash(hash) - } - - // TODO factor with iter_build (reuse cacheaccum here). - #[inline(always)] - fn standard_extension( - &self, - item: &ItemContentStack, - branch_d: usize, - is_root: bool, - nkey: Option<(usize, usize)>, - extension_only: bool, - ) -> ChildReference> { - let key_branch = &self.prefix.inner().as_ref()[..]; - let last = self.items.len() - 1; - assert_eq!(self.items[last].depth, branch_d); - - let ItemContentStack { children, value: v, depth, .. } = item; - - debug_assert!(&branch_d == depth); - - let hashed; - let value = if let Some(v) = v.as_ref() { - Some(if let Some(value) = Value::new_inline(v.as_ref(), L::MAX_INLINE_VALUE) { - value - } else { - let mut prefix = NibbleSlice::new_offset(&key_branch, 0); - prefix.advance(branch_d); - - hashed = ::hash(v.as_ref()); - Value::Node(hashed.as_ref()) - }) - } else { - None - }; - - // encode branch - let branch_hash = if !extension_only { - let encoded = L::Codec::branch_node(children.iter(), value); - Self::process(encoded, is_root && nkey.is_none()) - } else { - // This is hacky but extension only store as first children - children[0].unwrap() - }; - - if let Some(nkeyix) = nkey { - let pr = NibbleSlice::new_offset(&key_branch, nkeyix.0); - let nib = pr.right_range_iter(nkeyix.1); - let encoded = L::Codec::extension_node(nib, nkeyix.1, branch_hash); - Self::process(encoded, is_root) - } else { - branch_hash - } - } - - #[inline(always)] - fn no_extension( - &self, - item: &ItemContentStack, - branch_d: usize, - is_root: bool, - nkey: Option<(usize, usize)>, - ) -> ChildReference> { - let key_branch = &self.prefix.inner().as_ref()[..]; - let ItemContentStack { children, value: v, depth, .. } = item; - - debug_assert!(&branch_d == depth); - // encode branch - let nkeyix = nkey.unwrap_or((branch_d, 0)); - let pr = NibbleSlice::new_offset(&key_branch, nkeyix.0); - let hashed; - let value = if let Some(v) = v.as_ref() { - Some(if let Some(value) = Value::new_inline(v.as_ref(), L::MAX_INLINE_VALUE) { - value - } else { - let mut prefix = NibbleSlice::new_offset(&key_branch, 0); - prefix.advance(branch_d); - hashed = ::hash(v.as_ref()); - Value::Node(hashed.as_ref()) - }) - } else { - if let ValueSet::HashOnly(h) = &v { - Some(Value::Node(h.as_ref())) - } else { - None - } - }; - - let encoded = L::Codec::branch_node_nibbled( - pr.right_range_iter(nkeyix.1), - nkeyix.1, - children.iter(), - value, - ); - Self::process(encoded, is_root) - } - - fn flush_value_change<'a>( - &self, - from_depth: usize, - to_depth: usize, - value: &ValueSet, Vec>, - is_root: bool, - ) -> ChildReference> { - let key_content = &self.prefix.inner().as_ref()[..]; - let k2 = &key_content[..to_depth / nibble_ops::NIBBLE_PER_BYTE]; - let pr = NibbleSlice::new_offset(k2, from_depth); - - let hashed; - let value = match value { - ValueSet::Standard(v) => - if let Some(value) = Value::new_inline(v.as_ref(), L::MAX_INLINE_VALUE) { - value - } else { - hashed = ::hash(v.as_ref()); - Value::Node(hashed.as_ref()) - }, - ValueSet::HashOnly(h) => { - Value::Node(h.as_ref()) // TODO may have following hash and fail? ont if leaf - }, - ValueSet::None => unreachable!("Not in cache accum"), - }; - let encoded = L::Codec::leaf_node(pr.right_iter(), pr.len(), value); - Self::process(encoded, is_root) - } - - #[inline(always)] - // TODO ret err on already set hash?? - fn set_value_change(&mut self, change: ValueSet, Vec>) { - if self.items.is_empty() { - self.stack_empty(0); - } - let last = self.items.len() - 1; - let mut item = &mut self.items[last]; - item.value = change; - } - - #[inline(always)] - fn set_branch_change( - &mut self, - branch_hash: TrieHash, - branch_index: u8, - ) -> Result<(), VerifyError, CError>> { - if self.items.is_empty() { - self.stack_empty(0); - } - let last = self.items.len() - 1; - let item = &mut self.items[last]; - let i = branch_index as usize; - if let Some(hash) = item.children[i].as_ref() { - return Err(VerifyError::ExtraneousHashReference(*hash.disp_hash())) - //return Err(CompactDecoderError::HashChildNotOmitted.into()) TODO - } - - item.children[i] = Some(ChildReference::Hash(branch_hash)); - Ok(()) - } -} diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index 6a2effe0..e81e4dbb 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -698,11 +698,7 @@ pub mod query_plan { assert!(!from.is_halted()); } if !from.is_halted() { - if kind == ProofKind::CompactContent { - proofs.push(vec![from.finish().buffer]); - } else { - proofs.push(from.finish().nodes); - } + proofs.push(from.finish().nodes); break } let rec = if conf.proof_spawn_with_persistence { @@ -711,11 +707,7 @@ pub mod query_plan { query_plan_iter = query_plan.as_ref(); from.stateless(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) }; - if kind == ProofKind::CompactContent { - proofs.push(vec![rec.buffer]); - } else { - proofs.push(rec.nodes); - } + proofs.push(rec.nodes); } crate::query_plan::check_proofs::( @@ -752,18 +744,6 @@ pub mod query_plan { proof_spawn_with_persistence: false, }; - /// Fuzzing conf 3. - pub const CONF3: Conf = Conf { - seed: 0u64, - kind: ProofKind::CompactContent, - nb_key_value: 300, - nb_small_value_set: 5, - nb_big_value_set: 5, - hash_only: false, - limit: 0, // no limit - proof_spawn_with_persistence: false, - }; - #[test] fn fuzz_query_plan_1() { use reference_trie::{RefHasher, SubstrateV1}; @@ -865,14 +845,4 @@ pub mod query_plan { fuzz_query_plan_conf::>(&context, conf, plan.clone()); } } - - #[test] - fn fuzz_query_plan_5() { - use reference_trie::{RefHasher, SubstrateV1}; - let plans = [ArbitraryQueryPlan(vec![(true, ArbitraryKey::Random(vec![]))])]; - let context: FuzzContext> = build_state(CONF3); - for plan in plans { - fuzz_query_plan::>(&context, plan.clone()); - } - } } diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index 688ca5c5..bbd804eb 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -20,11 +20,10 @@ use reference_trie::{test_layouts, TestTrieCache}; use std::collections::BTreeMap; use trie_db::{ - content_proof::IterOpProof, query_plan::{ - record_query_plan, verify_query_plan_iter, verify_query_plan_iter_content, - HaltedStateCheck, HaltedStateRecord, InMemQueryPlan, InMemQueryPlanItem, InMemoryRecorder, - ProofKind, QueryPlan, QueryPlanItem, ReadProofItem, Recorder, + record_query_plan, verify_query_plan_iter, HaltedStateCheck, HaltedStateRecord, + InMemQueryPlan, InMemQueryPlanItem, InMemoryRecorder, ProofKind, QueryPlan, QueryPlanItem, + ReadProofItem, Recorder, }, TrieDBBuilder, TrieDBMutBuilder, TrieHash, TrieLayout, TrieMut, }; @@ -41,12 +40,6 @@ fn test_query_plan_compact_internal() { test_query_plan_internal::(ProofKind::CompactNodes, true); } -test_layouts!(test_query_plan_content, test_query_plan_content_internal); -fn test_query_plan_content_internal() { - test_query_plan_internal::(ProofKind::CompactContent, false); - test_query_plan_internal::(ProofKind::CompactContent, true); -} - fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { let set = test_entries(); @@ -65,7 +58,7 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { }; let db = >::new(&db, &root).with_cache(&mut cache).build(); - if (kind == ProofKind::CompactContent || kind == ProofKind::CompactNodes) && L::USE_EXTENSION { + if kind == ProofKind::CompactNodes && L::USE_EXTENSION { // Compact proofs are not supported with extensions. // Requires changing the way extension are handled // when decoding (putting on stack). @@ -100,7 +93,7 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { for (_nb_plan, query_plan) in query_plans.iter().enumerate() { for limit_conf in [ (0, false), /* TODO uncomment (0, false), (1, false), (1, true), (2, false), (2, - * true), (3, true) */ + * true), (3, true) */ ] { let limit = limit_conf.0; let limit = (limit != 0).then(|| limit); @@ -116,22 +109,7 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { assert!(!from.is_halted()); } if !from.is_halted() { - if kind == ProofKind::CompactContent { - proofs.push(vec![from.finish().buffer]); - for proof in proofs.iter() { - let mut p = &proof[0][..]; - println!("proof start"); - while let Some((op, read)) = - trie_db::content_proof::Op::, _>::decode(p).ok() - { - println!("{:?}", op); - p = &p[read..]; - } - println!("proof end\n"); - } - } else { - proofs.push(from.finish().nodes); - } + proofs.push(from.finish().nodes); break } let rec = if limit_conf.1 { @@ -140,11 +118,7 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { } else { from.statefull(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) }; - if kind == ProofKind::CompactContent { - proofs.push(vec![rec.buffer]); - } else { - proofs.push(rec.nodes); - } + proofs.push(rec.nodes); } let content: BTreeMap<_, _> = set.iter().map(|(k, v)| (k.to_vec(), v.to_vec())).collect(); @@ -469,13 +443,8 @@ pub fn check_proofs( let mut full_proof: Vec> = Default::default(); proofs.reverse(); - let is_content_proof = kind == ProofKind::CompactContent; let query_plan: QueryPlan<_> = query_plan_in_mem.as_ref(); - let mut run_state: Option> = Some(if is_content_proof { - HaltedStateCheck::Content(query_plan.into()) - } else { - HaltedStateCheck::Node(query_plan.into()) - }); + let mut run_state: Option> = Some(query_plan.into()); let mut query_plan_iter: QueryPlan<_> = query_plan_in_mem.as_ref(); let mut current_plan = query_plan_iter.items.next(); let mut has_run_full = false; @@ -491,43 +460,10 @@ pub fn check_proofs( proofs.clear(); std::mem::take(&mut full_proof) }; - let (mut verify_iter, mut verify_iter_content) = if is_content_proof { - let proof_iter: IterOpProof<_, _> = (&proof[0]).into(); - ( - None, - Some( - verify_query_plan_iter_content::>( - state, - proof_iter, - Some(root.clone()), - ) - .unwrap(), - ), - ) - } else { - ( - Some( - verify_query_plan_iter::( - state, - proof.into_iter(), - Some(root.clone()), - ) - .unwrap(), - ), - None, - ) - }; - let mut next_item = || { - if let Some(verify_iter) = verify_iter.as_mut() { - verify_iter.next() - } else if let Some(verify_iter_content) = verify_iter_content.as_mut() { - verify_iter_content.next() - } else { - None - } - }; - - while let Some(item) = next_item() { + let mut verify_iter = + verify_query_plan_iter::(state, proof.into_iter(), Some(root.clone())) + .unwrap(); + while let Some(item) = verify_iter.next() { match item.unwrap() { ReadProofItem::Hash(key, hash) => { assert!(hash_only); @@ -619,11 +555,7 @@ pub fn check_proofs( current_plan = query_plan_iter.items.next(); let query_plan_iter_2 = query_plan_in_mem.as_ref(); - run_state = Some(if is_content_proof { - HaltedStateCheck::Content(query_plan_iter_2.into()) - } else { - HaltedStateCheck::Node(query_plan_iter_2.into()) - }); + run_state = Some(query_plan_iter_2.into()); } } else { has_run_full = true; From 7d2406dbf37fa9d921f60fc0a421d52d7a3c3783 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 10:23:06 +0200 Subject: [PATCH 133/154] rem useless is_inlie --- trie-db/src/query_plan/record.rs | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index b960c714..dd05970f 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -26,11 +26,6 @@ pub struct Recorder { } impl Recorder { - // TODO rem - fn record_inline(&self) -> bool { - false - } - /// Check and update start at record. /// When return true, do record. /// Else already was. @@ -561,9 +556,7 @@ impl RecordStack { let prefix = &mut self.prefix; let mut stack_extension = false; let mut from_branch = None; - let mut item_depth = 0; let child_handle = if let Some(item) = self.items.last_mut() { - item_depth = item.depth; //if inline_only && item.accessed_children_node.at(child_index as usize) { debug_assert!(!item.accessed_children_node.at(child_index as usize)); /* if item.accessed_children_node.at(child_index as usize) { @@ -605,12 +598,6 @@ impl RecordStack { NodeHandle::Inline(_) => { // TODO consider not going into inline for all proof but content. // Returning NotStacked here sounds safe, then the is_inline field is not needed. - if self.recorder.record_inline() { - if !self.recorder.check_start_at(item_depth) { - // inline in a previous proof - return Ok(TryStackChildResult::NotStackedBranch) - } - } is_inline = true; }, NodeHandle::Hash(_) => { @@ -620,14 +607,6 @@ impl RecordStack { self.halt = true; } */ - if self.recorder.record_inline() { - /* TODO bad - // ignore hash in inline call, but mark as accessed. - if let Some(accessed_children_node) = from_branch { - accessed_children_node.set(child_index as usize, true); - } - */ - } return Ok(TryStackChildResult::NotStackedBranch) } else if self.halt && from_branch.is_some() { // halt condition @@ -636,7 +615,7 @@ impl RecordStack { }, } if let Some(accessed_children_node) = from_branch { - if !is_inline || self.recorder.record_inline() { + if !is_inline { accessed_children_node.set(child_index as usize, true); } From e537914bcbf0c7271c11ab5cf705fb021eb9c4e4 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 10:25:04 +0200 Subject: [PATCH 134/154] rem ignore unordered --- trie-db/src/query_plan/mod.rs | 10 +--------- trie-db/src/query_plan/record.rs | 6 +----- trie-db/src/query_plan/verify.rs | 8 ++------ trie-db/test/src/fuzz.rs | 1 - trie-db/test/src/query_plan.rs | 3 --- 5 files changed, 4 insertions(+), 24 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index 4ec55f75..cccfb404 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -104,8 +104,6 @@ impl<'a> QueryPlanItem<'a> { pub struct InMemQueryPlan { pub items: Vec, pub kind: ProofKind, - // TODO rem - pub ignore_unordered: bool, } /// Iterator as type of mapped slice iter is very noisy. @@ -126,19 +124,13 @@ impl<'a> Iterator for QueryPlanItemIter<'a> { impl InMemQueryPlan { /// Get ref. pub fn as_ref(&self) -> QueryPlan { - QueryPlan { - items: QueryPlanItemIter(&self.items, 0), - kind: self.kind, - ignore_unordered: self.ignore_unordered, - _ph: PhantomData, - } + QueryPlan { items: QueryPlanItemIter(&self.items, 0), kind: self.kind, _ph: PhantomData } } } /// Query plan. pub struct QueryPlan<'a, I> { pub items: I, - pub ignore_unordered: bool, pub kind: ProofKind, pub _ph: PhantomData<&'a ()>, } diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index dd05970f..194a474d 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -427,11 +427,7 @@ pub fn record_query_plan< let (ordered, common_nibbles) = prev_query.as_ref().map(|p| p.before(&query)).unwrap_or((true, 0)); if !ordered { - if query_plan.ignore_unordered { - continue - } else { - return Err(VerifyError::UnorderedKey(query.key.to_vec())) - } + return Err(VerifyError::UnorderedKey(query.key.to_vec())) } let skip_query = loop { match from.stack.prefix.len().cmp(&common_nibbles) { diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 6d0183d7..ac01f521 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -235,12 +235,8 @@ where (true, 0) }; if !ordered { - if query_plan.ignore_unordered { - continue - } else { - self.state = ReadProofState::Finished; - return Some(Err(VerifyError::UnorderedKey(next.key.to_vec()))) - } + self.state = ReadProofState::Finished; + return Some(Err(VerifyError::UnorderedKey(next.key.to_vec()))) } match self.stack.pop_until(Some(common_nibbles), &self.expected_root, false) { diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index e81e4dbb..2f4db2fe 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -647,7 +647,6 @@ pub mod query_plan { let mut query_plan = InMemQueryPlan { items: Vec::with_capacity(set.len()), kind: conf.kind, - ignore_unordered: false, }; for (key, not_prefix) in set.into_iter() { if let Some(pref) = prev_pref.as_ref() { diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index bbd804eb..8bf25c0f 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -69,7 +69,6 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { let query_plans = [ InMemQueryPlan { items: vec![InMemQueryPlanItem::new(b"".to_vec(), hash_only, true)], - ignore_unordered: false, kind, }, InMemQueryPlan { @@ -77,7 +76,6 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), InMemQueryPlanItem::new(b"do".to_vec(), hash_only, true), ], - ignore_unordered: false, kind, }, InMemQueryPlan { @@ -86,7 +84,6 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { InMemQueryPlanItem::new(b"doge".to_vec(), hash_only, false), InMemQueryPlanItem::new(b"horsey".to_vec(), hash_only, false), ], - ignore_unordered: false, kind, }, ]; From 5fa784dad35753f8182418d9132f851a60d7a78d Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 10:33:24 +0200 Subject: [PATCH 135/154] remove proof recorder trait. --- trie-db/src/query_plan/mod.rs | 27 --- trie-db/src/query_plan/record.rs | 53 +++-- trie-db/test/src/fuzz.rs | 22 +-- trie-db/test/src/query_plan.rs | 320 +------------------------------ 4 files changed, 42 insertions(+), 380 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index cccfb404..5b858d1b 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -201,33 +201,6 @@ struct StackedNodeRecord { is_inline: bool, } -// TODO try rem -/// Allows sending proof recording as it is produced. -pub trait RecorderOutput { - /// Append a delimited sequence of bytes (usually a node). - fn write_entry(&mut self, bytes: Cow<[u8]>); -} - -/// Simple in memory recorder. -/// Depending on type of proof, nodes or buffer should -/// be used. -/// Sequence is guaranteed by pushing buffer in nodes -/// every time a node is written. -#[derive(Default)] -pub struct InMemoryRecorder { - pub nodes: Vec, - pub buffer: Vec, -} - -impl RecorderOutput for InMemoryRecorder { - fn write_entry(&mut self, bytes: Cow<[u8]>) { - if !self.buffer.is_empty() { - self.nodes.push(core::mem::take(&mut self.buffer)); - } - self.nodes.push(bytes.into_owned()); - } -} - /// Limits to size proof to record. struct Limits { remaining_node: Option, diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 194a474d..a867a0b6 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -17,15 +17,15 @@ use super::*; /// Simplified recorder. -pub struct Recorder { - output: RecorderStateInner, +pub struct Recorder { + output: RecorderStateInner, limits: Limits, // on restore only record content AFTER this position. start_at: Option, _ph: PhantomData, } -impl Recorder { +impl Recorder { /// Check and update start at record. /// When return true, do record. /// Else already was. @@ -39,7 +39,7 @@ impl Recorder { } /// Get back output handle from a recorder. - pub fn output(self) -> O { + pub fn output(self) -> Vec { match self.output { RecorderStateInner::Stream(output) | RecorderStateInner::Compact { output, .. } => output, @@ -49,7 +49,7 @@ impl Recorder { /// Instantiate a new recorder. pub fn new( kind: ProofKind, - output: O, + output: Vec, limit_node: Option, limit_size: Option, ) -> Self { @@ -76,7 +76,7 @@ impl Recorder { L::Codec::DELTA_COMPACT_OMITTED_NODE, is_root, ); - output.write_entry(item.node.data().into()); + output.push(item.node.data().into()); }, RecorderStateInner::Compact { output: _, proof, stacked_pos } => if !item.is_inline { @@ -102,7 +102,7 @@ impl Recorder { match &mut self.output { RecorderStateInner::Stream(output) => { res |= self.limits.add_value(value.len(), L::Codec::DELTA_COMPACT_OMITTED_VALUE); - output.write_entry(value.into()); + output.push(value.into()); }, RecorderStateInner::Compact { output: _, proof, stacked_pos: _ } => { res |= self.limits.add_value(value.len(), L::Codec::DELTA_COMPACT_OMITTED_VALUE); @@ -113,12 +113,12 @@ impl Recorder { } } -enum RecorderStateInner { +enum RecorderStateInner { /// For FullNodes proofs, just send node to this stream. - Stream(O), + Stream(Vec), /// For FullNodes proofs, Requires keeping all proof before sending it. Compact { - output: O, + output: Vec, proof: Vec>, /// Stacked position in proof to modify proof as needed /// when information got accessed. @@ -128,25 +128,25 @@ enum RecorderStateInner { /// When process is halted keep execution state /// to restore later. -pub struct HaltedStateRecord { +pub struct HaltedStateRecord { currently_query_item: Option, - stack: RecordStack, + stack: RecordStack, // This indicate a restore point, it takes precedence over // stack and currently_query_item. from: Option<(Vec, bool)>, } -impl HaltedStateRecord { +impl HaltedStateRecord { /// Indicate we reuse the query plan iterator /// and stack. - pub fn statefull(&mut self, recorder: Recorder) -> O { + pub fn statefull(&mut self, recorder: Recorder) -> Vec { let result = core::mem::replace(&mut self.stack.recorder, recorder); result.output() } /// Indicate to use stateless (on a fresh proof /// and a fresh query plan iterator). - pub fn stateless(&mut self, recorder: Recorder) -> O { + pub fn stateless(&mut self, recorder: Recorder) -> Vec { let new_start = Self::from_start(recorder); let old = core::mem::replace(self, new_start); self.from = old.from; @@ -155,12 +155,12 @@ impl HaltedStateRecord { } /// Init from start. - pub fn from_start(recorder: Recorder) -> Self { + pub fn from_start(recorder: Recorder) -> Self { Self::from_at(recorder, None) } /// Init from position or start. - pub fn from_at(recorder: Recorder, at: Option<(Vec, bool)>) -> Self { + pub fn from_at(recorder: Recorder, at: Option<(Vec, bool)>) -> Self { HaltedStateRecord { currently_query_item: None, stack: RecordStack { @@ -186,7 +186,7 @@ impl HaltedStateRecord { } /// Finalize state, and return the proof output. - pub fn finish(self) -> O { + pub fn finish(self) -> Vec { self.stack.recorder.output() } @@ -216,7 +216,7 @@ impl HaltedStateRecord { } } for entry in core::mem::take(proof) { - output.write_entry(entry.into()); + output.push(entry.into()); } }, RecorderStateInner::Stream(_output) => { @@ -354,8 +354,8 @@ impl HaltedStateRecord { } } -struct RecordStack { - recorder: Recorder, +struct RecordStack { + recorder: Recorder, items: Vec, prefix: NibbleVec, iter_prefix: Option<(usize, bool)>, @@ -367,15 +367,10 @@ struct RecordStack { /// /// TODO output and restart are mutually exclusive. -> enum /// or remove output from halted state. -pub fn record_query_plan< - 'a, - L: TrieLayout, - I: Iterator>, - O: RecorderOutput, ->( +pub fn record_query_plan<'a, L: TrieLayout, I: Iterator>>( db: &TrieDB, query_plan: &mut QueryPlan<'a, I>, - from: &mut HaltedStateRecord, + from: &mut HaltedStateRecord, ) -> Result<(), VerifyError, CError>> { let dummy_parent_hash = TrieHash::::default(); let mut stateless = false; @@ -539,7 +534,7 @@ pub fn record_query_plan< Ok(()) } -impl RecordStack { +impl RecordStack { fn try_stack_child<'a>( &mut self, child_index: u8, diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index 2f4db2fe..a23c791b 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -482,8 +482,8 @@ pub mod query_plan { use std::collections::{BTreeMap, BTreeSet}; use trie_db::{ query_plan::{ - record_query_plan, HaltedStateRecord, InMemQueryPlan, InMemQueryPlanItem, - InMemoryRecorder, ProofKind, Recorder, + record_query_plan, HaltedStateRecord, InMemQueryPlan, InMemQueryPlanItem, ProofKind, + Recorder, }, TrieHash, TrieLayout, }; @@ -644,10 +644,8 @@ pub mod query_plan { } } let mut prev_pref: Option> = None; - let mut query_plan = InMemQueryPlan { - items: Vec::with_capacity(set.len()), - kind: conf.kind, - }; + let mut query_plan = + InMemQueryPlan { items: Vec::with_capacity(set.len()), kind: conf.kind }; for (key, not_prefix) in set.into_iter() { if let Some(pref) = prev_pref.as_ref() { if key.starts_with(pref) { @@ -682,7 +680,7 @@ pub mod query_plan { let kind = conf.kind; let limit = conf.limit; let limit = (limit != 0).then(|| limit); - let recorder = Recorder::new(conf.kind, InMemoryRecorder::default(), limit, None); + let recorder = Recorder::new(conf.kind, Default::default(), limit, None); let mut from = HaltedStateRecord::from_start(recorder); let mut proofs: Vec>> = Default::default(); let mut query_plan_iter = query_plan.as_ref(); @@ -691,22 +689,22 @@ pub mod query_plan { .with_cache(&mut cache) .build(); loop { - record_query_plan::(&db, &mut query_plan_iter, &mut from).unwrap(); + record_query_plan::(&db, &mut query_plan_iter, &mut from).unwrap(); if limit.is_none() { assert!(!from.is_halted()); } if !from.is_halted() { - proofs.push(from.finish().nodes); + proofs.push(from.finish()); break } let rec = if conf.proof_spawn_with_persistence { - from.statefull(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) + from.statefull(Recorder::new(kind, Default::default(), limit, None)) } else { query_plan_iter = query_plan.as_ref(); - from.stateless(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) + from.stateless(Recorder::new(kind, Default::default(), limit, None)) }; - proofs.push(rec.nodes); + proofs.push(rec); } crate::query_plan::check_proofs::( diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index 8bf25c0f..e3ef8445 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -22,8 +22,8 @@ use std::collections::BTreeMap; use trie_db::{ query_plan::{ record_query_plan, verify_query_plan_iter, HaltedStateCheck, HaltedStateRecord, - InMemQueryPlan, InMemQueryPlanItem, InMemoryRecorder, ProofKind, QueryPlan, QueryPlanItem, - ReadProofItem, Recorder, + InMemQueryPlan, InMemQueryPlanItem, ProofKind, QueryPlan, QueryPlanItem, ReadProofItem, + Recorder, }, TrieDBBuilder, TrieDBMutBuilder, TrieHash, TrieLayout, TrieMut, }; @@ -94,336 +94,32 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { ] { let limit = limit_conf.0; let limit = (limit != 0).then(|| limit); - let recorder = Recorder::new(kind, InMemoryRecorder::default(), limit, None); + let recorder = Recorder::new(kind, Default::default(), limit, None); let mut from = HaltedStateRecord::from_start(recorder); // no limit let mut proofs: Vec>> = Default::default(); let mut query_plan_iter = query_plan.as_ref(); loop { - record_query_plan::(&db, &mut query_plan_iter, &mut from).unwrap(); + record_query_plan::(&db, &mut query_plan_iter, &mut from).unwrap(); if limit.is_none() { assert!(!from.is_halted()); } if !from.is_halted() { - proofs.push(from.finish().nodes); + proofs.push(from.finish()); break } let rec = if limit_conf.1 { query_plan_iter = query_plan.as_ref(); - from.stateless(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) + from.stateless(Recorder::new(kind, Default::default(), limit, None)) } else { - from.statefull(Recorder::new(kind, InMemoryRecorder::default(), limit, None)) + from.statefull(Recorder::new(kind, Default::default(), limit, None)) }; - proofs.push(rec.nodes); + proofs.push(rec); } let content: BTreeMap<_, _> = set.iter().map(|(k, v)| (k.to_vec(), v.to_vec())).collect(); check_proofs::(proofs, query_plan, kind, root, &content, hash_only); - - /* TODO this static check keep it somehow ?? - if kind == ProofKind::CompactContent { - fn shifted(bytes: &[u8], aligned: bool) -> Vec { - let mut shifted: Vec = vec![]; - let last = bytes.len(); - bytes.iter().enumerate().for_each(|(i, b)| { - shifted.last_mut().map(|l| { - *l |= *b >> 4; - }); - if !(i == last - 1 && aligned) { - shifted.push(*b << 4); - } - }); - shifted - } - - fn hash + Default>(b: &[u8]) -> H { - let mut hash = H::default(); - hash.as_mut().copy_from_slice(&b[..]); - hash - } - - let all = match L::MAX_INLINE_VALUE { - Some(1) => true, - Some(33) => false, - _ => continue, - }; - let mut nb = 0; - let mut proofs = proofs.clone(); - while let Some(proof) = proofs.pop() { - use trie_db::content_proof::Op; - // full on iter all - // assert_eq!(proofs.len(), 1); - assert_eq!(proof.len(), 1); - - let refs: Vec, Vec>> = - match (limit.unwrap_or(0), nb_plan, nb) { - (0, 0, 0) => vec![ - Op::KeyPush(b"alfa".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"bravo", false), 0xf0), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - ], - (0, 1, 0) => - if all { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::HashChild( - hash( - &[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, - 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, - 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, - 248, - ][..], - ), - 1, - ), - Op::HashChild( - hash( - &[ - 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, - 14, 161, 91, 109, 136, 84, 6, 128, 190, 201, - 49, 142, 21, 154, 250, 246, 133, 0, 199, 138, - 49, - ][..], - ), - 8, - ), - ] - } else { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - // inline ix 8 - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - Op::KeyPop(9), - Op::HashChild( - hash( - &[ - 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, - 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, - 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, - 119, - ][..], - ), - 1, - ), - ] - }, - (0, 2, 0) => - // bravo, doge, horsey - if all { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - // hash value here is not really good (could only be - // with child hashes when no hash query). - Op::HashValue(hash( - &[ - 48, 51, 75, 77, 6, 75, 210, 124, 205, 63, 59, 165, - 81, 140, 222, 237, 196, 168, 203, 206, 105, 245, - 15, 154, 233, 147, 189, 123, 194, 243, 179, 137, - ][..], - )), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::HashValue(hash( - &[ - 104, 225, 103, 23, 160, 148, 143, 214, 98, 64, 250, - 245, 134, 99, 233, 36, 28, 150, 26, 205, 25, 165, - 122, 211, 170, 180, 45, 82, 143, 71, 191, 19, - ][..], - )), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::HashValue(hash( - &[ - 170, 195, 61, 227, 244, 86, 86, 205, 233, 84, 40, - 116, 166, 25, 158, 33, 18, 236, 208, 172, 115, 246, - 158, 34, 158, 170, 197, 139, 219, 254, 124, 136, - ][..], - )), - Op::KeyPop(5), - Op::HashChild( - hash( - &[ - 115, 96, 173, 184, 157, 30, 165, 173, 98, 91, - 45, 97, 173, 249, 2, 240, 133, 247, 131, 7, - 128, 195, 235, 114, 210, 152, 24, 22, 105, 232, - 147, 171, - ][..], - ), - 5, - ), - Op::KeyPop(4), - Op::HashChild( - hash( - &[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, - 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, - 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, - 248, - ][..], - ), - 1, - ), - ] - } else { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - Op::KeyPop(9), - Op::HashChild( - hash( - &[ - 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, - 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, - 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, - 119, - ][..], - ), - 1, - ), - ] - }, - (1, 2, 0) => - // bravo, doge, horsey - if all { - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::HashChild( - hash( - &[ - 44, 27, 209, 105, 69, 70, 73, 254, 82, 36, 236, - 20, 32, 247, 110, 189, 213, 140, 86, 162, 229, - 70, 86, 163, 223, 26, 52, 253, 176, 201, 65, - 248, - ][..], - ), - 1, - ), - Op::HashChild( - hash( - &[ - 223, 91, 16, 28, 134, 71, 144, 93, 127, 153, - 131, 180, 101, 103, 252, 121, 200, 66, 33, 188, - 58, 187, 247, 197, 65, 169, 112, 46, 241, 22, - 96, 196, - ][..], - ), - 4, - ), - Op::HashChild( - hash( - &[ - 31, 82, 102, 128, 24, 85, 151, 92, 70, 18, 78, - 14, 161, 91, 109, 136, 84, 6, 128, 190, 201, - 49, 142, 21, 154, 250, 246, 133, 0, 199, 138, - 49, - ][..], - ), - 8, - ), - ] - } else { - break - /* - vec![ - Op::KeyPush(b"bravo".to_vec(), 0xff), - Op::Value(b"bravo".to_vec()), - Op::KeyPop(9), - Op::KeyPush(shifted(b"do", false), 0xf0), - Op::Value(b"verb".to_vec()), - Op::KeyPush(b"g".to_vec(), 0xff), - Op::Value(b"puppy".to_vec()), - Op::KeyPush(b"e".to_vec(), 0xff), - Op::Value([0; 32].to_vec()), - Op::KeyPop(7), - Op::KeyPush(shifted(b"horse", false), 0xf0), - Op::Value(b"stallion".to_vec()), - Op::KeyPop(5), - Op::KeyPush(shifted(b"use", false), 0xf0), - Op::Value(b"building".to_vec()), - Op::KeyPop(9), - Op::HashChild( - (&[ - 225, 211, 100, 128, 231, 82, 240, 112, 33, 165, - 225, 30, 244, 128, 56, 45, 17, 21, 138, 87, 3, - 211, 231, 109, 244, 137, 208, 244, 12, 65, 196, - 119, - ][..]) - .into(), - 1, - ), - ] - */ - }, - - _ => break, - }; - let mut encoded = InMemoryRecorder::default(); - for r in refs { - r.encode_into(&mut encoded); - } - assert_eq!(proof[0], encoded.buffer); - nb += 1; - } - // continue - */ } } } From e094f757b0ec16a8e9058d25b5226738b6170b55 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 10:36:08 +0200 Subject: [PATCH 136/154] nostd --- trie-db/src/query_plan/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index 5b858d1b..04c26aaf 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -352,7 +352,7 @@ impl From<(ItemStackNode, bool)> for StackedNod match child.build(node_data) { NodeHandle::Inline(data) if data.is_empty() => (), child => { - use std::convert::TryInto; + use crate::rstd::convert::TryInto; let child_ref = child.try_into().expect("TODO proper error and not using From"); @@ -371,7 +371,7 @@ impl From<(ItemStackNode, bool)> for StackedNod match children[i].as_ref().map(|c| c.build(node_data)) { Some(NodeHandle::Inline(data)) if data.is_empty() => (), Some(child) => { - use std::convert::TryInto; + use crate::rstd::convert::TryInto; let child_ref = child .try_into() .expect("TODO proper error and not using From"); From 99f62cf8444965f91a1b37b5de3fdb73a679296f Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 10:39:32 +0200 Subject: [PATCH 137/154] rem switch query plan into --- trie-db/src/query_plan/mod.rs | 2 -- trie-db/src/query_plan/verify.rs | 20 +++++++------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index 04c26aaf..e363a2fe 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -301,8 +301,6 @@ enum ReadProofState { Running, /// Switch next item. SwitchQueryPlan, - /// Switch next item, previous access was into the child node. - SwitchQueryPlanInto, // TODO check if still use /// Proof read. PlanConsumed, /// Proof read. diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index ac01f521..4f36e8e9 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -224,7 +224,6 @@ where return Some(Ok(ReadProofItem::EndPrefix)) } if self.state == ReadProofState::SwitchQueryPlan || - self.state == ReadProofState::SwitchQueryPlanInto || self.state == ReadProofState::NotStarted { let query_plan = self.query_plan.as_mut().expect("Removed with state"); @@ -243,7 +242,7 @@ where Ok(true) => { self.current = Some(next); let current = self.current.as_ref().expect("current is set"); - return self.missing_switch_next(current.as_prefix, current.key, false) + return self.missing_switch_next(current.as_prefix, current.key) }, Err(e) => { self.state = ReadProofState::Finished; @@ -368,7 +367,7 @@ where Ordering::Greater => { // two consecutive query in a node that hide them (two miss in a same proof // node). - return self.missing_switch_next(as_prefix, to_check.key, false) + return self.missing_switch_next(as_prefix, to_check.key) }, } @@ -380,7 +379,7 @@ where ); continue } - self.state = ReadProofState::SwitchQueryPlanInto; + self.state = ReadProofState::SwitchQueryPlan; match self.stack.access_value(&mut self.proof, check_hash, hash_only) { Ok((Some(value), None)) => return Some(Ok(ReadProofItem::Value(to_check.key.into(), value))), @@ -424,13 +423,13 @@ where ); continue } - self.state = ReadProofState::SwitchQueryPlanInto; + self.state = ReadProofState::SwitchQueryPlan; return Some(Ok(ReadProofItem::NoValue(to_check.key))) }, TryStackChildResult::NotStackedBranch | TryStackChildResult::NotStacked => - return self.missing_switch_next(as_prefix, to_check.key, false), + return self.missing_switch_next(as_prefix, to_check.key), TryStackChildResult::StackedAfter => - return self.missing_switch_next(as_prefix, to_check.key, true), + return self.missing_switch_next(as_prefix, to_check.key), TryStackChildResult::Halted => return Some(self.halt()), } } @@ -456,13 +455,8 @@ where &mut self, as_prefix: bool, key: &'a [u8], - into: bool, ) -> Option> { - self.state = if into { - ReadProofState::SwitchQueryPlanInto - } else { - ReadProofState::SwitchQueryPlan - }; + self.state = ReadProofState::SwitchQueryPlan; if as_prefix { self.send_enter_prefix = Some(key.to_vec()); return Some(Ok(ReadProofItem::EndPrefix)) From 794c1764df7b4dd6092718e1603853068564ebaf Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 10:52:00 +0200 Subject: [PATCH 138/154] std --- trie-db/src/query_plan/mod.rs | 23 +++++++++++++++-------- trie-db/src/query_plan/verify.rs | 13 +++++++------ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index e363a2fe..14ae40e5 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -32,7 +32,10 @@ use crate::{ borrow::{Borrow, Cow}, boxed::Box, cmp::*, + convert::{TryFrom, TryInto}, result::Result, + vec, + vec::Vec, }, CError, ChildReference, DBValue, NibbleVec, Trie, TrieDB, TrieHash, TrieLayout, }; @@ -335,8 +338,12 @@ enum ItemStackNode { Node(OwnedNode), } -impl From<(ItemStackNode, bool)> for StackedNodeCheck { - fn from((node, is_compact): (ItemStackNode, bool)) -> Self { +impl TryFrom<(ItemStackNode, bool)> for StackedNodeCheck { + type Error = VerifyError, CError>; + + fn try_from( + (node, is_compact): (ItemStackNode, bool), + ) -> crate::rstd::result::Result { let children = if !is_compact { Vec::new() } else { @@ -350,9 +357,9 @@ impl From<(ItemStackNode, bool)> for StackedNod match child.build(node_data) { NodeHandle::Inline(data) if data.is_empty() => (), child => { - use crate::rstd::convert::TryInto; + // TODO better error let child_ref = - child.try_into().expect("TODO proper error and not using From"); + child.try_into().map_err(|_| VerifyError::ExtraneousNode)?; result[0] = Some(child_ref); }, @@ -369,10 +376,10 @@ impl From<(ItemStackNode, bool)> for StackedNod match children[i].as_ref().map(|c| c.build(node_data)) { Some(NodeHandle::Inline(data)) if data.is_empty() => (), Some(child) => { - use crate::rstd::convert::TryInto; + // TODO better error let child_ref = child .try_into() - .expect("TODO proper error and not using From"); + .map_err(|_| VerifyError::ExtraneousNode)?; result[i] = Some(child_ref); }, @@ -386,13 +393,13 @@ impl From<(ItemStackNode, bool)> for StackedNod } }; - StackedNodeCheck { + Ok(StackedNodeCheck { node, depth: 0, next_descended_child: 0, children, attached_value_hash: None, - } + }) } } diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 4f36e8e9..45950bc2 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -20,7 +20,7 @@ use core::marker::PhantomData; use crate::{ nibble::{nibble_ops, nibble_ops::NIBBLE_LENGTH, NibbleSlice}, proof::VerifyError, - rstd::{boxed::Box, result::Result}, + rstd::{boxed::Box, convert::TryInto, result::Result}, CError, TrieHash, TrieLayout, }; pub use record::{record_query_plan, HaltedStateRecord, Recorder}; @@ -561,7 +561,7 @@ impl Stack { Ok(node) => node, Err(e) => return Err(VerifyError::DecodeError(e)), }; - (ItemStackNode::Node(node), self.is_compact).into() + (ItemStackNode::Node(node), self.is_compact).try_into()? } else { // try access in inline then return ( @@ -571,7 +571,7 @@ impl Stack { }), self.is_compact, ) - .into() + .try_into()? }, NodeHandle::Hash(hash) => { let Some(mut encoded_node) = proof.next() else { @@ -599,7 +599,7 @@ impl Stack { if !self.is_compact && check_hash { verify_hash::(node.data(), hash)?; } - (ItemStackNode::Node(node), self.is_compact).into() + (ItemStackNode::Node(node), self.is_compact).try_into()? }, }; let node_data = node.data(); @@ -654,7 +654,7 @@ impl Stack { verify_hash::(encoded_branch.borrow(), hash)?; } node = match OwnedNode::new::(encoded_branch) { - Ok(node) => (ItemStackNode::Node(node), self.is_compact).into(), + Ok(node) => (ItemStackNode::Node(node), self.is_compact).try_into()?, Err(e) => return Err(VerifyError::DecodeError(e)), }; }, @@ -674,7 +674,8 @@ impl Stack { */ } else { node = match OwnedNode::new::(data.to_vec()) { - Ok(node) => (ItemStackNode::Inline(node), self.is_compact).into(), + Ok(node) => + (ItemStackNode::Inline(node), self.is_compact).try_into()?, Err(e) => return Err(VerifyError::DecodeError(e)), }; } From 82fe104196d846c294c9103553fe4838666094ac Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 10:55:32 +0200 Subject: [PATCH 139/154] rem todos --- trie-db/src/query_plan/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index 14ae40e5..f8a1d35e 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -459,7 +459,6 @@ pub enum ReadProofItem<'a, L: TrieLayout, C, D: SplitFirst> { Halted(Box>), /// Seen value and key in proof. /// We only return content matching the query plan. - /// TODO should be possible to return &Vec Value(Cow<'a, [u8]>, Vec), /// Seen hash of value and key in proof. /// We only return content matching the query plan. @@ -469,10 +468,8 @@ pub enum ReadProofItem<'a, L: TrieLayout, C, D: SplitFirst> { /// Seen fully covered prefix in proof, this is only /// return when we read the proof with the query input (otherwhise /// we would need to indicate every child without a hash as a prefix). - /// TODO unused implement StartPrefix(Vec), /// End of a previously start prefix. - /// TODO unused implement EndPrefix, } From f18da5db0bdea596a7b857eb66b5d8c39fbccdeb Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 11:10:06 +0200 Subject: [PATCH 140/154] mod errors --- trie-db/src/query_plan/mod.rs | 12 ++- trie-db/src/query_plan/verify.rs | 132 +++++++++++++++++++++++-------- 2 files changed, 105 insertions(+), 39 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index f8a1d35e..821c8b07 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -27,7 +27,6 @@ use crate::{ nibble::{nibble_ops, nibble_ops::NIBBLE_LENGTH, LeftNibbleSlice, NibbleSlice}, node::{NodeHandle, NodePlan, OwnedNode, Value}, node_codec::NodeCodec, - proof::VerifyError, rstd::{ borrow::{Borrow, Cow}, boxed::Box, @@ -41,7 +40,7 @@ use crate::{ }; use hash_db::Hasher; pub use record::{record_query_plan, HaltedStateRecord, Recorder}; -pub use verify::{verify_query_plan_iter, HaltedStateCheck}; +pub use verify::{verify_query_plan_iter, Error as VerifyError, HaltedStateCheck}; mod record; mod verify; @@ -357,9 +356,9 @@ impl TryFrom<(ItemStackNode, bool)> for Stacked match child.build(node_data) { NodeHandle::Inline(data) if data.is_empty() => (), child => { - // TODO better error - let child_ref = - child.try_into().map_err(|_| VerifyError::ExtraneousNode)?; + let child_ref = child + .try_into() + .map_err(|d| VerifyError::InvalidNodeHandle(d))?; result[0] = Some(child_ref); }, @@ -376,10 +375,9 @@ impl TryFrom<(ItemStackNode, bool)> for Stacked match children[i].as_ref().map(|c| c.build(node_data)) { Some(NodeHandle::Inline(data)) if data.is_empty() => (), Some(child) => { - // TODO better error let child_ref = child .try_into() - .map_err(|_| VerifyError::ExtraneousNode)?; + .map_err(|d| VerifyError::InvalidNodeHandle(d))?; result[i] = Some(child_ref); }, diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 45950bc2..4ba21bda 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -19,15 +19,85 @@ use core::marker::PhantomData; use crate::{ nibble::{nibble_ops, nibble_ops::NIBBLE_LENGTH, NibbleSlice}, - proof::VerifyError, rstd::{boxed::Box, convert::TryInto, result::Result}, CError, TrieHash, TrieLayout, }; pub use record::{record_query_plan, HaltedStateRecord, Recorder}; +/// Errors that may occur during proof verification. Most of the errors types simply indicate that +/// the proof is invalid with respect to the statement being verified, and the exact error type can +/// be used for debugging. +#[derive(PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum Error { + /// The statement being verified contains multiple key-value pairs with the same key. The + /// parameter is the duplicated key. + DuplicateKey(Vec), + /// The statement being verified contains key not ordered properly. + UnorderedKey(Vec), + /// The proof contains at least one extraneous node. + ExtraneousNode, + /// The proof contains at least one extraneous value which should have been omitted from the + /// proof. + ExtraneousValue(Vec), + /// The proof contains at least one extraneous hash reference the should have been omitted. + ExtraneousHashReference(HO), + /// The proof contains an invalid child reference that exceeds the hash length. + /// TODO extension only? + InvalidChildReference(Vec), + /// The proof is missing trie nodes required to verify. + IncompleteProof, + /// The root hash computed from the proof is incorrect. + RootMismatch(HO), + /// The hash computed from a node is incorrect. + HashMismatch(HO), + /// One of the proof nodes could not be decoded. + DecodeError(CE), + /// Node does not match existing handle. + /// This should not happen. + InvalidNodeHandle(Vec), +} + +#[cfg(feature = "std")] +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + Error::DuplicateKey(key) => + write!(f, "Duplicate key in input statement: key={:?}", key), + Error::UnorderedKey(key) => + write!(f, "Unordered key in input statement: key={:?}", key), + Error::ExtraneousNode => write!(f, "Extraneous node found in proof"), + Error::ExtraneousValue(key) => + write!(f, "Extraneous value found in proof should have been omitted: key={:?}", key), + Error::ExtraneousHashReference(hash) => write!( + f, + "Extraneous hash reference found in proof should have been omitted: hash={:?}", + hash + ), + Error::InvalidChildReference(data) => + write!(f, "Invalid child reference exceeds hash length: {:?}", data), + Error::IncompleteProof => write!(f, "Proof is incomplete -- expected more nodes"), + Error::RootMismatch(hash) => write!(f, "Computed incorrect root {:?} from proof", hash), + Error::HashMismatch(hash) => write!(f, "Computed incorrect hash {:?} from node", hash), + Error::DecodeError(err) => write!(f, "Unable to decode proof node: {}", err), + Error::InvalidNodeHandle(node) => write!(f, "Invalid node handle: {:?}", node), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Error::DecodeError(err) => Some(err), + _ => None, + } + } +} + /// Result of verify iterator. type VerifyIteratorResult<'a, L, C, D> = - Result, VerifyError, CError>>; + Result, Error, CError>>; /// Proof reading iterator. pub struct ReadProofIterator<'a, L, C, D, P> @@ -86,7 +156,7 @@ pub fn verify_query_plan_iter<'a, L, C, D, P>( state: HaltedStateCheck<'a, L, C, D>, proof: P, expected_root: Option>, -) -> Result, VerifyError, CError>> +) -> Result, Error, CError>> where L: TrieLayout, C: Iterator>, @@ -235,7 +305,7 @@ where }; if !ordered { self.state = ReadProofState::Finished; - return Some(Err(VerifyError::UnorderedKey(next.key.to_vec()))) + return Some(Err(Error::UnorderedKey(next.key.to_vec()))) } match self.stack.pop_until(Some(common_nibbles), &self.expected_root, false) { @@ -444,7 +514,7 @@ where } else { if self.proof.next().is_some() { self.state = ReadProofState::Finished; - return Some(Err(VerifyError::ExtraneousNode)) + return Some(Err(Error::ExtraneousNode)) } } self.state = ReadProofState::Finished; @@ -510,7 +580,7 @@ impl Stack { proof: &mut impl Iterator, expected_root: &Option>, mut slice_query: Option<&mut NibbleSlice>, - ) -> Result, CError>> { + ) -> Result, CError>> { let check_hash = expected_root.is_some(); let items_len = self.items.len(); let child_handle = if let Some(node) = self.items.last_mut() { @@ -545,7 +615,7 @@ impl Stack { // ommitted hash let Some(mut encoded_node) = proof.next() else { // halt happens with a hash, this is not. - return Err(VerifyError::IncompleteProof) + return Err(Error::IncompleteProof) }; if self.is_compact && encoded_node.borrow().len() > 0 && @@ -559,7 +629,7 @@ impl Stack { } let node = match OwnedNode::new::(encoded_node) { Ok(node) => node, - Err(e) => return Err(VerifyError::DecodeError(e)), + Err(e) => return Err(Error::DecodeError(e)), }; (ItemStackNode::Node(node), self.is_compact).try_into()? } else { @@ -567,7 +637,7 @@ impl Stack { ( ItemStackNode::Inline(match OwnedNode::new::(data.to_vec()) { Ok(node) => node, - Err(e) => return Err(VerifyError::DecodeError(e)), + Err(e) => return Err(Error::DecodeError(e)), }), self.is_compact, ) @@ -580,7 +650,7 @@ impl Stack { if self.is_compact && items_len > self.start_items { let mut error_hash = TrieHash::::default(); error_hash.as_mut().copy_from_slice(hash); - return Err(VerifyError::ExtraneousHashReference(error_hash)) + return Err(Error::ExtraneousHashReference(error_hash)) } if self.is_compact && encoded_node.borrow().len() > 0 && @@ -594,7 +664,7 @@ impl Stack { } let node = match OwnedNode::new::(encoded_node) { Ok(node) => node, - Err(e) => return Err(VerifyError::DecodeError(e)), + Err(e) => return Err(Error::DecodeError(e)), }; if !self.is_compact && check_hash { verify_hash::(node.data(), hash)?; @@ -643,19 +713,19 @@ impl Stack { NodeHandle::Hash(hash) => { let Some(encoded_branch) = proof.next() else { // No halt on extension node (restart over a child index). - return Err(VerifyError::IncompleteProof) + return Err(Error::IncompleteProof) }; if self.is_compact { let mut error_hash = TrieHash::::default(); error_hash.as_mut().copy_from_slice(hash); - return Err(VerifyError::ExtraneousHashReference(error_hash)) + return Err(Error::ExtraneousHashReference(error_hash)) } if check_hash { verify_hash::(encoded_branch.borrow(), hash)?; } node = match OwnedNode::new::(encoded_branch) { Ok(node) => (ItemStackNode::Node(node), self.is_compact).try_into()?, - Err(e) => return Err(VerifyError::DecodeError(e)), + Err(e) => return Err(Error::DecodeError(e)), }; }, NodeHandle::Inline(data) => { @@ -665,24 +735,24 @@ impl Stack { // ommitted hash let Some(encoded_node) = proof.next() else { // halt happens with a hash, this is not. - return Err(VerifyError::IncompleteProof); + return Err(Error::IncompleteProof); }; node = match OwnedNode::new::(encoded_node) { Ok(node) => (ItemStackNode::Node(node), self.is_compact).into(), - Err(e) => return Err(VerifyError::DecodeError(e)), + Err(e) => return Err(Error::DecodeError(e)), }; */ } else { node = match OwnedNode::new::(data.to_vec()) { Ok(node) => (ItemStackNode::Inline(node), self.is_compact).try_into()?, - Err(e) => return Err(VerifyError::DecodeError(e)), + Err(e) => return Err(Error::DecodeError(e)), }; } }, } let NodePlan::Branch { .. } = node.node_plan() else { - return Err(VerifyError::IncompleteProof) // TODO make error type?? + return Err(Error::IncompleteProof) // TODO make error type?? }; } node.depth = self.prefix.len(); @@ -699,7 +769,7 @@ impl Stack { proof: &mut impl Iterator, check_hash: bool, hash_only: bool, - ) -> Result<(Option>, Option>), VerifyError, CError>> { + ) -> Result<(Option>, Option>), Error, CError>> { if let Some(node) = self.items.last() { let node_data = node.data(); @@ -716,11 +786,11 @@ impl Stack { assert!(self.is_compact); self.expect_value = false; if hash_only { - return Err(VerifyError::ExtraneousValue(Default::default())) + return Err(Error::ExtraneousValue(Default::default())) } let Some(value) = proof.next() else { - return Err(VerifyError::IncompleteProof) + return Err(Error::IncompleteProof) }; if check_hash { let hash = L::Hash::hash(value.borrow()); @@ -737,21 +807,19 @@ impl Stack { Value::Node(hash) => { if self.expect_value { if hash_only { - return Err(VerifyError::ExtraneousValue(Default::default())) + return Err(Error::ExtraneousValue(Default::default())) } self.expect_value = false; let mut error_hash = TrieHash::::default(); error_hash.as_mut().copy_from_slice(hash); - return Err(VerifyError::ExtraneousHashReference(error_hash)) + return Err(Error::ExtraneousHashReference(error_hash)) } if hash_only { let mut result_hash = TrieHash::::default(); result_hash.as_mut().copy_from_slice(hash); return Ok((None, Some(result_hash))) } - let Some(value) = proof.next() else { - return Err(VerifyError::IncompleteProof) - }; + let Some(value) = proof.next() else { return Err(Error::IncompleteProof) }; if check_hash { verify_hash::(value.borrow(), hash)?; } @@ -760,7 +828,7 @@ impl Stack { } } } else { - return Err(VerifyError::IncompleteProof) + return Err(Error::IncompleteProof) } Ok((None, None)) @@ -769,7 +837,7 @@ impl Stack { fn pop( &mut self, expected_root: &Option>, - ) -> Result, CError>> { + ) -> Result, CError>> { if self.iter_prefix.as_ref().map(|p| p.start == self.items.len()).unwrap_or(false) { return Ok(false) } @@ -798,7 +866,7 @@ impl Stack { { verify_hash::(&encoded_node, expected.as_ref())?; } else { - return Err(VerifyError::RootMismatch(Default::default())) + return Err(Error::RootMismatch(Default::default())) } } else { let expected = expected_root.as_ref().expect("checked above"); @@ -829,11 +897,11 @@ impl Stack { }, _ => // only non inline are stacked - return Err(VerifyError::RootMismatch(Default::default())), + return Err(Error::RootMismatch(Default::default())), } } else { if &Some(hash) != expected_root { - return Err(VerifyError::RootMismatch(hash)) + return Err(Error::RootMismatch(hash)) } } } @@ -851,7 +919,7 @@ impl Stack { target: Option, expected_root: &Option>, check_only: bool, - ) -> Result, CError>> { + ) -> Result, CError>> { if self.is_compact && expected_root.is_some() { // TODO pop with check only, here unefficient implementation where we just restore From 6d0a2795f855040dd990342efbb021c9a101fbba Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 11:41:19 +0200 Subject: [PATCH 141/154] error --- trie-db/src/query_plan/record.rs | 21 +++++++++++++++------ trie-db/src/trie_codec.rs | 14 +++++++------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index a867a0b6..bbd32ad5 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -190,7 +190,7 @@ impl HaltedStateRecord { self.stack.recorder.output() } - fn finalize(&mut self) { + fn finalize(&mut self) -> Result<(), VerifyError, CError>> { let stack = &mut self.stack; let items = &stack.items; match &mut stack.recorder.output { @@ -209,7 +209,15 @@ impl HaltedStateRecord { item.accessed_value_node, item.accessed_children_node, ) - .expect("TODO error handling, can it actually fail?"); + .map_err(|e| { + if let Some(data) = e { + // invalid node handle conversion for data + VerifyError::InvalidNodeHandle(data) + } else { + // unexpected node in proof + VerifyError::ExtraneousNode + } + })?; break } } @@ -223,6 +231,7 @@ impl HaltedStateRecord { // all written on access }, } + Ok(()) } /// Callback on node before a node in the stack. @@ -335,7 +344,7 @@ impl HaltedStateRecord { (self.stack.prefix.len() % nibble_ops::NIBBLE_PER_BYTE) != 0, )); self.stack.prefix.pop(); - self.finalize(); + self.finalize()?; self.stack.halt = false; self.from = dest_from; self.currently_query_item = prev_query.map(|q| q.to_owned()); @@ -430,7 +439,7 @@ pub fn record_query_plan<'a, L: TrieLayout, I: Iterator Ordering::Less => break true, Ordering::Greater => if !from.pop() { - from.finalize(); + from.finalize()?; return Ok(()) }, } @@ -505,7 +514,7 @@ pub fn record_query_plan<'a, L: TrieLayout, I: Iterator )); from.stack.prefix.pop(); from.currently_query_item = Some(query.to_owned()); - from.finalize(); + from.finalize()?; return Ok(()) }, } @@ -530,7 +539,7 @@ pub fn record_query_plan<'a, L: TrieLayout, I: Iterator } } */ - from.finalize(); + from.finalize()?; Ok(()) } diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index fb80e34f..69595600 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -102,6 +102,9 @@ impl EncoderStackEntry { /// Generates the encoding of the subtrie rooted at this entry. fn encode_node(&mut self) -> Result, C::HashOut, C::Error> { encode_node_internal::(&*self.node, self.omit_value, self.omit_children.as_slice()) + .map_err(|err| { + Box::new(TrieError::InvalidHash(C::HashOut::default(), err.unwrap_or_default())) + }) } } @@ -114,17 +117,14 @@ fn branch_children( node_data: &[u8], child_handles: &[Option; NIBBLE_LENGTH], omit_children: impl BitmapAccess, -) -> Result<[Option>; NIBBLE_LENGTH], C::HashOut, C::Error> { +) -> crate::rstd::result::Result<[Option>; NIBBLE_LENGTH], Vec> { let empty_child = ChildReference::Inline(C::HashOut::default(), 0); let mut children = [None; NIBBLE_LENGTH]; for i in 0..NIBBLE_LENGTH { children[i] = if omit_children.at(i) { Some(empty_child) } else if let Some(child_plan) = &child_handles[i] { - let child_ref = child_plan - .build(node_data) - .try_into() - .map_err(|hash| Box::new(TrieError::InvalidHash(C::HashOut::default(), hash)))?; + let child_ref = child_plan.build(node_data).try_into()?; Some(child_ref) } else { None @@ -137,7 +137,7 @@ pub(crate) fn encode_node_internal( node: &OwnedNode, omit_value: bool, omit_children: impl BitmapAccess, -) -> Result, C::HashOut, C::Error> { +) -> crate::rstd::result::Result, Option>> { let node_data = node.data(); let node_plan = node.node_plan(); let mut encoded = match node_plan { @@ -185,7 +185,7 @@ pub(crate) fn encode_node_internal( if let Some(header) = C::ESCAPE_HEADER { encoded.insert(0, header); } else { - return Err(Box::new(TrieError::InvalidStateRoot(Default::default()))) + return Err(None) } } Ok(encoded) From eedbf84b4015e8cf8ebf44b54a3f9012d756c78a Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 11:43:20 +0200 Subject: [PATCH 142/154] error not verify error --- trie-db/src/query_plan/mod.rs | 87 ++++++++++++++++++++++++++++---- trie-db/src/query_plan/record.rs | 25 +++++---- trie-db/src/query_plan/verify.rs | 71 -------------------------- 3 files changed, 90 insertions(+), 93 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index 821c8b07..85ed31d2 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -40,11 +40,82 @@ use crate::{ }; use hash_db::Hasher; pub use record::{record_query_plan, HaltedStateRecord, Recorder}; -pub use verify::{verify_query_plan_iter, Error as VerifyError, HaltedStateCheck}; +pub use verify::{verify_query_plan_iter, HaltedStateCheck}; mod record; mod verify; +/// Errors that may occur during proof verification. Most of the errors types simply indicate that +/// the proof is invalid with respect to the statement being verified, and the exact error type can +/// be used for debugging. +#[derive(PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum Error { + /// The statement being verified contains multiple key-value pairs with the same key. The + /// parameter is the duplicated key. + DuplicateKey(Vec), + /// The statement being verified contains key not ordered properly. + UnorderedKey(Vec), + /// The proof contains at least one extraneous node. + ExtraneousNode, + /// The proof contains at least one extraneous value which should have been omitted from the + /// proof. + ExtraneousValue(Vec), + /// The proof contains at least one extraneous hash reference the should have been omitted. + ExtraneousHashReference(HO), + /// The proof contains an invalid child reference that exceeds the hash length. + /// TODO extension only? + InvalidChildReference(Vec), + /// The proof is missing trie nodes required to verify. + IncompleteProof, + /// The root hash computed from the proof is incorrect. + RootMismatch(HO), + /// The hash computed from a node is incorrect. + HashMismatch(HO), + /// One of the proof nodes could not be decoded. + DecodeError(CE), + /// Node does not match existing handle. + /// This should not happen. + InvalidNodeHandle(Vec), +} + +#[cfg(feature = "std")] +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + Error::DuplicateKey(key) => + write!(f, "Duplicate key in input statement: key={:?}", key), + Error::UnorderedKey(key) => + write!(f, "Unordered key in input statement: key={:?}", key), + Error::ExtraneousNode => write!(f, "Extraneous node found in proof"), + Error::ExtraneousValue(key) => + write!(f, "Extraneous value found in proof should have been omitted: key={:?}", key), + Error::ExtraneousHashReference(hash) => write!( + f, + "Extraneous hash reference found in proof should have been omitted: hash={:?}", + hash + ), + Error::InvalidChildReference(data) => + write!(f, "Invalid child reference exceeds hash length: {:?}", data), + Error::IncompleteProof => write!(f, "Proof is incomplete -- expected more nodes"), + Error::RootMismatch(hash) => write!(f, "Computed incorrect root {:?} from proof", hash), + Error::HashMismatch(hash) => write!(f, "Computed incorrect hash {:?} from node", hash), + Error::DecodeError(err) => write!(f, "Unable to decode proof node: {}", err), + Error::InvalidNodeHandle(node) => write!(f, "Invalid node handle: {:?}", node), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Error::DecodeError(err) => Some(err), + _ => None, + } + } +} + /// Item to query, in memory. #[derive(Default, Clone, Debug)] pub struct InMemQueryPlanItem { @@ -338,7 +409,7 @@ enum ItemStackNode { } impl TryFrom<(ItemStackNode, bool)> for StackedNodeCheck { - type Error = VerifyError, CError>; + type Error = Error, CError>; fn try_from( (node, is_compact): (ItemStackNode, bool), @@ -356,10 +427,8 @@ impl TryFrom<(ItemStackNode, bool)> for Stacked match child.build(node_data) { NodeHandle::Inline(data) if data.is_empty() => (), child => { - let child_ref = child - .try_into() - .map_err(|d| VerifyError::InvalidNodeHandle(d))?; - + let child_ref = + child.try_into().map_err(|d| Error::InvalidNodeHandle(d))?; result[0] = Some(child_ref); }, } @@ -377,7 +446,7 @@ impl TryFrom<(ItemStackNode, bool)> for Stacked Some(child) => { let child_ref = child .try_into() - .map_err(|d| VerifyError::InvalidNodeHandle(d))?; + .map_err(|d| Error::InvalidNodeHandle(d))?; result[i] = Some(child_ref); }, @@ -420,12 +489,12 @@ impl StackedNodeCheck { fn verify_hash( data: &[u8], expected: &[u8], -) -> Result<(), VerifyError, CError>> { +) -> Result<(), Error, CError>> { let checked_hash = L::Hash::hash(data); if checked_hash.as_ref() != expected { let mut error_hash = TrieHash::::default(); error_hash.as_mut().copy_from_slice(expected); - Err(VerifyError::HashMismatch(error_hash)) + Err(Error::HashMismatch(error_hash)) } else { Ok(()) } diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index bbd32ad5..2afe5691 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -190,7 +190,7 @@ impl HaltedStateRecord { self.stack.recorder.output() } - fn finalize(&mut self) -> Result<(), VerifyError, CError>> { + fn finalize(&mut self) -> Result<(), Error, CError>> { let stack = &mut self.stack; let items = &stack.items; match &mut stack.recorder.output { @@ -212,10 +212,10 @@ impl HaltedStateRecord { .map_err(|e| { if let Some(data) = e { // invalid node handle conversion for data - VerifyError::InvalidNodeHandle(data) + Error::InvalidNodeHandle(data) } else { // unexpected node in proof - VerifyError::ExtraneousNode + Error::ExtraneousNode } })?; break @@ -298,7 +298,7 @@ impl HaltedStateRecord { db: Option<&TrieDB>, hash_only: bool, first_iter: bool, - ) -> Result, CError>> { + ) -> Result, CError>> { let dummy_parent_hash = TrieHash::::default(); if first_iter { self.stack.enter_prefix_iter(hash_only); @@ -380,7 +380,7 @@ pub fn record_query_plan<'a, L: TrieLayout, I: Iterator db: &TrieDB, query_plan: &mut QueryPlan<'a, I>, from: &mut HaltedStateRecord, -) -> Result<(), VerifyError, CError>> { +) -> Result<(), Error, CError>> { let dummy_parent_hash = TrieHash::::default(); let mut stateless = false; let mut statefull = None; @@ -431,7 +431,7 @@ pub fn record_query_plan<'a, L: TrieLayout, I: Iterator let (ordered, common_nibbles) = prev_query.as_ref().map(|p| p.before(&query)).unwrap_or((true, 0)); if !ordered { - return Err(VerifyError::UnorderedKey(query.key.to_vec())) + return Err(Error::UnorderedKey(query.key.to_vec())) } let skip_query = loop { match from.stack.prefix.len().cmp(&common_nibbles) { @@ -550,7 +550,7 @@ impl RecordStack { db: Option<&TrieDB>, parent_hash: TrieHash, mut slice_query: Option<&mut NibbleSlice>, - ) -> Result, CError>> { + ) -> Result, CError>> { let inline_only = db.is_none(); let mut is_inline = false; let prefix = &mut self.prefix; @@ -624,14 +624,13 @@ impl RecordStack { } let child_node = if let Some(db) = db { db.get_raw_or_lookup_with_cache(parent_hash, child_handle, prefix.as_prefix(), false) - .map_err(|_| VerifyError::IncompleteProof)? + .map_err(|_| Error::IncompleteProof)? .0 // actually incomplete db: TODO consider switching error } else { let NodeHandle::Inline(node_data) = child_handle else { unreachable!("call on non inline node when db is None"); }; - OwnedNode::new::(node_data.to_vec()) - .map_err(|_| VerifyError::IncompleteProof)? + OwnedNode::new::(node_data.to_vec()).map_err(|_| Error::IncompleteProof)? }; let node_data = child_node.data(); @@ -688,7 +687,7 @@ impl RecordStack { if stack_extension { let sbranch = self.try_stack_child(0, db, parent_hash, slice_query)?; let TryStackChildResult::StackedFull = sbranch else { - return Err(VerifyError::InvalidChildReference( + return Err(Error::InvalidChildReference( b"branch in db should follow extension".to_vec(), )) }; @@ -701,7 +700,7 @@ impl RecordStack { &mut self, db: Option<&TrieDB>, hash_only: bool, - ) -> Result, CError>> { + ) -> Result, CError>> { let Some(item) = self.items.last_mut() else { return Ok(false) }; let node_data = item.node.data(); @@ -725,7 +724,7 @@ impl RecordStack { let Some(value) = db.expect("non inline").db().get(&hash, self.prefix.as_prefix()) else { - return Err(VerifyError::IncompleteProof) + return Err(Error::IncompleteProof) }; self.halt |= self.recorder.record_value_node(value, self.prefix.len()); }, diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 4ba21bda..1635cca8 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -24,77 +24,6 @@ use crate::{ }; pub use record::{record_query_plan, HaltedStateRecord, Recorder}; -/// Errors that may occur during proof verification. Most of the errors types simply indicate that -/// the proof is invalid with respect to the statement being verified, and the exact error type can -/// be used for debugging. -#[derive(PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] -pub enum Error { - /// The statement being verified contains multiple key-value pairs with the same key. The - /// parameter is the duplicated key. - DuplicateKey(Vec), - /// The statement being verified contains key not ordered properly. - UnorderedKey(Vec), - /// The proof contains at least one extraneous node. - ExtraneousNode, - /// The proof contains at least one extraneous value which should have been omitted from the - /// proof. - ExtraneousValue(Vec), - /// The proof contains at least one extraneous hash reference the should have been omitted. - ExtraneousHashReference(HO), - /// The proof contains an invalid child reference that exceeds the hash length. - /// TODO extension only? - InvalidChildReference(Vec), - /// The proof is missing trie nodes required to verify. - IncompleteProof, - /// The root hash computed from the proof is incorrect. - RootMismatch(HO), - /// The hash computed from a node is incorrect. - HashMismatch(HO), - /// One of the proof nodes could not be decoded. - DecodeError(CE), - /// Node does not match existing handle. - /// This should not happen. - InvalidNodeHandle(Vec), -} - -#[cfg(feature = "std")] -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - match self { - Error::DuplicateKey(key) => - write!(f, "Duplicate key in input statement: key={:?}", key), - Error::UnorderedKey(key) => - write!(f, "Unordered key in input statement: key={:?}", key), - Error::ExtraneousNode => write!(f, "Extraneous node found in proof"), - Error::ExtraneousValue(key) => - write!(f, "Extraneous value found in proof should have been omitted: key={:?}", key), - Error::ExtraneousHashReference(hash) => write!( - f, - "Extraneous hash reference found in proof should have been omitted: hash={:?}", - hash - ), - Error::InvalidChildReference(data) => - write!(f, "Invalid child reference exceeds hash length: {:?}", data), - Error::IncompleteProof => write!(f, "Proof is incomplete -- expected more nodes"), - Error::RootMismatch(hash) => write!(f, "Computed incorrect root {:?} from proof", hash), - Error::HashMismatch(hash) => write!(f, "Computed incorrect hash {:?} from node", hash), - Error::DecodeError(err) => write!(f, "Unable to decode proof node: {}", err), - Error::InvalidNodeHandle(node) => write!(f, "Invalid node handle: {:?}", node), - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Error::DecodeError(err) => Some(err), - _ => None, - } - } -} - /// Result of verify iterator. type VerifyIteratorResult<'a, L, C, D> = Result, Error, CError>>; From b139440304d821554792b677adf8a5d830b68399 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 11:49:43 +0200 Subject: [PATCH 143/154] more errro --- trie-db/src/query_plan/record.rs | 38 +++++++++++++++++--------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 2afe5691..d785849f 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -237,18 +237,11 @@ impl HaltedStateRecord { /// Callback on node before a node in the stack. /// `at` is the the position in the stack (in some case we keep /// the stack and will not pop the node). - fn record_popped_node(&mut self, at: usize) { + fn record_popped_node(&mut self, at: usize) -> Result<(), Error, CError>> { let item = self.stack.items.get(at).expect("bounded check call"); if !self.stack.recorder.check_start_at(item.depth) { - return + return Ok(()) } - /* TODO rem Incorrect, if first child is inline, we would push more: push - * should only be flushed on first pop flush here. - if let RecorderStateInner::Content { .. } = &self.stack.recorder.output { - // if no value accessed, then we can have push then stack pop. - res |= self.stack.recorder.flush_compact_content_pushes(item.depth); - } - */ match &mut self.stack.recorder.output { RecorderStateInner::Stream(_) => (), @@ -260,36 +253,45 @@ impl HaltedStateRecord { item.accessed_value_node, item.accessed_children_node, ) - .expect("TODO error handling, can it actually fail?"); + .map_err(|e| { + if let Some(data) = e { + // invalid node handle conversion for data + Error::InvalidNodeHandle(data) + } else { + // unexpected node in proof + Error::ExtraneousNode + } + })?; } // else when restarting record, this is not to be recorded }, } + Ok(()) } - fn pop(&mut self) -> bool { + fn pop(&mut self) -> Result, CError>> { if self .stack .iter_prefix .map(|(l, _)| l == self.stack.items.len()) .unwrap_or(false) { - return false + return Ok(false) } let at = self.stack.items.len(); if at > 0 { - self.record_popped_node(at - 1); + self.record_popped_node(at - 1)?; } - if let Some(item) = self.stack.items.pop() { + Ok(if let Some(item) = self.stack.items.pop() { let depth = self.stack.items.last().map(|i| i.depth).unwrap_or(0); self.stack.prefix.drop_lasts(self.stack.prefix.len() - depth); if depth == item.depth { // Two consecutive identical depth is an extension - self.pop(); + self.pop()?; } true } else { false - } + }) } fn iter_prefix( @@ -354,7 +356,7 @@ impl HaltedStateRecord { } // pop - if !self.pop() { + if !self.pop()? { break } } @@ -438,7 +440,7 @@ pub fn record_query_plan<'a, L: TrieLayout, I: Iterator Ordering::Equal => break false, Ordering::Less => break true, Ordering::Greater => - if !from.pop() { + if !from.pop()? { from.finalize()?; return Ok(()) }, From 2a5e4646f2aa0596daa1104267219f9cfc7c8168 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 14:54:51 +0200 Subject: [PATCH 144/154] clean --- trie-db/src/query_plan/record.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index d785849f..34cc82ae 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -484,7 +484,6 @@ pub fn record_query_plan<'a, L: TrieLayout, I: Iterator let child_index = if from.stack.items.is_empty() { 0 } else { slice_query.at(0) }; from.stack.items.last_mut().map(|i| { - // TODO only needed for content but could be better to be always aligned i.next_descended_child = child_index + 1; }); match from.stack.try_stack_child( @@ -529,18 +528,6 @@ pub fn record_query_plan<'a, L: TrieLayout, I: Iterator from_query_ref = None; prev_query = Some(query); } - /* - // TODO loop redundant with finalize?? - loop { - if query_plan.kind.record_inline() { - from.try_stack_content_child()?; - } - - if !from.pop() { - break - } - } - */ from.finalize()?; Ok(()) } From 9cb92a10812049c19f89f9bf086bc641ef332624 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 15:00:26 +0200 Subject: [PATCH 145/154] no more record inline node --- trie-db/src/query_plan/record.rs | 52 +++++++++----------------------- 1 file changed, 15 insertions(+), 37 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 34cc82ae..8326706f 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -297,7 +297,7 @@ impl HaltedStateRecord { fn iter_prefix( &mut self, prev_query: Option<&QueryPlanItem>, - db: Option<&TrieDB>, + db: &TrieDB, hash_only: bool, first_iter: bool, ) -> Result, CError>> { @@ -455,7 +455,7 @@ pub fn record_query_plan<'a, L: TrieLayout, I: Iterator }; if let Some((_, hash_only)) = from.stack.iter_prefix.clone() { // statefull halted during iteration. - let halt = from.iter_prefix(Some(&query), Some(db), hash_only, false)?; + let halt = from.iter_prefix(Some(&query), db, hash_only, false)?; if halt { return Ok(()) } @@ -470,8 +470,7 @@ pub fn record_query_plan<'a, L: TrieLayout, I: Iterator if !from.stack.items.is_empty() { if slice_query.is_empty() { if query.as_prefix { - let halt = - from.iter_prefix(Some(&query), Some(db), query.hash_only, true)?; + let halt = from.iter_prefix(Some(&query), db, query.hash_only, true)?; if halt { return Ok(()) } @@ -488,7 +487,7 @@ pub fn record_query_plan<'a, L: TrieLayout, I: Iterator }); match from.stack.try_stack_child( child_index, - Some(db), + db, dummy_parent_hash, Some(&mut slice_query), )? { @@ -498,8 +497,7 @@ pub fn record_query_plan<'a, L: TrieLayout, I: Iterator TryStackChildResult::StackedAfter => break false, TryStackChildResult::StackedInto => { if query.as_prefix { - let halt = - from.iter_prefix(Some(&query), Some(db), query.hash_only, true)?; + let halt = from.iter_prefix(Some(&query), db, query.hash_only, true)?; if halt { return Ok(()) } @@ -523,7 +521,7 @@ pub fn record_query_plan<'a, L: TrieLayout, I: Iterator if touched { // try access value - from.stack.access_value(Some(db), query.hash_only)?; + from.stack.access_value(db, query.hash_only)?; } from_query_ref = None; prev_query = Some(query); @@ -536,11 +534,10 @@ impl RecordStack { fn try_stack_child<'a>( &mut self, child_index: u8, - db: Option<&TrieDB>, + db: &TrieDB, parent_hash: TrieHash, mut slice_query: Option<&mut NibbleSlice>, ) -> Result, CError>> { - let inline_only = db.is_none(); let mut is_inline = false; let prefix = &mut self.prefix; let mut stack_extension = false; @@ -564,10 +561,6 @@ impl RecordStack { if let &NodeHandle::Hash(_) = &child_handle { item.accessed_children_node.set(child_index as usize, true); } - if inline_only { - // mark all accesses ( - item.accessed_children_node.set(child_index as usize, true); - } child_handle } else { return Ok(TryStackChildResult::NotStacked) @@ -581,7 +574,7 @@ impl RecordStack { }, } } else { - NodeHandle::Hash(db.expect("Inline call on non empty stack only").root().as_ref()) + NodeHandle::Hash(db.root().as_ref()) }; match &child_handle { NodeHandle::Inline(_) => { @@ -590,14 +583,7 @@ impl RecordStack { is_inline = true; }, NodeHandle::Hash(_) => { - if inline_only { - /* TODO this should write on pop not on stack... - if self.recorder.touched_child_hash(hash, child_index) { - self.halt = true; - } - */ - return Ok(TryStackChildResult::NotStackedBranch) - } else if self.halt && from_branch.is_some() { + if self.halt && from_branch.is_some() { // halt condition return Ok(TryStackChildResult::Halted) } @@ -611,16 +597,10 @@ impl RecordStack { slice_query.as_mut().map(|s| s.advance(1)); prefix.push(child_index); } - let child_node = if let Some(db) = db { - db.get_raw_or_lookup_with_cache(parent_hash, child_handle, prefix.as_prefix(), false) - .map_err(|_| Error::IncompleteProof)? - .0 // actually incomplete db: TODO consider switching error - } else { - let NodeHandle::Inline(node_data) = child_handle else { - unreachable!("call on non inline node when db is None"); - }; - OwnedNode::new::(node_data.to_vec()).map_err(|_| Error::IncompleteProof)? - }; + let child_node = db + .get_raw_or_lookup_with_cache(parent_hash, child_handle, prefix.as_prefix(), false) + .map_err(|_| Error::IncompleteProof)? + .0; // actually incomplete db: TODO consider switching error let node_data = child_node.data(); //println!("r: {:?}", &node_data); @@ -687,7 +667,7 @@ impl RecordStack { fn access_value<'a>( &mut self, - db: Option<&TrieDB>, + db: &TrieDB, hash_only: bool, ) -> Result, CError>> { let Some(item) = self.items.last_mut() else { return Ok(false) }; @@ -710,9 +690,7 @@ impl RecordStack { item.accessed_value_node = true; let mut hash = TrieHash::::default(); hash.as_mut().copy_from_slice(hash_slice); - let Some(value) = - db.expect("non inline").db().get(&hash, self.prefix.as_prefix()) - else { + let Some(value) = db.db().get(&hash, self.prefix.as_prefix()) else { return Err(Error::IncompleteProof) }; self.halt |= self.recorder.record_value_node(value, self.prefix.len()); From df72ed41cfeb3c7fdff794edfdb2efdbfb306fc4 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 15:22:58 +0200 Subject: [PATCH 146/154] kind rather than is_compact --- trie-db/src/query_plan/mod.rs | 18 ++++++-- trie-db/src/query_plan/record.rs | 16 ++++--- trie-db/src/query_plan/verify.rs | 73 ++++++++++++++++++-------------- 3 files changed, 65 insertions(+), 42 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index 85ed31d2..d4433894 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -68,6 +68,8 @@ pub enum Error { InvalidChildReference(Vec), /// The proof is missing trie nodes required to verify. IncompleteProof, + /// Item missing in backend when recording. + IncompleteDB(HO), /// The root hash computed from the proof is incorrect. RootMismatch(HO), /// The hash computed from a node is incorrect. @@ -98,6 +100,7 @@ impl std::fmt::Display for Error write!(f, "Invalid child reference exceeds hash length: {:?}", data), Error::IncompleteProof => write!(f, "Proof is incomplete -- expected more nodes"), + Error::IncompleteDB(hash) => write!(f, "Missing node in db: {:?}", hash), Error::RootMismatch(hash) => write!(f, "Computed incorrect root {:?} from proof", hash), Error::HashMismatch(hash) => write!(f, "Computed incorrect hash {:?} from node", hash), Error::DecodeError(err) => write!(f, "Unable to decode proof node: {}", err), @@ -227,6 +230,13 @@ pub enum ProofKind { CompactNodes, } +impl ProofKind { + /// Check if compact variant of proof. + pub fn is_compact(self) -> bool { + matches!(self, ProofKind::CompactNodes) + } +} + #[derive(Default, Clone, Copy)] struct Bitmap(u16); @@ -408,13 +418,15 @@ enum ItemStackNode { Node(OwnedNode), } -impl TryFrom<(ItemStackNode, bool)> for StackedNodeCheck { +impl TryFrom<(ItemStackNode, ProofKind)> + for StackedNodeCheck +{ type Error = Error, CError>; fn try_from( - (node, is_compact): (ItemStackNode, bool), + (node, kind): (ItemStackNode, ProofKind), ) -> crate::rstd::result::Result { - let children = if !is_compact { + let children = if !kind.is_compact() { Vec::new() } else { match &node { diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 8326706f..eb0f5c19 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -375,9 +375,6 @@ struct RecordStack { } /// Run query plan on a full db and record it. -/// -/// TODO output and restart are mutually exclusive. -> enum -/// or remove output from halted state. pub fn record_query_plan<'a, L: TrieLayout, I: Iterator>>( db: &TrieDB, query_plan: &mut QueryPlan<'a, I>, @@ -578,8 +575,6 @@ impl RecordStack { }; match &child_handle { NodeHandle::Inline(_) => { - // TODO consider not going into inline for all proof but content. - // Returning NotStacked here sounds safe, then the is_inline field is not needed. is_inline = true; }, NodeHandle::Hash(_) => { @@ -599,8 +594,15 @@ impl RecordStack { } let child_node = db .get_raw_or_lookup_with_cache(parent_hash, child_handle, prefix.as_prefix(), false) - .map_err(|_| Error::IncompleteProof)? - .0; // actually incomplete db: TODO consider switching error + .map_err(|_| { + let mut hash = TrieHash::::default(); + if let NodeHandle::Hash(h) = &child_handle { + let bound = crate::rstd::cmp::min(h.as_ref().len(), h.len()); + hash.as_mut()[..bound].copy_from_slice(&h[..bound]); + } + Error::IncompleteDB(hash) + })? + .0; let node_data = child_node.data(); //println!("r: {:?}", &node_data); diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 1635cca8..d3ce5ab2 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -40,7 +40,7 @@ where // to avoid unsafe code when halting. query_plan: Option>, proof: P, - is_compact: bool, + kind: ProofKind, expected_root: Option>, current: Option>, current_offset: usize, @@ -57,20 +57,35 @@ struct Stack { // limit and wether we return value and if hash only iteration. iter_prefix: Option, start_items: usize, - is_compact: bool, + kind: ProofKind, expect_value: bool, accessed_root: bool, _ph: PhantomData, } +impl From for Stack { + fn from(kind: ProofKind) -> Self { + Self { + items: Default::default(), + start_items: 0, + prefix: Default::default(), + kind, + expect_value: false, + iter_prefix: None, + accessed_root: false, + _ph: PhantomData, + } + } +} + impl Clone for Stack { fn clone(&self) -> Self { - Stack { + Self { items: self.items.clone(), prefix: self.prefix.clone(), start_items: self.start_items.clone(), iter_prefix: self.iter_prefix.clone(), - is_compact: self.is_compact, + kind: self.kind, expect_value: self.expect_value, accessed_root: self.accessed_root, _ph: PhantomData, @@ -97,7 +112,7 @@ where Ok(ReadProofIterator { query_plan: Some(query_plan), proof, - is_compact: stack.is_compact, + kind: stack.kind, expected_root, current, state, @@ -117,7 +132,8 @@ where D: SplitFirst, { fn halt(&mut self) -> VerifyIteratorResult<'a, L, C, D> { - if self.is_compact { + // TODO also non compact to check hash of node in parent. + if self.kind.is_compact() { let r = self.stack.pop_until(None, &self.expected_root, true); if let Err(e) = r { self.state = ReadProofState::Finished; @@ -135,7 +151,7 @@ where items: Default::default(), start_items: 0, prefix: Default::default(), - is_compact: self.is_compact, + kind: self.kind, expect_value: false, iter_prefix: None, accessed_root: false, @@ -434,7 +450,7 @@ where } debug_assert!(self.state == ReadProofState::PlanConsumed); - if self.is_compact { + if self.kind.is_compact() { let r = self.stack.pop_until(None, &self.expected_root, false); if let Err(e) = r { self.state = ReadProofState::Finished; @@ -477,18 +493,12 @@ pub struct HaltedStateCheck<'a, L: TrieLayout, C, D: SplitFirst> { impl<'a, L: TrieLayout, C, D: SplitFirst> From> for HaltedStateCheck<'a, L, C, D> { fn from(query_plan: QueryPlan<'a, C>) -> Self { - // TODO a method in kind - let is_compact = match query_plan.kind { - ProofKind::FullNodes => false, - ProofKind::CompactNodes => true, - }; - HaltedStateCheck { stack: Stack { items: Default::default(), start_items: 0, prefix: Default::default(), - is_compact, + kind: query_plan.kind, expect_value: false, iter_prefix: None, accessed_root: false, @@ -532,7 +542,7 @@ impl Stack { if self.accessed_root { return Ok(TryStackChildResult::NotStacked) } - if self.is_compact { + if self.kind.is_compact() { NodeHandle::Inline(&[]) } else { NodeHandle::Hash(expected_root.as_ref().map(AsRef::as_ref).unwrap_or(&[])) @@ -540,13 +550,13 @@ impl Stack { }; let mut node: StackedNodeCheck<_, _> = match child_handle { NodeHandle::Inline(data) => - if self.is_compact && data.len() == 0 { + if self.kind.is_compact() && data.len() == 0 { // ommitted hash let Some(mut encoded_node) = proof.next() else { // halt happens with a hash, this is not. return Err(Error::IncompleteProof) }; - if self.is_compact && + if self.kind.is_compact() && encoded_node.borrow().len() > 0 && Some(encoded_node.borrow()[0]) == ::ESCAPE_HEADER @@ -560,7 +570,7 @@ impl Stack { Ok(node) => node, Err(e) => return Err(Error::DecodeError(e)), }; - (ItemStackNode::Node(node), self.is_compact).try_into()? + (ItemStackNode::Node(node), self.kind).try_into()? } else { // try access in inline then return ( @@ -568,7 +578,7 @@ impl Stack { Ok(node) => node, Err(e) => return Err(Error::DecodeError(e)), }), - self.is_compact, + self.kind, ) .try_into()? }, @@ -576,12 +586,12 @@ impl Stack { let Some(mut encoded_node) = proof.next() else { return Ok(TryStackChildResult::Halted) }; - if self.is_compact && items_len > self.start_items { + if self.kind.is_compact() && items_len > self.start_items { let mut error_hash = TrieHash::::default(); error_hash.as_mut().copy_from_slice(hash); return Err(Error::ExtraneousHashReference(error_hash)) } - if self.is_compact && + if self.kind.is_compact() && encoded_node.borrow().len() > 0 && Some(encoded_node.borrow()[0]) == ::ESCAPE_HEADER @@ -595,10 +605,10 @@ impl Stack { Ok(node) => node, Err(e) => return Err(Error::DecodeError(e)), }; - if !self.is_compact && check_hash { + if !self.kind.is_compact() && check_hash { verify_hash::(node.data(), hash)?; } - (ItemStackNode::Node(node), self.is_compact).try_into()? + (ItemStackNode::Node(node), self.kind).try_into()? }, }; let node_data = node.data(); @@ -644,7 +654,7 @@ impl Stack { // No halt on extension node (restart over a child index). return Err(Error::IncompleteProof) }; - if self.is_compact { + if self.kind.is_compact() { let mut error_hash = TrieHash::::default(); error_hash.as_mut().copy_from_slice(hash); return Err(Error::ExtraneousHashReference(error_hash)) @@ -653,12 +663,12 @@ impl Stack { verify_hash::(encoded_branch.borrow(), hash)?; } node = match OwnedNode::new::(encoded_branch) { - Ok(node) => (ItemStackNode::Node(node), self.is_compact).try_into()?, + Ok(node) => (ItemStackNode::Node(node), self.kind).try_into()?, Err(e) => return Err(Error::DecodeError(e)), }; }, NodeHandle::Inline(data) => { - if self.is_compact && data.len() == 0 { + if self.kind.is_compact() && data.len() == 0 { unimplemented!("This requires to put extension in stack"); /* // ommitted hash @@ -673,8 +683,7 @@ impl Stack { */ } else { node = match OwnedNode::new::(data.to_vec()) { - Ok(node) => - (ItemStackNode::Inline(node), self.is_compact).try_into()?, + Ok(node) => (ItemStackNode::Inline(node), self.kind).try_into()?, Err(e) => return Err(Error::DecodeError(e)), }; } @@ -712,7 +721,7 @@ impl Stack { match value { Value::Inline(value) => if self.expect_value { - assert!(self.is_compact); + assert!(self.kind.is_compact()); self.expect_value = false; if hash_only { return Err(Error::ExtraneousValue(Default::default())) @@ -773,7 +782,7 @@ impl Stack { if let Some(last) = self.items.pop() { let depth = self.items.last().map(|i| i.depth).unwrap_or(0); self.prefix.drop_lasts(self.prefix.len() - depth); - if self.is_compact && expected_root.is_some() { + if self.kind.is_compact() && expected_root.is_some() { match last.node { ItemStackNode::Inline(_) => (), ItemStackNode::Node(node) => { @@ -849,7 +858,7 @@ impl Stack { expected_root: &Option>, check_only: bool, ) -> Result, CError>> { - if self.is_compact && expected_root.is_some() { + if self.kind.is_compact() && expected_root.is_some() { // TODO pop with check only, here unefficient implementation where we just restore let mut restore = None; From 290a56a52e37804d1d2b73178f4ee938ffcd3e7c Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 15:46:39 +0200 Subject: [PATCH 147/154] more --- trie-db/src/query_plan/mod.rs | 4 ++++ trie-db/src/query_plan/verify.rs | 30 +++++------------------------- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index d4433894..14b7bcd6 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -79,6 +79,8 @@ pub enum Error { /// Node does not match existing handle. /// This should not happen. InvalidNodeHandle(Vec), + /// Node type in proof inconsistent with node ordering. + UnexpectedNodeType, } #[cfg(feature = "std")] @@ -105,6 +107,8 @@ impl std::fmt::Display for Error write!(f, "Computed incorrect hash {:?} from node", hash), Error::DecodeError(err) => write!(f, "Unable to decode proof node: {}", err), Error::InvalidNodeHandle(node) => write!(f, "Invalid node handle: {:?}", node), + Error::UnexpectedNodeType => + write!(f, "Node type in proof inconsistent with node ordering."), } } } diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index d3ce5ab2..80b1f652 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -144,20 +144,7 @@ where let query_plan = crate::rstd::mem::replace(&mut self.query_plan, None); let query_plan = query_plan.expect("Init with state"); let current = crate::rstd::mem::take(&mut self.current); - let mut stack = crate::rstd::mem::replace( - // TODO impl default and use take - &mut self.stack, - Stack { - items: Default::default(), - start_items: 0, - prefix: Default::default(), - kind: self.kind, - expect_value: false, - iter_prefix: None, - accessed_root: false, - _ph: PhantomData, - }, - ); + let mut stack = crate::rstd::mem::replace(&mut self.stack, self.kind.into()); stack.start_items = stack.items.len(); Ok(ReadProofItem::Halted(Box::new(HaltedStateCheck { query_plan, @@ -370,8 +357,8 @@ where let to_check = self.current.as_ref().expect("Init above"); let to_check_len = to_check.key.len() * nibble_ops::NIBBLE_PER_BYTE; let mut to_check_slice = to_check_slice.as_mut().expect("Init above"); - let as_prefix = to_check.as_prefix; // TODO useless? - let hash_only = to_check.hash_only; // TODO useless? + let as_prefix = to_check.as_prefix; + let hash_only = to_check.hash_only; let mut at_value = false; match self.stack.prefix.len().cmp(&to_check_len) { Ordering::Equal => @@ -562,8 +549,6 @@ impl Stack { ::ESCAPE_HEADER { self.expect_value = true; - // no value to visit TODO set a boolean to ensure we got a hash and don - // t expect reanding a node value encoded_node.split_first(); } let node = match OwnedNode::new::(encoded_node) { @@ -597,8 +582,6 @@ impl Stack { ::ESCAPE_HEADER { self.expect_value = true; - // no value to visit TODO set a boolean to ensure we got a hash and don - // t expect reanding a node value encoded_node.split_first(); } let node = match OwnedNode::new::(encoded_node) { @@ -690,7 +673,7 @@ impl Stack { }, } let NodePlan::Branch { .. } = node.node_plan() else { - return Err(Error::IncompleteProof) // TODO make error type?? + return Err(Error::UnexpectedNodeType) }; } node.depth = self.prefix.len(); @@ -737,7 +720,7 @@ impl Stack { return Ok((Some(value.borrow().to_vec()), None)) } else { if hash_only { - let hash = L::Hash::hash(value.borrow()); + let hash = L::Hash::hash(value); return Ok((None, Some(hash))) } return Ok((Some(value.to_vec()), None)) @@ -859,8 +842,6 @@ impl Stack { check_only: bool, ) -> Result, CError>> { if self.kind.is_compact() && expected_root.is_some() { - // TODO pop with check only, here unefficient implementation where we just restore - let mut restore = None; if check_only { restore = Some(self.clone()); @@ -891,7 +872,6 @@ impl Stack { return Ok(false) } } - // let target = target.unwrap_or(0); loop { if let Some(last) = self.items.last() { if let Some(target) = target.as_ref() { From 8e51c258ac302bd29b2878d0178f8b5ce1380582 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 15:56:10 +0200 Subject: [PATCH 148/154] no more todos --- trie-db/src/query_plan/record.rs | 4 +--- trie-db/src/query_plan/verify.rs | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index eb0f5c19..79d6bd13 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -195,9 +195,7 @@ impl HaltedStateRecord { let items = &stack.items; match &mut stack.recorder.output { RecorderStateInner::Compact { output, proof, stacked_pos } => { - // TODO apply same as content : record popped node calls?? - let restarted_from = 0; - if stacked_pos.len() > restarted_from { + if stacked_pos.len() > 0 { // halted: complete up to 0 and write all nodes keeping stack. let mut items = items.iter().rev(); while let Some(pos) = stacked_pos.pop() { diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 80b1f652..157ac4f0 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -132,7 +132,6 @@ where D: SplitFirst, { fn halt(&mut self) -> VerifyIteratorResult<'a, L, C, D> { - // TODO also non compact to check hash of node in parent. if self.kind.is_compact() { let r = self.stack.pop_until(None, &self.expected_root, true); if let Err(e) = r { From 3c305e3edfcdf7b62d198265044282c8956646e4 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 16:36:37 +0200 Subject: [PATCH 149/154] restore some changes --- test-support/reference-trie/src/lib.rs | 1 - trie-db/src/iterator.rs | 7 ++-- trie-db/src/nibble/leftnibbleslice.rs | 43 +--------------------- trie-db/src/nibble/mod.rs | 11 +++++- trie-db/src/proof/verify.rs | 7 ---- trie-db/src/trie_codec.rs | 51 +++++++++++++------------- 6 files changed, 39 insertions(+), 81 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 529c291f..3f295d82 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -603,7 +603,6 @@ impl<'a> Input for ByteSliceInput<'a> { impl NodeCodec for ReferenceNodeCodec { const DELTA_COMPACT_OMITTED_NODE: usize = 32; const DELTA_COMPACT_OMITTED_VALUE: usize = 30; - type Error = CodecError; type HashOut = H::Out; diff --git a/trie-db/src/iterator.rs b/trie-db/src/iterator.rs index 150bf286..dca3b8fb 100644 --- a/trie-db/src/iterator.rs +++ b/trie-db/src/iterator.rs @@ -34,8 +34,7 @@ enum Status { #[cfg_attr(feature = "std", derive(Debug))] #[derive(Eq, PartialEq)] -pub(crate) struct Crumb { - // TODO rem pub(crate) by having builder over nibllevec +pub struct Crumb { hash: Option, node: Arc>, status: Status, @@ -67,8 +66,8 @@ impl Crumb { /// Iterator for going through all nodes in the trie in pre-order traversal order. pub struct TrieDBRawIterator { - pub(crate) trail: Vec>, - pub(crate) key_nibbles: NibbleVec, + trail: Vec>, + key_nibbles: NibbleVec, } impl Clone for TrieDBRawIterator { diff --git a/trie-db/src/nibble/leftnibbleslice.rs b/trie-db/src/nibble/leftnibbleslice.rs index 9e9b8f66..931b06c2 100644 --- a/trie-db/src/nibble/leftnibbleslice.rs +++ b/trie-db/src/nibble/leftnibbleslice.rs @@ -16,18 +16,9 @@ use crate::rstd::cmp::{self, Ordering}; use crate::nibble::{ nibble_ops::{self, NIBBLE_PER_BYTE}, - NibbleSlice, NibbleVec, + LeftNibbleSlice, NibbleSlice, NibbleVec, }; -/// A representation of a nibble slice which is left-aligned. The regular `NibbleSlice` is -/// right-aligned, meaning it does not support efficient truncation from the right side. -/// -/// This is an immutable struct. No operations actually change it. -pub struct LeftNibbleSlice<'a> { - pub(crate) bytes: &'a [u8], - len: usize, -} - impl<'a> From<&'a NibbleVec> for LeftNibbleSlice<'a> { fn from(v: &'a NibbleVec) -> Self { LeftNibbleSlice { bytes: v.inner.as_slice(), len: v.len } @@ -40,16 +31,6 @@ impl<'a> LeftNibbleSlice<'a> { LeftNibbleSlice { bytes, len: bytes.len() * NIBBLE_PER_BYTE } } - /// Constructs a byte-aligned nibble slice from a byte slice. - pub fn new_with_mask(bytes: &'a [u8], mask: u8) -> Self { - let mut len = bytes.len() * NIBBLE_PER_BYTE; - // warn this is only working with hex trie - if mask != 255 { - len = len.saturating_sub(1); - } - LeftNibbleSlice { bytes, len } - } - /// Returns the length of the slice in nibbles. pub fn len(&self) -> usize { self.len @@ -82,28 +63,6 @@ impl<'a> LeftNibbleSlice<'a> { (0..partial.len()).all(|i| self.at(offset + i) == Some(partial.at(i))) } - /// How many of the same nibbles at the beginning do we match with `them`? - pub fn common_prefix(&self, them: &Self) -> usize { - let max = cmp::min(self.len, them.len); - let mut nb = 0; - for i in 0..(max / NIBBLE_PER_BYTE) { - if self.bytes[i] == them.bytes[i] { - nb += NIBBLE_PER_BYTE; - } else { - break - } - } - for i in 0..NIBBLE_PER_BYTE { - let s_at = self.at(nb + 1); - if s_at.is_some() && self.at(nb + i) == them.at(nb + i) { - nb += 1; - } else { - break - } - } - nb - } - fn cmp(&self, other: &Self) -> Ordering { let common_len = cmp::min(self.len(), other.len()); let common_byte_len = common_len / NIBBLE_PER_BYTE; diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index ecabad98..76f1642f 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -16,8 +16,6 @@ use crate::{node::NodeKey, rstd::cmp}; -pub use self::leftnibbleslice::LeftNibbleSlice; - mod leftnibbleslice; mod nibbleslice; mod nibblevec; @@ -208,6 +206,15 @@ pub struct NibbleSlice<'a> { offset: usize, } +/// A representation of a nibble slice which is left-aligned. The regular `NibbleSlice` is +/// right-aligned, meaning it does not support efficient truncation from the right side. +/// +/// This is an immutable struct. No operations actually change it. +pub struct LeftNibbleSlice<'a> { + bytes: &'a [u8], + len: usize, +} + /// Iterator type for a nibble slice. pub struct NibbleSliceIterator<'a> { p: &'a NibbleSlice<'a>, diff --git a/trie-db/src/proof/verify.rs b/trie-db/src/proof/verify.rs index 65717dc4..fedd0579 100644 --- a/trie-db/src/proof/verify.rs +++ b/trie-db/src/proof/verify.rs @@ -32,8 +32,6 @@ pub enum Error { /// The statement being verified contains multiple key-value pairs with the same key. The /// parameter is the duplicated key. DuplicateKey(Vec), - /// The statement being verified contains key not ordered properly. - UnorderedKey(Vec), /// The proof contains at least one extraneous node. ExtraneousNode, /// The proof contains at least one extraneous value which should have been omitted from the @@ -49,8 +47,6 @@ pub enum Error { IncompleteProof, /// The root hash computed from the proof is incorrect. RootMismatch(HO), - /// The hash computed from a node is incorrect. - HashMismatch(HO), /// One of the proof nodes could not be decoded. DecodeError(CE), } @@ -61,8 +57,6 @@ impl std::fmt::Display for Error write!(f, "Duplicate key in input statement: key={:?}", key), - Error::UnorderedKey(key) => - write!(f, "Unordered key in input statement: key={:?}", key), Error::ExtraneousNode => write!(f, "Extraneous node found in proof"), Error::ExtraneousValue(key) => write!(f, "Extraneous value found in proof should have been omitted: key={:?}", key), @@ -77,7 +71,6 @@ impl std::fmt::Display for Error write!(f, "Proof is incomplete -- expected more nodes"), Error::RootMismatch(hash) => write!(f, "Computed incorrect root {:?} from proof", hash), - Error::HashMismatch(hash) => write!(f, "Computed incorrect hash {:?} from node", hash), Error::DecodeError(err) => write!(f, "Unable to decode proof node: {}", err), } } diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 69595600..678466d4 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -108,31 +108,6 @@ impl EncoderStackEntry { } } -/// Generate the list of child references for a branch node with certain children omitted. -/// -/// Preconditions: -/// - omit_children has size NIBBLE_LENGTH. -/// - omit_children[i] is only true if child_handles[i] is Some -fn branch_children( - node_data: &[u8], - child_handles: &[Option; NIBBLE_LENGTH], - omit_children: impl BitmapAccess, -) -> crate::rstd::result::Result<[Option>; NIBBLE_LENGTH], Vec> { - let empty_child = ChildReference::Inline(C::HashOut::default(), 0); - let mut children = [None; NIBBLE_LENGTH]; - for i in 0..NIBBLE_LENGTH { - children[i] = if omit_children.at(i) { - Some(empty_child) - } else if let Some(child_plan) = &child_handles[i] { - let child_ref = child_plan.build(node_data).try_into()?; - Some(child_ref) - } else { - None - }; - } - Ok(children) -} - pub(crate) fn encode_node_internal( node: &OwnedNode, omit_value: bool, @@ -191,6 +166,32 @@ pub(crate) fn encode_node_internal( Ok(encoded) } +/// Generate the list of child references for a branch node with certain children omitted. +/// +/// Preconditions: +/// - omit_children has size NIBBLE_LENGTH. +/// - omit_children[i] is only true if child_handles[i] is Some +fn branch_children( + node_data: &[u8], + child_handles: &[Option; NIBBLE_LENGTH], + omit_children: impl BitmapAccess, +) -> crate::rstd::result::Result<[Option>; NIBBLE_LENGTH], Vec> { + let empty_child = ChildReference::Inline(C::HashOut::default(), 0); + let mut children = [None; NIBBLE_LENGTH]; + for i in 0..NIBBLE_LENGTH { + children[i] = if omit_children.at(i) { + Some(empty_child) + } else if let Some(child_plan) = &child_handles[i] { + let child_ref = child_plan.build(node_data).try_into()?; + Some(child_ref) + } else { + None + }; + } + Ok(children) +} + + /// Detached value if included does write a reserved header, /// followed by node encoded with 0 length value and the value /// as a standalone vec. From d42fd1424cafc53dcf2553d276bba8dbaf9be74a Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 16:41:57 +0200 Subject: [PATCH 150/154] restore no cache previous behavior --- trie-db/src/query_plan/record.rs | 8 +++++++- trie-db/src/trie_codec.rs | 1 - trie-db/src/triedb.rs | 12 ++++++++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 79d6bd13..9a2bb02c 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -591,7 +591,13 @@ impl RecordStack { prefix.push(child_index); } let child_node = db - .get_raw_or_lookup_with_cache(parent_hash, child_handle, prefix.as_prefix(), false) + .get_raw_or_lookup_with_cache( + parent_hash, + child_handle, + prefix.as_prefix(), + false, + true, + ) .map_err(|_| { let mut hash = TrieHash::::default(); if let NodeHandle::Hash(h) = &child_handle { diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 678466d4..6fc5f199 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -191,7 +191,6 @@ fn branch_children( Ok(children) } - /// Detached value if included does write a reserved header, /// followed by node encoded with 0 length value and the value /// as a standalone vec. diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index b7073571..55f723bc 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -154,7 +154,13 @@ where partial_key: Prefix, record_access: bool, ) -> Result<(OwnedNode, Option>), TrieHash, CError> { - self.get_raw_or_lookup_with_cache(parent_hash, node_handle, partial_key, record_access) + self.get_raw_or_lookup_with_cache( + parent_hash, + node_handle, + partial_key, + record_access, + false, + ) } /// Same as get_raw_or_lookup but with optionally use of the node cache. @@ -167,12 +173,14 @@ where node_handle: NodeHandle, partial_key: Prefix, record_access: bool, + use_cache: bool, ) -> Result<(OwnedNode, Option>), TrieHash, CError> { let (node_hash, node_data) = match node_handle { NodeHandle::Hash(data) => { let node_hash = decode_hash::(data) .ok_or_else(|| Box::new(TrieError::InvalidHash(parent_hash, data.to_vec())))?; - if let Some(c) = self.cache.as_ref() { + if use_cache && self.cache.as_ref().is_some() { + let c = self.cache.as_ref().expect("checked above"); let mut cache = c.borrow_mut(); let node = cache.get_or_insert_node(node_hash, &mut || { let node_data = self.db.get(&node_hash, partial_key).ok_or_else(|| { From d218181db4fe4885fead8ff664527a092710d265 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 16:56:35 +0200 Subject: [PATCH 151/154] fix imports warnings --- trie-db/src/triedbmut.rs | 10 --------- trie-db/test/src/fuzz.rs | 2 +- trie-db/test/src/lib.rs | 39 ++++++++++++++++++++++++++++++++++ trie-db/test/src/proof.rs | 28 +----------------------- trie-db/test/src/query_plan.rs | 25 ++++++++++++---------- 5 files changed, 55 insertions(+), 49 deletions(-) diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 96da9d3c..e6c19353 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -563,16 +563,6 @@ pub enum ChildReference { Inline(HO, usize), // usize is the length of the node data we store in the `H::Out` } -impl ChildReference { - // Representation of hash, may contain filler bytes. - pub fn disp_hash(&self) -> &HO { - match self { - ChildReference::Hash(h) => h, - ChildReference::Inline(h, _) => h, - } - } -} - impl<'a, HO> TryFrom> for ChildReference where HO: AsRef<[u8]> + AsMut<[u8]> + Default + Clone + Copy, diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index a23c791b..8515b15a 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -475,7 +475,7 @@ fn test_trie_codec_proof(entries: Vec<(Vec, Vec)>, keys: /// Query plan proof fuzzing. pub mod query_plan { use super::*; - use crate::proof::{test_entries, MemoryDB}; + use crate::{test_entries, MemoryDB}; use arbitrary::Arbitrary; use rand::{rngs::SmallRng, RngCore, SeedableRng}; use reference_trie::TestTrieCache; diff --git a/trie-db/test/src/lib.rs b/trie-db/test/src/lib.rs index e379db56..370093cb 100644 --- a/trie-db/test/src/lib.rs +++ b/trie-db/test/src/lib.rs @@ -14,16 +14,55 @@ //! Tests for trie-db crate. +#[cfg(test)] mod fatdb; +#[cfg(test)] mod fatdbmut; pub mod fuzz; +#[cfg(test)] mod iter_build; +#[cfg(test)] mod iterator; +#[cfg(test)] mod proof; mod query_plan; +#[cfg(test)] mod recorder; +#[cfg(test)] mod sectriedb; +#[cfg(test)] mod sectriedbmut; +#[cfg(test)] mod trie_codec; +#[cfg(test)] mod triedb; +#[cfg(test)] mod triedbmut; + +use trie_db::{DBValue, TrieLayout}; + +/// Testing memory db type. +pub type MemoryDB = memory_db::MemoryDB< + ::Hash, + memory_db::HashKey<::Hash>, + DBValue, +>; + +/// Set of entries for base testing. +pub fn test_entries() -> Vec<(&'static [u8], &'static [u8])> { + vec![ + // "alfa" is at a hash-referenced leaf node. + (b"alfa", &[0; 32]), + // "bravo" is at an inline leaf node. + (b"bravo", b"bravo"), + // "do" is at a hash-referenced branch node. + (b"do", b"verb"), + // "dog" is at a hash-referenced branch node. + (b"dog", b"puppy"), + // "doge" is at a hash-referenced leaf node. + (b"doge", &[0; 32]), + // extension node "o" (plus nibble) to next branch. + (b"horse", b"stallion"), + (b"house", b"building"), + ] +} diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index b78c3366..53faa814 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -12,40 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::{test_entries, MemoryDB}; use hash_db::Hasher; use reference_trie::{test_layouts, NoExtensionLayout}; - use trie_db::{ proof::{generate_proof, verify_proof, VerifyError}, DBValue, Trie, TrieDBBuilder, TrieDBMutBuilder, TrieLayout, TrieMut, }; -/// Testing memory db type. -pub type MemoryDB = memory_db::MemoryDB< - ::Hash, - memory_db::HashKey<::Hash>, - DBValue, ->; - -/// Set of entries for base testing. -pub fn test_entries() -> Vec<(&'static [u8], &'static [u8])> { - vec![ - // "alfa" is at a hash-referenced leaf node. - (b"alfa", &[0; 32]), - // "bravo" is at an inline leaf node. - (b"bravo", b"bravo"), - // "do" is at a hash-referenced branch node. - (b"do", b"verb"), - // "dog" is at a hash-referenced branch node. - (b"dog", b"puppy"), - // "doge" is at a hash-referenced leaf node. - (b"doge", &[0; 32]), - // extension node "o" (plus nibble) to next branch. - (b"horse", b"stallion"), - (b"house", b"building"), - ] -} - fn test_generate_proof( entries: Vec<(&'static [u8], &'static [u8])>, keys: Vec<&'static [u8]>, diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index e3ef8445..0bfafd86 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -14,39 +14,42 @@ //! Query plan tests. -use crate::proof::{test_entries, MemoryDB}; use hash_db::Hasher; -use reference_trie::{test_layouts, TestTrieCache}; +use reference_trie::test_layouts; use std::collections::BTreeMap; use trie_db::{ query_plan::{ - record_query_plan, verify_query_plan_iter, HaltedStateCheck, HaltedStateRecord, - InMemQueryPlan, InMemQueryPlanItem, ProofKind, QueryPlan, QueryPlanItem, ReadProofItem, - Recorder, + verify_query_plan_iter, HaltedStateCheck, + InMemQueryPlan, ProofKind, QueryPlan, QueryPlanItem, ReadProofItem, }, - TrieDBBuilder, TrieDBMutBuilder, TrieHash, TrieLayout, TrieMut, + TrieHash, TrieLayout, }; test_layouts!(test_query_plan_full, test_query_plan_full_internal); +#[cfg(test)] fn test_query_plan_full_internal() { test_query_plan_internal::(ProofKind::FullNodes, false); test_query_plan_internal::(ProofKind::FullNodes, true); } test_layouts!(test_query_plan_compact, test_query_plan_compact_internal); +#[cfg(test)] fn test_query_plan_compact_internal() { test_query_plan_internal::(ProofKind::CompactNodes, false); test_query_plan_internal::(ProofKind::CompactNodes, true); } +#[cfg(test)] fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { - let set = test_entries(); + use trie_db::query_plan::{Recorder, InMemQueryPlanItem}; + use trie_db::{TrieDBBuilder, TrieDBMutBuilder, TrieMut}; + let set = crate::test_entries(); - let mut cache = TestTrieCache::::default(); + let mut cache = reference_trie::TestTrieCache::::default(); let (db, root) = { - let mut db = >::default(); + let mut db = >::default(); let mut root = Default::default(); { let mut trie = >::new(&mut db, &mut root).build(); @@ -95,12 +98,12 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { let limit = limit_conf.0; let limit = (limit != 0).then(|| limit); let recorder = Recorder::new(kind, Default::default(), limit, None); - let mut from = HaltedStateRecord::from_start(recorder); + let mut from = trie_db::query_plan::HaltedStateRecord::from_start(recorder); // no limit let mut proofs: Vec>> = Default::default(); let mut query_plan_iter = query_plan.as_ref(); loop { - record_query_plan::(&db, &mut query_plan_iter, &mut from).unwrap(); + trie_db::query_plan::record_query_plan::(&db, &mut query_plan_iter, &mut from).unwrap(); if limit.is_none() { assert!(!from.is_halted()); From e262647362c1532afb1b9c5f8e19bbf36b9082d4 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 17:03:15 +0200 Subject: [PATCH 152/154] renaming --- trie-db/src/query_plan/mod.rs | 22 +++++------ trie-db/src/query_plan/record.rs | 8 ++-- trie-db/src/query_plan/verify.rs | 14 +++---- trie-db/test/src/fuzz.rs | 4 +- trie-db/test/src/query_plan.rs | 64 ++++++++++++++++++++++---------- 5 files changed, 68 insertions(+), 44 deletions(-) diff --git a/trie-db/src/query_plan/mod.rs b/trie-db/src/query_plan/mod.rs index 14b7bcd6..b9223773 100644 --- a/trie-db/src/query_plan/mod.rs +++ b/trie-db/src/query_plan/mod.rs @@ -125,32 +125,32 @@ impl std::error::Error for /// Item to query, in memory. #[derive(Default, Clone, Debug)] -pub struct InMemQueryPlanItem { +pub struct QueryPlanItem { key: Vec, hash_only: bool, as_prefix: bool, } -impl InMemQueryPlanItem { +impl QueryPlanItem { /// Create new item. pub fn new(key: Vec, hash_only: bool, as_prefix: bool) -> Self { Self { key, hash_only, as_prefix } } /// Get ref. - pub fn as_ref(&self) -> QueryPlanItem { - QueryPlanItem { key: &self.key, hash_only: self.hash_only, as_prefix: self.as_prefix } + pub fn as_ref(&self) -> QueryPlanItemRef { + QueryPlanItemRef { key: &self.key, hash_only: self.hash_only, as_prefix: self.as_prefix } } } /// Item to query. #[derive(Clone, Debug, Eq, PartialEq)] -pub struct QueryPlanItem<'a> { +pub struct QueryPlanItemRef<'a> { pub key: &'a [u8], pub hash_only: bool, pub as_prefix: bool, } -impl<'a> QueryPlanItem<'a> { +impl<'a> QueryPlanItemRef<'a> { fn before(&self, other: &Self) -> (bool, usize) { let (common_depth, ordering) = nibble_ops::biggest_depth_and_order(&self.key, &other.key); @@ -170,8 +170,8 @@ impl<'a> QueryPlanItem<'a> { ) } - fn to_owned(&self) -> InMemQueryPlanItem { - InMemQueryPlanItem { + fn to_owned(&self) -> QueryPlanItem { + QueryPlanItem { key: self.key.to_vec(), hash_only: self.hash_only, as_prefix: self.as_prefix, @@ -182,15 +182,15 @@ impl<'a> QueryPlanItem<'a> { /// Query plan in memory. #[derive(Clone, Debug)] pub struct InMemQueryPlan { - pub items: Vec, + pub items: Vec, pub kind: ProofKind, } /// Iterator as type of mapped slice iter is very noisy. -pub struct QueryPlanItemIter<'a>(&'a Vec, usize); +pub struct QueryPlanItemIter<'a>(&'a Vec, usize); impl<'a> Iterator for QueryPlanItemIter<'a> { - type Item = QueryPlanItem<'a>; + type Item = QueryPlanItemRef<'a>; fn next(&mut self) -> Option { if self.1 >= self.0.len() { diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 9a2bb02c..37c45d6e 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -129,7 +129,7 @@ enum RecorderStateInner { /// When process is halted keep execution state /// to restore later. pub struct HaltedStateRecord { - currently_query_item: Option, + currently_query_item: Option, stack: RecordStack, // This indicate a restore point, it takes precedence over // stack and currently_query_item. @@ -294,7 +294,7 @@ impl HaltedStateRecord { fn iter_prefix( &mut self, - prev_query: Option<&QueryPlanItem>, + prev_query: Option<&QueryPlanItemRef>, db: &TrieDB, hash_only: bool, first_iter: bool, @@ -373,7 +373,7 @@ struct RecordStack { } /// Run query plan on a full db and record it. -pub fn record_query_plan<'a, L: TrieLayout, I: Iterator>>( +pub fn record_query_plan<'a, L: TrieLayout, I: Iterator>>( db: &TrieDB, query_plan: &mut QueryPlan<'a, I>, from: &mut HaltedStateRecord, @@ -401,7 +401,7 @@ pub fn record_query_plan<'a, L: TrieLayout, I: Iterator } } - let mut prev_query: Option = None; + let mut prev_query: Option = None; let from_query = from.currently_query_item.take(); let mut from_query_ref = from_query.as_ref().map(|f| f.as_ref()); while let Some(query) = from_query_ref.clone().or_else(|| query_plan.items.next()) { diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index 157ac4f0..fa97143a 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -32,7 +32,7 @@ type VerifyIteratorResult<'a, L, C, D> = pub struct ReadProofIterator<'a, L, C, D, P> where L: TrieLayout, - C: Iterator>, + C: Iterator>, P: Iterator, D: SplitFirst, { @@ -42,7 +42,7 @@ where proof: P, kind: ProofKind, expected_root: Option>, - current: Option>, + current: Option>, current_offset: usize, state: ReadProofState, stack: Stack, @@ -103,7 +103,7 @@ pub fn verify_query_plan_iter<'a, L, C, D, P>( ) -> Result, Error, CError>> where L: TrieLayout, - C: Iterator>, + C: Iterator>, P: Iterator, D: SplitFirst, { @@ -127,7 +127,7 @@ where impl<'a, L, C, D, P> ReadProofIterator<'a, L, C, D, P> where L: TrieLayout, - C: Iterator>, + C: Iterator>, P: Iterator, D: SplitFirst, { @@ -169,7 +169,7 @@ where impl<'a, L, C, D, P> Iterator for ReadProofIterator<'a, L, C, D, P> where L: TrieLayout, - C: Iterator>, + C: Iterator>, P: Iterator, D: SplitFirst, { @@ -199,7 +199,7 @@ where impl<'a, L, C, D, P> ReadProofIterator<'a, L, C, D, P> where L: TrieLayout, - C: Iterator>, + C: Iterator>, P: Iterator, D: SplitFirst, { @@ -471,7 +471,7 @@ where /// to restore later. pub struct HaltedStateCheck<'a, L: TrieLayout, C, D: SplitFirst> { query_plan: QueryPlan<'a, C>, - current: Option>, + current: Option>, stack: Stack, state: ReadProofState, restore_offset: usize, diff --git a/trie-db/test/src/fuzz.rs b/trie-db/test/src/fuzz.rs index 8515b15a..4d5ccf4d 100644 --- a/trie-db/test/src/fuzz.rs +++ b/trie-db/test/src/fuzz.rs @@ -482,7 +482,7 @@ pub mod query_plan { use std::collections::{BTreeMap, BTreeSet}; use trie_db::{ query_plan::{ - record_query_plan, HaltedStateRecord, InMemQueryPlan, InMemQueryPlanItem, ProofKind, + record_query_plan, HaltedStateRecord, InMemQueryPlan, ProofKind, QueryPlanItem, Recorder, }, TrieHash, TrieLayout, @@ -658,7 +658,7 @@ pub mod query_plan { prev_pref = Some(key.clone()); } - query_plan.items.push(InMemQueryPlanItem::new(key, conf.hash_only, !not_prefix)); + query_plan.items.push(QueryPlanItem::new(key, conf.hash_only, !not_prefix)); } query_plan } diff --git a/trie-db/test/src/query_plan.rs b/trie-db/test/src/query_plan.rs index 0bfafd86..e12aab71 100644 --- a/trie-db/test/src/query_plan.rs +++ b/trie-db/test/src/query_plan.rs @@ -20,8 +20,8 @@ use reference_trie::test_layouts; use std::collections::BTreeMap; use trie_db::{ query_plan::{ - verify_query_plan_iter, HaltedStateCheck, - InMemQueryPlan, ProofKind, QueryPlan, QueryPlanItem, ReadProofItem, + verify_query_plan_iter, HaltedStateCheck, InMemQueryPlan, ProofKind, QueryPlan, + QueryPlanItemRef, ReadProofItem, }, TrieHash, TrieLayout, }; @@ -42,8 +42,10 @@ fn test_query_plan_compact_internal() { #[cfg(test)] fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { - use trie_db::query_plan::{Recorder, InMemQueryPlanItem}; - use trie_db::{TrieDBBuilder, TrieDBMutBuilder, TrieMut}; + use trie_db::{ + query_plan::{QueryPlanItem, Recorder}, + TrieDBBuilder, TrieDBMutBuilder, TrieMut, + }; let set = crate::test_entries(); let mut cache = reference_trie::TestTrieCache::::default(); @@ -70,22 +72,19 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { return } let query_plans = [ - InMemQueryPlan { - items: vec![InMemQueryPlanItem::new(b"".to_vec(), hash_only, true)], - kind, - }, + InMemQueryPlan { items: vec![QueryPlanItem::new(b"".to_vec(), hash_only, true)], kind }, InMemQueryPlan { items: vec![ - InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), - InMemQueryPlanItem::new(b"do".to_vec(), hash_only, true), + QueryPlanItem::new(b"bravo".to_vec(), hash_only, false), + QueryPlanItem::new(b"do".to_vec(), hash_only, true), ], kind, }, InMemQueryPlan { items: vec![ - InMemQueryPlanItem::new(b"bravo".to_vec(), hash_only, false), - InMemQueryPlanItem::new(b"doge".to_vec(), hash_only, false), - InMemQueryPlanItem::new(b"horsey".to_vec(), hash_only, false), + QueryPlanItem::new(b"bravo".to_vec(), hash_only, false), + QueryPlanItem::new(b"doge".to_vec(), hash_only, false), + QueryPlanItem::new(b"horsey".to_vec(), hash_only, false), ], kind, }, @@ -103,7 +102,12 @@ fn test_query_plan_internal(kind: ProofKind, hash_only: bool) { let mut proofs: Vec>> = Default::default(); let mut query_plan_iter = query_plan.as_ref(); loop { - trie_db::query_plan::record_query_plan::(&db, &mut query_plan_iter, &mut from).unwrap(); + trie_db::query_plan::record_query_plan::( + &db, + &mut query_plan_iter, + &mut from, + ) + .unwrap(); if limit.is_none() { assert!(!from.is_halted()); @@ -173,7 +177,11 @@ pub fn check_proofs( } else { assert_eq!( current_plan.as_ref(), - Some(&QueryPlanItem { key: &key, hash_only: true, as_prefix: false }) + Some(&QueryPlanItemRef { + key: &key, + hash_only: true, + as_prefix: false + }) ); current_plan = query_plan_iter.items.next(); } @@ -189,7 +197,11 @@ pub fn check_proofs( } else { assert_eq!( current_plan.as_ref(), - Some(&QueryPlanItem { key: &key, hash_only: false, as_prefix: false }) + Some(&QueryPlanItemRef { + key: &key, + hash_only: false, + as_prefix: false + }) ); current_plan = query_plan_iter.items.next(); } @@ -200,12 +212,20 @@ pub fn check_proofs( if hash_only { assert_eq!( current_plan.as_ref(), - Some(&QueryPlanItem { key: &key, hash_only: true, as_prefix: false }) + Some(&QueryPlanItemRef { + key: &key, + hash_only: true, + as_prefix: false + }) ); } else { assert_eq!( current_plan.as_ref(), - Some(&QueryPlanItem { key: &key, hash_only: false, as_prefix: false }) + Some(&QueryPlanItemRef { + key: &key, + hash_only: false, + as_prefix: false + }) ); } current_plan = query_plan_iter.items.next(); @@ -216,12 +236,16 @@ pub fn check_proofs( if hash_only { assert_eq!( current_plan.as_ref(), - Some(&QueryPlanItem { key: &prefix, hash_only: true, as_prefix: true }) + Some(&QueryPlanItemRef { + key: &prefix, + hash_only: true, + as_prefix: true + }) ); } else { assert_eq!( current_plan.as_ref(), - Some(&QueryPlanItem { + Some(&QueryPlanItemRef { key: &prefix, hash_only: false, as_prefix: true From cfd1ccd79250f83b3f425b8c08d11fee39aa1432 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 18:07:23 +0200 Subject: [PATCH 153/154] clean a bit more --- trie-db/src/query_plan/record.rs | 4 ++-- trie-db/src/query_plan/verify.rs | 17 +++-------------- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/trie-db/src/query_plan/record.rs b/trie-db/src/query_plan/record.rs index 37c45d6e..e4566717 100644 --- a/trie-db/src/query_plan/record.rs +++ b/trie-db/src/query_plan/record.rs @@ -540,10 +540,10 @@ impl RecordStack { let child_handle = if let Some(item) = self.items.last_mut() { //if inline_only && item.accessed_children_node.at(child_index as usize) { debug_assert!(!item.accessed_children_node.at(child_index as usize)); - /* if item.accessed_children_node.at(child_index as usize) { + if item.accessed_children_node.at(child_index as usize) { // No reason to go twice in a same branch return Ok(TryStackChildResult::NotStackedBranch) - }*/ + } let node_data = item.node.data(); diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index fa97143a..fc8b9916 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -651,18 +651,7 @@ impl Stack { }, NodeHandle::Inline(data) => { if self.kind.is_compact() && data.len() == 0 { - unimplemented!("This requires to put extension in stack"); - /* - // ommitted hash - let Some(encoded_node) = proof.next() else { - // halt happens with a hash, this is not. - return Err(Error::IncompleteProof); - }; - node = match OwnedNode::new::(encoded_node) { - Ok(node) => (ItemStackNode::Node(node), self.is_compact).into(), - Err(e) => return Err(Error::DecodeError(e)), - }; - */ + unimplemented!("This will requires to put extension in stack"); } else { node = match OwnedNode::new::(data.to_vec()) { Ok(node) => (ItemStackNode::Inline(node), self.kind).try_into()?, @@ -811,12 +800,12 @@ impl Stack { Some(ChildReference::Hash(hash)); }, Some(ChildReference::Inline(_h, size)) if size == 0 => { - // Complete + // Remove hash from compact, complete it. parent.children[at as usize] = Some(ChildReference::Hash(hash)); }, + // non null inline, only non inline are stacked. _ => - // only non inline are stacked return Err(Error::RootMismatch(Default::default())), } } else { From be884366ec3be60ffee84c83af455a2fa4c25cae Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 1 Aug 2023 18:08:43 +0200 Subject: [PATCH 154/154] fmt --- trie-db/src/query_plan/verify.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/trie-db/src/query_plan/verify.rs b/trie-db/src/query_plan/verify.rs index fc8b9916..8641178d 100644 --- a/trie-db/src/query_plan/verify.rs +++ b/trie-db/src/query_plan/verify.rs @@ -649,7 +649,7 @@ impl Stack { Err(e) => return Err(Error::DecodeError(e)), }; }, - NodeHandle::Inline(data) => { + NodeHandle::Inline(data) => if self.kind.is_compact() && data.len() == 0 { unimplemented!("This will requires to put extension in stack"); } else { @@ -657,8 +657,7 @@ impl Stack { Ok(node) => (ItemStackNode::Inline(node), self.kind).try_into()?, Err(e) => return Err(Error::DecodeError(e)), }; - } - }, + }, } let NodePlan::Branch { .. } = node.node_plan() else { return Err(Error::UnexpectedNodeType) @@ -805,8 +804,7 @@ impl Stack { Some(ChildReference::Hash(hash)); }, // non null inline, only non inline are stacked. - _ => - return Err(Error::RootMismatch(Default::default())), + _ => return Err(Error::RootMismatch(Default::default())), } } else { if &Some(hash) != expected_root {