#![cfg_attr(not(feature = "std"), no_std)]
use cumulus_primitives_core::relay_chain;
use sp_core::H256;
use sp_runtime::traits::{Hash, HashingFor};
use sp_state_machine::{Backend, TrieBackend, TrieBackendBuilder};
use sp_std::vec::Vec;
use sp_trie::{HashDBT, MemoryDB, StorageProof, EMPTY_PREFIX};
#[cfg(test)]
mod tests;
#[derive(Debug, PartialEq)]
pub enum ProofError {
RootMismatch,
Proof,
Absent,
BlockNumberNotPresent,
}
pub type RawStorageProof = Vec<Vec<u8>>;
#[derive(Debug)]
pub struct StorageProofChecker<H>
where
H: Hash,
{
trie_backend: TrieBackend<MemoryDB<H>, H>,
}
impl<H: Hash> StorageProofChecker<H> {
pub fn new(
storage_root: H::Out,
raw_proof: impl IntoIterator<Item = Vec<u8>>,
) -> Result<Self, ProofError> {
let storage_proof = StorageProof::new(raw_proof);
let db = storage_proof.into_memory_db::<H>();
if !db.contains(&storage_root, EMPTY_PREFIX) {
return Err(ProofError::RootMismatch);
}
let trie_backend = TrieBackendBuilder::new(db, storage_root).build();
Ok(Self { trie_backend })
}
pub fn read_entry(&self, key: &[u8]) -> Result<Vec<u8>, ProofError> {
self.trie_backend
.storage(key)
.map_err(|_| ProofError::Proof)?
.ok_or(ProofError::Absent)
}
pub fn read_entries(&self, keys: &[&[u8]]) -> Result<Vec<Vec<u8>>, ProofError> {
let mut values = Vec::new();
for key in keys {
let value = self.read_entry(key)?;
values.push(value);
}
Ok(values)
}
}
pub fn verify_entry(
expected_root: H256,
proof: impl IntoIterator<Item = Vec<u8>>,
key: &[u8],
) -> Result<Vec<u8>, ProofError> {
let proof_checker =
StorageProofChecker::<HashingFor<relay_chain::Block>>::new(expected_root, proof)?;
proof_checker.read_entry(key)
}
pub fn verify_entries(
expected_root: H256,
proof: impl IntoIterator<Item = Vec<u8>>,
keys: &[&[u8]],
) -> Result<Vec<Vec<u8>>, ProofError> {
let proof_checker =
StorageProofChecker::<HashingFor<relay_chain::Block>>::new(expected_root, proof)?;
proof_checker.read_entries(keys)
}