// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see .
//! Logic for working with storage proofs.
use pezframe_support::PalletError;
use pezsp_core::RuntimeDebug;
use pezsp_std::vec::Vec;
use pezsp_trie::{
accessed_nodes_tracker::AccessedNodesTracker, read_trie_value, LayoutV1, MemoryDB, StorageProof,
};
use codec::{Decode, DecodeWithMemTracking, Encode};
use hash_db::{HashDB, Hasher, EMPTY_PREFIX};
#[cfg(feature = "test-helpers")]
use pezsp_trie::{recorder_ext::RecorderExt, Recorder, TrieDBBuilder, TrieError, TrieHash};
use scale_info::TypeInfo;
#[cfg(feature = "test-helpers")]
use trie_db::{Trie, TrieConfiguration, TrieDBMut};
/// Errors that can occur when interacting with `UnverifiedStorageProof` and `VerifiedStorageProof`.
#[derive(
Clone, Encode, Decode, DecodeWithMemTracking, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo,
)]
pub enum StorageProofError {
/// Call to `generate_trie_proof()` failed.
UnableToGenerateTrieProof,
/// Call to `verify_trie_proof()` failed.
InvalidProof,
/// The `Vec` entries weren't sorted as expected.
UnsortedEntries,
/// The provided key wasn't found.
UnavailableKey,
/// The value associated to the provided key is `None`.
EmptyVal,
/// Error decoding value associated to a provided key.
DecodeError,
/// At least one key or node wasn't read.
UnusedKey,
/// Expected storage root is missing from the proof. (for non-compact proofs)
StorageRootMismatch,
/// Unable to reach expected storage value using provided trie nodes. (for non-compact proofs)
StorageValueUnavailable,
/// The proof contains duplicate nodes. (for non-compact proofs)
DuplicateNodes,
}
impl From for StorageProofError {
fn from(e: pezsp_trie::StorageProofError) -> Self {
match e {
pezsp_trie::StorageProofError::DuplicateNodes => StorageProofError::DuplicateNodes,
}
}
}
impl From for StorageProofError {
fn from(e: pezsp_trie::accessed_nodes_tracker::Error) -> Self {
match e {
pezsp_trie::accessed_nodes_tracker::Error::UnusedNodes => StorageProofError::UnusedKey,
}
}
}
/// Raw storage proof type (just raw trie nodes).
pub type RawStorageProof = pezsp_trie::RawStorageProof;
/// Calculates size for `RawStorageProof`.
pub fn raw_storage_proof_size(raw_storage_proof: &RawStorageProof) -> usize {
raw_storage_proof
.iter()
.fold(0usize, |sum, node| sum.saturating_add(node.len()))
}
/// Storage values size requirements.
///
/// This is currently used by benchmarks when generating storage proofs.
#[cfg(feature = "test-helpers")]
#[derive(Clone, Copy, Debug, Default)]
pub struct UnverifiedStorageProofParams {
/// Expected storage proof size in bytes.
pub db_size: Option,
}
#[cfg(feature = "test-helpers")]
impl UnverifiedStorageProofParams {
/// Make storage proof parameters that require proof of at least `db_size` bytes.
pub fn from_db_size(db_size: u32) -> Self {
Self { db_size: Some(db_size) }
}
}
/// This struct is used to read storage values from a subset of a Merklized database. The "proof"
/// is a subset of the nodes in the Merkle structure of the database, so that it provides
/// authentication against a known Merkle root as well as the values in the
/// database themselves.
pub struct StorageProofChecker
where
H: Hasher,
{
root: H::Out,
db: MemoryDB,
accessed_nodes_tracker: AccessedNodesTracker,
}
impl StorageProofChecker
where
H: Hasher,
{
/// Constructs a new storage proof checker.
///
/// This returns an error if the given proof is invalid with respect to the given root.
pub fn new(root: H::Out, proof: RawStorageProof) -> Result {
let proof = StorageProof::new_with_duplicate_nodes_check(proof)?;
let recorder = AccessedNodesTracker::new(proof.len());
let db = proof.into_memory_db();
if !db.contains(&root, EMPTY_PREFIX) {
return Err(StorageProofError::StorageRootMismatch);
}
Ok(StorageProofChecker { root, db, accessed_nodes_tracker: recorder })
}
/// Returns error if the proof has some nodes that are left intact by previous `read_value`
/// calls.
pub fn ensure_no_unused_nodes(self) -> Result<(), StorageProofError> {
self.accessed_nodes_tracker.ensure_no_unused_nodes().map_err(Into::into)
}
/// Reads a value from the available subset of storage. If the value cannot be read due to an
/// incomplete or otherwise invalid proof, this function returns an error.
pub fn read_value(&mut self, key: &[u8]) -> Result