Merge pull request #13 from logos-co/cl/ensure-output-value-is-non-zero

cl: ban zero valued outputs
This commit is contained in:
davidrusu 2024-07-30 18:42:34 +04:00 committed by GitHub
commit e9d43eaee9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 64 additions and 29 deletions

View File

@ -1,4 +1,4 @@
use crate::error::Result; use crate::error::{Error, Result};
pub struct ProvedBundle { pub struct ProvedBundle {
pub bundle: cl::Bundle, pub bundle: cl::Bundle,
@ -6,7 +6,7 @@ pub struct ProvedBundle {
} }
impl 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<Self> {
// need to show that bundle is balanced. // need to show that bundle is balanced.
// i.e. the sum of ptx balances is 0 // i.e. the sum of ptx balances is 0
@ -23,7 +23,7 @@ impl ProvedBundle {
let opts = risc0_zkvm::ProverOpts::succinct(); let opts = risc0_zkvm::ProverOpts::succinct();
let prove_info = prover let prove_info = prover
.prove_with_opts(env, nomos_cl_risc0_proofs::BUNDLE_ELF, &opts) .prove_with_opts(env, nomos_cl_risc0_proofs::BUNDLE_ELF, &opts)
.unwrap(); .map_err(|_| Error::Risc0ProofFailed)?;
println!( println!(
"STARK 'bundle' prover time: {:.2?}, total_cycles: {}", "STARK 'bundle' prover time: {:.2?}, total_cycles: {}",
@ -33,10 +33,10 @@ impl ProvedBundle {
let receipt = prove_info.receipt; let receipt = prove_info.receipt;
Self { Ok(Self {
bundle: bundle.clone(), bundle: bundle.clone(),
risc0_receipt: receipt, risc0_receipt: receipt,
} })
} }
pub fn public(&self) -> Result<cl::Balance> { pub fn public(&self) -> Result<cl::Balance> {

View File

@ -6,4 +6,6 @@ pub type Result<T> = core::result::Result<T, Error>;
pub enum Error { pub enum Error {
#[error("risc0 failed to serde")] #[error("risc0 failed to serde")]
Risc0Serde(#[from] risc0_zkvm::serde::Error), Risc0Serde(#[from] risc0_zkvm::serde::Error),
#[error("risc0 failed to prove execution of the zkvm")]
Risc0ProofFailed,
} }

View File

@ -1,6 +1,6 @@
use proof_statements::input::{InputPrivate, InputPublic}; use proof_statements::input::{InputPrivate, InputPublic};
use crate::error::Result; use crate::error::{Error, Result};
const MAX_NOTE_COMMS: usize = 2usize.pow(8); const MAX_NOTE_COMMS: usize = 2usize.pow(8);
@ -11,7 +11,10 @@ pub struct ProvedInput {
} }
impl 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<Self> {
let output_cm = input.note_commitment(); let output_cm = input.note_commitment();
let cm_leaves = note_commitment_leaves(note_commitments); let cm_leaves = note_commitment_leaves(note_commitments);
@ -42,7 +45,7 @@ impl ProvedInput {
let opts = risc0_zkvm::ProverOpts::succinct(); let opts = risc0_zkvm::ProverOpts::succinct();
let prove_info = prover let prove_info = prover
.prove_with_opts(env, nomos_cl_risc0_proofs::INPUT_ELF, &opts) .prove_with_opts(env, nomos_cl_risc0_proofs::INPUT_ELF, &opts)
.unwrap(); .map_err(|_| Error::Risc0ProofFailed)?;
println!( println!(
"STARK 'input' prover time: {:.2?}, total_cycles: {}", "STARK 'input' prover time: {:.2?}, total_cycles: {}",
@ -52,13 +55,13 @@ impl ProvedInput {
// extract the receipt. // extract the receipt.
let receipt = prove_info.receipt; let receipt = prove_info.receipt;
Self { Ok(Self {
input: InputPublic { input: InputPublic {
cm_root: cl::merkle::root(cm_leaves), cm_root: cl::merkle::root(cm_leaves),
input: input.commit(), input: input.commit(),
}, },
risc0_receipt: receipt, risc0_receipt: receipt,
} })
} }
pub fn public(&self) -> Result<InputPublic> { pub fn public(&self) -> Result<InputPublic> {
@ -103,7 +106,7 @@ mod test {
let notes = vec![input.note_commitment()]; let notes = vec![input.note_commitment()];
let mut proved_input = ProvedInput::prove(&input, &notes); let mut proved_input = ProvedInput::prove(&input, &notes).unwrap();
let expected_public_inputs = InputPublic { let expected_public_inputs = InputPublic {
cm_root: cl::merkle::root(note_commitment_leaves(&notes)), cm_root: cl::merkle::root(note_commitment_leaves(&notes)),

View File

@ -1,4 +1,4 @@
use crate::error::Result; use crate::error::{Error, Result};
pub struct ProvedOutput { pub struct ProvedOutput {
pub output: cl::Output, pub output: cl::Output,
@ -6,7 +6,7 @@ pub struct ProvedOutput {
} }
impl ProvedOutput { impl ProvedOutput {
pub fn prove(witness: &cl::OutputWitness) -> Self { pub fn prove(witness: &cl::OutputWitness) -> Result<Self> {
let env = risc0_zkvm::ExecutorEnv::builder() let env = risc0_zkvm::ExecutorEnv::builder()
.write(&witness) .write(&witness)
.unwrap() .unwrap()
@ -20,7 +20,7 @@ impl ProvedOutput {
let opts = risc0_zkvm::ProverOpts::succinct(); let opts = risc0_zkvm::ProverOpts::succinct();
let prove_info = prover let prove_info = prover
.prove_with_opts(env, nomos_cl_risc0_proofs::OUTPUT_ELF, &opts) .prove_with_opts(env, nomos_cl_risc0_proofs::OUTPUT_ELF, &opts)
.unwrap(); .map_err(|_| Error::Risc0ProofFailed)?;
println!( println!(
"STARK 'output' prover time: {:.2?}, total_cycles: {}", "STARK 'output' prover time: {:.2?}, total_cycles: {}",
@ -30,10 +30,10 @@ impl ProvedOutput {
let receipt = prove_info.receipt; let receipt = prove_info.receipt;
Self { Ok(Self {
output: witness.commit(), output: witness.commit(),
risc0_receipt: receipt, risc0_receipt: receipt,
} })
} }
pub fn public(&self) -> Result<cl::Output> { pub fn public(&self) -> Result<cl::Output> {
@ -70,7 +70,7 @@ mod test {
nonce: cl::NullifierNonce::random(&mut rng), 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(); let expected_output_cm = output.commit();
@ -100,4 +100,17 @@ mod test {
assert!(!proved_output.verify()); 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());
}
} }

View File

@ -2,7 +2,9 @@ use std::collections::BTreeMap;
use proof_statements::death_constraint::DeathConstraintPublic; 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)] #[derive(Debug, Clone)]
pub struct PartialTxInput { pub struct PartialTxInput {
@ -29,18 +31,27 @@ impl ProvedPartialTx {
ptx: &cl::PartialTxWitness, ptx: &cl::PartialTxWitness,
mut death_proofs: BTreeMap<cl::Nullifier, DeathProof>, mut death_proofs: BTreeMap<cl::Nullifier, DeathProof>,
note_commitments: &[cl::NoteCommitment], note_commitments: &[cl::NoteCommitment],
) -> ProvedPartialTx { ) -> Result<ProvedPartialTx> {
Self { let inputs = ptx
inputs: Vec::from_iter(ptx.inputs.iter().map(|i| { .inputs
PartialTxInput { .iter()
input: ProvedInput::prove(i, note_commitments), .map(|i| {
Ok(PartialTxInput {
input: ProvedInput::prove(i, note_commitments)?,
death: death_proofs death: death_proofs
.remove(&i.nullifier()) .remove(&i.nullifier())
.expect("Input missing death proof"), .expect("Input missing death proof"),
} })
})), })
outputs: Vec::from_iter(ptx.outputs.iter().map(ProvedOutput::prove)), .collect::<Result<_>>()?;
}
let outputs = ptx
.outputs
.iter()
.map(ProvedOutput::prove)
.collect::<Result<_>>()?;
Ok(Self { inputs, outputs })
} }
pub fn ptx(&self) -> cl::PartialTx { pub fn ptx(&self) -> cl::PartialTx {

View File

@ -68,7 +68,7 @@ fn test_simple_transfer() {
// assume we only have one note commitment on chain for now ... // assume we only have one note commitment on chain for now ...
let note_commitments = vec![utxo.commit_note()]; let note_commitments = vec![utxo.commit_note()];
let proved_ptx = ProvedPartialTx::prove(&ptx_witness, death_proofs, &note_commitments); let proved_ptx = ProvedPartialTx::prove(&ptx_witness, death_proofs, &note_commitments).unwrap();
assert!(proved_ptx.verify()); // It's a valid ptx. assert!(proved_ptx.verify()); // It's a valid ptx.
@ -80,6 +80,6 @@ fn test_simple_transfer() {
balance_blinding: ptx_witness.balance_blinding(), 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. assert!(proved_bundle.verify()); // The bundle is balanced.
} }

View File

@ -7,6 +7,12 @@ use risc0_zkvm::guest::env;
fn main() { fn main() {
let output: cl::OutputWitness = env::read(); 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(); let output_cm = output.commit();
env::commit(&output_cm); env::commit(&output_cm);
} }