referrerpolicy=no-referrer-when-downgrade

pallet_mmr/
lib.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
18//! # Merkle Mountain Range
19//!
20//! ## Overview
21//!
22//! Details on Merkle Mountain Ranges (MMRs) can be found here:
23//! <https://github.com/mimblewimble/grin/blob/master/doc/mmr.md>
24//!
25//! The MMR pallet constructs an MMR from leaf data obtained on every block from
26//! `LeafDataProvider`. MMR nodes are stored both in:
27//! - on-chain storage - hashes only; not full leaf content;
28//! - off-chain storage - via Indexing API we push full leaf content (and all internal nodes as
29//! well) to the Off-chain DB, so that the data is available for Off-chain workers.
30//! Hashing used for MMR is configurable independently from the rest of the runtime (i.e. not using
31//! `frame_system::Hashing`) so something compatible with external chains can be used (like
32//! Keccak256 for Ethereum compatibility).
33//!
34//! Depending on the usage context (off-chain vs on-chain) the pallet is able to:
35//! - verify MMR leaf proofs (on-chain)
36//! - generate leaf proofs (off-chain)
37//!
38//! See [primitives::Compact] documentation for how you can optimize proof size for leafs that are
39//! composed from multiple elements.
40//!
41//! ## What for?
42//!
43//! Primary use case for this pallet is to generate MMR root hashes, that can latter on be used by
44//! BEEFY protocol (see <https://github.com/paritytech/grandpa-bridge-gadget>).
45//! MMR root hashes along with BEEFY will make it possible to build Super Light Clients (SLC) of
46//! Substrate-based chains. The SLC will be able to follow finality and can be shown proofs of more
47//! details that happened on the source chain.
48//! In that case the chain which contains the pallet generates the Root Hashes and Proofs, which
49//! are then presented to another chain acting as a light client which can verify them.
50//!
51//! Secondary use case is to archive historical data, but still be able to retrieve them on-demand
52//! if needed. For instance if parent block hashes are stored in the MMR it's possible at any point
53//! in time to provide an MMR proof about some past block hash, while this data can be safely pruned
54//! from on-chain storage.
55//!
56//! NOTE This pallet is experimental and not proven to work in production.
57#![cfg_attr(not(feature = "std"), no_std)]
58
59extern crate alloc;
60
61use alloc::vec::Vec;
62use log;
63
64use frame::prelude::*;
65
66pub use sp_mmr_primitives::{
67	self as primitives, utils, utils::NodesUtils, AncestryProof, Error, FullLeaf, LeafDataProvider,
68	LeafIndex, LeafProof, NodeIndex, OnNewRoot,
69};
70
71pub use pallet::*;
72
73#[cfg(feature = "runtime-benchmarks")]
74mod benchmarking;
75mod default_weights;
76mod mmr;
77#[cfg(test)]
78mod mock;
79#[cfg(test)]
80mod tests;
81
82/// The most common use case for MMRs is to store historical block hashes,
83/// so that any point in time in the future we can receive a proof about some past
84/// blocks without using excessive on-chain storage.
85///
86/// Hence we implement the [LeafDataProvider] for [ParentNumberAndHash] which is a
87/// crate-local wrapper over [frame_system::Pallet]. Since the current block hash
88/// is not available (since the block is not finished yet),
89/// we use the `parent_hash` here along with parent block number.
90pub struct ParentNumberAndHash<T: Config> {
91	_phantom: PhantomData<T>,
92}
93
94impl<T: Config> LeafDataProvider for ParentNumberAndHash<T> {
95	type LeafData = (BlockNumberFor<T>, <T as frame_system::Config>::Hash);
96
97	fn leaf_data() -> Self::LeafData {
98		(
99			frame_system::Pallet::<T>::block_number().saturating_sub(One::one()),
100			frame_system::Pallet::<T>::parent_hash(),
101		)
102	}
103}
104
105/// Block hash provider for a given block number.
106pub trait BlockHashProvider<BlockNumber, BlockHash> {
107	fn block_hash(block_number: BlockNumber) -> BlockHash;
108}
109
110/// Default implementation of BlockHashProvider using frame_system.
111pub struct DefaultBlockHashProvider<T: Config> {
112	_phantom: core::marker::PhantomData<T>,
113}
114
115impl<T: Config> BlockHashProvider<BlockNumberFor<T>, T::Hash> for DefaultBlockHashProvider<T> {
116	fn block_hash(block_number: BlockNumberFor<T>) -> T::Hash {
117		frame_system::Pallet::<T>::block_hash(block_number)
118	}
119}
120
121pub trait WeightInfo {
122	fn on_initialize(peaks: u32) -> Weight;
123}
124
125/// This trait decoples dependencies on pallets needed for benchmarking.
126#[cfg(feature = "runtime-benchmarks")]
127pub trait BenchmarkHelper {
128	fn setup();
129}
130
131#[cfg(feature = "runtime-benchmarks")]
132impl BenchmarkHelper for () {
133	fn setup() {}
134}
135
136/// An MMR specific to the pallet.
137type ModuleMmr<StorageType, T, I> = mmr::Mmr<StorageType, T, I, LeafOf<T, I>>;
138
139/// Leaf data.
140type LeafOf<T, I> = <<T as Config<I>>::LeafData as LeafDataProvider>::LeafData;
141
142/// Hashing used for the pallet.
143pub(crate) type HashingOf<T, I> = <T as Config<I>>::Hashing;
144/// Hash type used for the pallet.
145pub(crate) type HashOf<T, I> = <<T as Config<I>>::Hashing as Hash>::Output;
146
147#[frame::pallet]
148pub mod pallet {
149	use super::*;
150
151	#[pallet::pallet]
152	pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
153
154	/// This pallet's configuration trait
155	#[pallet::config]
156	pub trait Config<I: 'static = ()>: frame_system::Config {
157		/// Prefix for elements stored in the Off-chain DB via Indexing API.
158		///
159		/// Each node of the MMR is inserted both on-chain and off-chain via Indexing API.
160		/// The former does not store full leaf content, just its compact version (hash),
161		/// and some of the inner mmr nodes might be pruned from on-chain storage.
162		/// The latter will contain all the entries in their full form.
163		///
164		/// Each node is stored in the Off-chain DB under key derived from the
165		/// [`Self::INDEXING_PREFIX`] and its in-tree index (MMR position).
166		const INDEXING_PREFIX: &'static [u8];
167
168		/// A hasher type for MMR.
169		///
170		/// To construct trie nodes that result in merging (bagging) two peaks, depending on the
171		/// node kind we take either:
172		/// - The node (hash) itself if it's an inner node.
173		/// - The hash of SCALE-encoding of the leaf data if it's a leaf node.
174		///
175		/// Then we create a tuple of these two hashes, SCALE-encode it (concatenate) and
176		/// hash, to obtain a new MMR inner node - the new peak.
177		type Hashing: Hash;
178
179		/// Data stored in the leaf nodes.
180		///
181		/// The [LeafData](primitives::LeafDataProvider) is responsible for returning the entire
182		/// leaf data that will be inserted to the MMR.
183		/// [LeafDataProvider](primitives::LeafDataProvider)s can be composed into tuples to put
184		/// multiple elements into the tree. In such a case it might be worth using
185		/// [primitives::Compact] to make MMR proof for one element of the tuple leaner.
186		///
187		/// Note that the leaf at each block MUST be unique. You may want to include a block hash or
188		/// block number as an easiest way to ensure that.
189		/// Also note that the leaf added by each block is expected to only reference data coming
190		/// from ancestor blocks (leaves are saved offchain using `(pos, parent_hash)` key to be
191		/// fork-resistant, as such conflicts could only happen on 1-block deep forks, which means
192		/// two forks with identical line of ancestors compete to write the same offchain key, but
193		/// that's fine as long as leaves only contain data coming from ancestors - conflicting
194		/// writes are identical).
195		type LeafData: LeafDataProvider;
196
197		/// A hook to act on the new MMR root.
198		///
199		/// For some applications it might be beneficial to make the MMR root available externally
200		/// apart from having it in the storage. For instance you might output it in the header
201		/// digest (see [`frame_system::Pallet::deposit_log`]) to make it available for Light
202		/// Clients. Hook complexity should be `O(1)`.
203		type OnNewRoot: OnNewRoot<HashOf<Self, I>>;
204
205		/// Block hash provider for a given block number.
206		type BlockHashProvider: BlockHashProvider<
207			BlockNumberFor<Self>,
208			<Self as frame_system::Config>::Hash,
209		>;
210
211		/// Weights for this pallet.
212		type WeightInfo: WeightInfo;
213
214		/// Benchmarking setup helper trait.
215		#[cfg(feature = "runtime-benchmarks")]
216		type BenchmarkHelper: BenchmarkHelper;
217	}
218
219	/// Latest MMR Root hash.
220	#[pallet::storage]
221	pub type RootHash<T: Config<I>, I: 'static = ()> = StorageValue<_, HashOf<T, I>, ValueQuery>;
222
223	/// Current size of the MMR (number of leaves).
224	#[pallet::storage]
225	pub type NumberOfLeaves<T, I = ()> = StorageValue<_, LeafIndex, ValueQuery>;
226
227	/// Hashes of the nodes in the MMR.
228	///
229	/// Note this collection only contains MMR peaks, the inner nodes (and leaves)
230	/// are pruned and only stored in the Offchain DB.
231	#[pallet::storage]
232	pub type Nodes<T: Config<I>, I: 'static = ()> =
233		StorageMap<_, Identity, NodeIndex, HashOf<T, I>, OptionQuery>;
234
235	/// Helper flag used in the runtime benchmarks for the initial setup.
236	#[cfg(feature = "runtime-benchmarks")]
237	#[pallet::storage]
238	pub type UseLocalStorage<T, I = ()> = StorageValue<_, bool, ValueQuery>;
239
240	#[pallet::hooks]
241	impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
242		fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
243			let leaves = NumberOfLeaves::<T, I>::get();
244			let peaks_before = NodesUtils::new(leaves).number_of_peaks();
245			let data = T::LeafData::leaf_data();
246
247			// append new leaf to MMR
248			let mut mmr: ModuleMmr<mmr::storage::RuntimeStorage, T, I> = mmr::Mmr::new(leaves);
249			// MMR push never fails, but better safe than sorry.
250			if mmr.push(data).is_none() {
251				log::error!(target: "runtime::mmr", "MMR push failed");
252				return T::WeightInfo::on_initialize(peaks_before as u32);
253			}
254			// Update the size, `mmr.finalize()` should also never fail.
255			let (leaves, root) = match mmr.finalize() {
256				Ok((leaves, root)) => (leaves, root),
257				Err(e) => {
258					log::error!(target: "runtime::mmr", "MMR finalize failed: {:?}", e);
259					return T::WeightInfo::on_initialize(peaks_before as u32);
260				},
261			};
262			<T::OnNewRoot as OnNewRoot<_>>::on_new_root(&root);
263
264			NumberOfLeaves::<T, I>::put(leaves);
265			RootHash::<T, I>::put(root);
266
267			let peaks_after = NodesUtils::new(leaves).number_of_peaks();
268
269			T::WeightInfo::on_initialize(peaks_before.max(peaks_after) as u32)
270		}
271	}
272}
273
274/// Stateless MMR proof verification for batch of leaves.
275///
276/// This function can be used to verify received MMR [primitives::LeafProof] (`proof`)
277/// for given leaves set (`leaves`) against a known MMR root hash (`root`).
278/// Note, the leaves should be sorted such that corresponding leaves and leaf indices have the
279/// same position in both the `leaves` vector and the `leaf_indices` vector contained in the
280/// [primitives::LeafProof].
281pub fn verify_leaves_proof<H, L>(
282	root: H::Output,
283	leaves: Vec<mmr::Node<H, L>>,
284	proof: LeafProof<H::Output>,
285) -> Result<(), Error>
286where
287	H: Hash,
288	L: FullLeaf,
289{
290	let is_valid = mmr::verify_leaves_proof::<H, L>(root, leaves, proof)?;
291	if is_valid {
292		Ok(())
293	} else {
294		Err(Error::Verify.log_debug(("The proof is incorrect.", root)))
295	}
296}
297
298/// Stateless ancestry proof verification.
299pub fn verify_ancestry_proof<H, L>(
300	root: H::Output,
301	ancestry_proof: AncestryProof<H::Output>,
302) -> Result<H::Output, Error>
303where
304	H: Hash,
305	L: FullLeaf,
306{
307	mmr::verify_ancestry_proof::<H, L>(root, ancestry_proof)
308		.map_err(|_| Error::Verify.log_debug(("The ancestry proof is incorrect.", root)))
309}
310
311impl<T: Config<I>, I: 'static> Pallet<T, I> {
312	/// Build offchain key from `parent_hash` of block that originally added node `pos` to MMR.
313	///
314	/// This combination makes the offchain (key,value) entry resilient to chain forks.
315	fn node_temp_offchain_key(
316		pos: NodeIndex,
317		parent_hash: <T as frame_system::Config>::Hash,
318	) -> Vec<u8> {
319		NodesUtils::node_temp_offchain_key::<HeaderFor<T>>(&T::INDEXING_PREFIX, pos, parent_hash)
320	}
321
322	/// Build canonical offchain key for node `pos` in MMR.
323	///
324	/// Used for nodes added by now finalized blocks.
325	/// Never read keys using `node_canon_offchain_key` unless you sure that
326	/// there's no `node_offchain_key` key in the storage.
327	fn node_canon_offchain_key(pos: NodeIndex) -> Vec<u8> {
328		NodesUtils::node_canon_offchain_key(&T::INDEXING_PREFIX, pos)
329	}
330
331	/// Provide the parent number for the block that added `leaf_index` to the MMR.
332	fn leaf_index_to_parent_block_num(leaf_index: LeafIndex) -> BlockNumberFor<T> {
333		// leaves are zero-indexed and were added one per block since pallet activation,
334		// while block numbers are one-indexed, so block number that added `leaf_idx` is:
335		// `block_num = block_num_when_pallet_activated + leaf_idx + 1`
336		// `block_num = (current_block_num - leaves_count) + leaf_idx + 1`
337		// `parent_block_num = current_block_num - leaves_count + leaf_idx`.
338		<frame_system::Pallet<T>>::block_number()
339			.saturating_sub(NumberOfLeaves::<T, I>::get().saturated_into())
340			.saturating_add(leaf_index.saturated_into())
341	}
342
343	/// Convert a block number into a leaf index.
344	fn block_num_to_leaf_index(block_num: BlockNumberFor<T>) -> Result<LeafIndex, Error>
345	where
346		T: frame_system::Config,
347	{
348		let first_mmr_block = utils::first_mmr_block_num::<HeaderFor<T>>(
349			<frame_system::Pallet<T>>::block_number(),
350			NumberOfLeaves::<T, I>::get(),
351		)?;
352
353		utils::block_num_to_leaf_index::<HeaderFor<T>>(block_num, first_mmr_block)
354	}
355
356	/// Convert a block number into a leaf index.
357	pub fn block_num_to_leaf_count(block_num: BlockNumberFor<T>) -> Result<LeafIndex, Error>
358	where
359		T: frame_system::Config,
360	{
361		let leaf_index = Self::block_num_to_leaf_index(block_num)?;
362		Ok(leaf_index.saturating_add(1))
363	}
364
365	/// Generate an MMR proof for the given `block_numbers`.
366	/// If `best_known_block_number = Some(n)`, this generates a historical proof for
367	/// the chain with head at height `n`.
368	/// Else it generates a proof for the MMR at the current block height.
369	///
370	/// Note this method can only be used from an off-chain context
371	/// (Offchain Worker or Runtime API call), since it requires
372	/// all the leaves to be present.
373	/// It may return an error or panic if used incorrectly.
374	pub fn generate_proof(
375		block_numbers: Vec<BlockNumberFor<T>>,
376		best_known_block_number: Option<BlockNumberFor<T>>,
377	) -> Result<(Vec<LeafOf<T, I>>, LeafProof<HashOf<T, I>>), Error> {
378		// check whether best_known_block_number provided, else use current best block
379		let best_known_block_number =
380			best_known_block_number.unwrap_or_else(|| <frame_system::Pallet<T>>::block_number());
381
382		let leaf_count = Self::block_num_to_leaf_count(best_known_block_number)?;
383
384		// we need to translate the block_numbers into leaf indices.
385		let leaf_indices = block_numbers
386			.iter()
387			.map(|block_num| -> Result<LeafIndex, Error> {
388				Self::block_num_to_leaf_index(*block_num)
389			})
390			.collect::<Result<Vec<LeafIndex>, _>>()?;
391
392		let mmr: ModuleMmr<mmr::storage::OffchainStorage, T, I> = mmr::Mmr::new(leaf_count);
393		mmr.generate_proof(leaf_indices)
394	}
395
396	/// Verify MMR proof for given `leaves`.
397	///
398	/// This method is safe to use within the runtime code.
399	/// It will return `Ok(())` if the proof is valid
400	/// and an `Err(..)` if MMR is inconsistent (some leaves are missing)
401	/// or the proof is invalid.
402	pub fn verify_leaves(
403		leaves: Vec<LeafOf<T, I>>,
404		proof: LeafProof<HashOf<T, I>>,
405	) -> Result<(), Error> {
406		if proof.leaf_count > NumberOfLeaves::<T, I>::get() ||
407			proof.leaf_count == 0 ||
408			proof.items.len().saturating_add(leaves.len()) as u64 > proof.leaf_count
409		{
410			return Err(
411				Error::Verify.log_debug("The proof has incorrect number of leaves or proof items.")
412			);
413		}
414
415		let mmr: ModuleMmr<mmr::storage::OffchainStorage, T, I> = mmr::Mmr::new(proof.leaf_count);
416		let is_valid = mmr.verify_leaves_proof(leaves, proof)?;
417		if is_valid {
418			Ok(())
419		} else {
420			Err(Error::Verify.log_debug("The proof is incorrect."))
421		}
422	}
423
424	pub fn generate_ancestry_proof(
425		prev_block_number: BlockNumberFor<T>,
426		best_known_block_number: Option<BlockNumberFor<T>>,
427	) -> Result<AncestryProof<HashOf<T, I>>, Error> {
428		// check whether best_known_block_number provided, else use current best block
429		let best_known_block_number =
430			best_known_block_number.unwrap_or_else(|| <frame_system::Pallet<T>>::block_number());
431
432		let leaf_count = Self::block_num_to_leaf_count(best_known_block_number)?;
433		let prev_leaf_count = Self::block_num_to_leaf_count(prev_block_number)?;
434
435		let mmr: ModuleMmr<mmr::storage::OffchainStorage, T, I> = mmr::Mmr::new(leaf_count);
436		mmr.generate_ancestry_proof(prev_leaf_count)
437	}
438
439	#[cfg(feature = "runtime-benchmarks")]
440	pub fn generate_mock_ancestry_proof() -> Result<AncestryProof<HashOf<T, I>>, Error> {
441		let leaf_count = Self::block_num_to_leaf_count(<frame_system::Pallet<T>>::block_number())?;
442		let mmr: ModuleMmr<mmr::storage::OffchainStorage, T, I> = mmr::Mmr::new(leaf_count);
443		mmr.generate_mock_ancestry_proof()
444	}
445
446	pub fn is_ancestry_proof_optimal(
447		ancestry_proof: &primitives::AncestryProof<HashOf<T, I>>,
448	) -> bool {
449		mmr::is_ancestry_proof_optimal::<HashingOf<T, I>>(ancestry_proof)
450	}
451
452	pub fn verify_ancestry_proof(
453		root: HashOf<T, I>,
454		ancestry_proof: AncestryProof<HashOf<T, I>>,
455	) -> Result<HashOf<T, I>, Error> {
456		verify_ancestry_proof::<HashingOf<T, I>, LeafOf<T, I>>(root, ancestry_proof)
457	}
458
459	/// Return the on-chain MMR root hash.
460	pub fn mmr_root() -> HashOf<T, I> {
461		RootHash::<T, I>::get()
462	}
463
464	/// Returns the current size of the MMR (number of leaves).
465	pub fn mmr_leaves() -> LeafIndex {
466		NumberOfLeaves::<T, I>::get()
467	}
468
469	/// Returns the hash of a node in the MMR, if one exists.
470	pub fn mmr_peak(node_index: NodeIndex) -> Option<HashOf<T, I>> {
471		Nodes::<T, I>::get(node_index)
472	}
473}