trie_db/proof/
verify.rs

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