tetsy_trie_db/proof/
verify.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5//     http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12
13//! Verification of compact proofs for Merkle-Patricia tries.
14
15use crate::rstd::{
16	convert::TryInto, iter::Peekable, marker::PhantomData, result::Result, vec, vec::Vec,
17};
18use crate::{
19	CError, ChildReference, nibble::LeftNibbleSlice, nibble_ops::NIBBLE_LENGTH,
20	node::{Node, NodeHandle}, NodeCodec, TrieHash, TrieLayout,
21};
22use tetsy_hash_db::Hasher;
23
24
25/// Errors that may occur during proof verification. Most of the errors types simply indicate that
26/// the proof is invalid with respect to the statement being verified, and the exact error type can
27/// be used for debugging.
28#[derive(PartialEq, Eq)]
29#[cfg_attr(feature = "std", derive(Debug))]
30pub enum Error<HO, CE> {
31	/// The statement being verified contains multiple key-value pairs with the same key. The
32	/// parameter is the duplicated key.
33	DuplicateKey(Vec<u8>),
34	/// The proof contains at least one extraneous node.
35	ExtraneousNode,
36	/// The proof contains at least one extraneous value which should have been omitted from the
37	/// proof.
38	ExtraneousValue(Vec<u8>),
39	/// The proof contains at least one extraneous hash reference the should have been omitted.
40	ExtraneousHashReference(HO),
41	/// The proof contains an invalid child reference that exceeds the hash length.
42	InvalidChildReference(Vec<u8>),
43	/// The proof indicates that an expected value was not found in the trie.
44	ValueMismatch(Vec<u8>),
45	/// The proof is missing trie nodes required to verify.
46	IncompleteProof,
47	/// The root hash computed from the proof is incorrect.
48	RootMismatch(HO),
49	/// One of the proof nodes could not be decoded.
50	DecodeError(CE),
51}
52
53#[cfg(feature = "std")]
54impl<HO: std::fmt::Debug, CE: std::error::Error> std::fmt::Display for Error<HO, CE> {
55	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
56		match self {
57			Error::DuplicateKey(key) =>
58				write!(f, "Duplicate key in input statement: key={:?}", key),
59			Error::ExtraneousNode =>
60				write!(f, "Extraneous node found in proof"),
61			Error::ExtraneousValue(key) =>
62				write!(
63					f,
64					"Extraneous value found in proof should have been omitted: key={:?}",
65					key
66				),
67			Error::ExtraneousHashReference(hash) =>
68				write!(
69					f,
70					"Extraneous hash reference found in proof should have been omitted: hash={:?}",
71					hash
72				),
73			Error::InvalidChildReference(data) =>
74				write!(f, "Invalid child reference exceeds hash length: {:?}", data),
75			Error::ValueMismatch(key) =>
76				write!(f, "Expected value was not found in the trie: key={:?}", key),
77			Error::IncompleteProof =>
78				write!(f, "Proof is incomplete -- expected more nodes"),
79			Error::RootMismatch(hash) =>
80				write!(f, "Computed incorrect root {:?} from proof", hash),
81			Error::DecodeError(err) =>
82				write!(f, "Unable to decode proof node: {}", err),
83		}
84	}
85}
86
87#[cfg(feature = "std")]
88impl<HO: std::fmt::Debug, CE: std::error::Error + 'static> std::error::Error for Error<HO, CE> {
89	fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
90		match self {
91			Error::DecodeError(err) => Some(err),
92			_ => None,
93		}
94	}
95}
96
97struct StackEntry<'a, C: NodeCodec> {
98	/// The prefix is the nibble path to the node in the trie.
99	prefix: LeftNibbleSlice<'a>,
100	node: Node<'a>,
101	is_inline: bool,
102	/// The value associated with this trie node.
103	value: Option<&'a [u8]>,
104	/// The next entry in the stack is a child of the preceding entry at this index. For branch
105	/// nodes, the index is in [0, NIBBLE_LENGTH] and for extension nodes, the index is in [0, 1].
106	child_index: usize,
107	/// The child references to use in reconstructing the trie nodes.
108	children: Vec<Option<ChildReference<C::HashOut>>>,
109	_marker: PhantomData<C>,
110}
111
112impl<'a, C: NodeCodec> StackEntry<'a, C> {
113	fn new(node_data: &'a [u8], prefix: LeftNibbleSlice<'a>, is_inline: bool)
114		   -> Result<Self, Error<C::HashOut, C::Error>>
115	{
116		let node = C::decode(node_data)
117			.map_err(Error::DecodeError)?;
118		let children_len = match node {
119			Node::Empty | Node::Leaf(..) => 0,
120			Node::Extension(..) => 1,
121			Node::Branch(..) | Node::NibbledBranch(..) => NIBBLE_LENGTH,
122		};
123		let value = match node {
124			Node::Empty | Node::Extension(_, _) => None,
125			Node::Leaf(_, value) => Some(value),
126			Node::Branch(_, value) | Node::NibbledBranch(_, _, value) => value,
127		};
128		Ok(StackEntry {
129			node,
130			is_inline,
131			prefix,
132			value,
133			child_index: 0,
134			children: vec![None; children_len],
135			_marker: PhantomData::default(),
136		})
137	}
138
139	/// Encode this entry to an encoded trie node with data properly reconstructed.
140	fn encode_node(mut self) -> Result<Vec<u8>, Error<C::HashOut, C::Error>> {
141		self.complete_children()?;
142		Ok(match self.node {
143			Node::Empty =>
144				C::empty_node().to_vec(),
145			Node::Leaf(partial, _) => {
146				let value = self.value
147					.expect(
148						"value is assigned to Some in StackEntry::new; \
149						value is only ever reassigned in the ValueMatch::MatchesLeaf match \
150						clause, which assigns only to Some"
151					);
152				C::leaf_node(partial.right(), value)
153			}
154			Node::Extension(partial, _) => {
155				let child = self.children[0]
156					.expect("the child must be completed since child_index is 1");
157				C::extension_node(
158					partial.right_iter(),
159					partial.len(),
160					child
161				)
162			}
163			Node::Branch(_, _) =>
164				C::branch_node(
165					self.children.iter(),
166					self.value,
167				),
168			Node::NibbledBranch(partial, _, _) =>
169				C::branch_node_nibbled(
170					partial.right_iter(),
171					partial.len(),
172					self.children.iter(),
173					self.value,
174				),
175		})
176	}
177
178	fn advance_child_index<I>(
179		&mut self,
180		child_prefix: LeftNibbleSlice<'a>,
181		proof_iter: &mut I,
182	) -> Result<Self, Error<C::HashOut, C::Error>>
183		where
184			I: Iterator<Item=&'a Vec<u8>>,
185	{
186		match self.node {
187			Node::Extension(_, child) => {
188				// Guaranteed because of sorted keys order.
189				assert_eq!(self.child_index, 0);
190				Self::make_child_entry(proof_iter, child, child_prefix)
191			}
192			Node::Branch(children, _) | Node::NibbledBranch(_, children, _) => {
193				// because this is a branch
194				assert!(child_prefix.len() > 0);
195				let child_index = child_prefix.at(child_prefix.len() - 1)
196					.expect("it's less than prefix.len(); qed")
197					as usize;
198				while self.child_index < child_index {
199					if let Some(child) = children[self.child_index] {
200						let child_ref = child.try_into()
201							.map_err(Error::InvalidChildReference)?;
202						self.children[self.child_index] = Some(child_ref);
203					}
204					self.child_index += 1;
205				}
206				let child = children[self.child_index]
207					.expect("guaranteed by advance_item");
208				Self::make_child_entry(proof_iter, child, child_prefix)
209			}
210			_ => panic!("cannot have children"),
211		}
212	}
213
214	/// Populate the remaining references in `children` with references copied the node itself.
215	fn complete_children(&mut self) -> Result<(), Error<C::HashOut, C::Error>> {
216		match self.node {
217			Node::Extension(_, child) if self.child_index == 0 => {
218				let child_ref = child.try_into()
219					.map_err(Error::InvalidChildReference)?;
220				self.children[self.child_index] = Some(child_ref);
221				self.child_index += 1;
222			}
223			Node::Branch(children, _) | Node::NibbledBranch(_, children, _) => {
224				while self.child_index < NIBBLE_LENGTH {
225					if let Some(child) = children[self.child_index] {
226						let child_ref = child.try_into()
227							.map_err(Error::InvalidChildReference)?;
228						self.children[self.child_index] = Some(child_ref);
229					}
230					self.child_index += 1;
231				}
232			}
233			_ => {}
234		}
235		Ok(())
236	}
237
238	fn make_child_entry<I>(
239		proof_iter: &mut I,
240		child: NodeHandle<'a>,
241		prefix: LeftNibbleSlice<'a>,
242	) -> Result<Self, Error<C::HashOut, C::Error>>
243		where
244			I: Iterator<Item=&'a Vec<u8>>,
245	{
246		match child {
247			NodeHandle::Inline(data) => {
248				if data.is_empty() {
249					let node_data = proof_iter.next()
250						.ok_or(Error::IncompleteProof)?;
251					StackEntry::new(node_data, prefix, false)
252				} else {
253					StackEntry::new(data, prefix, true)
254				}
255			}
256			NodeHandle::Hash(data) => {
257				let mut hash = C::HashOut::default();
258				if data.len() != hash.as_ref().len() {
259					return Err(Error::InvalidChildReference(data.to_vec()));
260				}
261				hash.as_mut().copy_from_slice(data);
262				Err(Error::ExtraneousHashReference(hash))
263			}
264		}
265	}
266
267	fn advance_item<I>(&mut self, items_iter: &mut Peekable<I>)
268					   -> Result<Step<'a>, Error<C::HashOut, C::Error>>
269		where
270			I: Iterator<Item=(&'a [u8], Option<&'a [u8]>)>
271	{
272		let step = loop {
273			if let Some((key_bytes, value)) = items_iter.peek().cloned() {
274				let key = LeftNibbleSlice::new(key_bytes);
275				if key.starts_with(&self.prefix) {
276					match match_key_to_node(&key, self.prefix.len(), &self.node) {
277						ValueMatch::MatchesLeaf => {
278							if value.is_none() {
279								return Err(Error::ValueMismatch(key_bytes.to_vec()));
280							}
281							self.value = value;
282						}
283						ValueMatch::MatchesBranch =>
284							self.value = value,
285						ValueMatch::NotFound =>
286							if value.is_some() {
287								return Err(Error::ValueMismatch(key_bytes.to_vec()));
288							},
289						ValueMatch::NotOmitted =>
290							return Err(Error::ExtraneousValue(key_bytes.to_vec())),
291						ValueMatch::IsChild(child_prefix) =>
292							break Step::Descend(child_prefix),
293					}
294
295					items_iter.next();
296					continue;
297				}
298			}
299			break Step::UnwindStack;
300		};
301		Ok(step)
302	}
303}
304
305enum ValueMatch<'a> {
306	/// The key matches a leaf node, so the value at the key must be present.
307	MatchesLeaf,
308	/// The key matches a branch node, so the value at the key may or may not be present.
309	MatchesBranch,
310	/// The key was not found to correspond to value in the trie, so must not be present.
311	NotFound,
312	/// The key matches a location in trie, but the value was not omitted.
313	NotOmitted,
314	/// The key may match below a child of this node. Parameter is the prefix of the child node.
315	IsChild(LeftNibbleSlice<'a>),
316}
317
318/// Determines whether a node on the stack carries a value at the given key or whether any nodes
319/// in the subtrie do. The prefix of the node is given by the first `prefix_len` nibbles of `key`.
320fn match_key_to_node<'a>(key: &LeftNibbleSlice<'a>, prefix_len: usize, node: &Node)
321						 -> ValueMatch<'a>
322{
323	match node {
324		Node::Empty => ValueMatch::NotFound,
325		Node::Leaf(partial, value) => {
326			if key.contains(partial, prefix_len) &&
327				key.len() == prefix_len + partial.len() {
328				if value.is_empty() {
329					ValueMatch::MatchesLeaf
330				} else {
331					ValueMatch::NotOmitted
332				}
333			} else {
334				ValueMatch::NotFound
335			}
336		}
337		Node::Extension(partial, _) => {
338			if key.contains(partial, prefix_len) {
339				ValueMatch::IsChild(key.truncate(prefix_len + partial.len()))
340			} else {
341				ValueMatch::NotFound
342			}
343		}
344		Node::Branch(children, value) => {
345			match_key_to_branch_node(key, prefix_len, children, value)
346		}
347		Node::NibbledBranch(partial, children, value) => {
348			if key.contains(partial, prefix_len) {
349				match_key_to_branch_node(key, prefix_len + partial.len(), children, value)
350			} else {
351				ValueMatch::NotFound
352			}
353		}
354	}
355}
356
357/// Determines whether a branch node on the stack carries a value at the given key or whether any
358/// nodes in the subtrie do. The key of the branch node value is given by the first
359/// `prefix_plus_partial_len` nibbles of `key`.
360fn match_key_to_branch_node<'a>(
361	key: &LeftNibbleSlice<'a>,
362	prefix_plus_partial_len: usize,
363	children: &[Option<NodeHandle>; NIBBLE_LENGTH],
364	value: &Option<&[u8]>,
365) -> ValueMatch<'a>
366{
367	if key.len() == prefix_plus_partial_len {
368		if value.is_none() {
369			ValueMatch::MatchesBranch
370		} else {
371			ValueMatch::NotOmitted
372		}
373	} else {
374		let index = key.at(prefix_plus_partial_len)
375			.expect("it's less than prefix.len(); qed")
376			as usize;
377		if children[index].is_some() {
378			ValueMatch::IsChild(key.truncate(prefix_plus_partial_len + 1))
379		} else {
380			ValueMatch::NotFound
381		}
382	}
383}
384
385enum Step<'a> {
386	Descend(LeftNibbleSlice<'a>),
387	UnwindStack,
388}
389
390/// Verify a compact proof for key-value pairs in a trie given a root hash.
391pub fn verify_proof<'a, L, I, K, V>(root: &<L::Hash as Hasher>::Out, proof: &[Vec<u8>], items: I)
392									-> Result<(), Error<TrieHash<L>, CError<L>>>
393	where
394		L: TrieLayout,
395		I: IntoIterator<Item=&'a (K, Option<V>)>,
396		K: 'a + AsRef<[u8]>,
397		V: 'a + AsRef<[u8]>,
398{
399	// Sort items.
400	let mut items = items.into_iter()
401		.map(|(k, v)| (k.as_ref(), v.as_ref().map(|v| v.as_ref())))
402		.collect::<Vec<_>>();
403	items.sort();
404
405	if items.is_empty() {
406		return if proof.is_empty() {
407			Ok(())
408		} else {
409			Err(Error::ExtraneousNode)
410		};
411	}
412
413	// Check for duplicates.
414	for i in 1..items.len() {
415		if items[i].0 == items[i - 1].0 {
416			return Err(Error::DuplicateKey(items[i].0.to_vec()));
417		}
418	}
419
420	// Iterate simultaneously in order through proof nodes and key-value pairs to verify.
421	let mut proof_iter = proof.iter();
422	let mut items_iter = items.into_iter().peekable();
423
424	// A stack of child references to fill in omitted branch children for later trie nodes in the
425	// proof.
426	let mut stack: Vec<StackEntry<L::Codec>> = Vec::new();
427
428	let root_node = match proof_iter.next() {
429		Some(node) => node,
430		None => return Err(Error::IncompleteProof),
431	};
432	let mut last_entry = StackEntry::new(
433		root_node,
434		LeftNibbleSlice::new(&[]),
435		false
436	)?;
437	loop {
438		// Insert omitted value.
439		match last_entry.advance_item(&mut items_iter)? {
440			Step::Descend(child_prefix) => {
441				let next_entry = last_entry.advance_child_index(child_prefix, &mut proof_iter)?;
442				stack.push(last_entry);
443				last_entry = next_entry;
444			}
445			Step::UnwindStack => {
446				let is_inline = last_entry.is_inline;
447				let node_data = last_entry.encode_node()?;
448
449				let child_ref = if is_inline {
450					if node_data.len() > L::Hash::LENGTH {
451						return Err(Error::InvalidChildReference(node_data));
452					}
453					let mut hash = <TrieHash<L>>::default();
454					&mut hash.as_mut()[..node_data.len()].copy_from_slice(node_data.as_ref());
455					ChildReference::Inline(hash, node_data.len())
456				} else {
457					let hash = L::Hash::hash(&node_data);
458					ChildReference::Hash(hash)
459				};
460
461				if let Some(entry) = stack.pop() {
462					last_entry = entry;
463					last_entry.children[last_entry.child_index] = Some(child_ref);
464					last_entry.child_index += 1;
465				} else {
466					if proof_iter.next().is_some() {
467						return Err(Error::ExtraneousNode);
468					}
469					let computed_root = match child_ref {
470						ChildReference::Hash(hash) => hash,
471						ChildReference::Inline(_, _) => panic!(
472							"the bottom item on the stack has is_inline = false; qed"
473						),
474					};
475					if computed_root != *root {
476						return Err(Error::RootMismatch(computed_root));
477					}
478					break;
479				}
480			}
481		}
482	}
483
484	Ok(())
485}