referrerpolicy=no-referrer-when-downgrade

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#[derive(Encode, Decode, DecodeWithMemTracking, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)]
367pub struct AncestryProof<Hash> {
368	/// Peaks of the ancestor's mmr
369	pub prev_peaks: Vec<Hash>,
370	/// Number of leaves in the ancestor's MMR.
371	pub prev_leaf_count: u64,
372	/// Number of leaves in MMR, when the proof was generated.
373	pub leaf_count: NodeIndex,
374	/// Proof elements
375	/// (positions and hashes of siblings of inner nodes on the path to the previous peaks).
376	pub items: Vec<(u64, Hash)>,
377}
378
379/// Merkle Mountain Range operation error.
380#[cfg_attr(feature = "std", derive(thiserror::Error))]
381#[derive(RuntimeDebug, codec::Encode, codec::Decode, PartialEq, Eq, TypeInfo)]
382pub enum Error {
383	/// Error during translation of a block number into a leaf index.
384	#[cfg_attr(feature = "std", error("Error performing numeric op"))]
385	InvalidNumericOp,
386	/// Error while pushing new node.
387	#[cfg_attr(feature = "std", error("Error pushing new node"))]
388	Push,
389	/// Error getting the new root.
390	#[cfg_attr(feature = "std", error("Error getting new root"))]
391	GetRoot,
392	/// Error committing changes.
393	#[cfg_attr(feature = "std", error("Error committing changes"))]
394	Commit,
395	/// Error during proof generation.
396	#[cfg_attr(feature = "std", error("Error generating proof"))]
397	GenerateProof,
398	/// Proof verification error.
399	#[cfg_attr(feature = "std", error("Invalid proof"))]
400	Verify,
401	/// Leaf not found in the storage.
402	#[cfg_attr(feature = "std", error("Leaf was not found"))]
403	LeafNotFound,
404	/// Mmr Pallet not included in runtime
405	#[cfg_attr(feature = "std", error("MMR pallet not included in runtime"))]
406	PalletNotIncluded,
407	/// Cannot find the requested leaf index
408	#[cfg_attr(feature = "std", error("Requested leaf index invalid"))]
409	InvalidLeafIndex,
410	/// The provided best know block number is invalid.
411	#[cfg_attr(feature = "std", error("Provided best known block number invalid"))]
412	InvalidBestKnownBlock,
413}
414
415impl Error {
416	#![allow(unused_variables)]
417	/// Consume given error `e` with `self` and generate a native log entry with error details.
418	pub fn log_error(self, e: impl fmt::Debug) -> Self {
419		log::error!(
420			target: "runtime::mmr",
421			"[{:?}] MMR error: {:?}",
422			self,
423			e,
424		);
425		self
426	}
427
428	/// Consume given error `e` with `self` and generate a native log entry with error details.
429	pub fn log_debug(self, e: impl fmt::Debug) -> Self {
430		log::debug!(
431			target: "runtime::mmr",
432			"[{:?}] MMR error: {:?}",
433			self,
434			e,
435		);
436		self
437	}
438}
439
440sp_api::decl_runtime_apis! {
441	/// API to interact with MMR pallet.
442	#[api_version(2)]
443	pub trait MmrApi<Hash: codec::Codec, BlockNumber: codec::Codec> {
444		/// Return the on-chain MMR root hash.
445		fn mmr_root() -> Result<Hash, Error>;
446
447		/// Return the number of MMR blocks in the chain.
448		fn mmr_leaf_count() -> Result<LeafIndex, Error>;
449
450		/// Generate MMR proof for a series of block numbers. If `best_known_block_number = Some(n)`,
451		/// use historical MMR state at given block height `n`. Else, use current MMR state.
452		fn generate_proof(
453			block_numbers: Vec<BlockNumber>,
454			best_known_block_number: Option<BlockNumber>
455		) -> Result<(Vec<EncodableOpaqueLeaf>, LeafProof<Hash>), Error>;
456
457		/// Verify MMR proof against on-chain MMR for a batch of leaves.
458		///
459		/// Note this function will use on-chain MMR root hash and check if the proof matches the hash.
460		/// Note, the leaves should be sorted such that corresponding leaves and leaf indices have the
461		/// same position in both the `leaves` vector and the `leaf_indices` vector contained in the [LeafProof]
462		fn verify_proof(leaves: Vec<EncodableOpaqueLeaf>, proof: LeafProof<Hash>) -> Result<(), Error>;
463
464		/// Verify MMR proof against given root hash for a batch of leaves.
465		///
466		/// Note this function does not require any on-chain storage - the
467		/// proof is verified against given MMR root hash.
468		///
469		/// Note, the leaves should be sorted such that corresponding leaves and leaf indices have the
470		/// same position in both the `leaves` vector and the `leaf_indices` vector contained in the [LeafProof]
471		fn verify_proof_stateless(root: Hash, leaves: Vec<EncodableOpaqueLeaf>, proof: LeafProof<Hash>)
472			-> Result<(), Error>;
473	}
474}
475
476#[cfg(test)]
477mod tests {
478	use super::*;
479
480	use codec::Decode;
481	use sp_core::H256;
482	use sp_runtime::traits::Keccak256;
483
484	pub(crate) fn hex(s: &str) -> H256 {
485		s.parse().unwrap()
486	}
487
488	type Test = DataOrHash<Keccak256, String>;
489	type TestCompact = Compact<Keccak256, (Test, Test)>;
490	type TestProof = LeafProof<<Keccak256 as traits::Hash>::Output>;
491
492	#[test]
493	fn should_encode_decode_proof() {
494		// given
495		let proof: TestProof = LeafProof {
496			leaf_indices: vec![5],
497			leaf_count: 10,
498			items: vec![
499				hex("c3e7ba6b511162fead58f2c8b5764ce869ed1118011ac37392522ed16720bbcd"),
500				hex("d3e7ba6b511162fead58f2c8b5764ce869ed1118011ac37392522ed16720bbcd"),
501				hex("e3e7ba6b511162fead58f2c8b5764ce869ed1118011ac37392522ed16720bbcd"),
502			],
503		};
504
505		// when
506		let encoded = codec::Encode::encode(&proof);
507		let decoded = TestProof::decode(&mut &*encoded);
508
509		// then
510		assert_eq!(decoded, Ok(proof));
511	}
512
513	#[test]
514	fn should_encode_decode_correctly_if_no_compact() {
515		// given
516		let cases = vec![
517			Test::Data("Hello World!".into()),
518			Test::Hash(hex("c3e7ba6b511162fead58f2c8b5764ce869ed1118011ac37392522ed16720bbcd")),
519			Test::Data("".into()),
520			Test::Data("3e48d6bcd417fb22e044747242451e2c0f3e602d1bcad2767c34808621956417".into()),
521		];
522
523		// when
524		let encoded = cases.iter().map(codec::Encode::encode).collect::<Vec<_>>();
525
526		let decoded = encoded.iter().map(|x| Test::decode(&mut &**x)).collect::<Vec<_>>();
527
528		// then
529		assert_eq!(
530			decoded,
531			cases.into_iter().map(Result::<_, codec::Error>::Ok).collect::<Vec<_>>()
532		);
533		// check encoding correctness
534		assert_eq!(
535			&encoded[0],
536			&array_bytes::hex2bytes_unchecked("00343048656c6c6f20576f726c6421")
537		);
538		assert_eq!(
539			encoded[1].as_slice(),
540			array_bytes::hex2bytes_unchecked(
541				"01c3e7ba6b511162fead58f2c8b5764ce869ed1118011ac37392522ed16720bbcd"
542			)
543			.as_slice()
544		);
545	}
546
547	#[test]
548	fn should_return_the_hash_correctly() {
549		// given
550		let a = Test::Data("Hello World!".into());
551		let b = Test::Hash(hex("c3e7ba6b511162fead58f2c8b5764ce869ed1118011ac37392522ed16720bbcd"));
552
553		// when
554		let a = a.hash();
555		let b = b.hash();
556
557		// then
558		assert_eq!(a, hex("a9c321be8c24ba4dc2bd73f5300bde67dc57228ab8b68b607bb4c39c5374fac9"));
559		assert_eq!(b, hex("c3e7ba6b511162fead58f2c8b5764ce869ed1118011ac37392522ed16720bbcd"));
560	}
561
562	#[test]
563	fn compact_should_work() {
564		// given
565		let a = Test::Data("Hello World!".into());
566		let b = Test::Data("".into());
567
568		// when
569		let c: TestCompact = Compact::new((a.clone(), b.clone()));
570		let d: TestCompact = Compact::new((Test::Hash(a.hash()), Test::Hash(b.hash())));
571
572		// then
573		assert_eq!(c.hash(), d.hash());
574	}
575
576	#[test]
577	fn compact_should_encode_decode_correctly() {
578		// given
579		let a = Test::Data("Hello World!".into());
580		let b = Test::Data("".into());
581
582		let c: TestCompact = Compact::new((a.clone(), b.clone()));
583		let d: TestCompact = Compact::new((Test::Hash(a.hash()), Test::Hash(b.hash())));
584		let cases = vec![c, d.clone()];
585
586		// when
587		let encoded_compact =
588			cases.iter().map(|c| c.using_encoded(|x| x.to_vec(), true)).collect::<Vec<_>>();
589
590		let encoded =
591			cases.iter().map(|c| c.using_encoded(|x| x.to_vec(), false)).collect::<Vec<_>>();
592
593		let decoded_compact = encoded_compact
594			.iter()
595			.map(|x| TestCompact::decode(&mut &**x))
596			.collect::<Vec<_>>();
597
598		let decoded = encoded.iter().map(|x| TestCompact::decode(&mut &**x)).collect::<Vec<_>>();
599
600		// then
601		assert_eq!(
602			decoded,
603			cases.into_iter().map(Result::<_, codec::Error>::Ok).collect::<Vec<_>>()
604		);
605
606		assert_eq!(decoded_compact, vec![Ok(d.clone()), Ok(d.clone())]);
607	}
608
609	#[test]
610	fn opaque_leaves_should_be_full_leaf_compatible() {
611		// given
612		let a = Test::Data("Hello World!".into());
613		let b = Test::Data("".into());
614
615		let c: TestCompact = Compact::new((a.clone(), b.clone()));
616		let d: TestCompact = Compact::new((Test::Hash(a.hash()), Test::Hash(b.hash())));
617		let cases = vec![c, d.clone()];
618
619		let encoded_compact = cases
620			.iter()
621			.map(|c| c.using_encoded(|x| x.to_vec(), true))
622			.map(OpaqueLeaf::from_encoded_leaf)
623			.collect::<Vec<_>>();
624
625		let opaque = cases.iter().map(OpaqueLeaf::from_leaf).collect::<Vec<_>>();
626
627		// then
628		assert_eq!(encoded_compact, opaque);
629	}
630
631	#[test]
632	fn encode_opaque_leaf_should_be_scale_compatible() {
633		use codec::Encode;
634
635		// given
636		let a = Test::Data("Hello World!".into());
637		let case1 = EncodableOpaqueLeaf::from_leaf(&a);
638		let case2 = EncodableOpaqueLeaf::from_opaque_leaf(OpaqueLeaf(a.encode()));
639		let case3 = a.encode().encode();
640
641		// when
642		let encoded = vec![&case1, &case2].into_iter().map(|x| x.encode()).collect::<Vec<_>>();
643		let decoded = vec![&*encoded[0], &*encoded[1], &*case3]
644			.into_iter()
645			.map(|x| EncodableOpaqueLeaf::decode(&mut &*x))
646			.collect::<Vec<_>>();
647
648		// then
649		assert_eq!(case1, case2);
650		assert_eq!(encoded[0], encoded[1]);
651		// then encoding should also match double-encoded leaf.
652		assert_eq!(encoded[0], case3);
653
654		assert_eq!(decoded[0], decoded[1]);
655		assert_eq!(decoded[1], decoded[2]);
656		assert_eq!(decoded[0], Ok(case2));
657		assert_eq!(decoded[1], Ok(case1));
658	}
659}