goas: replace input/output proofs with ptx proof
This commit is contained in:
parent
ff0afeebd7
commit
310932818a
|
@ -77,10 +77,7 @@ fn test_deposit() {
|
|||
|
||||
assert!(deposit_proof.verify());
|
||||
|
||||
assert_eq!(
|
||||
deposit_proof.outputs[0].output,
|
||||
zone_end.state_note.commit()
|
||||
);
|
||||
assert_eq!(deposit_proof.ptx.outputs[0], zone_end.state_note.commit());
|
||||
assert_eq!(
|
||||
zone_end.state_note.note.state,
|
||||
StateWitness {
|
||||
|
|
|
@ -97,10 +97,7 @@ fn test_withdrawal() {
|
|||
|
||||
assert!(withdraw_proof.verify());
|
||||
|
||||
assert_eq!(
|
||||
withdraw_proof.outputs[0].output,
|
||||
zone_end.state_note.commit()
|
||||
);
|
||||
assert_eq!(withdraw_proof.ptx.outputs[0], zone_end.state_note.commit());
|
||||
assert_eq!(
|
||||
zone_end.state_note.note.state,
|
||||
StateWitness {
|
||||
|
|
|
@ -34,13 +34,13 @@ impl PtxRoot {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PartialTx {
|
||||
pub inputs: Vec<Input>,
|
||||
pub outputs: Vec<Output>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PartialTxWitness {
|
||||
pub inputs: Vec<InputWitness>,
|
||||
pub outputs: Vec<OutputWitness>,
|
||||
|
|
|
@ -1,76 +1,120 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use ledger_proof_statements::death_constraint::DeathConstraintPublic;
|
||||
|
||||
use crate::{
|
||||
death_constraint::DeathProof, error::Result, input::ProvedInput, output::ProvedOutput,
|
||||
use ledger_proof_statements::{
|
||||
death_constraint::DeathConstraintPublic,
|
||||
ptx::{PtxPrivate, PtxPublic},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PartialTxInput {
|
||||
pub input: ProvedInput,
|
||||
pub death: DeathProof,
|
||||
}
|
||||
use crate::{
|
||||
death_constraint::DeathProof, error::{Error, Result}
|
||||
};
|
||||
|
||||
const MAX_NOTE_COMMS: usize = 2usize.pow(8);
|
||||
|
||||
impl PartialTxInput {
|
||||
fn verify(&self, ptx_root: cl::PtxRoot) -> bool {
|
||||
let nf = self.input.input.input.nullifier;
|
||||
self.input.input.input.death_cm == self.death.death_commitment() // ensure the death proof is actually for this input
|
||||
&& self.input.verify() // ensure the input proof is valid
|
||||
&& self.death.verify(DeathConstraintPublic { nf, ptx_root }) // verify the death constraint was satisfied
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ProvedPartialTx {
|
||||
pub inputs: Vec<PartialTxInput>,
|
||||
pub outputs: Vec<ProvedOutput>,
|
||||
pub ptx: cl::PartialTx,
|
||||
pub cm_root: [u8;32],
|
||||
pub death_proofs: BTreeMap<cl::Nullifier, DeathProof>,
|
||||
pub risc0_receipt: risc0_zkvm::Receipt,
|
||||
}
|
||||
|
||||
impl ProvedPartialTx {
|
||||
pub fn prove(
|
||||
ptx: &cl::PartialTxWitness,
|
||||
mut death_proofs: BTreeMap<cl::Nullifier, DeathProof>,
|
||||
death_proofs: BTreeMap<cl::Nullifier, DeathProof>,
|
||||
note_commitments: &[cl::NoteCommitment],
|
||||
) -> Result<ProvedPartialTx> {
|
||||
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"),
|
||||
})
|
||||
})
|
||||
.collect::<Result<_>>()?;
|
||||
let cm_leaves = note_commitment_leaves(note_commitments);
|
||||
|
||||
let outputs = ptx
|
||||
.outputs
|
||||
.iter()
|
||||
.map(ProvedOutput::prove)
|
||||
.collect::<Result<_>>()?;
|
||||
let input_cm_paths = Vec::from_iter(ptx.inputs.iter().map(|input| {
|
||||
let output_cm = input.note_commitment();
|
||||
|
||||
Ok(Self { inputs, outputs })
|
||||
let cm_idx = note_commitments
|
||||
.iter()
|
||||
.position(|c| c == &output_cm)
|
||||
.unwrap();
|
||||
|
||||
cl::merkle::path(cm_leaves, cm_idx)
|
||||
}));
|
||||
let cm_root = cl::merkle::root(cm_leaves);
|
||||
let ptx_private = PtxPrivate {
|
||||
ptx: ptx.clone(),
|
||||
input_cm_paths,
|
||||
cm_root,
|
||||
};
|
||||
|
||||
let env = risc0_zkvm::ExecutorEnv::builder()
|
||||
.write(&ptx_private)
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// Obtain the default prover.
|
||||
let prover = risc0_zkvm::default_prover();
|
||||
|
||||
let start_t = std::time::Instant::now();
|
||||
|
||||
// Proof information by proving the specified ELF binary.
|
||||
// This struct contains the receipt along with statistics about execution of the guest
|
||||
let opts = risc0_zkvm::ProverOpts::succinct();
|
||||
let prove_info = prover
|
||||
.prove_with_opts(env, nomos_cl_risc0_proofs::PTX_ELF, &opts)
|
||||
.map_err(|_| Error::Risc0ProofFailed)?;
|
||||
|
||||
println!(
|
||||
"STARK 'ptx' prover time: {:.2?}, total_cycles: {}",
|
||||
start_t.elapsed(),
|
||||
prove_info.stats.total_cycles
|
||||
);
|
||||
// extract the receipt.
|
||||
let receipt = prove_info.receipt;
|
||||
|
||||
Ok(Self {
|
||||
ptx: ptx.commit(),
|
||||
cm_root,
|
||||
risc0_receipt: receipt,
|
||||
death_proofs,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ptx(&self) -> cl::PartialTx {
|
||||
cl::PartialTx {
|
||||
inputs: Vec::from_iter(self.inputs.iter().map(|i| i.input.input.input)),
|
||||
outputs: Vec::from_iter(self.outputs.iter().map(|o| o.output)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify_inputs(&self) -> bool {
|
||||
let ptx_root = self.ptx().root();
|
||||
self.inputs.iter().all(|i| i.verify(ptx_root))
|
||||
}
|
||||
|
||||
pub fn verify_outputs(&self) -> bool {
|
||||
self.outputs.iter().all(|o| o.verify())
|
||||
pub fn public(&self) -> Result<PtxPublic> {
|
||||
Ok(self.risc0_receipt.journal.decode()?)
|
||||
}
|
||||
|
||||
pub fn verify(&self) -> bool {
|
||||
self.verify_inputs() && self.verify_outputs()
|
||||
let Ok(proved_ptx_inputs) = self.public() else {
|
||||
return false;
|
||||
};
|
||||
if (PtxPublic { ptx: self.ptx.clone(), cm_root: self.cm_root }) != proved_ptx_inputs {
|
||||
return false;
|
||||
}
|
||||
|
||||
let ptx_root = self.ptx.root();
|
||||
|
||||
for input in self.ptx.inputs.iter() {
|
||||
let nf = input.nullifier;
|
||||
let Some(death_proof) = self.death_proofs.get(&nf) else {
|
||||
return false;
|
||||
};
|
||||
if input.death_cm != death_proof.death_commitment() {
|
||||
// ensure the death proof is actually for this input
|
||||
return false;
|
||||
}
|
||||
if !death_proof.verify(DeathConstraintPublic { nf, ptx_root }) {
|
||||
// verify the death constraint was satisfied
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
self.risc0_receipt
|
||||
.verify(nomos_cl_risc0_proofs::PTX_ID)
|
||||
.is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
fn note_commitment_leaves(note_commitments: &[cl::NoteCommitment]) -> [[u8; 32]; MAX_NOTE_COMMS] {
|
||||
let note_comm_bytes = Vec::from_iter(note_commitments.iter().map(|c| c.as_bytes().to_vec()));
|
||||
let cm_leaves = cl::merkle::padded_leaves::<MAX_NOTE_COMMS>(¬e_comm_bytes);
|
||||
cm_leaves
|
||||
}
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pub mod death_constraint;
|
||||
pub mod input;
|
||||
pub mod ptx;
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use cl::{PartialTx, PartialTxWitness};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PtxPublic {
|
||||
pub ptx: PartialTx,
|
||||
pub cm_root: [u8; 32],
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PtxPrivate {
|
||||
pub ptx: PartialTxWitness,
|
||||
pub input_cm_paths: Vec<Vec<cl::merkle::PathNode>>,
|
||||
pub cm_root: [u8; 32],
|
||||
}
|
|
@ -7,5 +7,5 @@ edition = "2021"
|
|||
risc0-build = { version = "1.0" }
|
||||
|
||||
[package.metadata.risc0]
|
||||
methods = ["input", "output", "bundle", "death_constraint_nop"]
|
||||
methods = ["input", "output", "bundle", "death_constraint_nop", "ptx"]
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "ptx"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
risc0-zkvm = { version = "1.0", default-features = false, features = ['std'] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
cl = { path = "../../cl" }
|
||||
ledger_proof_statements = { path = "../../ledger_proof_statements" }
|
||||
|
||||
|
||||
[patch.crates-io]
|
||||
# add RISC Zero accelerator support for all downstream usages of the following crates.
|
||||
sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.8-risczero.0" }
|
||||
crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.5-risczero.0" }
|
||||
curve25519-dalek = { git = "https://github.com/risc0/curve25519-dalek", tag = "curve25519-4.1.2-risczero.0" }
|
|
@ -0,0 +1,28 @@
|
|||
/// Input Proof
|
||||
use cl::merkle;
|
||||
use ledger_proof_statements::ptx::{PtxPrivate, PtxPublic};
|
||||
use risc0_zkvm::guest::env;
|
||||
|
||||
fn main() {
|
||||
let PtxPrivate {
|
||||
ptx,
|
||||
input_cm_paths,
|
||||
cm_root,
|
||||
} = env::read();
|
||||
|
||||
assert_eq!(ptx.inputs.len(), input_cm_paths.len());
|
||||
for (input, cm_path) in ptx.inputs.iter().zip(input_cm_paths) {
|
||||
let note_cm = input.note_commitment();
|
||||
let cm_leaf = merkle::leaf(note_cm.as_bytes());
|
||||
assert_eq!(cm_root, merkle::path_root(cm_leaf, &cm_path));
|
||||
}
|
||||
|
||||
for output in ptx.outputs.iter() {
|
||||
assert!(output.note.value > 0);
|
||||
}
|
||||
|
||||
env::commit(&PtxPublic {
|
||||
ptx: ptx.commit(),
|
||||
cm_root,
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue