use super::{nibble, proof_decode};
use alloc::{vec, vec::Vec};
use core::{fmt, iter, mem};
mod tests;
pub struct Config<'a> {
pub prefix: &'a [u8],
pub trie_root_hash: [u8; 32],
pub full_storage_values_required: bool,
}
pub fn prefix_scan(config: Config) -> PrefixScan {
PrefixScan {
trie_root_hash: config.trie_root_hash,
full_storage_values_required: config.full_storage_values_required,
next_queries: vec![(
nibble::bytes_to_nibbles(config.prefix.iter().copied()).collect(),
QueryTy::Exact,
)],
final_result: Vec::with_capacity(32),
}
}
pub struct PrefixScan {
trie_root_hash: [u8; 32],
full_storage_values_required: bool,
next_queries: Vec<(Vec<nibble::Nibble>, QueryTy)>,
final_result: Vec<(Vec<u8>, StorageValue)>,
}
#[derive(Copy, Clone, Debug)]
enum QueryTy {
Exact,
Direction,
}
impl PrefixScan {
pub fn requested_keys(&self) -> impl Iterator<Item = impl Iterator<Item = nibble::Nibble>> {
self.next_queries.iter().map(|(l, _)| l.iter().copied())
}
pub fn request_storage_values(&self) -> bool {
self.full_storage_values_required
}
pub fn resume_all_keys(self, proof: &[u8]) -> Result<ResumeOutcome, (Self, Error)> {
self.resume_inner(false, proof)
}
pub fn resume_partial(self, proof: &[u8]) -> Result<ResumeOutcome, (Self, Error)> {
self.resume_inner(true, proof)
}
fn resume_inner(
mut self,
allow_incomplete_proof: bool,
proof: &[u8],
) -> Result<ResumeOutcome, (Self, Error)> {
let decoded_proof =
match proof_decode::decode_and_verify_proof(proof_decode::Config { proof }) {
Ok(d) => d,
Err(err) => return Err((self, Error::InvalidProof(err))),
};
let mut non_terminal_queries = mem::take(&mut self.next_queries);
for is_first_iteration in iter::once(true).chain(iter::repeat(false)) {
let mut next = Vec::with_capacity(non_terminal_queries.len() * 2);
debug_assert!(!non_terminal_queries.is_empty());
while let Some((query_key, query_ty)) = non_terminal_queries.pop() {
if !self.next_queries.is_empty() && is_first_iteration && !allow_incomplete_proof {
self.next_queries.extend(next.into_iter());
return Err((self, Error::MissingProofEntry));
}
let info = {
let info_of_node = match query_ty {
QueryTy::Exact => &query_key[..],
QueryTy::Direction => &query_key[..query_key.len() - 1],
};
match (
decoded_proof
.trie_node_info(&self.trie_root_hash, info_of_node.iter().copied()),
query_ty,
) {
(Ok(info), QueryTy::Exact) => info,
(Ok(info), QueryTy::Direction) => {
match info.children.child(query_key[query_key.len() - 1]) {
proof_decode::Child::InProof { child_key, .. } => {
next.push((child_key.collect::<Vec<_>>(), QueryTy::Exact));
continue;
}
proof_decode::Child::AbsentFromProof { .. } => {
self.next_queries.push((query_key, QueryTy::Direction));
continue;
}
proof_decode::Child::NoChild => {
unreachable!()
}
}
}
(Err(proof_decode::IncompleteProofError { .. }), _) => {
self.next_queries.push((query_key, query_ty));
continue;
}
}
};
if matches!(
info.storage_value,
proof_decode::StorageValue::Known { .. }
| proof_decode::StorageValue::HashKnownValueMissing(_)
) {
let value = match info.storage_value {
proof_decode::StorageValue::HashKnownValueMissing(_)
if self.full_storage_values_required =>
{
self.next_queries.push((query_key, query_ty));
continue;
}
proof_decode::StorageValue::HashKnownValueMissing(hash) => {
debug_assert!(!self.full_storage_values_required);
StorageValue::Hash(*hash)
}
proof_decode::StorageValue::Known { value, .. } => {
StorageValue::Value(value.to_vec())
}
proof_decode::StorageValue::None => unreachable!(),
};
debug_assert_eq!(query_key.len() % 2, 0);
let key = query_key
.chunks(2)
.map(|n| (u8::from(n[0]) << 4) | u8::from(n[1]))
.collect::<Vec<_>>();
debug_assert!(!self.final_result.iter().any(|(n, _)| *n == key));
self.final_result.push((key, value));
}
for (nibble, child) in info.children.children().enumerate() {
match child {
proof_decode::Child::NoChild => continue,
proof_decode::Child::AbsentFromProof { .. } => {
let mut direction = query_key.clone();
direction.push(
nibble::Nibble::try_from(u8::try_from(nibble).unwrap()).unwrap(),
);
next.push((direction, QueryTy::Direction));
}
proof_decode::Child::InProof { child_key, .. } => {
next.push((child_key.collect::<Vec<_>>(), QueryTy::Exact))
}
}
}
}
if next.is_empty() && self.next_queries.is_empty() {
return Ok(ResumeOutcome::Success {
entries: self.final_result,
full_storage_values_required: self.full_storage_values_required,
});
}
if next.is_empty() {
debug_assert!(!self.next_queries.is_empty());
if is_first_iteration {
return Err((self, Error::MissingProofEntry));
} else {
break;
}
}
non_terminal_queries = next;
}
Ok(ResumeOutcome::InProgress(self))
}
}
impl fmt::Debug for PrefixScan {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("PrefixScan").finish()
}
}
#[derive(Debug)]
pub enum ResumeOutcome {
InProgress(PrefixScan),
Success {
entries: Vec<(Vec<u8>, StorageValue)>,
full_storage_values_required: bool,
},
}
#[derive(Debug)]
pub enum StorageValue {
Value(Vec<u8>),
Hash([u8; 32]),
}
#[derive(Debug, Clone, derive_more::Display, derive_more::Error)]
pub enum Error {
#[display("{_0}")]
InvalidProof(proof_decode::Error),
MissingProofEntry,
}