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!(deposit_proof.verify());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(deposit_proof.ptx.outputs[0], zone_end.state_note.commit());
|
||||||
deposit_proof.outputs[0].output,
|
|
||||||
zone_end.state_note.commit()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
zone_end.state_note.note.state,
|
zone_end.state_note.note.state,
|
||||||
StateWitness {
|
StateWitness {
|
||||||
|
|
|
@ -97,10 +97,7 @@ fn test_withdrawal() {
|
||||||
|
|
||||||
assert!(withdraw_proof.verify());
|
assert!(withdraw_proof.verify());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(withdraw_proof.ptx.outputs[0], zone_end.state_note.commit());
|
||||||
withdraw_proof.outputs[0].output,
|
|
||||||
zone_end.state_note.commit()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
zone_end.state_note.note.state,
|
zone_end.state_note.note.state,
|
||||||
StateWitness {
|
StateWitness {
|
||||||
|
|
|
@ -34,13 +34,13 @@ impl PtxRoot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct PartialTx {
|
pub struct PartialTx {
|
||||||
pub inputs: Vec<Input>,
|
pub inputs: Vec<Input>,
|
||||||
pub outputs: Vec<Output>,
|
pub outputs: Vec<Output>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct PartialTxWitness {
|
pub struct PartialTxWitness {
|
||||||
pub inputs: Vec<InputWitness>,
|
pub inputs: Vec<InputWitness>,
|
||||||
pub outputs: Vec<OutputWitness>,
|
pub outputs: Vec<OutputWitness>,
|
||||||
|
|
|
@ -1,76 +1,120 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use ledger_proof_statements::death_constraint::DeathConstraintPublic;
|
use ledger_proof_statements::{
|
||||||
|
death_constraint::DeathConstraintPublic,
|
||||||
use crate::{
|
ptx::{PtxPrivate, PtxPublic},
|
||||||
death_constraint::DeathProof, error::Result, input::ProvedInput, output::ProvedOutput,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
use crate::{
|
||||||
pub struct PartialTxInput {
|
death_constraint::DeathProof, error::{Error, Result}
|
||||||
pub input: ProvedInput,
|
};
|
||||||
pub death: DeathProof,
|
|
||||||
}
|
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 struct ProvedPartialTx {
|
||||||
pub inputs: Vec<PartialTxInput>,
|
pub ptx: cl::PartialTx,
|
||||||
pub outputs: Vec<ProvedOutput>,
|
pub cm_root: [u8;32],
|
||||||
|
pub death_proofs: BTreeMap<cl::Nullifier, DeathProof>,
|
||||||
|
pub risc0_receipt: risc0_zkvm::Receipt,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProvedPartialTx {
|
impl ProvedPartialTx {
|
||||||
pub fn prove(
|
pub fn prove(
|
||||||
ptx: &cl::PartialTxWitness,
|
ptx: &cl::PartialTxWitness,
|
||||||
mut death_proofs: BTreeMap<cl::Nullifier, DeathProof>,
|
death_proofs: BTreeMap<cl::Nullifier, DeathProof>,
|
||||||
note_commitments: &[cl::NoteCommitment],
|
note_commitments: &[cl::NoteCommitment],
|
||||||
) -> Result<ProvedPartialTx> {
|
) -> Result<ProvedPartialTx> {
|
||||||
let inputs = ptx
|
let cm_leaves = note_commitment_leaves(note_commitments);
|
||||||
.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 outputs = ptx
|
let input_cm_paths = Vec::from_iter(ptx.inputs.iter().map(|input| {
|
||||||
.outputs
|
let output_cm = input.note_commitment();
|
||||||
.iter()
|
|
||||||
.map(ProvedOutput::prove)
|
|
||||||
.collect::<Result<_>>()?;
|
|
||||||
|
|
||||||
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 {
|
pub fn public(&self) -> Result<PtxPublic> {
|
||||||
cl::PartialTx {
|
Ok(self.risc0_receipt.journal.decode()?)
|
||||||
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 verify(&self) -> bool {
|
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 death_constraint;
|
||||||
pub mod input;
|
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" }
|
risc0-build = { version = "1.0" }
|
||||||
|
|
||||||
[package.metadata.risc0]
|
[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