Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions durable-storage/src/avl/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -170,6 +173,31 @@ impl<TreeId, M: BytesMode + AtomMode> Node<TreeId, M> {
}
}

/// Construct a [`Node`] from its branches in a proof, advancing the deserialiser context.
pub(super) fn from_branches<D: DeserialiserNode>(
ctx: D,
) -> Result<(D, Self), <D::Parent as Deserialiser>::Error>
where
Atom<Meta, M>: FromProof,
Bytes<M>: FromProof,
TreeId: FromProof,
{
let (ctx, meta) = ctx.next_branch::<Atom<Meta, M>>()?;
let (ctx, data) = ctx.next_branch::<Bytes<M>>()?;
let (ctx, left) = ctx.next_branch::<TreeId>()?;
let (ctx, right) = ctx.next_branch::<TreeId>()?;

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
Expand Down
112 changes: 112 additions & 0 deletions durable-storage/src/avl/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -515,6 +523,110 @@ impl<R: Resolver<LazyTreeId, Tree<LazyNodeId>>> Resolver<ProveTreeId, Tree<Prove
}
}

/// Identifier for a node resolved in [`Verify`] mode.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps worth a comment about why Absent doesn't exist

pub enum VerifyNodeId {
Present(Node<VerifyTreeId, Verify>),
Blind(Hash),
}

impl FromProof for VerifyNodeId {
fn from_proof<Proof: Deserialiser>(proof: Proof) -> SuspendedResult<Proof, Self> {
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<Arc<VerifyNodeId>>),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep the reference counting in the node/node ID, like LazyNodeId and ArcNodeId.

Blind(Hash),
Absent,
}

impl FromProof for VerifyTreeId {
fn from_proof<Proof: Deserialiser>(proof: Proof) -> SuspendedResult<Proof, Self> {
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::<bool>())?;
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() },
}
Comment on lines +561 to +573
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This portion should be done by the parser (not necessarily FromProof) for Tree. I think the VerifyTreeId type doesn't need to implement on behalf of Tree.

}
}
}
}

/// Adapter that projects in-memory AVL identifiers into verify-mode values.
pub struct VerifyResolver;

impl Resolver<VerifyNodeId, Node<VerifyTreeId, Verify>> for VerifyResolver {
fn resolve<'a>(
&self,
id: &'a VerifyNodeId,
) -> Result<&'a Node<VerifyTreeId, Verify>, 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<VerifyTreeId, Verify>, OperationalError> {
match id {
VerifyNodeId::Present(inner) => Ok(inner),
// SAFETY: called only in `Verify` mode
VerifyNodeId::Blind(_) => unsafe { not_found() },
}
}
}

impl Resolver<VerifyTreeId, Tree<Arc<VerifyNodeId>>> for VerifyResolver {
Copy link
Copy Markdown
Collaborator

@vapourismo vapourismo Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something doesn't add up here. Resolving VerifyTreeId -> Tree<Arc<VerifyNodeId>> implies that Arc<VerifyNodeId> is a node ID, but it's not. The Arc prevents it.

fn resolve<'a>(
&self,
id: &'a VerifyTreeId,
) -> Result<&'a Tree<Arc<VerifyNodeId>>, 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<Arc<VerifyNodeId>>, 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;
Expand Down
Loading