referrerpolicy=no-referrer-when-downgrade

pallet_mmr/mmr/
mmr.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use crate::{
19	mmr::{
20		storage::{OffchainStorage, RuntimeStorage, Storage},
21		Hasher, Node, NodeOf,
22	},
23	primitives::{
24		mmr_lib, mmr_lib::MMRStoreReadOps, utils::NodesUtils, AncestryProof, Error, FullLeaf,
25		LeafIndex, LeafProof, NodeIndex,
26	},
27	Config, HashOf, HashingOf,
28};
29use alloc::vec::Vec;
30use frame::prelude::*;
31
32/// Stateless verification of the proof for a batch of leaves.
33/// Note, the leaves should be sorted such that corresponding leaves and leaf indices have the
34/// same position in both the `leaves` vector and the `leaf_indices` vector contained in the
35/// [primitives::LeafProof]
36pub fn verify_leaves_proof<H, L>(
37	root: H::Output,
38	leaves: Vec<Node<H, L>>,
39	proof: LeafProof<H::Output>,
40) -> Result<bool, Error>
41where
42	H: Hash,
43	L: FullLeaf,
44{
45	let size = NodesUtils::new(proof.leaf_count).size();
46
47	if leaves.len() != proof.leaf_indices.len() {
48		return Err(Error::Verify.log_debug("Proof leaf_indices not same length with leaves"))
49	}
50
51	let leaves_and_position_data = proof
52		.leaf_indices
53		.into_iter()
54		.map(|index| mmr_lib::leaf_index_to_pos(index))
55		.zip(leaves.into_iter())
56		.collect();
57
58	let p = mmr_lib::MerkleProof::<Node<H, L>, Hasher<H, L>>::new(
59		size,
60		proof.items.into_iter().map(Node::Hash).collect(),
61	);
62	p.verify(Node::Hash(root), leaves_and_position_data)
63		.map_err(|e| Error::Verify.log_debug(e))
64}
65
66pub fn is_ancestry_proof_optimal<H>(ancestry_proof: &AncestryProof<H::Output>) -> bool
67where
68	H: frame::traits::Hash,
69{
70	let prev_mmr_size = NodesUtils::new(ancestry_proof.prev_leaf_count).size();
71	let mmr_size = NodesUtils::new(ancestry_proof.leaf_count).size();
72
73	let expected_proof_size =
74		mmr_lib::ancestry_proof::expected_ancestry_proof_size(prev_mmr_size, mmr_size);
75	ancestry_proof.items.len() == expected_proof_size
76}
77
78pub fn verify_ancestry_proof<H, L>(
79	root: H::Output,
80	ancestry_proof: AncestryProof<H::Output>,
81) -> Result<H::Output, Error>
82where
83	H: Hash,
84	L: FullLeaf,
85{
86	let mmr_size = NodesUtils::new(ancestry_proof.leaf_count).size();
87
88	let prev_peaks_proof = mmr_lib::NodeMerkleProof::<Node<H, L>, Hasher<H, L>>::new(
89		mmr_size,
90		ancestry_proof
91			.items
92			.into_iter()
93			.map(|(index, hash)| (index, Node::Hash(hash)))
94			.collect(),
95	);
96
97	let raw_ancestry_proof = mmr_lib::AncestryProof::<Node<H, L>, Hasher<H, L>> {
98		prev_mmr_size: mmr_lib::helper::leaf_index_to_mmr_size(ancestry_proof.prev_leaf_count - 1),
99		prev_peaks: ancestry_proof.prev_peaks.into_iter().map(|hash| Node::Hash(hash)).collect(),
100		prev_peaks_proof,
101	};
102
103	let prev_root = mmr_lib::ancestry_proof::bagging_peaks_hashes::<Node<H, L>, Hasher<H, L>>(
104		raw_ancestry_proof.prev_peaks.clone(),
105	)
106	.map_err(|e| Error::Verify.log_debug(e))?;
107	raw_ancestry_proof
108		.verify_ancestor(Node::Hash(root), prev_root.clone())
109		.map_err(|e| Error::Verify.log_debug(e))?;
110
111	Ok(prev_root.hash())
112}
113
114/// A wrapper around an MMR library to expose limited functionality.
115///
116/// Available functions depend on the storage kind ([Runtime](crate::mmr::storage::RuntimeStorage)
117/// vs [Off-chain](crate::mmr::storage::OffchainStorage)).
118pub struct Mmr<StorageType, T, I, L>
119where
120	T: Config<I>,
121	I: 'static,
122	L: FullLeaf,
123	Storage<StorageType, T, I, L>:
124		MMRStoreReadOps<NodeOf<T, I, L>> + mmr_lib::MMRStoreWriteOps<NodeOf<T, I, L>>,
125{
126	mmr: mmr_lib::MMR<NodeOf<T, I, L>, Hasher<HashingOf<T, I>, L>, Storage<StorageType, T, I, L>>,
127	leaves: NodeIndex,
128}
129
130impl<StorageType, T, I, L> Mmr<StorageType, T, I, L>
131where
132	T: Config<I>,
133	I: 'static,
134	L: FullLeaf,
135	Storage<StorageType, T, I, L>:
136		MMRStoreReadOps<NodeOf<T, I, L>> + mmr_lib::MMRStoreWriteOps<NodeOf<T, I, L>>,
137{
138	/// Create a pointer to an existing MMR with given number of leaves.
139	pub fn new(leaves: NodeIndex) -> Self {
140		let size = NodesUtils::new(leaves).size();
141		Self { mmr: mmr_lib::MMR::new(size, Default::default()), leaves }
142	}
143
144	/// Verify proof for a set of leaves.
145	/// Note, the leaves should be sorted such that corresponding leaves and leaf indices have
146	/// the same position in both the `leaves` vector and the `leaf_indices` vector contained in the
147	/// [primitives::LeafProof]
148	pub fn verify_leaves_proof(
149		&self,
150		leaves: Vec<L>,
151		proof: LeafProof<HashOf<T, I>>,
152	) -> Result<bool, Error> {
153		let p = mmr_lib::MerkleProof::<NodeOf<T, I, L>, Hasher<HashingOf<T, I>, L>>::new(
154			self.mmr.mmr_size(),
155			proof.items.into_iter().map(Node::Hash).collect(),
156		);
157
158		if leaves.len() != proof.leaf_indices.len() {
159			return Err(Error::Verify.log_debug("Proof leaf_indices not same length with leaves"))
160		}
161
162		let leaves_positions_and_data = proof
163			.leaf_indices
164			.into_iter()
165			.map(|index| mmr_lib::leaf_index_to_pos(index))
166			.zip(leaves.into_iter().map(|leaf| Node::Data(leaf)))
167			.collect();
168		let root = self.mmr.get_root().map_err(|e| Error::GetRoot.log_error(e))?;
169		p.verify(root, leaves_positions_and_data)
170			.map_err(|e| Error::Verify.log_debug(e))
171	}
172
173	/// Return the internal size of the MMR (number of nodes).
174	#[cfg(any(test, feature = "runtime-benchmarks"))]
175	pub fn size(&self) -> NodeIndex {
176		self.mmr.mmr_size()
177	}
178}
179
180/// Runtime specific MMR functions.
181impl<T, I, L> Mmr<RuntimeStorage, T, I, L>
182where
183	T: Config<I>,
184	I: 'static,
185	L: FullLeaf,
186{
187	/// Push another item to the MMR.
188	///
189	/// Returns element position (index) in the MMR.
190	pub fn push(&mut self, leaf: L) -> Option<NodeIndex> {
191		let position =
192			self.mmr.push(Node::Data(leaf)).map_err(|e| Error::Push.log_error(e)).ok()?;
193
194		self.leaves += 1;
195
196		Some(position)
197	}
198
199	/// Commit the changes to underlying storage, return current number of leaves and
200	/// calculate the new MMR's root hash.
201	pub fn finalize(mut self) -> Result<(NodeIndex, HashOf<T, I>), Error> {
202		let root = self.mmr.get_root().map_err(|e| Error::GetRoot.log_error(e))?;
203		self.mmr.commit().map_err(|e| Error::Commit.log_error(e))?;
204		Ok((self.leaves, root.hash()))
205	}
206}
207
208/// Off-chain specific MMR functions.
209impl<T, I, L> Mmr<OffchainStorage, T, I, L>
210where
211	T: Config<I>,
212	I: 'static,
213	L: FullLeaf + codec::Decode,
214{
215	/// Generate a proof for given leaf indices.
216	///
217	/// Proof generation requires all the nodes (or their hashes) to be available in the storage.
218	/// (i.e. you can't run the function in the pruned storage).
219	pub fn generate_proof(
220		&self,
221		leaf_indices: Vec<NodeIndex>,
222	) -> Result<(Vec<L>, LeafProof<HashOf<T, I>>), Error> {
223		let positions = leaf_indices
224			.iter()
225			.map(|index| mmr_lib::leaf_index_to_pos(*index))
226			.collect::<Vec<_>>();
227		let store = <Storage<OffchainStorage, T, I, L>>::default();
228		let leaves = positions
229			.iter()
230			.map(|pos| match store.get_elem(*pos) {
231				Ok(Some(Node::Data(leaf))) => Ok(leaf),
232				e => Err(Error::LeafNotFound.log_debug(e)),
233			})
234			.collect::<Result<Vec<_>, Error>>()?;
235
236		let leaf_count = self.leaves;
237		self.mmr
238			.gen_proof(positions)
239			.map_err(|e| Error::GenerateProof.log_error(e))
240			.map(|p| LeafProof {
241				leaf_indices,
242				leaf_count,
243				items: p.proof_items().iter().map(|x| x.hash()).collect(),
244			})
245			.map(|p| (leaves, p))
246	}
247
248	pub fn generate_ancestry_proof(
249		&self,
250		prev_leaf_count: LeafIndex,
251	) -> Result<AncestryProof<HashOf<T, I>>, Error> {
252		let prev_mmr_size = NodesUtils::new(prev_leaf_count).size();
253		let raw_ancestry_proof = self
254			.mmr
255			.gen_ancestry_proof(prev_mmr_size)
256			.map_err(|e| Error::GenerateProof.log_error(e))?;
257
258		Ok(AncestryProof {
259			prev_peaks: raw_ancestry_proof.prev_peaks.into_iter().map(|p| p.hash()).collect(),
260			prev_leaf_count,
261			leaf_count: self.leaves,
262			items: raw_ancestry_proof
263				.prev_peaks_proof
264				.proof_items()
265				.iter()
266				.map(|(index, item)| (*index, item.hash()))
267				.collect(),
268		})
269	}
270
271	/// Generate an inflated ancestry proof for the latest leaf in the MMR.
272	///
273	/// The generated proof contains all the leafs in the MMR, so this way we can generate a proof
274	/// with exactly `leaf_count` items.
275	#[cfg(feature = "runtime-benchmarks")]
276	pub fn generate_mock_ancestry_proof(&self) -> Result<AncestryProof<HashOf<T, I>>, Error> {
277		use crate::ModuleMmr;
278		use alloc::vec;
279		use mmr_lib::helper;
280
281		let mmr: ModuleMmr<OffchainStorage, T, I> = Mmr::new(self.leaves);
282		let store = <Storage<OffchainStorage, T, I, L>>::default();
283
284		let mut prev_peaks = vec![];
285		for peak_pos in helper::get_peaks(mmr.size()) {
286			let peak = store
287				.get_elem(peak_pos)
288				.map_err(|_| Error::GenerateProof)?
289				.ok_or(Error::GenerateProof)?
290				.hash();
291			prev_peaks.push(peak);
292		}
293
294		let mut proof_items = vec![];
295		for leaf_idx in 0..self.leaves {
296			let leaf_pos = NodesUtils::leaf_index_to_leaf_node_index(leaf_idx);
297			let leaf = store
298				.get_elem(leaf_pos)
299				.map_err(|_| Error::GenerateProof)?
300				.ok_or(Error::GenerateProof)?
301				.hash();
302			proof_items.push((leaf_pos, leaf));
303		}
304
305		Ok(AncestryProof {
306			prev_peaks,
307			prev_leaf_count: self.leaves,
308			leaf_count: self.leaves,
309			items: proof_items,
310		})
311	}
312}