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}