sp_mmr_primitives/
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 primitive types.
19
20#![cfg_attr(not(feature = "std"), no_std)]
21#![warn(missing_docs)]
22
23extern crate alloc;
24
25pub use mmr_lib;
26
27#[cfg(not(feature = "std"))]
28use alloc::vec::Vec;
29use codec::{Decode, DecodeWithMemTracking, Encode};
30use core::fmt;
31use scale_info::TypeInfo;
32use sp_debug_derive::RuntimeDebug;
33use sp_runtime::traits;
34
35pub mod utils;
36
37/// Prefix for elements stored in the Off-chain DB via Indexing API.
38pub const INDEXING_PREFIX: &'static [u8] = b"mmr";
39
40/// A type to describe node position in the MMR (node index).
41pub type NodeIndex = u64;
42
43/// A type to describe leaf position in the MMR.
44///
45/// Note this is different from [`NodeIndex`], which can be applied to
46/// both leafs and inner nodes. Leafs will always have consecutive `LeafIndex`,
47/// but might be actually at different positions in the MMR `NodeIndex`.
48pub type LeafIndex = u64;
49
50/// A provider of the MMR's leaf data.
51pub trait LeafDataProvider {
52	/// A type that should end up in the leaf of MMR.
53	type LeafData: FullLeaf + codec::Decode;
54
55	/// The method to return leaf data that should be placed
56	/// in the leaf node appended MMR at this block.
57	///
58	/// This is being called by the `on_initialize` method of
59	/// this pallet at the very beginning of each block.
60	fn leaf_data() -> Self::LeafData;
61}
62
63impl LeafDataProvider for () {
64	type LeafData = ();
65
66	fn leaf_data() -> Self::LeafData {
67		()
68	}
69}
70
71/// New MMR root notification hook.
72pub trait OnNewRoot<Hash> {
73	/// Function called by the pallet in case new MMR root has been computed.
74	fn on_new_root(root: &Hash);
75}
76
77/// No-op implementation of [OnNewRoot].
78impl<Hash> OnNewRoot<Hash> for () {
79	fn on_new_root(_root: &Hash) {}
80}
81
82/// A full leaf content stored in the offchain-db.
83pub trait FullLeaf: Clone + PartialEq + fmt::Debug {
84	/// Encode the leaf either in its full or compact form.
85	///
86	/// NOTE the encoding returned here MUST be `Decode`able into `FullLeaf`.
87	fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F, compact: bool) -> R;
88}
89
90impl<T: codec::Encode + codec::Decode + Clone + PartialEq + fmt::Debug> FullLeaf for T {
91	fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F, _compact: bool) -> R {
92		codec::Encode::using_encoded(self, f)
93	}
94}
95
96/// A helper type to allow using arbitrary SCALE-encoded leaf data in the RuntimeApi.
97///
98/// The point is to be able to verify MMR proofs from external MMRs, where we don't
99/// know the exact leaf type, but it's enough for us to have it SCALE-encoded.
100///
101/// Note the leaf type should be encoded in its compact form when passed through this type.
102/// See [FullLeaf] documentation for details.
103///
104/// This type does not implement SCALE encoding/decoding on purpose to avoid confusion,
105/// it would have to be SCALE-compatible with the concrete leaf type, but due to SCALE limitations
106/// it's not possible to know how many bytes the encoding of concrete leaf type uses.
107#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
108#[derive(RuntimeDebug, Clone, PartialEq)]
109pub struct OpaqueLeaf(
110	/// Raw bytes of the leaf type encoded in its compact form.
111	///
112	/// NOTE it DOES NOT include length prefix (like `Vec<u8>` encoding would).
113	#[cfg_attr(feature = "serde", serde(with = "sp_core::bytes"))]
114	pub Vec<u8>,
115);
116
117impl OpaqueLeaf {
118	/// Convert a concrete MMR leaf into an opaque type.
119	pub fn from_leaf<T: FullLeaf>(leaf: &T) -> Self {
120		let encoded_leaf = leaf.using_encoded(|d| d.to_vec(), true);
121		OpaqueLeaf::from_encoded_leaf(encoded_leaf)
122	}
123
124	/// Create a `OpaqueLeaf` given raw bytes of compact-encoded leaf.
125	pub fn from_encoded_leaf(encoded_leaf: Vec<u8>) -> Self {
126		OpaqueLeaf(encoded_leaf)
127	}
128
129	/// Attempt to decode the leaf into expected concrete type.
130	pub fn try_decode<T: codec::Decode>(&self) -> Option<T> {
131		codec::Decode::decode(&mut &*self.0).ok()
132	}
133}
134
135impl FullLeaf for OpaqueLeaf {
136	fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F, _compact: bool) -> R {
137		f(&self.0)
138	}
139}
140
141/// A type-safe wrapper for the concrete leaf type.
142///
143/// This structure serves merely to avoid passing raw `Vec<u8>` around.
144/// It must be `Vec<u8>`-encoding compatible.
145///
146/// It is different from [`OpaqueLeaf`], because it does implement `Codec`
147/// and the encoding has to match raw `Vec<u8>` encoding.
148#[derive(codec::Encode, codec::Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)]
149pub struct EncodableOpaqueLeaf(pub Vec<u8>);
150
151impl EncodableOpaqueLeaf {
152	/// Convert a concrete leaf into encodable opaque version.
153	pub fn from_leaf<T: FullLeaf>(leaf: &T) -> Self {
154		let opaque = OpaqueLeaf::from_leaf(leaf);
155		Self::from_opaque_leaf(opaque)
156	}
157
158	/// Given an opaque leaf, make it encodable.
159	pub fn from_opaque_leaf(opaque: OpaqueLeaf) -> Self {
160		Self(opaque.0)
161	}
162
163	/// Try to convert into a [OpaqueLeaf].
164	pub fn into_opaque_leaf(self) -> OpaqueLeaf {
165		// wrap into `OpaqueLeaf` type
166		OpaqueLeaf::from_encoded_leaf(self.0)
167	}
168}
169
170/// An element representing either full data or its hash.
171///
172/// See [Compact] to see how it may be used in practice to reduce the size
173/// of proofs in case multiple [LeafDataProvider]s are composed together.
174/// This is also used internally by the MMR to differentiate leaf nodes (data)
175/// and inner nodes (hashes).
176///
177/// [DataOrHash::hash] method calculates the hash of this element in its compact form,
178/// so should be used instead of hashing the encoded form (which will always be non-compact).
179#[derive(RuntimeDebug, Clone, PartialEq)]
180pub enum DataOrHash<H: traits::Hash, L> {
181	/// Arbitrary data in its full form.
182	Data(L),
183	/// A hash of some data.
184	Hash(H::Output),
185}
186
187impl<H: traits::Hash, L> From<L> for DataOrHash<H, L> {
188	fn from(l: L) -> Self {
189		Self::Data(l)
190	}
191}
192
193mod encoding {
194	use super::*;
195
196	/// A helper type to implement [codec::Codec] for [DataOrHash].
197	#[derive(codec::Encode, codec::Decode)]
198	enum Either<A, B> {
199		Left(A),
200		Right(B),
201	}
202
203	impl<H: traits::Hash, L: FullLeaf> codec::Encode for DataOrHash<H, L> {
204		fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
205			match self {
206				Self::Data(l) => l.using_encoded(
207					|data| Either::<&[u8], &H::Output>::Left(data).encode_to(dest),
208					false,
209				),
210				Self::Hash(h) => Either::<&[u8], &H::Output>::Right(h).encode_to(dest),
211			}
212		}
213	}
214
215	impl<H: traits::Hash, L: FullLeaf + codec::Decode> codec::Decode for DataOrHash<H, L> {
216		fn decode<I: codec::Input>(value: &mut I) -> Result<Self, codec::Error> {
217			let decoded: Either<Vec<u8>, H::Output> = Either::decode(value)?;
218			Ok(match decoded {
219				Either::Left(l) => DataOrHash::Data(L::decode(&mut &*l)?),
220				Either::Right(r) => DataOrHash::Hash(r),
221			})
222		}
223	}
224}
225
226impl<H: traits::Hash, L: FullLeaf> DataOrHash<H, L> {
227	/// Retrieve a hash of this item.
228	///
229	/// Depending on the node type it's going to either be a contained value for [DataOrHash::Hash]
230	/// node, or a hash of SCALE-encoded [DataOrHash::Data] data.
231	pub fn hash(&self) -> H::Output {
232		match *self {
233			Self::Data(ref leaf) => leaf.using_encoded(<H as traits::Hash>::hash, true),
234			Self::Hash(ref hash) => *hash,
235		}
236	}
237}
238
239/// A composition of multiple leaf elements with compact form representation.
240///
241/// When composing together multiple [LeafDataProvider]s you will end up with
242/// a tuple of `LeafData` that each element provides.
243///
244/// However this will cause the leaves to have significant size, while for some
245/// use cases it will be enough to prove only one element of the tuple.
246/// That's the rationale for [Compact] struct. We wrap each element of the tuple
247/// into [DataOrHash] and each tuple element is hashed first before constructing
248/// the final hash of the entire tuple. This allows you to replace tuple elements
249/// you don't care about with their hashes.
250#[derive(RuntimeDebug, Clone, PartialEq)]
251pub struct Compact<H, T> {
252	/// Internal tuple representation.
253	pub tuple: T,
254	_hash: core::marker::PhantomData<H>,
255}
256
257impl<H, T> core::ops::Deref for Compact<H, T> {
258	type Target = T;
259
260	fn deref(&self) -> &Self::Target {
261		&self.tuple
262	}
263}
264
265impl<H, T> Compact<H, T> {
266	/// Create a new [Compact] wrapper for a tuple.
267	pub fn new(tuple: T) -> Self {
268		Self { tuple, _hash: Default::default() }
269	}
270}
271
272impl<H, T: codec::Decode> codec::Decode for Compact<H, T> {
273	fn decode<I: codec::Input>(value: &mut I) -> Result<Self, codec::Error> {
274		T::decode(value).map(Compact::new)
275	}
276}
277
278macro_rules! impl_leaf_data_for_tuple {
279	( $( $name:ident : $id:tt ),+ ) => {
280		/// [FullLeaf] implementation for `Compact<H, (DataOrHash<H, Tuple>, ...)>`
281		impl<H, $( $name ),+> FullLeaf for Compact<H, ( $( DataOrHash<H, $name>, )+ )> where
282			H: traits::Hash,
283			$( $name: FullLeaf ),+
284		{
285			fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F, compact: bool) -> R {
286				if compact {
287					codec::Encode::using_encoded(&(
288						$( DataOrHash::<H, $name>::Hash(self.tuple.$id.hash()), )+
289					), f)
290				} else {
291					codec::Encode::using_encoded(&self.tuple, f)
292				}
293			}
294		}
295
296		/// [LeafDataProvider] implementation for `Compact<H, (DataOrHash<H, Tuple>, ...)>`
297		///
298		/// This provides a compact-form encoding for tuples wrapped in [Compact].
299		impl<H, $( $name ),+> LeafDataProvider for Compact<H, ( $( $name, )+ )> where
300			H: traits::Hash,
301			$( $name: LeafDataProvider ),+
302		{
303			type LeafData = Compact<
304				H,
305				( $( DataOrHash<H, $name::LeafData>, )+ ),
306			>;
307
308			fn leaf_data() -> Self::LeafData {
309				let tuple = (
310					$( DataOrHash::Data($name::leaf_data()), )+
311				);
312				Compact::new(tuple)
313			}
314		}
315
316		/// [LeafDataProvider] implementation for `(Tuple, ...)`
317		///
318		/// This provides regular (non-compactable) composition of [LeafDataProvider]s.
319		impl<$( $name ),+> LeafDataProvider for ( $( $name, )+ ) where
320			( $( $name::LeafData, )+ ): FullLeaf,
321			$( $name: LeafDataProvider ),+
322		{
323			type LeafData = ( $( $name::LeafData, )+ );
324
325			fn leaf_data() -> Self::LeafData {
326				(
327					$( $name::leaf_data(), )+
328				)
329			}
330		}
331	}
332}
333
334/// Test functions implementation for `Compact<H, (DataOrHash<H, Tuple>, ...)>`
335#[cfg(test)]
336impl<H, A, B> Compact<H, (DataOrHash<H, A>, DataOrHash<H, B>)>
337where
338	H: traits::Hash,
339	A: FullLeaf,
340	B: FullLeaf,
341{
342	/// Retrieve a hash of this item in its compact form.
343	pub fn hash(&self) -> H::Output {
344		self.using_encoded(<H as traits::Hash>::hash, true)
345	}
346}
347
348impl_leaf_data_for_tuple!(A:0);
349impl_leaf_data_for_tuple!(A:0, B:1);
350impl_leaf_data_for_tuple!(A:0, B:1, C:2);
351impl_leaf_data_for_tuple!(A:0, B:1, C:2, D:3);
352impl_leaf_data_for_tuple!(A:0, B:1, C:2, D:3, E:4);
353
354/// An MMR proof data for a group of leaves.
355#[derive(codec::Encode, codec::Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)]
356pub struct LeafProof<Hash> {
357	/// The indices of the leaves the proof is for.
358	pub leaf_indices: Vec<LeafIndex>,
359	/// Number of leaves in MMR, when the proof was generated.
360	pub leaf_count: NodeIndex,
361	/// Proof elements (hashes of siblings of inner nodes on the path to the leafs).
362	pub items: Vec<Hash>,
363}
364
365/// An MMR ancestry proof for a prior mmr root.
366#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
367#[derive(Encode, Decode, DecodeWithMemTracking, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)]
368pub struct AncestryProof<Hash> {
369	/// Peaks of the ancestor's mmr
370	pub prev_peaks: Vec<Hash>,
371	/// Number of leaves in the ancestor's MMR.
372	pub prev_leaf_count: u64,
373	/// Number of leaves in MMR, when the proof was generated.
374	pub leaf_count: NodeIndex,
375	/// Proof elements
376	/// (positions and hashes of siblings of inner nodes on the path to the previous peaks).
377	pub items: Vec<(u64, Hash)>,
378}
379
380/// Merkle Mountain Range operation error.
381#[cfg_attr(feature = "std", derive(thiserror::Error))]
382#[derive(RuntimeDebug, codec::Encode, codec::Decode, PartialEq, Eq, TypeInfo)]
383pub enum Error {
384	/// Error during translation of a block number into a leaf index.
385	#[cfg_attr(feature = "std", error("Error performing numeric op"))]
386	InvalidNumericOp,
387	/// Error while pushing new node.
388	#[cfg_attr(feature = "std", error("Error pushing new node"))]
389	Push,
390	/// Error getting the new root.
391	#[cfg_attr(feature = "std", error("Error getting new root"))]
392	GetRoot,
393	/// Error committing changes.
394	#[cfg_attr(feature = "std", error("Error committing changes"))]
395	Commit,
396	/// Error during proof generation.
397	#[cfg_attr(feature = "std", error("Error generating proof"))]
398	GenerateProof,
399	/// Proof verification error.
400	#[cfg_attr(feature = "std", error("Invalid proof"))]
401	Verify,
402	/// Leaf not found in the storage.
403	#[cfg_attr(feature = "std", error("Leaf was not found"))]
404	LeafNotFound,
405	/// Mmr Pallet not included in runtime
406	#[cfg_attr(feature = "std", error("MMR pallet not included in runtime"))]
407	PalletNotIncluded,
408	/// Cannot find the requested leaf index
409	#[cfg_attr(feature = "std", error("Requested leaf index invalid"))]
410	InvalidLeafIndex,
411	/// The provided best know block number is invalid.
412	#[cfg_attr(feature = "std", error("Provided best known block number invalid"))]
413	InvalidBestKnownBlock,
414}
415
416impl Error {
417	#![allow(unused_variables)]
418	/// Consume given error `e` with `self` and generate a native log entry with error details.
419	pub fn log_error(self, e: impl fmt::Debug) -> Self {
420		log::error!(
421			target: "runtime::mmr",
422			"[{:?}] MMR error: {:?}",
423			self,
424			e,
425		);
426		self
427	}
428
429	/// Consume given error `e` with `self` and generate a native log entry with error details.
430	pub fn log_debug(self, e: impl fmt::Debug) -> Self {
431		log::debug!(
432			target: "runtime::mmr",
433			"[{:?}] MMR error: {:?}",
434			self,
435			e,
436		);
437		self
438	}
439}
440
441sp_api::decl_runtime_apis! {
442	/// API to interact with MMR pallet.
443	#[api_version(3)]
444	pub trait MmrApi<Hash: codec::Codec, BlockNumber: codec::Codec> {
445		/// Return the on-chain MMR root hash.
446		fn mmr_root() -> Result<Hash, Error>;
447
448		/// Return the number of MMR blocks in the chain.
449		fn mmr_leaf_count() -> Result<LeafIndex, Error>;
450
451		/// Generate MMR proof for a series of block numbers. If `best_known_block_number = Some(n)`,
452		/// use historical MMR state at given block height `n`. Else, use current MMR state.
453		fn generate_proof(
454			block_numbers: Vec<BlockNumber>,
455			best_known_block_number: Option<BlockNumber>
456		) -> Result<(Vec<EncodableOpaqueLeaf>, LeafProof<Hash>), Error>;
457
458		/// Generates a proof that the `prev_block_number` is part of the canonical chain at
459		/// `best_known_block_number`.
460		fn generate_ancestry_proof(
461			prev_block_number: BlockNumber,
462			best_known_block_number: Option<BlockNumber>,
463		) -> Result<AncestryProof<Hash>, Error>;
464
465		/// Verify MMR proof against on-chain MMR for a batch of leaves.
466		///
467		/// Note this function will use on-chain MMR root hash and check if the proof matches the hash.
468		/// Note, the leaves should be sorted such that corresponding leaves and leaf indices have the
469		/// same position in both the `leaves` vector and the `leaf_indices` vector contained in the [LeafProof]
470		fn verify_proof(leaves: Vec<EncodableOpaqueLeaf>, proof: LeafProof<Hash>) -> Result<(), Error>;
471
472		/// Verify MMR proof against given root hash for a batch of leaves.
473		///
474		/// Note this function does not require any on-chain storage - the
475		/// proof is verified against given MMR root hash.
476		///
477		/// Note, the leaves should be sorted such that corresponding leaves and leaf indices have the
478		/// same position in both the `leaves` vector and the `leaf_indices` vector contained in the [LeafProof]
479		fn verify_proof_stateless(root: Hash, leaves: Vec<EncodableOpaqueLeaf>, proof: LeafProof<Hash>)
480			-> Result<(), Error>;
481	}
482}
483
484#[cfg(test)]
485mod tests {
486	use super::*;
487
488	use codec::Decode;
489	use sp_core::H256;
490	use sp_runtime::traits::Keccak256;
491
492	pub(crate) fn hex(s: &str) -> H256 {
493		s.parse().unwrap()
494	}
495
496	type Test = DataOrHash<Keccak256, String>;
497	type TestCompact = Compact<Keccak256, (Test, Test)>;
498	type TestProof = LeafProof<<Keccak256 as traits::Hash>::Output>;
499
500	#[test]
501	fn should_encode_decode_proof() {
502		// given
503		let proof: TestProof = LeafProof {
504			leaf_indices: vec![5],
505			leaf_count: 10,
506			items: vec![
507				hex("c3e7ba6b511162fead58f2c8b5764ce869ed1118011ac37392522ed16720bbcd"),
508				hex("d3e7ba6b511162fead58f2c8b5764ce869ed1118011ac37392522ed16720bbcd"),
509				hex("e3e7ba6b511162fead58f2c8b5764ce869ed1118011ac37392522ed16720bbcd"),
510			],
511		};
512
513		// when
514		let encoded = codec::Encode::encode(&proof);
515		let decoded = TestProof::decode(&mut &*encoded);
516
517		// then
518		assert_eq!(decoded, Ok(proof));
519	}
520
521	#[test]
522	fn should_encode_decode_correctly_if_no_compact() {
523		// given
524		let cases = vec![
525			Test::Data("Hello World!".into()),
526			Test::Hash(hex("c3e7ba6b511162fead58f2c8b5764ce869ed1118011ac37392522ed16720bbcd")),
527			Test::Data("".into()),
528			Test::Data("3e48d6bcd417fb22e044747242451e2c0f3e602d1bcad2767c34808621956417".into()),
529		];
530
531		// when
532		let encoded = cases.iter().map(codec::Encode::encode).collect::<Vec<_>>();
533
534		let decoded = encoded.iter().map(|x| Test::decode(&mut &**x)).collect::<Vec<_>>();
535
536		// then
537		assert_eq!(
538			decoded,
539			cases.into_iter().map(Result::<_, codec::Error>::Ok).collect::<Vec<_>>()
540		);
541		// check encoding correctness
542		assert_eq!(
543			&encoded[0],
544			&array_bytes::hex2bytes_unchecked("00343048656c6c6f20576f726c6421")
545		);
546		assert_eq!(
547			encoded[1].as_slice(),
548			array_bytes::hex2bytes_unchecked(
549				"01c3e7ba6b511162fead58f2c8b5764ce869ed1118011ac37392522ed16720bbcd"
550			)
551			.as_slice()
552		);
553	}
554
555	#[test]
556	fn should_return_the_hash_correctly() {
557		// given
558		let a = Test::Data("Hello World!".into());
559		let b = Test::Hash(hex("c3e7ba6b511162fead58f2c8b5764ce869ed1118011ac37392522ed16720bbcd"));
560
561		// when
562		let a = a.hash();
563		let b = b.hash();
564
565		// then
566		assert_eq!(a, hex("a9c321be8c24ba4dc2bd73f5300bde67dc57228ab8b68b607bb4c39c5374fac9"));
567		assert_eq!(b, hex("c3e7ba6b511162fead58f2c8b5764ce869ed1118011ac37392522ed16720bbcd"));
568	}
569
570	#[test]
571	fn compact_should_work() {
572		// given
573		let a = Test::Data("Hello World!".into());
574		let b = Test::Data("".into());
575
576		// when
577		let c: TestCompact = Compact::new((a.clone(), b.clone()));
578		let d: TestCompact = Compact::new((Test::Hash(a.hash()), Test::Hash(b.hash())));
579
580		// then
581		assert_eq!(c.hash(), d.hash());
582	}
583
584	#[test]
585	fn compact_should_encode_decode_correctly() {
586		// given
587		let a = Test::Data("Hello World!".into());
588		let b = Test::Data("".into());
589
590		let c: TestCompact = Compact::new((a.clone(), b.clone()));
591		let d: TestCompact = Compact::new((Test::Hash(a.hash()), Test::Hash(b.hash())));
592		let cases = vec![c, d.clone()];
593
594		// when
595		let encoded_compact =
596			cases.iter().map(|c| c.using_encoded(|x| x.to_vec(), true)).collect::<Vec<_>>();
597
598		let encoded =
599			cases.iter().map(|c| c.using_encoded(|x| x.to_vec(), false)).collect::<Vec<_>>();
600
601		let decoded_compact = encoded_compact
602			.iter()
603			.map(|x| TestCompact::decode(&mut &**x))
604			.collect::<Vec<_>>();
605
606		let decoded = encoded.iter().map(|x| TestCompact::decode(&mut &**x)).collect::<Vec<_>>();
607
608		// then
609		assert_eq!(
610			decoded,
611			cases.into_iter().map(Result::<_, codec::Error>::Ok).collect::<Vec<_>>()
612		);
613
614		assert_eq!(decoded_compact, vec![Ok(d.clone()), Ok(d.clone())]);
615	}
616
617	#[test]
618	fn opaque_leaves_should_be_full_leaf_compatible() {
619		// given
620		let a = Test::Data("Hello World!".into());
621		let b = Test::Data("".into());
622
623		let c: TestCompact = Compact::new((a.clone(), b.clone()));
624		let d: TestCompact = Compact::new((Test::Hash(a.hash()), Test::Hash(b.hash())));
625		let cases = vec![c, d.clone()];
626
627		let encoded_compact = cases
628			.iter()
629			.map(|c| c.using_encoded(|x| x.to_vec(), true))
630			.map(OpaqueLeaf::from_encoded_leaf)
631			.collect::<Vec<_>>();
632
633		let opaque = cases.iter().map(OpaqueLeaf::from_leaf).collect::<Vec<_>>();
634
635		// then
636		assert_eq!(encoded_compact, opaque);
637	}
638
639	#[test]
640	fn encode_opaque_leaf_should_be_scale_compatible() {
641		use codec::Encode;
642
643		// given
644		let a = Test::Data("Hello World!".into());
645		let case1 = EncodableOpaqueLeaf::from_leaf(&a);
646		let case2 = EncodableOpaqueLeaf::from_opaque_leaf(OpaqueLeaf(a.encode()));
647		let case3 = a.encode().encode();
648
649		// when
650		let encoded = vec![&case1, &case2].into_iter().map(|x| x.encode()).collect::<Vec<_>>();
651		let decoded = vec![&*encoded[0], &*encoded[1], &*case3]
652			.into_iter()
653			.map(|x| EncodableOpaqueLeaf::decode(&mut &*x))
654			.collect::<Vec<_>>();
655
656		// then
657		assert_eq!(case1, case2);
658		assert_eq!(encoded[0], encoded[1]);
659		// then encoding should also match double-encoded leaf.
660		assert_eq!(encoded[0], case3);
661
662		assert_eq!(decoded[0], decoded[1]);
663		assert_eq!(decoded[1], decoded[2]);
664		assert_eq!(decoded[0], Ok(case2));
665		assert_eq!(decoded[1], Ok(case1));
666	}
667}