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