Skip to main content

topsoil_core/traits/
proving.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later WITH Classpath-exception-2.0
6
7//! Provides functionality for verifying proofs.
8
9use alloc::vec::Vec;
10use codec::{Decode, Encode};
11use subsoil::core::Hasher;
12use subsoil::runtime::DispatchError;
13
14// Re-export the `proving_trie` types and traits.
15pub use subsoil::runtime::proving_trie::*;
16
17/// Something that can verify the existence of some data in a given proof.
18pub trait VerifyExistenceProof {
19	/// The proof type.
20	type Proof;
21	/// The hash type.
22	type Hash;
23
24	/// Verify the given `proof`.
25	///
26	/// Ensures that the `proof` was build for `root` and returns the proved data.
27	fn verify_proof(proof: Self::Proof, root: &Self::Hash) -> Result<Vec<u8>, DispatchError>;
28}
29
30/// Implements [`VerifyExistenceProof`] using a binary merkle tree.
31pub struct BinaryMerkleTreeProver<H>(core::marker::PhantomData<H>);
32
33impl<H: Hasher> VerifyExistenceProof for BinaryMerkleTreeProver<H>
34where
35	H::Out: Decode + Encode,
36{
37	type Proof = subsoil::binary_merkle_tree::MerkleProof<H::Out, Vec<u8>>;
38	type Hash = H::Out;
39
40	fn verify_proof(proof: Self::Proof, root: &Self::Hash) -> Result<Vec<u8>, DispatchError> {
41		if proof.root != *root {
42			return Err(TrieError::RootMismatch.into());
43		}
44
45		if subsoil::binary_merkle_tree::verify_proof::<H, _, _>(
46			&proof.root,
47			proof.proof,
48			proof.number_of_leaves,
49			proof.leaf_index,
50			&proof.leaf,
51		) {
52			Ok(proof.leaf)
53		} else {
54			Err(TrieError::IncompleteProof.into())
55		}
56	}
57}
58
59impl<H: Hasher> ProofToHashes for BinaryMerkleTreeProver<H> {
60	type Proof = subsoil::binary_merkle_tree::MerkleProof<H::Out, Vec<u8>>;
61
62	// This base 2 merkle trie includes a `proof` field which is a `Vec<Hash>`.
63	// The length of this vector tells us the depth of the proof, and how many
64	// hashes we need to calculate.
65	fn proof_to_hashes(proof: &Self::Proof) -> Result<u32, DispatchError> {
66		let depth = proof.proof.len();
67		Ok(depth as u32)
68	}
69}
70
71/// Proof used by [`SixteenPatriciaMerkleTreeProver`] for [`VerifyExistenceProof`].
72#[derive(Encode, Decode, Clone)]
73pub struct SixteenPatriciaMerkleTreeExistenceProof {
74	/// The key of the value to prove.
75	pub key: Vec<u8>,
76	/// The value for that the existence is proved.
77	pub value: Vec<u8>,
78	/// The encoded nodes to prove the existence of the data under `key`.
79	pub proof: Vec<Vec<u8>>,
80}
81
82/// Implements [`VerifyExistenceProof`] using a 16-patricia merkle tree.
83pub struct SixteenPatriciaMerkleTreeProver<H>(core::marker::PhantomData<H>);
84
85impl<H: Hasher> VerifyExistenceProof for SixteenPatriciaMerkleTreeProver<H> {
86	type Proof = SixteenPatriciaMerkleTreeExistenceProof;
87	type Hash = H::Out;
88
89	fn verify_proof(proof: Self::Proof, root: &Self::Hash) -> Result<Vec<u8>, DispatchError> {
90		subsoil::trie::verify_trie_proof::<subsoil::trie::LayoutV1<H>, _, _, _>(
91			&root,
92			&proof.proof,
93			[&(&proof.key, Some(&proof.value))],
94		)
95		.map_err(|err| TrieError::from(err).into())
96		.map(|_| proof.value)
97	}
98}
99
100impl<H: Hasher> ProofToHashes for SixteenPatriciaMerkleTreeProver<H> {
101	type Proof = SixteenPatriciaMerkleTreeExistenceProof;
102
103	// This base 16 trie uses a raw proof of `Vec<Vec<u8>`, where the length of the first `Vec`
104	// is the depth of the trie. We can use this to predict the number of hashes.
105	fn proof_to_hashes(proof: &Self::Proof) -> Result<u32, DispatchError> {
106		let depth = proof.proof.len();
107		Ok(depth as u32)
108	}
109}
110
111#[cfg(test)]
112mod tests {
113	use super::*;
114	use subsoil::runtime::{
115		proving_trie::{base16::BasicProvingTrie, ProvingTrie},
116		traits::BlakeTwo256,
117	};
118
119	#[test]
120	fn verify_binary_merkle_tree_prover_works() {
121		let proof = subsoil::binary_merkle_tree::merkle_proof::<BlakeTwo256, _, _>(
122			vec![b"hey".encode(), b"yes".encode()],
123			1,
124		);
125		let root = proof.root;
126
127		assert_eq!(
128			BinaryMerkleTreeProver::<BlakeTwo256>::verify_proof(proof, &root).unwrap(),
129			b"yes".encode()
130		);
131	}
132
133	#[test]
134	fn verify_sixteen_patricia_merkle_tree_prover_works() {
135		let trie = BasicProvingTrie::<BlakeTwo256, u32, _>::generate_for(vec![
136			(0u32, String::from("hey")),
137			(1u32, String::from("yes")),
138		])
139		.unwrap();
140		let proof = trie.create_proof(&1u32).unwrap();
141		let structured_proof: Vec<Vec<u8>> = Decode::decode(&mut &proof[..]).unwrap();
142		let root = *trie.root();
143
144		let proof = SixteenPatriciaMerkleTreeExistenceProof {
145			key: 1u32.encode(),
146			value: String::from("yes").encode(),
147			proof: structured_proof,
148		};
149
150		assert_eq!(
151			SixteenPatriciaMerkleTreeProver::<BlakeTwo256>::verify_proof(proof, &root).unwrap(),
152			String::from("yes").encode()
153		);
154	}
155
156	#[test]
157	fn proof_to_hashes_sixteen() {
158		let mut i: u32 = 1;
159
160		// Compute log base 16 and round up
161		let log16 = |x: u32| -> u32 {
162			let x_f64 = x as f64;
163			let log16_x = (x_f64.ln() / 16_f64.ln()).ceil();
164			log16_x as u32
165		};
166
167		while i < 10_000_000 {
168			let trie = BasicProvingTrie::<BlakeTwo256, u32, _>::generate_for(
169				(0..i).map(|i| (i, u128::from(i))),
170			)
171			.unwrap();
172			let proof = trie.create_proof(&0).unwrap();
173			let structured_proof: Vec<Vec<u8>> = Decode::decode(&mut &proof[..]).unwrap();
174			let root = *trie.root();
175
176			let proof = SixteenPatriciaMerkleTreeExistenceProof {
177				key: 0u32.encode(),
178				value: 0u128.encode(),
179				proof: structured_proof,
180			};
181			let hashes =
182				SixteenPatriciaMerkleTreeProver::<BlakeTwo256>::proof_to_hashes(&proof).unwrap();
183			let log16 = log16(i).max(1);
184			assert_eq!(hashes, log16);
185
186			assert_eq!(
187				SixteenPatriciaMerkleTreeProver::<BlakeTwo256>::verify_proof(proof.clone(), &root)
188					.unwrap(),
189				proof.value
190			);
191
192			i = i * 10;
193		}
194	}
195
196	#[test]
197	fn proof_to_hashes_binary() {
198		let mut i: u32 = 1;
199		while i < 10_000_000 {
200			let proof = subsoil::binary_merkle_tree::merkle_proof::<BlakeTwo256, _, _>(
201				(0..i).map(|i| u128::from(i).encode()),
202				0,
203			);
204			let root = proof.root;
205
206			let hashes = BinaryMerkleTreeProver::<BlakeTwo256>::proof_to_hashes(&proof).unwrap();
207			let log2 = (i as f64).log2().ceil() as u32;
208			assert_eq!(hashes, log2);
209
210			assert_eq!(
211				BinaryMerkleTreeProver::<BlakeTwo256>::verify_proof(proof, &root).unwrap(),
212				0u128.encode()
213			);
214
215			i = i * 10;
216		}
217	}
218}