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	#[pallet::getter(fn mmr_leaves)]
226	pub type NumberOfLeaves<T, I = ()> = StorageValue<_, LeafIndex, ValueQuery>;
227
228	/// Hashes of the nodes in the MMR.
229	///
230	/// Note this collection only contains MMR peaks, the inner nodes (and leaves)
231	/// are pruned and only stored in the Offchain DB.
232	#[pallet::storage]
233	#[pallet::getter(fn mmr_peak)]
234	pub type Nodes<T: Config<I>, I: 'static = ()> =
235		StorageMap<_, Identity, NodeIndex, HashOf<T, I>, OptionQuery>;
236
237	/// Helper flag used in the runtime benchmarks for the initial setup.
238	#[cfg(feature = "runtime-benchmarks")]
239	#[pallet::storage]
240	pub type UseLocalStorage<T, I = ()> = StorageValue<_, bool, ValueQuery>;
241
242	#[pallet::hooks]
243	impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
244		fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
245			let leaves = NumberOfLeaves::<T, I>::get();
246			let peaks_before = NodesUtils::new(leaves).number_of_peaks();
247			let data = T::LeafData::leaf_data();
248
249			// append new leaf to MMR
250			let mut mmr: ModuleMmr<mmr::storage::RuntimeStorage, T, I> = mmr::Mmr::new(leaves);
251			// MMR push never fails, but better safe than sorry.
252			if mmr.push(data).is_none() {
253				log::error!(target: "runtime::mmr", "MMR push failed");
254				return T::WeightInfo::on_initialize(peaks_before as u32)
255			}
256			// Update the size, `mmr.finalize()` should also never fail.
257			let (leaves, root) = match mmr.finalize() {
258				Ok((leaves, root)) => (leaves, root),
259				Err(e) => {
260					log::error!(target: "runtime::mmr", "MMR finalize failed: {:?}", e);
261					return T::WeightInfo::on_initialize(peaks_before as u32)
262				},
263			};
264			<T::OnNewRoot as OnNewRoot<_>>::on_new_root(&root);
265
266			NumberOfLeaves::<T, I>::put(leaves);
267			RootHash::<T, I>::put(root);
268
269			let peaks_after = NodesUtils::new(leaves).number_of_peaks();
270
271			T::WeightInfo::on_initialize(peaks_before.max(peaks_after) as u32)
272		}
273	}
274}
275
276/// Stateless MMR proof verification for batch of leaves.
277///
278/// This function can be used to verify received MMR [primitives::LeafProof] (`proof`)
279/// for given leaves set (`leaves`) against a known MMR root hash (`root`).
280/// Note, the leaves should be sorted such that corresponding leaves and leaf indices have the
281/// same position in both the `leaves` vector and the `leaf_indices` vector contained in the
282/// [primitives::LeafProof].
283pub fn verify_leaves_proof<H, L>(
284	root: H::Output,
285	leaves: Vec<mmr::Node<H, L>>,
286	proof: LeafProof<H::Output>,
287) -> Result<(), Error>
288where
289	H: Hash,
290	L: FullLeaf,
291{
292	let is_valid = mmr::verify_leaves_proof::<H, L>(root, leaves, proof)?;
293	if is_valid {
294		Ok(())
295	} else {
296		Err(Error::Verify.log_debug(("The proof is incorrect.", root)))
297	}
298}
299
300/// Stateless ancestry proof verification.
301pub fn verify_ancestry_proof<H, L>(
302	root: H::Output,
303	ancestry_proof: AncestryProof<H::Output>,
304) -> Result<H::Output, Error>
305where
306	H: Hash,
307	L: FullLeaf,
308{
309	mmr::verify_ancestry_proof::<H, L>(root, ancestry_proof)
310		.map_err(|_| Error::Verify.log_debug(("The ancestry proof is incorrect.", root)))
311}
312
313impl<T: Config<I>, I: 'static> Pallet<T, I> {
314	/// Build offchain key from `parent_hash` of block that originally added node `pos` to MMR.
315	///
316	/// This combination makes the offchain (key,value) entry resilient to chain forks.
317	fn node_temp_offchain_key(
318		pos: NodeIndex,
319		parent_hash: <T as frame_system::Config>::Hash,
320	) -> Vec<u8> {
321		NodesUtils::node_temp_offchain_key::<HeaderFor<T>>(&T::INDEXING_PREFIX, pos, parent_hash)
322	}
323
324	/// Build canonical offchain key for node `pos` in MMR.
325	///
326	/// Used for nodes added by now finalized blocks.
327	/// Never read keys using `node_canon_offchain_key` unless you sure that
328	/// there's no `node_offchain_key` key in the storage.
329	fn node_canon_offchain_key(pos: NodeIndex) -> Vec<u8> {
330		NodesUtils::node_canon_offchain_key(&T::INDEXING_PREFIX, pos)
331	}
332
333	/// Provide the parent number for the block that added `leaf_index` to the MMR.
334	fn leaf_index_to_parent_block_num(leaf_index: LeafIndex) -> BlockNumberFor<T> {
335		// leaves are zero-indexed and were added one per block since pallet activation,
336		// while block numbers are one-indexed, so block number that added `leaf_idx` is:
337		// `block_num = block_num_when_pallet_activated + leaf_idx + 1`
338		// `block_num = (current_block_num - leaves_count) + leaf_idx + 1`
339		// `parent_block_num = current_block_num - leaves_count + leaf_idx`.
340		<frame_system::Pallet<T>>::block_number()
341			.saturating_sub(Self::mmr_leaves().saturated_into())
342			.saturating_add(leaf_index.saturated_into())
343	}
344
345	/// Convert a block number into a leaf index.
346	fn block_num_to_leaf_index(block_num: BlockNumberFor<T>) -> Result<LeafIndex, Error>
347	where
348		T: frame_system::Config,
349	{
350		let first_mmr_block = utils::first_mmr_block_num::<HeaderFor<T>>(
351			<frame_system::Pallet<T>>::block_number(),
352			NumberOfLeaves::<T, I>::get(),
353		)?;
354
355		utils::block_num_to_leaf_index::<HeaderFor<T>>(block_num, first_mmr_block)
356	}
357
358	/// Convert a block number into a leaf index.
359	pub fn block_num_to_leaf_count(block_num: BlockNumberFor<T>) -> Result<LeafIndex, Error>
360	where
361		T: frame_system::Config,
362	{
363		let leaf_index = Self::block_num_to_leaf_index(block_num)?;
364		Ok(leaf_index.saturating_add(1))
365	}
366
367	/// Generate an MMR proof for the given `block_numbers`.
368	/// If `best_known_block_number = Some(n)`, this generates a historical proof for
369	/// the chain with head at height `n`.
370	/// Else it generates a proof for the MMR at the current block height.
371	///
372	/// Note this method can only be used from an off-chain context
373	/// (Offchain Worker or Runtime API call), since it requires
374	/// all the leaves to be present.
375	/// It may return an error or panic if used incorrectly.
376	pub fn generate_proof(
377		block_numbers: Vec<BlockNumberFor<T>>,
378		best_known_block_number: Option<BlockNumberFor<T>>,
379	) -> Result<(Vec<LeafOf<T, I>>, LeafProof<HashOf<T, I>>), Error> {
380		// check whether best_known_block_number provided, else use current best block
381		let best_known_block_number =
382			best_known_block_number.unwrap_or_else(|| <frame_system::Pallet<T>>::block_number());
383
384		let leaf_count = Self::block_num_to_leaf_count(best_known_block_number)?;
385
386		// we need to translate the block_numbers into leaf indices.
387		let leaf_indices = block_numbers
388			.iter()
389			.map(|block_num| -> Result<LeafIndex, Error> {
390				Self::block_num_to_leaf_index(*block_num)
391			})
392			.collect::<Result<Vec<LeafIndex>, _>>()?;
393
394		let mmr: ModuleMmr<mmr::storage::OffchainStorage, T, I> = mmr::Mmr::new(leaf_count);
395		mmr.generate_proof(leaf_indices)
396	}
397
398	/// Verify MMR proof for given `leaves`.
399	///
400	/// This method is safe to use within the runtime code.
401	/// It will return `Ok(())` if the proof is valid
402	/// and an `Err(..)` if MMR is inconsistent (some leaves are missing)
403	/// or the proof is invalid.
404	pub fn verify_leaves(
405		leaves: Vec<LeafOf<T, I>>,
406		proof: LeafProof<HashOf<T, I>>,
407	) -> Result<(), Error> {
408		if proof.leaf_count > NumberOfLeaves::<T, I>::get() ||
409			proof.leaf_count == 0 ||
410			proof.items.len().saturating_add(leaves.len()) as u64 > proof.leaf_count
411		{
412			return Err(
413				Error::Verify.log_debug("The proof has incorrect number of leaves or proof items.")
414			)
415		}
416
417		let mmr: ModuleMmr<mmr::storage::OffchainStorage, T, I> = mmr::Mmr::new(proof.leaf_count);
418		let is_valid = mmr.verify_leaves_proof(leaves, proof)?;
419		if is_valid {
420			Ok(())
421		} else {
422			Err(Error::Verify.log_debug("The proof is incorrect."))
423		}
424	}
425
426	pub fn generate_ancestry_proof(
427		prev_block_number: BlockNumberFor<T>,
428		best_known_block_number: Option<BlockNumberFor<T>>,
429	) -> Result<AncestryProof<HashOf<T, I>>, Error> {
430		// check whether best_known_block_number provided, else use current best block
431		let best_known_block_number =
432			best_known_block_number.unwrap_or_else(|| <frame_system::Pallet<T>>::block_number());
433
434		let leaf_count = Self::block_num_to_leaf_count(best_known_block_number)?;
435		let prev_leaf_count = Self::block_num_to_leaf_count(prev_block_number)?;
436
437		let mmr: ModuleMmr<mmr::storage::OffchainStorage, T, I> = mmr::Mmr::new(leaf_count);
438		mmr.generate_ancestry_proof(prev_leaf_count)
439	}
440
441	#[cfg(feature = "runtime-benchmarks")]
442	pub fn generate_mock_ancestry_proof() -> Result<AncestryProof<HashOf<T, I>>, Error> {
443		let leaf_count = Self::block_num_to_leaf_count(<frame_system::Pallet<T>>::block_number())?;
444		let mmr: ModuleMmr<mmr::storage::OffchainStorage, T, I> = mmr::Mmr::new(leaf_count);
445		mmr.generate_mock_ancestry_proof()
446	}
447
448	pub fn is_ancestry_proof_optimal(
449		ancestry_proof: &primitives::AncestryProof<HashOf<T, I>>,
450	) -> bool {
451		mmr::is_ancestry_proof_optimal::<HashingOf<T, I>>(ancestry_proof)
452	}
453
454	pub fn verify_ancestry_proof(
455		root: HashOf<T, I>,
456		ancestry_proof: AncestryProof<HashOf<T, I>>,
457	) -> Result<HashOf<T, I>, Error> {
458		verify_ancestry_proof::<HashingOf<T, I>, LeafOf<T, I>>(root, ancestry_proof)
459	}
460
461	/// Return the on-chain MMR root hash.
462	pub fn mmr_root() -> HashOf<T, I> {
463		RootHash::<T, I>::get()
464	}
465}