diff --git a/goas/cl/ledger/src/bundle.rs b/goas/cl/ledger/src/bundle.rs index 7517be3..030a3f1 100644 --- a/goas/cl/ledger/src/bundle.rs +++ b/goas/cl/ledger/src/bundle.rs @@ -1,4 +1,4 @@ -use crate::error::Result; +use crate::error::{Error, Result}; pub struct ProvedBundle { pub bundle: cl::Bundle, @@ -6,7 +6,7 @@ pub struct ProvedBundle { } impl ProvedBundle { - pub fn prove(bundle: &cl::Bundle, bundle_witness: &cl::BundleWitness) -> Self { + pub fn prove(bundle: &cl::Bundle, bundle_witness: &cl::BundleWitness) -> Result { // need to show that bundle is balanced. // i.e. the sum of ptx balances is 0 @@ -23,7 +23,7 @@ impl ProvedBundle { let opts = risc0_zkvm::ProverOpts::succinct(); let prove_info = prover .prove_with_opts(env, nomos_cl_risc0_proofs::BUNDLE_ELF, &opts) - .unwrap(); + .map_err(|_| Error::Risc0ProofFailed)?; println!( "STARK 'bundle' prover time: {:.2?}, total_cycles: {}", @@ -33,10 +33,10 @@ impl ProvedBundle { let receipt = prove_info.receipt; - Self { + Ok(Self { bundle: bundle.clone(), risc0_receipt: receipt, - } + }) } pub fn public(&self) -> Result { diff --git a/goas/cl/ledger/src/error.rs b/goas/cl/ledger/src/error.rs index 3204a72..4431a25 100644 --- a/goas/cl/ledger/src/error.rs +++ b/goas/cl/ledger/src/error.rs @@ -6,4 +6,6 @@ pub type Result = core::result::Result; pub enum Error { #[error("risc0 failed to serde")] Risc0Serde(#[from] risc0_zkvm::serde::Error), + #[error("risc0 failed to prove execution of the zkvm")] + Risc0ProofFailed, } diff --git a/goas/cl/ledger/src/input.rs b/goas/cl/ledger/src/input.rs index 49ad59f..87000ad 100644 --- a/goas/cl/ledger/src/input.rs +++ b/goas/cl/ledger/src/input.rs @@ -1,6 +1,6 @@ use proof_statements::input::{InputPrivate, InputPublic}; -use crate::error::Result; +use crate::error::{Error, Result}; const MAX_NOTE_COMMS: usize = 2usize.pow(8); @@ -11,7 +11,10 @@ pub struct ProvedInput { } impl ProvedInput { - pub fn prove(input: &cl::InputWitness, note_commitments: &[cl::NoteCommitment]) -> Self { + pub fn prove( + input: &cl::InputWitness, + note_commitments: &[cl::NoteCommitment], + ) -> Result { let output_cm = input.note_commitment(); let cm_leaves = note_commitment_leaves(note_commitments); @@ -42,7 +45,7 @@ impl ProvedInput { let opts = risc0_zkvm::ProverOpts::succinct(); let prove_info = prover .prove_with_opts(env, nomos_cl_risc0_proofs::INPUT_ELF, &opts) - .unwrap(); + .map_err(|_| Error::Risc0ProofFailed)?; println!( "STARK 'input' prover time: {:.2?}, total_cycles: {}", @@ -52,13 +55,13 @@ impl ProvedInput { // extract the receipt. let receipt = prove_info.receipt; - Self { + Ok(Self { input: InputPublic { cm_root: cl::merkle::root(cm_leaves), input: input.commit(), }, risc0_receipt: receipt, - } + }) } pub fn public(&self) -> Result { @@ -103,7 +106,7 @@ mod test { let notes = vec![input.note_commitment()]; - let mut proved_input = ProvedInput::prove(&input, ¬es); + let mut proved_input = ProvedInput::prove(&input, ¬es).unwrap(); let expected_public_inputs = InputPublic { cm_root: cl::merkle::root(note_commitment_leaves(¬es)), diff --git a/goas/cl/ledger/src/output.rs b/goas/cl/ledger/src/output.rs index 0ad6ee3..d3fd71b 100644 --- a/goas/cl/ledger/src/output.rs +++ b/goas/cl/ledger/src/output.rs @@ -1,4 +1,4 @@ -use crate::error::Result; +use crate::error::{Error, Result}; pub struct ProvedOutput { pub output: cl::Output, @@ -6,7 +6,7 @@ pub struct ProvedOutput { } impl ProvedOutput { - pub fn prove(witness: &cl::OutputWitness) -> Self { + pub fn prove(witness: &cl::OutputWitness) -> Result { let env = risc0_zkvm::ExecutorEnv::builder() .write(&witness) .unwrap() @@ -20,7 +20,7 @@ impl ProvedOutput { let opts = risc0_zkvm::ProverOpts::succinct(); let prove_info = prover .prove_with_opts(env, nomos_cl_risc0_proofs::OUTPUT_ELF, &opts) - .unwrap(); + .map_err(|_| Error::Risc0ProofFailed)?; println!( "STARK 'output' prover time: {:.2?}, total_cycles: {}", @@ -30,10 +30,10 @@ impl ProvedOutput { let receipt = prove_info.receipt; - Self { + Ok(Self { output: witness.commit(), risc0_receipt: receipt, - } + }) } pub fn public(&self) -> Result { @@ -70,7 +70,7 @@ mod test { nonce: cl::NullifierNonce::random(&mut rng), }; - let mut proved_output = ProvedOutput::prove(&output); + let mut proved_output = ProvedOutput::prove(&output).unwrap(); let expected_output_cm = output.commit(); @@ -100,4 +100,17 @@ mod test { assert!(!proved_output.verify()); } } + + #[test] + fn test_zero_output_is_rejected() { + let mut rng = thread_rng(); + + let output = cl::OutputWitness::random( + cl::NoteWitness::basic(0, "NMO"), + cl::NullifierSecret::random(&mut rng).commit(), + &mut rng, + ); + + assert!(ProvedOutput::prove(&output).is_err()); + } } diff --git a/goas/cl/ledger/src/partial_tx.rs b/goas/cl/ledger/src/partial_tx.rs index d647eba..998b729 100644 --- a/goas/cl/ledger/src/partial_tx.rs +++ b/goas/cl/ledger/src/partial_tx.rs @@ -2,7 +2,9 @@ use std::collections::BTreeMap; use proof_statements::death_constraint::DeathConstraintPublic; -use crate::{death_constraint::DeathProof, input::ProvedInput, output::ProvedOutput}; +use crate::{ + death_constraint::DeathProof, error::Result, input::ProvedInput, output::ProvedOutput, +}; #[derive(Debug, Clone)] pub struct PartialTxInput { @@ -29,18 +31,27 @@ impl ProvedPartialTx { ptx: &cl::PartialTxWitness, mut death_proofs: BTreeMap, note_commitments: &[cl::NoteCommitment], - ) -> ProvedPartialTx { - Self { - inputs: Vec::from_iter(ptx.inputs.iter().map(|i| { - PartialTxInput { - input: ProvedInput::prove(i, note_commitments), + ) -> Result { + let inputs = ptx + .inputs + .iter() + .map(|i| { + Ok(PartialTxInput { + input: ProvedInput::prove(i, note_commitments)?, death: death_proofs .remove(&i.nullifier()) .expect("Input missing death proof"), - } - })), - outputs: Vec::from_iter(ptx.outputs.iter().map(ProvedOutput::prove)), - } + }) + }) + .collect::>()?; + + let outputs = ptx + .outputs + .iter() + .map(ProvedOutput::prove) + .collect::>()?; + + Ok(Self { inputs, outputs }) } pub fn ptx(&self) -> cl::PartialTx { diff --git a/goas/cl/ledger/tests/simple_transfer.rs b/goas/cl/ledger/tests/simple_transfer.rs index 81ca545..9d83f48 100644 --- a/goas/cl/ledger/tests/simple_transfer.rs +++ b/goas/cl/ledger/tests/simple_transfer.rs @@ -68,7 +68,7 @@ fn test_simple_transfer() { // assume we only have one note commitment on chain for now ... let note_commitments = vec![utxo.commit_note()]; - let proved_ptx = ProvedPartialTx::prove(&ptx_witness, death_proofs, ¬e_commitments); + let proved_ptx = ProvedPartialTx::prove(&ptx_witness, death_proofs, ¬e_commitments).unwrap(); assert!(proved_ptx.verify()); // It's a valid ptx. @@ -80,6 +80,6 @@ fn test_simple_transfer() { balance_blinding: ptx_witness.balance_blinding(), }; - let proved_bundle = ProvedBundle::prove(&bundle, &bundle_witness); + let proved_bundle = ProvedBundle::prove(&bundle, &bundle_witness).unwrap(); assert!(proved_bundle.verify()); // The bundle is balanced. } diff --git a/goas/cl/risc0_proofs/output/src/main.rs b/goas/cl/risc0_proofs/output/src/main.rs index 33b5ee0..2674230 100644 --- a/goas/cl/risc0_proofs/output/src/main.rs +++ b/goas/cl/risc0_proofs/output/src/main.rs @@ -7,6 +7,12 @@ use risc0_zkvm::guest::env; fn main() { let output: cl::OutputWitness = env::read(); + + // 0 does not contribute to balance, implications of this are unclear + // therefore out of an abundance of caution, we disallow these zero + // valued "dummy notes". + assert!(output.note.value > 0); + let output_cm = output.commit(); env::commit(&output_cm); }