Skip to content
Draft
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
11 changes: 11 additions & 0 deletions src/riscv/lib/src/pvm/node_pvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use super::Pvm;
use super::outbox::OutboxProof;
use super::outbox::OutboxProofError;
use super::outbox::Output;
use super::outbox::OutputInfo;
use crate::machine_state::page_cache::EmptyPageCache;
use crate::machine_state::page_cache::PageCache;
use crate::machine_state::page_cache::PageCacheInterpreted;
Expand Down Expand Up @@ -234,6 +235,16 @@ impl NodePvm<Verify, EmptyPageCache> {
Some(pvm.input_request())
})
}

/// Read the outbox message at the given level and index.
///
/// In the context of a `NodePvm` in `Verify` mode instantiated from an outbox proof,
/// this function enables proof verification by checking that a particular outbox
/// message is stored in the otubox of a given PVM state commitment at the given
/// level and index.
pub fn read_outbox_message(&self, info: OutputInfo) -> Result<Output, OutboxProofError> {
self.with_backend(|pvm| pvm.get_outbox_message(info))
}
}

impl PartialEq for NodePvm {
Expand Down
10 changes: 10 additions & 0 deletions src/riscv/lib/src/pvm/outbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ impl OutboxProof {
Self { proof, info }
}

/// Get the output information
pub fn info(self) -> OutputInfo {
self.info
}

/// Get the Merkle proof
pub fn proof(&self) -> &MerkleProof {
&self.proof
}

/// Get the state hash of the outbox proof
pub fn state_hash(&self) -> Hash {
self.proof.root_hash()
Expand Down
44 changes: 37 additions & 7 deletions src/riscv/lib/src/stepper/pvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use octez_riscv_data::hash::Hash;
use octez_riscv_data::hash::HashFold;
use octez_riscv_data::hash::PartialHash;
use octez_riscv_data::hash::PartialHashFold;
use octez_riscv_data::merkle_proof::proof_tree::MerkleProof;
use octez_riscv_data::merkle_tree::MerkleTreeFold;
use octez_riscv_data::mode::Mode;
use octez_riscv_data::mode::Normal;
Expand Down Expand Up @@ -46,6 +47,7 @@ use crate::pvm::hooks::PvmHooks;
use crate::pvm::outbox::OutboxProof;
use crate::pvm::outbox::OutboxProofError;
use crate::pvm::outbox::Output;
use crate::pvm::outbox::OutputInfo;
use crate::range_utils::bound_saturating_sub;
use crate::state_backend::OwnedProofPart;
use crate::state_backend::ProofPart;
Expand Down Expand Up @@ -257,6 +259,14 @@ impl<H: PvmHooks, MC: MemoryConfig, PC: PageCache<MC, M>, M: AtomMode + DataSpac
}
}

/// Get the outbox message at the given level and index. This is the state transition
/// captured in outbox proofs.
///
/// Returns `None` if the requested message is not found in the outbox.
fn get_outbox_message(&self, info: OutputInfo) -> Result<Output, OutboxProofError> {
self.pvm.get_outbox_message(info)
}

/// Re-bind the PVM type by cloning the underlying regions.
pub fn rebind_via_clone(&mut self)
where
Expand Down Expand Up @@ -295,12 +305,10 @@ impl<H, MC: MemoryConfig, M: AtomMode + DataSpaceMode, PC: PageCache<MC, M>>
stepper.verify_proof_internal(ProofPart::Present(proof.tree()), proof.final_state_hash())
}

/// Verify a Merkle proof. The [`PvmStepper`] is used for inbox information.
pub fn verify_proof(&self, proof: Proof) -> Result<(), ProofVerificationFailure>
where
for<'a> MC::State<Verify>: Foldable<PartialHashFold<'a>>,
{
let proof_tree = ProofTree::Present(proof.tree());
fn start_verifier(
&self,
proof_tree: ProofPart<'_, MerkleProof>,
) -> Result<PvmStepper<NoHooks, MC, Verify, EmptyPageCache>, ProofVerificationFailure> {
let (pvm, deserialised_proof_tree) = deserialise_owned::deserialise(proof_tree)
.map_err(ProofVerificationFailure::BadDeserialisation)?;

Expand All @@ -313,10 +321,32 @@ impl<H, MC: MemoryConfig, M: AtomMode + DataSpaceMode, PC: PageCache<MC, M>>
"The Merkle proof tree obtained through deserialisation should match the original proof tree"
);

let stepper = self.to_verify_stepper(pvm)?;
self.to_verify_stepper(pvm)
}

/// Verify a Merkle proof. The [`PvmStepper`] is used for inbox information.
pub fn verify_proof(&self, proof: Proof) -> Result<(), ProofVerificationFailure>
where
for<'a> MC::State<Verify>: Foldable<PartialHashFold<'a>>,
{
let proof_tree = ProofTree::Present(proof.tree());
let stepper = self.start_verifier(proof_tree)?;
stepper.verify_proof_internal(proof_tree, proof.final_state_hash())
}

pub fn verify_outbox_proof(
&self,
outbox_proof: OutboxProof,
) -> Result<Output, ProofVerificationFailure> {
let proof_tree = ProofTree::Present(outbox_proof.proof());
let stepper = self.start_verifier(proof_tree)?;

let info = outbox_proof.info();
stepper
.get_outbox_message(info)
.map_err(|_| ProofVerificationFailure::StepperError)
}

fn to_verify_stepper(
&self,
pvm: Pvm<MC, EmptyPageCache, Verify>,
Expand Down
7 changes: 5 additions & 2 deletions src/riscv/lib/tests/test_outbox_proofs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,15 @@ fn test_outbox_proofs(inputs: &TestConfig) {
)
};

assert_eq!(stepper.hash(), proof.state_hash());

let mut mint = goldenfile::Mint::new(inputs.golden_dir);
let mut proof_capture = mint.new_goldenfile("outbox_proof").unwrap();
let proof_bytes = hex::encode(proof_serialisation);
writeln!(proof_capture, "{proof_bytes}").unwrap();

eprintln!("> Verifying outbox proof ...");
assert_eq!(stepper.hash(), proof.state_hash());
let output_from_proof = stepper.verify_outbox_proof(proof).unwrap();
assert_eq!(output, output_from_proof);
}

#[test]
Expand Down
Loading