diff --git a/goas/cl/cl/src/input.rs b/goas/cl/cl/src/input.rs index 445d8c8..dde6f36 100644 --- a/goas/cl/cl/src/input.rs +++ b/goas/cl/cl/src/input.rs @@ -51,8 +51,8 @@ impl InputWitness { } impl Input { - pub fn to_bytes(&self) -> [u8; 64] { - let mut bytes = [0u8; 64]; + pub fn to_bytes(&self) -> [u8; 96] { + let mut bytes = [0u8; 96]; bytes[..32].copy_from_slice(self.nullifier.as_bytes()); bytes[32..64].copy_from_slice(&self.balance.to_bytes()); bytes[64..96].copy_from_slice(&self.death_cm.0); diff --git a/goas/cl/proof_statements/src/death_constraint.rs b/goas/cl/proof_statements/src/death_constraint.rs new file mode 100644 index 0000000..efcac9e --- /dev/null +++ b/goas/cl/proof_statements/src/death_constraint.rs @@ -0,0 +1,9 @@ +use cl::{Nullifier, PtxRoot}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DeathConstraintPublic { + pub cm_root: [u8; 32], + pub nf: Nullifier, + pub ptx_root: PtxRoot, +} diff --git a/goas/cl/proof_statements/src/lib.rs b/goas/cl/proof_statements/src/lib.rs index 7839bc5..8e58650 100644 --- a/goas/cl/proof_statements/src/lib.rs +++ b/goas/cl/proof_statements/src/lib.rs @@ -1 +1,3 @@ +pub mod death_constraint; pub mod input; +pub mod ptx; diff --git a/goas/cl/proof_statements/src/ptx.rs b/goas/cl/proof_statements/src/ptx.rs new file mode 100644 index 0000000..7c7f9d7 --- /dev/null +++ b/goas/cl/proof_statements/src/ptx.rs @@ -0,0 +1,35 @@ +use cl::{merkle, InputWitness, OutputWitness, PtxRoot}; +use serde::{Deserialize, Serialize}; +/// An input to a partial transaction +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct PartialTxInputPrivate { + pub input: InputWitness, + pub cm_path: Vec, + pub ptx_path: Vec, +} + +impl PartialTxInputPrivate { + pub fn ptx_root(&self) -> PtxRoot { + let leaf = merkle::leaf(&self.input.commit().to_bytes()); + PtxRoot(merkle::path_root(leaf, &self.ptx_path)) + } + + pub fn cm_root(&self) -> [u8; 32] { + let leaf = merkle::leaf(self.input.to_output_witness().commit_note().as_bytes()); + merkle::path_root(leaf, &self.cm_path) + } +} + +/// An output to a partial transaction +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct PartialTxOutputPrivate { + pub output: OutputWitness, + pub ptx_path: Vec, +} + +impl PartialTxOutputPrivate { + pub fn ptx_root(&self) -> PtxRoot { + let leaf = merkle::leaf(&self.output.commit().to_bytes()); + PtxRoot(merkle::path_root(leaf, &self.ptx_path)) + } +} diff --git a/goas/zone/Cargo.toml b/goas/zone/Cargo.toml index 229aa89..b8dd50d 100644 --- a/goas/zone/Cargo.toml +++ b/goas/zone/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = [ "common","host", "methods"] +members = [ "common","host", "methods", "proof_statements", "risc0_proofs"] # Always optimize; building and running the guest takes much longer without optimization. [profile.dev] diff --git a/goas/zone/methods/guest/src/main.rs b/goas/zone/methods/guest/src/main.rs index 6e55dc4..ae9483f 100644 --- a/goas/zone/methods/guest/src/main.rs +++ b/goas/zone/methods/guest/src/main.rs @@ -12,8 +12,8 @@ fn withdraw(mut state: StateWitness, withdraw: Withdraw) -> StateWitness { nf, } = withdraw; - let from = state.balances.entry(from).or_insert(0); - *from = from.checked_sub(amount).unwrap(); + let from_balance = state.balances.entry(from).or_insert(0); + *from_balance = from.checked_sub(amount).expect("insufficient funds in account"); let spend_auth = Spend { amount: amount.into(), to, diff --git a/goas/zone/proof_statements/Cargo.toml b/goas/zone/proof_statements/Cargo.toml index 6f5d3f8..d7b9b87 100644 --- a/goas/zone/proof_statements/Cargo.toml +++ b/goas/zone/proof_statements/Cargo.toml @@ -5,4 +5,5 @@ edition = "2021" [dependencies] cl = { path = "../../cl/cl" } +proof_statements = { path = "../../cl/proof_statements" } serde = { version = "1.0", features = ["derive"] } diff --git a/goas/zone/proof_statements/src/zone_funds.rs b/goas/zone/proof_statements/src/zone_funds.rs index ddbf0c9..5da4346 100644 --- a/goas/zone/proof_statements/src/zone_funds.rs +++ b/goas/zone/proof_statements/src/zone_funds.rs @@ -1,12 +1,6 @@ +use proof_statements::{ptx::PartialTxInputPrivate, ptx::PartialTxOutputPrivate}; use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct SpendFundsPublic { - pub cm_root: [u8; 32], - pub nf: cl::Nullifier, - pub ptx_root: cl::PtxRoot, -} - /// An event that authorizes spending zone funds #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct Spend { @@ -32,23 +26,13 @@ impl Spend { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct SpendFundsPrivate { /// The note we're spending - pub in_zone_funds: cl::InputWitness, - /// Path to the ptx root - pub in_zone_funds_ptx_path: Vec, - /// Path to the root of the merkle tree over the commitment set - pub in_zone_funds_cm_path: Vec, - pub in_zone_funds_nf_sk: cl::NullifierSecret, + pub in_zone_funds: PartialTxInputPrivate, /// The zone note that is authorizing the spend - pub zone_note: cl::OutputWitness, - /// Path to the ptx root - pub zone_note_ptx_path: Vec, + pub zone_note: PartialTxOutputPrivate, /// The note that is being created to send the change back to the zone - pub out_zone_funds: cl::OutputWitness, - /// Path to the ptx root - pub out_zone_funds_ptx_path: Vec, + pub out_zone_funds: PartialTxOutputPrivate, /// The spent funds note - pub spent_note: cl::OutputWitness, - pub spent_note_ptx_path: Vec, + pub spent_note: PartialTxOutputPrivate, /// The event emitted by the zone that authorizes the spend pub spend_event: Spend, /// Path to the zone output state diff --git a/goas/zone/risc0_proofs/Cargo.toml b/goas/zone/risc0_proofs/Cargo.toml index 6b1b574..e73bbbb 100644 --- a/goas/zone/risc0_proofs/Cargo.toml +++ b/goas/zone/risc0_proofs/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "nomos_cl_risc0_proofs" +name = "goas_risc0_proofs" version = "0.1.0" edition = "2021" diff --git a/goas/zone/risc0_proofs/spend_zone_funds/Cargo.toml b/goas/zone/risc0_proofs/spend_zone_funds/Cargo.toml index 9e068e6..4d65001 100644 --- a/goas/zone/risc0_proofs/spend_zone_funds/Cargo.toml +++ b/goas/zone/risc0_proofs/spend_zone_funds/Cargo.toml @@ -8,8 +8,9 @@ edition = "2021" [dependencies] risc0-zkvm = { version = "1.0", default-features = false, features = ['std'] } serde = { version = "1.0", features = ["derive"] } -cl = { path = "../../cl" } -proof_statements = { path = "../../proof_statements" } +cl = { path = "../../../cl/cl" } +goas_proof_statements = { path = "../../proof_statements" } +proof_statements = { path = "../../../cl/proof_statements" } sha2 = "0.10" diff --git a/goas/zone/risc0_proofs/spend_zone_funds/src/main.rs b/goas/zone/risc0_proofs/spend_zone_funds/src/main.rs index 94a9ba6..18a00ea 100644 --- a/goas/zone/risc0_proofs/spend_zone_funds/src/main.rs +++ b/goas/zone/risc0_proofs/spend_zone_funds/src/main.rs @@ -3,8 +3,8 @@ /// Our goal: prove the zone authorized spending of funds use cl::merkle; use cl::nullifier::{Nullifier, NullifierNonce, NullifierSecret}; -use cl::partial_tx::PtxRoot; -use proof_statements::zone_funds::{SpendFundsPrivate, SpendFundsPublic}; +use goas_proof_statements::zone_funds::SpendFundsPrivate; +use proof_statements::death_constraint::DeathConstraintPublic; use risc0_zkvm::guest::env; use sha2::{Digest, Sha256}; @@ -15,97 +15,75 @@ fn main() { zone_note, spent_note, spend_event, - in_zone_funds_ptx_path, - in_zone_funds_cm_path, - out_zone_funds_ptx_path, - zone_note_ptx_path, - spent_note_ptx_path, spend_event_state_path, - in_zone_funds_nf_sk: _, } = env::read(); - let in_zone_funds_cm = in_zone_funds.commit(); - let in_zone_funds_leaf = merkle::leaf(&in_zone_funds_cm.to_bytes()); - let cm_root = merkle::path_root(in_zone_funds_leaf, &in_zone_funds_cm_path); - let ptx_root = merkle::path_root(in_zone_funds_leaf, &in_zone_funds_ptx_path); - let in_zone_funds_nf = Nullifier::new(in_zone_funds.nf_sk, in_zone_funds.nonce); + let cm_root = in_zone_funds.cm_root(); + let ptx_root = in_zone_funds.ptx_root(); + let nf = Nullifier::new(in_zone_funds.input.nf_sk, in_zone_funds.input.nonce); // check the zone funds note is the one in the spend event - assert_eq!(in_zone_funds_nf, spend_event.nf); - - let zone_note_cm = zone_note.commit_note(); - let zone_note_leaf = merkle::leaf(zone_note_cm.as_bytes()); - assert_eq!( - ptx_root, - merkle::path_root(zone_note_leaf, &zone_note_ptx_path) - ); + assert_eq!(nf, spend_event.nf); + assert_eq!(ptx_root, zone_note.ptx_root()); // assert the spent event was an output of the zone stf let spend_event_leaf = merkle::leaf(&spend_event.to_bytes()); // TODO: zones will have some more state assert_eq!( - zone_note.note.state, + zone_note.output.note.state, merkle::path_root(spend_event_leaf, &spend_event_state_path) ); - let out_zone_funds_cm = out_zone_funds.commit_note(); - let out_zone_funds_leaf = merkle::leaf(out_zone_funds_cm.as_bytes()); - assert_eq!( - ptx_root, - merkle::path_root(out_zone_funds_leaf, &out_zone_funds_ptx_path) - ); + assert_eq!(ptx_root, out_zone_funds.ptx_root()); // Check we return the rest of the funds back to the zone let change = in_zone_funds + .input .note .balance .value .checked_sub(spend_event.amount) .unwrap(); - assert_eq!(out_zone_funds.note.balance.value, change); + assert_eq!(out_zone_funds.output.note.balance.value, change); // zone funds output should have the same death constraints as the zone funds input assert_eq!( - out_zone_funds.note.death_constraint, - in_zone_funds.note.death_constraint + out_zone_funds.output.note.death_constraint, + in_zone_funds.input.note.death_constraint ); assert_eq!( - out_zone_funds.note.balance.unit, - in_zone_funds.note.balance.unit + out_zone_funds.output.note.balance.unit, + in_zone_funds.input.note.balance.unit ); // zone funds nullifier, nonce and value blinding should be public so that everybody can spend it assert_eq!( - out_zone_funds.nf_pk, + out_zone_funds.output.nf_pk, NullifierSecret::from_bytes([0; 16]).commit() ); assert_eq!( - out_zone_funds.note.balance.blinding, - in_zone_funds.note.balance.blinding + out_zone_funds.output.note.balance.blinding, + in_zone_funds.input.note.balance.blinding ); let mut evolved_nonce = [0; 16]; - evolved_nonce[..16].copy_from_slice(&Sha256::digest(&out_zone_funds.nonce.as_bytes())[..16]); + evolved_nonce[..16] + .copy_from_slice(&Sha256::digest(&out_zone_funds.output.nonce.as_bytes())[..16]); assert_eq!( - out_zone_funds.nonce, + out_zone_funds.output.nonce, NullifierNonce::from_bytes(evolved_nonce) ); - let spent_note_cm = spent_note.commit_note(); - let spent_note_leaf = merkle::leaf(spent_note_cm.as_bytes()); - assert_eq!( - ptx_root, - merkle::path_root(spent_note_leaf, &spent_note_ptx_path) - ); + assert_eq!(ptx_root, spent_note.ptx_root()); // check the correct amount of funds is being spent - assert_eq!(spent_note.note.balance.value, spend_event.amount); + assert_eq!(spent_note.output.note.balance.value, spend_event.amount); assert_eq!( - spent_note.note.balance.unit, - in_zone_funds.note.balance.unit + spent_note.output.note.balance.unit, + in_zone_funds.input.note.balance.unit ); // check the correct recipient is being paid - assert_eq!(spent_note.nf_pk, spend_event.to); + assert_eq!(spent_note.output.nf_pk, spend_event.to); - env::commit(&SpendFundsPublic { + env::commit(&DeathConstraintPublic { cm_root, - ptx_root: PtxRoot::from(ptx_root), - nf: in_zone_funds_nf, + ptx_root, + nf, }); }