diff --git a/durable-storage/src/avl/node.rs b/durable-storage/src/avl/node.rs index 865e2f19d8..6e98ef0435 100644 --- a/durable-storage/src/avl/node.rs +++ b/durable-storage/src/avl/node.rs @@ -19,6 +19,9 @@ use octez_riscv_data::foldable::Foldable; use octez_riscv_data::foldable::NodeFold; use octez_riscv_data::hash::Hash; use octez_riscv_data::hash::HashFold; +use octez_riscv_data::merkle_proof::Deserialiser; +use octez_riscv_data::merkle_proof::DeserialiserNode; +use octez_riscv_data::merkle_proof::FromProof; use octez_riscv_data::mode::Mode; use octez_riscv_data::mode::Normal; use octez_riscv_data::mode::Prove; @@ -170,6 +173,31 @@ impl Node { } } + /// Construct a [`Node`] from its branches in a proof, advancing the deserialiser context. + pub(super) fn from_branches( + ctx: D, + ) -> Result<(D, Self), ::Error> + where + Atom: FromProof, + Bytes: FromProof, + TreeId: FromProof, + { + let (ctx, meta) = ctx.next_branch::>()?; + let (ctx, data) = ctx.next_branch::>()?; + let (ctx, left) = ctx.next_branch::()?; + let (ctx, right) = ctx.next_branch::()?; + + let node = Node { + meta, + data, + left, + right, + hash: Default::default(), + }; + + Ok((ctx, node)) + } + /// Converts the [`Node`] to an encoded, serialisable representation, /// [`NodeHashRepresentation`], potentially re-hashing uncached [`Node`]s. pub(crate) fn to_encode<'a>(&'a self) -> impl Encode + 'a diff --git a/durable-storage/src/avl/resolver.rs b/durable-storage/src/avl/resolver.rs index d005b3ad25..eadaf75977 100644 --- a/durable-storage/src/avl/resolver.rs +++ b/durable-storage/src/avl/resolver.rs @@ -36,9 +36,17 @@ use octez_riscv_data::foldable::Fold; use octez_riscv_data::foldable::Foldable; use octez_riscv_data::hash::Hash; use octez_riscv_data::hash::HashFold; +use octez_riscv_data::merkle_proof::Deserialiser; +use octez_riscv_data::merkle_proof::DeserialiserNode; +use octez_riscv_data::merkle_proof::FromProof; +use octez_riscv_data::merkle_proof::Partial; +use octez_riscv_data::merkle_proof::Suspended; +use octez_riscv_data::merkle_proof::SuspendedResult; use octez_riscv_data::mode::Mode; use octez_riscv_data::mode::Normal; use octez_riscv_data::mode::Prove; +use octez_riscv_data::mode::Verify; +use octez_riscv_data::mode::utils::not_found; use octez_riscv_data::serialisation::deserialise; use trait_set::trait_set; @@ -515,6 +523,110 @@ impl>> Resolver), + Blind(Hash), +} + +impl FromProof for VerifyNodeId { + fn from_proof(proof: Proof) -> SuspendedResult { + let ctx = proof.into_node()?; + match ctx.presence() { + Partial::Blinded(hash) => ctx.done(VerifyNodeId::Blind(hash)), + Partial::Present(()) => { + let (ctx, node) = Node::from_branches(ctx)?; + ctx.done(VerifyNodeId::Present(node)) + } + // SAFETY: called only in `Verify` mode + Partial::Absent => unsafe { not_found() }, + } + } +} + +/// Identifier for a tree resolved in [`Verify`] mode. +pub enum VerifyTreeId { + Present(Tree>), + Blind(Hash), + Absent, +} + +impl FromProof for VerifyTreeId { + fn from_proof(proof: Proof) -> SuspendedResult { + let ctx = proof.into_node()?; + match ctx.presence() { + Partial::Absent => ctx.done(VerifyTreeId::Absent), + Partial::Blinded(hash) => ctx.done(VerifyTreeId::Blind(hash)), + Partial::Present(()) => { + let (ctx, present) = ctx.next_branch_with(|proof| proof.into_leaf::())?; + match present { + Partial::Present(true) => { + let (ctx, node_id) = ctx.next_branch_with(|proof| { + let suspended = VerifyNodeId::from_proof(proof)?; + Ok(suspended.map(Arc::new)) + })?; + ctx.done(VerifyTreeId::Present(Tree::from(Some(node_id)))) + } + Partial::Present(false) => ctx.done(VerifyTreeId::Present(Tree::default())), + // SAFETY: called only in `Verify` mode + Partial::Blinded(_) | Partial::Absent => unsafe { not_found() }, + } + } + } + } +} + +/// Adapter that projects in-memory AVL identifiers into verify-mode values. +pub struct VerifyResolver; + +impl Resolver> for VerifyResolver { + fn resolve<'a>( + &self, + id: &'a VerifyNodeId, + ) -> Result<&'a Node, OperationalError> { + match id { + VerifyNodeId::Present(inner) => Ok(inner), + // SAFETY: called only in `Verify` mode + VerifyNodeId::Blind(_) => unsafe { not_found() }, + } + } + + fn resolve_mut<'a>( + &mut self, + id: &'a mut VerifyNodeId, + ) -> Result<&'a mut Node, OperationalError> { + match id { + VerifyNodeId::Present(inner) => Ok(inner), + // SAFETY: called only in `Verify` mode + VerifyNodeId::Blind(_) => unsafe { not_found() }, + } + } +} + +impl Resolver>> for VerifyResolver { + fn resolve<'a>( + &self, + id: &'a VerifyTreeId, + ) -> Result<&'a Tree>, OperationalError> { + match id { + VerifyTreeId::Present(inner) => Ok(inner), + // SAFETY: called only in `Verify` mode + VerifyTreeId::Blind(_) | VerifyTreeId::Absent => unsafe { not_found() }, + } + } + + fn resolve_mut<'a>( + &mut self, + id: &'a mut VerifyTreeId, + ) -> Result<&'a mut Tree>, OperationalError> { + match id { + VerifyTreeId::Present(inner) => Ok(inner), + // SAFETY: called only in `Verify` mode + VerifyTreeId::Blind(_) | VerifyTreeId::Absent => unsafe { not_found() }, + } + } +} + #[cfg(test)] mod tests { use std::sync::Arc;