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}