goas: rename DeathConstraint to Constraint

This commit is contained in:
David Rusu 2024-08-26 23:53:51 +04:00
parent 23df7331c8
commit cffd687e50
25 changed files with 224 additions and 214 deletions

View File

@ -1,6 +1,6 @@
pub mod mmr; pub mod mmr;
use cl::{balance::Unit, NoteCommitment}; use cl::{balance::Unit, ConstraintCommitment, NoteCommitment};
use ed25519_dalek::{ use ed25519_dalek::{
ed25519::{signature::SignerMut, SignatureBytes}, ed25519::{signature::SignerMut, SignatureBytes},
Signature, SigningKey, VerifyingKey, PUBLIC_KEY_LENGTH, Signature, SigningKey, VerifyingKey, PUBLIC_KEY_LENGTH,
@ -27,16 +27,16 @@ pub fn new_account(mut rng: impl CryptoRngCore) -> SigningKey {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct ZoneMetadata { pub struct ZoneMetadata {
pub zone_vk: [u8; 32], pub zone_constraint: ConstraintCommitment,
pub funds_vk: [u8; 32], pub funds_constraint: ConstraintCommitment,
pub unit: Unit, pub unit: Unit,
} }
impl ZoneMetadata { impl ZoneMetadata {
pub fn id(&self) -> [u8; 32] { pub fn id(&self) -> [u8; 32] {
let mut hasher = Sha256::new(); let mut hasher = Sha256::new();
hasher.update(self.zone_vk); hasher.update(self.zone_constraint.0);
hasher.update(self.funds_vk); hasher.update(self.funds_constraint.0);
hasher.update(self.unit); hasher.update(self.unit);
hasher.finalize().into() hasher.finalize().into()
} }

View File

@ -1,5 +1,6 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use cl::ConstraintCommitment;
use common::{ use common::{
mmr::MMR, AccountId, IncludedTxWitness, SignedBoundTx, StateWitness, Tx, ZoneMetadata, mmr::MMR, AccountId, IncludedTxWitness, SignedBoundTx, StateWitness, Tx, ZoneMetadata,
}; };
@ -78,7 +79,7 @@ fn zone_fund_utxo(
cl::NoteWitness { cl::NoteWitness {
value, value,
unit: *common::ZONE_CL_FUNDS_UNIT, unit: *common::ZONE_CL_FUNDS_UNIT,
death_constraint: zone_meta.funds_vk, constraint: zone_meta.funds_constraint,
state: zone_meta.id(), state: zone_meta.id(),
}, },
cl::NullifierNonce::random(&mut rng), cl::NullifierNonce::random(&mut rng),
@ -90,33 +91,29 @@ fn zone_state_utxo(zone: &StateWitness, mut rng: impl CryptoRngCore) -> cl::Outp
cl::NoteWitness { cl::NoteWitness {
value: 1, value: 1,
unit: zone.zone_metadata.unit, unit: zone.zone_metadata.unit,
death_constraint: zone.zone_metadata.zone_vk, constraint: zone.zone_metadata.zone_constraint,
state: zone.commit().0, state: zone.commit().0,
}, },
cl::NullifierNonce::random(&mut rng), cl::NullifierNonce::random(&mut rng),
) )
} }
pub fn user_atomic_transfer_death_constraint() -> [u8; 32] { pub fn user_atomic_transfer_constraint() -> ConstraintCommitment {
ledger::death_constraint::risc0_id_to_cl_death_constraint( ledger::constraint::risc0_constraint(goas_risc0_proofs::USER_ATOMIC_TRANSFER_ID)
goas_risc0_proofs::USER_ATOMIC_TRANSFER_ID,
)
} }
pub fn zone_state_death_constraint() -> [u8; 32] { pub fn zone_state_constraint() -> ConstraintCommitment {
ledger::death_constraint::risc0_id_to_cl_death_constraint(goas_risc0_proofs::ZONE_STATE_ID) ledger::constraint::risc0_constraint(goas_risc0_proofs::ZONE_STATE_ID)
} }
pub fn zone_fund_death_constraint() -> [u8; 32] { pub fn zone_fund_constraint() -> ConstraintCommitment {
ledger::death_constraint::risc0_id_to_cl_death_constraint( ledger::constraint::risc0_constraint(goas_risc0_proofs::SPEND_ZONE_FUNDS_ID)
goas_risc0_proofs::SPEND_ZONE_FUNDS_ID,
)
} }
pub fn zone_metadata(zone_mnemonic: &str) -> ZoneMetadata { pub fn zone_metadata(zone_mnemonic: &str) -> ZoneMetadata {
ZoneMetadata { ZoneMetadata {
zone_vk: zone_state_death_constraint(), zone_constraint: zone_state_constraint(),
funds_vk: zone_fund_death_constraint(), funds_constraint: zone_fund_constraint(),
unit: cl::note::derive_unit(zone_mnemonic), unit: cl::note::derive_unit(zone_mnemonic),
} }
} }
@ -127,7 +124,7 @@ pub fn prove_zone_stf(
zone_in: cl::PartialTxInputWitness, zone_in: cl::PartialTxInputWitness,
zone_out: cl::PartialTxOutputWitness, zone_out: cl::PartialTxOutputWitness,
funds_out: cl::PartialTxOutputWitness, funds_out: cl::PartialTxOutputWitness,
) -> ledger::DeathProof { ) -> ledger::ConstraintProof {
let private_inputs = ZoneStatePrivate { let private_inputs = ZoneStatePrivate {
state, state,
inputs, inputs,
@ -156,14 +153,14 @@ pub fn prove_zone_stf(
prove_info.stats.total_cycles prove_info.stats.total_cycles
); );
let receipt = prove_info.receipt; let receipt = prove_info.receipt;
ledger::DeathProof::from_risc0(goas_risc0_proofs::ZONE_STATE_ID, receipt) ledger::ConstraintProof::from_risc0(goas_risc0_proofs::ZONE_STATE_ID, receipt)
} }
pub fn prove_zone_fund_constraint( pub fn prove_zone_fund_constraint(
in_zone_funds: cl::PartialTxInputWitness, in_zone_funds: cl::PartialTxInputWitness,
zone_note: cl::PartialTxOutputWitness, zone_note: cl::PartialTxOutputWitness,
out_zone_state: &StateWitness, out_zone_state: &StateWitness,
) -> ledger::DeathProof { ) -> ledger::ConstraintProof {
let private_inputs = SpendFundsPrivate { let private_inputs = SpendFundsPrivate {
in_zone_funds, in_zone_funds,
zone_note, zone_note,
@ -190,10 +187,10 @@ pub fn prove_zone_fund_constraint(
prove_info.stats.total_cycles prove_info.stats.total_cycles
); );
let receipt = prove_info.receipt; let receipt = prove_info.receipt;
ledger::DeathProof::from_risc0(goas_risc0_proofs::SPEND_ZONE_FUNDS_ID, receipt) ledger::ConstraintProof::from_risc0(goas_risc0_proofs::SPEND_ZONE_FUNDS_ID, receipt)
} }
pub fn prove_user_atomic_transfer(atomic_transfer: UserAtomicTransfer) -> ledger::DeathProof { pub fn prove_user_atomic_transfer(atomic_transfer: UserAtomicTransfer) -> ledger::ConstraintProof {
let env = risc0_zkvm::ExecutorEnv::builder() let env = risc0_zkvm::ExecutorEnv::builder()
.write(&atomic_transfer) .write(&atomic_transfer)
.unwrap() .unwrap()
@ -214,7 +211,7 @@ pub fn prove_user_atomic_transfer(atomic_transfer: UserAtomicTransfer) -> ledger
prove_info.stats.total_cycles prove_info.stats.total_cycles
); );
let receipt = prove_info.receipt; let receipt = prove_info.receipt;
ledger::DeathProof::from_risc0(goas_risc0_proofs::USER_ATOMIC_TRANSFER_ID, receipt) ledger::ConstraintProof::from_risc0(goas_risc0_proofs::USER_ATOMIC_TRANSFER_ID, receipt)
} }
#[cfg(test)] #[cfg(test)]
@ -225,7 +222,8 @@ mod tests {
}; };
use common::{BoundTx, Deposit, Withdraw}; use common::{BoundTx, Deposit, Withdraw};
use goas_proof_statements::user_note::UserIntent; use goas_proof_statements::user_note::UserIntent;
use ledger_proof_statements::death_constraint::DeathConstraintPublic; use ledger::ConstraintProof;
use ledger_proof_statements::constraint::ConstraintPublic;
use super::*; use super::*;
@ -277,7 +275,7 @@ mod tests {
ptx.output_witness(1), ptx.output_witness(1),
); );
assert!(proof.verify(DeathConstraintPublic { assert!(proof.verify(ConstraintPublic {
nf: zone_start.state_input_witness().nullifier(), nf: zone_start.state_input_witness().nullifier(),
ptx_root: ptx.commit().root(), ptx_root: ptx.commit().root(),
})) }))
@ -297,7 +295,7 @@ mod tests {
let proof = let proof =
prove_zone_fund_constraint(ptx.input_witness(0), ptx.output_witness(0), &zone.state); prove_zone_fund_constraint(ptx.input_witness(0), ptx.output_witness(0), &zone.state);
assert!(proof.verify(DeathConstraintPublic { assert!(proof.verify(ConstraintPublic {
nf: zone.fund_input_witness().nullifier(), nf: zone.fund_input_witness().nullifier(),
ptx_root: ptx.commit().root(), ptx_root: ptx.commit().root(),
})) }))
@ -327,7 +325,12 @@ mod tests {
}, },
}; };
let user_note = cl::InputWitness::public(cl::OutputWitness::public( let user_note = cl::InputWitness::public(cl::OutputWitness::public(
NoteWitness::new(1, derive_unit("INTENT"), [0u8; 32], user_intent.commit()), NoteWitness::new(
1,
derive_unit("INTENT"),
ConstraintProof::nop_constraint(),
user_intent.commit(),
),
NullifierNonce::random(&mut rng), NullifierNonce::random(&mut rng),
)); ));
@ -353,7 +356,7 @@ mod tests {
let proof = prove_user_atomic_transfer(user_atomic_transfer); let proof = prove_user_atomic_transfer(user_atomic_transfer);
assert!(proof.verify(DeathConstraintPublic { assert!(proof.verify(ConstraintPublic {
nf: user_note.nullifier(), nf: user_note.nullifier(),
ptx_root: ptx.commit().root(), ptx_root: ptx.commit().root(),
})) }))

View File

@ -34,7 +34,7 @@ fn test_atomic_transfer() {
NoteWitness { NoteWitness {
value: 1, value: 1,
unit: cl::note::derive_unit("INTENT"), unit: cl::note::derive_unit("INTENT"),
death_constraint: executor::user_atomic_transfer_death_constraint(), constraint: executor::user_atomic_transfer_constraint(),
state: alice_intent.commit(), state: alice_intent.commit(),
}, },
NullifierNonce::random(&mut rng), NullifierNonce::random(&mut rng),
@ -86,7 +86,7 @@ fn test_atomic_transfer() {
&mut alice, &mut alice,
); );
let death_proofs = BTreeMap::from_iter([ let constraint_proofs = BTreeMap::from_iter([
( (
alice_intent_in.nullifier(), alice_intent_in.nullifier(),
executor::prove_user_atomic_transfer(UserAtomicTransfer { executor::prove_user_atomic_transfer(UserAtomicTransfer {
@ -153,7 +153,7 @@ fn test_atomic_transfer() {
let atomic_transfer_proof = ledger::partial_tx::ProvedPartialTx::prove( let atomic_transfer_proof = ledger::partial_tx::ProvedPartialTx::prove(
&atomic_transfer_ptx, &atomic_transfer_ptx,
death_proofs, constraint_proofs,
&note_commitments, &note_commitments,
) )
.expect("atomic transfer proof failed"); .expect("atomic transfer proof failed");

View File

@ -3,7 +3,7 @@ use std::collections::BTreeMap;
use cl::{BalanceWitness, NoteWitness, NullifierSecret}; use cl::{BalanceWitness, NoteWitness, NullifierSecret};
use common::{mmr::MMR, new_account, BoundTx, SignedBoundTx, StateWitness, Tx, ZONE_CL_FUNDS_UNIT}; use common::{mmr::MMR, new_account, BoundTx, SignedBoundTx, StateWitness, Tx, ZONE_CL_FUNDS_UNIT};
use executor::ZoneNotes; use executor::ZoneNotes;
use ledger::death_constraint::DeathProof; use ledger::constraint::ConstraintProof;
#[test] #[test]
fn test_deposit() { fn test_deposit() {
@ -27,7 +27,7 @@ fn test_deposit() {
NoteWitness::stateless( NoteWitness::stateless(
78, 78,
*ZONE_CL_FUNDS_UNIT, *ZONE_CL_FUNDS_UNIT,
DeathProof::nop_constraint(), // alice should demand a tx inclusion proof for the deposit ConstraintProof::nop_constraint(), // alice should demand a tx inclusion proof for the deposit
), ),
alice_cl_sk.commit(), alice_cl_sk.commit(),
&mut rng, &mut rng,
@ -49,7 +49,7 @@ fn test_deposit() {
&mut alice, &mut alice,
); );
let death_proofs = BTreeMap::from_iter([ let constraint_proofs = BTreeMap::from_iter([
( (
zone_start.state_input_witness().nullifier(), zone_start.state_input_witness().nullifier(),
executor::prove_zone_stf( executor::prove_zone_stf(
@ -62,7 +62,10 @@ fn test_deposit() {
), ),
( (
alice_deposit.nullifier(), alice_deposit.nullifier(),
ledger::DeathProof::prove_nop(alice_deposit.nullifier(), deposit_ptx.commit().root()), ledger::ConstraintProof::prove_nop(
alice_deposit.nullifier(),
deposit_ptx.commit().root(),
),
), ),
]); ]);
@ -71,8 +74,11 @@ fn test_deposit() {
alice_deposit.note_commitment(), alice_deposit.note_commitment(),
]; ];
let deposit_proof = let deposit_proof = ledger::partial_tx::ProvedPartialTx::prove(
ledger::partial_tx::ProvedPartialTx::prove(&deposit_ptx, death_proofs, &note_commitments) &deposit_ptx,
constraint_proofs,
&note_commitments,
)
.expect("deposit proof failed"); .expect("deposit proof failed");
assert!(deposit_proof.verify()); assert!(deposit_proof.verify());

View File

@ -3,7 +3,7 @@ use std::collections::BTreeMap;
use cl::{BalanceWitness, NoteWitness, NullifierSecret}; use cl::{BalanceWitness, NoteWitness, NullifierSecret};
use common::{mmr::MMR, new_account, BoundTx, SignedBoundTx, StateWitness, Tx, ZONE_CL_FUNDS_UNIT}; use common::{mmr::MMR, new_account, BoundTx, SignedBoundTx, StateWitness, Tx, ZONE_CL_FUNDS_UNIT};
use executor::ZoneNotes; use executor::ZoneNotes;
use ledger::death_constraint::DeathProof; use ledger::constraint::ConstraintProof;
#[test] #[test]
fn test_withdrawal() { fn test_withdrawal() {
@ -18,7 +18,7 @@ fn test_withdrawal() {
let alice_intent = cl::InputWitness::from_output( let alice_intent = cl::InputWitness::from_output(
cl::OutputWitness::random( cl::OutputWitness::random(
NoteWitness::stateless(1, *ZONE_CL_FUNDS_UNIT, DeathProof::nop_constraint()), // TODO, intent should be in the death constraint NoteWitness::stateless(1, *ZONE_CL_FUNDS_UNIT, ConstraintProof::nop_constraint()), // TODO, intent should be in the constraint
alice_cl_sk.commit(), alice_cl_sk.commit(),
&mut rng, &mut rng,
), ),
@ -36,7 +36,7 @@ fn test_withdrawal() {
NoteWitness::stateless( NoteWitness::stateless(
withdraw.amount, withdraw.amount,
*ZONE_CL_FUNDS_UNIT, *ZONE_CL_FUNDS_UNIT,
DeathProof::nop_constraint(), ConstraintProof::nop_constraint(),
), ),
alice_cl_sk.commit(), alice_cl_sk.commit(),
&mut rng, &mut rng,
@ -60,7 +60,7 @@ fn test_withdrawal() {
&mut alice, &mut alice,
); );
let death_proofs = BTreeMap::from_iter([ let constraint_proofs = BTreeMap::from_iter([
( (
zone_start.state_input_witness().nullifier(), zone_start.state_input_witness().nullifier(),
executor::prove_zone_stf( executor::prove_zone_stf(
@ -81,7 +81,7 @@ fn test_withdrawal() {
), ),
( (
alice_intent.nullifier(), alice_intent.nullifier(),
DeathProof::prove_nop(alice_intent.nullifier(), withdraw_ptx.commit().root()), ConstraintProof::prove_nop(alice_intent.nullifier(), withdraw_ptx.commit().root()),
), ),
]); ]);
@ -91,8 +91,11 @@ fn test_withdrawal() {
alice_intent.note_commitment(), alice_intent.note_commitment(),
]; ];
let withdraw_proof = let withdraw_proof = ledger::partial_tx::ProvedPartialTx::prove(
ledger::partial_tx::ProvedPartialTx::prove(&withdraw_ptx, death_proofs, &note_commitments) &withdraw_ptx,
constraint_proofs,
&note_commitments,
)
.expect("withdraw proof failed"); .expect("withdraw proof failed");
assert!(withdraw_proof.verify()); assert!(withdraw_proof.verify());

View File

@ -9,6 +9,6 @@ pub fn assert_is_zone_note(
) { ) {
assert_eq!(state_roots.commit().0, note.state); assert_eq!(state_roots.commit().0, note.state);
assert_eq!(zone_meta.id(), state_roots.zone_id); assert_eq!(zone_meta.id(), state_roots.zone_id);
assert_eq!(zone_meta.zone_vk, note.death_constraint); assert_eq!(zone_meta.zone_constraint, note.constraint);
assert_eq!(zone_meta.unit, note.unit); assert_eq!(zone_meta.unit, note.unit);
} }

View File

@ -12,7 +12,7 @@
/// Thep User Note will encode the logic that orchestrates the withdrawal from zone A /// Thep User Note will encode the logic that orchestrates the withdrawal from zone A
/// and deposit to zone B. /// and deposit to zone B.
/// ///
/// The User Notes death constraint requires the following statements to be satisfied /// The User Notes constraint requires the following statements to be satisfied
/// in order for the fee to be captured. /// in order for the fee to be captured.
/// ///
/// 1. w_tx = withdraw(amt=100 NMO, from=Alice) tx was included in Zone A. /// 1. w_tx = withdraw(amt=100 NMO, from=Alice) tx was included in Zone A.
@ -22,7 +22,7 @@
/// Details: /// Details:
/// - the withdrawal in zone A must not be a general withdrawal tx, it must be bound to the user note. /// - the withdrawal in zone A must not be a general withdrawal tx, it must be bound to the user note.
/// i.e. the user_note must be present in the ptx for the withdrawal to be valid in Zone A. /// i.e. the user_note must be present in the ptx for the withdrawal to be valid in Zone A.
use ledger_proof_statements::death_constraint::DeathConstraintPublic; use ledger_proof_statements::constraint::ConstraintPublic;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
@ -67,7 +67,7 @@ pub struct UserAtomicTransfer {
} }
impl UserAtomicTransfer { impl UserAtomicTransfer {
pub fn assert_constraints(&self) -> DeathConstraintPublic { pub fn assert_constraints(&self) -> ConstraintPublic {
// user committed to these actions in the user note // user committed to these actions in the user note
assert_eq!(self.user_intent.commit(), self.user_note.input.note.state); assert_eq!(self.user_intent.commit(), self.user_note.input.note.state);
@ -103,6 +103,6 @@ impl UserAtomicTransfer {
let ptx_root = cl::PtxRoot(cl::merkle::node(input_root, output_root)); let ptx_root = cl::PtxRoot(cl::merkle::node(input_root, output_root));
let nf = self.user_note.input.nullifier(); let nf = self.user_note.input.nullifier();
DeathConstraintPublic { ptx_root, nf } ConstraintPublic { ptx_root, nf }
} }
} }

View File

@ -4,7 +4,7 @@
use cl::merkle; use cl::merkle;
use cl::partial_tx::PtxRoot; use cl::partial_tx::PtxRoot;
use goas_proof_statements::zone_funds::SpendFundsPrivate; use goas_proof_statements::zone_funds::SpendFundsPrivate;
use ledger_proof_statements::death_constraint::DeathConstraintPublic; use ledger_proof_statements::constraint::ConstraintPublic;
use risc0_zkvm::guest::env; use risc0_zkvm::guest::env;
fn main() { fn main() {
@ -25,5 +25,5 @@ fn main() {
let nf = in_zone_funds.input.nullifier(); let nf = in_zone_funds.input.nullifier();
env::commit(&DeathConstraintPublic { ptx_root, nf }); env::commit(&ConstraintPublic { ptx_root, nf });
} }

View File

@ -1,9 +1,9 @@
use goas_proof_statements::user_note::UserAtomicTransfer; use goas_proof_statements::user_note::UserAtomicTransfer;
use ledger_proof_statements::death_constraint::DeathConstraintPublic; use ledger_proof_statements::constraint::ConstraintPublic;
use risc0_zkvm::guest::env; use risc0_zkvm::guest::env;
fn main() { fn main() {
let transfer: UserAtomicTransfer = env::read(); let transfer: UserAtomicTransfer = env::read();
let public: DeathConstraintPublic = transfer.assert_constraints(); let public: ConstraintPublic = transfer.assert_constraints();
env::commit(&public); env::commit(&public);
} }

View File

@ -5,7 +5,7 @@ use cl::{
use common::*; use common::*;
use goas_proof_statements::zone_state::ZoneStatePrivate; use goas_proof_statements::zone_state::ZoneStatePrivate;
use ledger_proof_statements::death_constraint::DeathConstraintPublic; use ledger_proof_statements::constraint::ConstraintPublic;
use risc0_zkvm::guest::env; use risc0_zkvm::guest::env;
fn validate_zone_transition( fn validate_zone_transition(
@ -26,8 +26,8 @@ fn validate_zone_transition(
assert_eq!(out_note.output.note.unit, metadata.unit); assert_eq!(out_note.output.note.unit, metadata.unit);
// ensure constraints match metadata // ensure constraints match metadata
assert_eq!(in_note.input.note.death_constraint, metadata.zone_vk); assert_eq!(in_note.input.note.constraint, metadata.zone_constraint);
assert_eq!(out_note.output.note.death_constraint, metadata.zone_vk); assert_eq!(out_note.output.note.constraint, metadata.zone_constraint);
// nullifier secret is propagated // nullifier secret is propagated
assert_eq!(in_note.input.nf_sk.commit(), out_note.output.nf_pk); assert_eq!(in_note.input.nf_sk.commit(), out_note.output.nf_pk);
@ -39,7 +39,7 @@ fn validate_zone_transition(
let expected_note_witness = NoteWitness::new( let expected_note_witness = NoteWitness::new(
out_state.total_balance(), out_state.total_balance(),
*ZONE_CL_FUNDS_UNIT, *ZONE_CL_FUNDS_UNIT,
metadata.funds_vk, metadata.funds_constraint,
metadata.id(), metadata.id(),
); );
assert_eq!( assert_eq!(
@ -65,7 +65,7 @@ fn main() {
let input_root = zone_in.input_root(); let input_root = zone_in.input_root();
let output_root = zone_out.output_root(); let output_root = zone_out.output_root();
let pub_inputs = DeathConstraintPublic { let pub_inputs = ConstraintPublic {
ptx_root: PtxRoot(cl::merkle::node(input_root, output_root)), ptx_root: PtxRoot(cl::merkle::node(input_root, output_root)),
nf: zone_in.input.nullifier(), nf: zone_in.input.nullifier(),
}; };

View File

@ -25,7 +25,7 @@ pub struct UnitBalance {
impl UnitBalance { impl UnitBalance {
pub fn is_zero(&self) -> bool { pub fn is_zero(&self) -> bool {
return self.pos == self.neg; self.pos == self.neg
} }
} }

View File

@ -3,7 +3,7 @@
/// Partial transactions, as the name suggests, are transactions /// Partial transactions, as the name suggests, are transactions
/// which on their own may not balance (i.e. \sum inputs != \sum outputs) /// which on their own may not balance (i.e. \sum inputs != \sum outputs)
use crate::{ use crate::{
note::{DeathCommitment, NoteWitness}, note::{ConstraintCommitment, NoteWitness},
nullifier::{Nullifier, NullifierNonce, NullifierSecret}, nullifier::{Nullifier, NullifierNonce, NullifierSecret},
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Input { pub struct Input {
pub nullifier: Nullifier, pub nullifier: Nullifier,
pub death_cm: DeathCommitment, pub constraint: ConstraintCommitment,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
@ -60,7 +60,7 @@ impl InputWitness {
pub fn commit(&self) -> Input { pub fn commit(&self) -> Input {
Input { Input {
nullifier: self.nullifier(), nullifier: self.nullifier(),
death_cm: self.note.death_commitment(), constraint: self.note.constraint,
} }
} }
@ -73,7 +73,7 @@ impl Input {
pub fn to_bytes(&self) -> [u8; 64] { pub fn to_bytes(&self) -> [u8; 64] {
let mut bytes = [0u8; 64]; let mut bytes = [0u8; 64];
bytes[..32].copy_from_slice(self.nullifier.as_bytes()); bytes[..32].copy_from_slice(self.nullifier.as_bytes());
bytes[32..64].copy_from_slice(&self.death_cm.0); bytes[32..64].copy_from_slice(&self.constraint.0);
bytes bytes
} }
} }

View File

@ -12,7 +12,7 @@ pub mod partial_tx;
pub use balance::{Balance, BalanceWitness}; pub use balance::{Balance, BalanceWitness};
pub use bundle::{Bundle, BundleWitness}; pub use bundle::{Bundle, BundleWitness};
pub use input::{Input, InputWitness}; pub use input::{Input, InputWitness};
pub use note::{DeathCommitment, NoteCommitment, NoteWitness}; pub use note::{ConstraintCommitment, NoteCommitment, NoteWitness};
pub use nullifier::{Nullifier, NullifierCommitment, NullifierNonce, NullifierSecret}; pub use nullifier::{Nullifier, NullifierCommitment, NullifierNonce, NullifierSecret};
pub use output::{Output, OutputWitness}; pub use output::{Output, OutputWitness};
pub use partial_tx::{ pub use partial_tx::{

View File

@ -7,15 +7,17 @@ use crate::{
}; };
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct DeathCommitment(pub [u8; 32]); pub struct ConstraintCommitment(pub [u8; 32]);
pub fn death_commitment(death_constraint: &[u8]) -> DeathCommitment { impl ConstraintCommitment {
pub fn from_vk(constraint_vk: &[u8]) -> Self {
let mut hasher = Sha256::new(); let mut hasher = Sha256::new();
hasher.update(b"NOMOS_CL_DEATH_COMMIT"); hasher.update(b"NOMOS_CL_CONSTRAINT_COMMIT");
hasher.update(death_constraint); hasher.update(constraint_vk);
let death_cm: [u8; 32] = hasher.finalize().into(); let constraint_cm: [u8; 32] = hasher.finalize().into();
DeathCommitment(death_cm) Self(constraint_cm)
}
} }
pub fn derive_unit(unit: &str) -> Unit { pub fn derive_unit(unit: &str) -> Unit {
@ -39,26 +41,26 @@ impl NoteCommitment {
pub struct NoteWitness { pub struct NoteWitness {
pub value: u64, pub value: u64,
pub unit: Unit, pub unit: Unit,
pub death_constraint: [u8; 32], // death constraint verification key pub constraint: ConstraintCommitment,
pub state: [u8; 32], pub state: [u8; 32],
} }
impl NoteWitness { impl NoteWitness {
pub fn new(value: u64, unit: Unit, death_constraint: [u8; 32], state: [u8; 32]) -> Self { pub fn new(value: u64, unit: Unit, constraint: ConstraintCommitment, state: [u8; 32]) -> Self {
Self { Self {
value, value,
unit, unit,
death_constraint, constraint,
state, state,
} }
} }
pub fn basic(value: u64, unit: Unit) -> Self { pub fn basic(value: u64, unit: Unit) -> Self {
Self::new(value, unit, [0u8; 32], [0u8; 32]) Self::new(value, unit, ConstraintCommitment([0u8; 32]), [0u8; 32])
} }
pub fn stateless(value: u64, unit: Unit, death_constraint: [u8; 32]) -> Self { pub fn stateless(value: u64, unit: Unit, constraint: ConstraintCommitment) -> Self {
Self::new(value, unit, death_constraint, [0u8; 32]) Self::new(value, unit, constraint, [0u8; 32])
} }
pub fn commit(&self, nf_pk: NullifierCommitment, nonce: NullifierNonce) -> NoteCommitment { pub fn commit(&self, nf_pk: NullifierCommitment, nonce: NullifierNonce) -> NoteCommitment {
@ -73,8 +75,8 @@ impl NoteWitness {
// COMMIT TO STATE // COMMIT TO STATE
hasher.update(self.state); hasher.update(self.state);
// COMMIT TO DEATH CONSTRAINT // COMMIT TO CONSTRAINT
hasher.update(self.death_constraint); hasher.update(self.constraint.0);
// COMMIT TO NULLIFIER // COMMIT TO NULLIFIER
hasher.update(nf_pk.as_bytes()); hasher.update(nf_pk.as_bytes());
@ -83,10 +85,6 @@ impl NoteWitness {
let commit_bytes: [u8; 32] = hasher.finalize().into(); let commit_bytes: [u8; 32] = hasher.finalize().into();
NoteCommitment(commit_bytes) NoteCommitment(commit_bytes)
} }
pub fn death_commitment(&self) -> DeathCommitment {
death_commitment(&self.death_constraint)
}
} }
#[cfg(test)] #[cfg(test)]
@ -116,7 +114,7 @@ mod test {
..reference_note ..reference_note
}, },
NoteWitness { NoteWitness {
death_constraint: [1u8; 32], constraint: ConstraintCommitment::from_vk(&[1u8; 32]),
..reference_note ..reference_note
}, },
NoteWitness { NoteWitness {

View File

@ -0,0 +1,87 @@
use cl::ConstraintCommitment;
use ledger_proof_statements::constraint::ConstraintPublic;
use crate::error::Result;
#[derive(Debug, Clone)]
pub struct ConstraintProof {
pub risc0_id: [u32; 8],
pub risc0_receipt: risc0_zkvm::Receipt,
}
pub fn risc0_constraint(risc0_id: [u32; 8]) -> ConstraintCommitment {
// Commit to a RISC0 ID for use as a note constraint
let mut bytes = [0u8; 32];
for (i, word) in risc0_id.iter().enumerate() {
let word_bytes = word.to_le_bytes();
bytes[i * 4 + 0] = word_bytes[0];
bytes[i * 4 + 1] = word_bytes[1];
bytes[i * 4 + 2] = word_bytes[2];
bytes[i * 4 + 3] = word_bytes[3];
}
ConstraintCommitment::from_vk(&bytes)
}
impl ConstraintProof {
pub fn from_risc0(risc0_id: [u32; 8], risc0_receipt: risc0_zkvm::Receipt) -> Self {
Self {
risc0_id,
risc0_receipt,
}
}
pub fn constraint(&self) -> ConstraintCommitment {
risc0_constraint(self.risc0_id)
}
pub fn public(&self) -> Result<ConstraintPublic> {
Ok(self.risc0_receipt.journal.decode()?)
}
pub fn verify(&self, expected_public: ConstraintPublic) -> bool {
let Ok(public) = self.public() else {
return false;
};
expected_public == public && self.risc0_receipt.verify(self.risc0_id).is_ok()
}
pub fn nop_constraint() -> ConstraintCommitment {
risc0_constraint(nomos_cl_risc0_proofs::CONSTRAINT_NOP_ID)
}
pub fn prove_nop(nf: cl::Nullifier, ptx_root: cl::PtxRoot) -> Self {
let constraint_public = ConstraintPublic { nf, ptx_root };
let env = risc0_zkvm::ExecutorEnv::builder()
.write(&constraint_public)
.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::CONSTRAINT_NOP_ELF, &opts)
.unwrap();
println!(
"STARK 'constraint-nop' prover time: {:.2?}, total_cycles: {}",
start_t.elapsed(),
prove_info.stats.total_cycles
);
// extract the receipt.
let receipt = prove_info.receipt;
Self::from_risc0(nomos_cl_risc0_proofs::CONSTRAINT_NOP_ID, receipt)
}
}

View File

@ -1,90 +0,0 @@
use ledger_proof_statements::death_constraint::DeathConstraintPublic;
use sha2::{Digest, Sha256};
use crate::error::Result;
pub type Risc0DeathConstraintId = [u32; 8];
#[derive(Debug, Clone)]
pub struct DeathProof {
pub constraint: Risc0DeathConstraintId,
pub risc0_receipt: risc0_zkvm::Receipt,
}
pub fn risc0_id_to_cl_death_constraint(risc0_id: Risc0DeathConstraintId) -> [u8; 32] {
// RISC0 proof ids have the format: [u32; 8], and cl death constraint ids have the format [u8; 32].
// CL death constraints are meaningless beyond being binding, therefore we merely need a collision
// resisitant mapping of RISC0 ids to cl death constraints.
let mut hasher = Sha256::new();
hasher.update(b"NOMOS_RISC0_ID_TO_CL_DEATH_CONSTRAINT");
for word in risc0_id {
hasher.update(u32::to_ne_bytes(word));
}
let death_constraint: [u8; 32] = hasher.finalize().into();
death_constraint
}
impl DeathProof {
pub fn from_risc0(
risc0_id: Risc0DeathConstraintId,
risc0_receipt: risc0_zkvm::Receipt,
) -> Self {
Self {
constraint: risc0_id,
risc0_receipt,
}
}
pub fn death_commitment(&self) -> cl::DeathCommitment {
cl::note::death_commitment(&risc0_id_to_cl_death_constraint(self.constraint))
}
pub fn public(&self) -> Result<DeathConstraintPublic> {
Ok(self.risc0_receipt.journal.decode()?)
}
pub fn verify(&self, expected_public: DeathConstraintPublic) -> bool {
let Ok(public) = self.public() else {
return false;
};
expected_public == public && self.risc0_receipt.verify(self.constraint).is_ok()
}
pub fn nop_constraint() -> [u8; 32] {
risc0_id_to_cl_death_constraint(nomos_cl_risc0_proofs::DEATH_CONSTRAINT_NOP_ID)
}
pub fn prove_nop(nf: cl::Nullifier, ptx_root: cl::PtxRoot) -> Self {
let death_public = DeathConstraintPublic { nf, ptx_root };
let env = risc0_zkvm::ExecutorEnv::builder()
.write(&death_public)
.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::DEATH_CONSTRAINT_NOP_ELF, &opts)
.unwrap();
println!(
"STARK 'death-nop' prover time: {:.2?}, total_cycles: {}",
start_t.elapsed(),
prove_info.stats.total_cycles
);
// extract the receipt.
let receipt = prove_info.receipt;
Self::from_risc0(nomos_cl_risc0_proofs::DEATH_CONSTRAINT_NOP_ID, receipt)
}
}

View File

@ -1,6 +1,6 @@
pub mod bundle; pub mod bundle;
pub mod death_constraint; pub mod constraint;
pub mod error; pub mod error;
pub mod partial_tx; pub mod partial_tx;
pub use death_constraint::DeathProof; pub use constraint::ConstraintProof;

View File

@ -1,28 +1,28 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use ledger_proof_statements::{ use ledger_proof_statements::{
death_constraint::DeathConstraintPublic, constraint::ConstraintPublic,
ptx::{PtxPrivate, PtxPublic}, ptx::{PtxPrivate, PtxPublic},
}; };
use crate::{ use crate::{
death_constraint::DeathProof, error::{Error, Result} constraint::ConstraintProof,
error::{Error, Result},
}; };
const MAX_NOTE_COMMS: usize = 2usize.pow(8); const MAX_NOTE_COMMS: usize = 2usize.pow(8);
pub struct ProvedPartialTx { pub struct ProvedPartialTx {
pub ptx: cl::PartialTx, pub ptx: cl::PartialTx,
pub cm_root: [u8; 32], pub cm_root: [u8; 32],
pub death_proofs: BTreeMap<cl::Nullifier, DeathProof>, pub constraint_proofs: BTreeMap<cl::Nullifier, ConstraintProof>,
pub risc0_receipt: risc0_zkvm::Receipt, pub risc0_receipt: risc0_zkvm::Receipt,
} }
impl ProvedPartialTx { impl ProvedPartialTx {
pub fn prove( pub fn prove(
ptx: &cl::PartialTxWitness, ptx: &cl::PartialTxWitness,
death_proofs: BTreeMap<cl::Nullifier, DeathProof>, constraint_proofs: BTreeMap<cl::Nullifier, ConstraintProof>,
note_commitments: &[cl::NoteCommitment], note_commitments: &[cl::NoteCommitment],
) -> Result<ProvedPartialTx> { ) -> Result<ProvedPartialTx> {
let cm_leaves = note_commitment_leaves(note_commitments); let cm_leaves = note_commitment_leaves(note_commitments);
@ -67,14 +67,12 @@ impl ProvedPartialTx {
start_t.elapsed(), start_t.elapsed(),
prove_info.stats.total_cycles prove_info.stats.total_cycles
); );
// extract the receipt.
let receipt = prove_info.receipt;
Ok(Self { Ok(Self {
ptx: ptx.commit(), ptx: ptx.commit(),
cm_root, cm_root,
risc0_receipt: receipt, risc0_receipt: prove_info.receipt,
death_proofs, constraint_proofs,
}) })
} }
@ -86,7 +84,11 @@ impl ProvedPartialTx {
let Ok(proved_ptx_inputs) = self.public() else { let Ok(proved_ptx_inputs) = self.public() else {
return false; return false;
}; };
if (PtxPublic { ptx: self.ptx.clone(), cm_root: self.cm_root }) != proved_ptx_inputs { let expected_ptx_inputs = PtxPublic {
ptx: self.ptx.clone(),
cm_root: self.cm_root,
};
if expected_ptx_inputs != proved_ptx_inputs {
return false; return false;
} }
@ -94,15 +96,15 @@ impl ProvedPartialTx {
for input in self.ptx.inputs.iter() { for input in self.ptx.inputs.iter() {
let nf = input.nullifier; let nf = input.nullifier;
let Some(death_proof) = self.death_proofs.get(&nf) else { let Some(constraint_proof) = self.constraint_proofs.get(&nf) else {
return false; return false;
}; };
if input.death_cm != death_proof.death_commitment() { if input.constraint != constraint_proof.constraint() {
// ensure the death proof is actually for this input // ensure the constraint proof is actually for this input
return false; return false;
} }
if !death_proof.verify(DeathConstraintPublic { nf, ptx_root }) { if !constraint_proof.verify(ConstraintPublic { nf, ptx_root }) {
// verify the death constraint was satisfied // verify the constraint was satisfied
return false; return false;
} }
} }

View File

@ -1,7 +1,7 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use cl::{note::derive_unit, BalanceWitness}; use cl::{note::derive_unit, BalanceWitness};
use ledger::{bundle::ProvedBundle, death_constraint::DeathProof, partial_tx::ProvedPartialTx}; use ledger::{bundle::ProvedBundle, constraint::ConstraintProof, partial_tx::ProvedPartialTx};
use rand_core::CryptoRngCore; use rand_core::CryptoRngCore;
struct User(cl::NullifierSecret); struct User(cl::NullifierSecret);
@ -41,7 +41,7 @@ fn test_simple_transfer() {
// Alice has an unspent note worth 10 NMO // Alice has an unspent note worth 10 NMO
let utxo = receive_utxo( let utxo = receive_utxo(
cl::NoteWitness::stateless(10, nmo, DeathProof::nop_constraint()), cl::NoteWitness::stateless(10, nmo, ConstraintProof::nop_constraint()),
alice.pk(), alice.pk(),
&mut rng, &mut rng,
); );
@ -61,17 +61,18 @@ fn test_simple_transfer() {
balance_blinding: BalanceWitness::random_blinding(&mut rng), balance_blinding: BalanceWitness::random_blinding(&mut rng),
}; };
// Prove the death constraints for alices input (she uses the no-op death constraint) // Prove the constraints for alices input (she uses the no-op constraint)
let death_proofs = BTreeMap::from_iter(ptx_witness.inputs.iter().map(|i| { let constraint_proofs = BTreeMap::from_iter(ptx_witness.inputs.iter().map(|i| {
( (
i.nullifier(), i.nullifier(),
DeathProof::prove_nop(i.nullifier(), ptx_witness.commit().root()), ConstraintProof::prove_nop(i.nullifier(), ptx_witness.commit().root()),
) )
})); }));
// 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).unwrap(); let proved_ptx =
ProvedPartialTx::prove(&ptx_witness, constraint_proofs, &note_commitments).unwrap();
assert!(proved_ptx.verify()); // It's a valid ptx. assert!(proved_ptx.verify()); // It's a valid ptx.

View File

@ -2,7 +2,7 @@ use cl::{Nullifier, PtxRoot};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct DeathConstraintPublic { pub struct ConstraintPublic {
pub nf: Nullifier, pub nf: Nullifier,
pub ptx_root: PtxRoot, pub ptx_root: PtxRoot,
} }

View File

@ -1,3 +1,3 @@
pub mod death_constraint; pub mod constraint;
pub mod ptx; pub mod ptx;
pub mod bundle; pub mod bundle;

View File

@ -7,5 +7,5 @@ edition = "2021"
risc0-build = { version = "1.0" } risc0-build = { version = "1.0" }
[package.metadata.risc0] [package.metadata.risc0]
methods = ["bundle", "death_constraint_nop", "ptx"] methods = ["bundle", "constraint_nop", "ptx"]

View File

@ -1,5 +1,5 @@
[package] [package]
name = "death_constraint_nop" name = "constraint_nop"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"

View File

@ -0,0 +1,8 @@
/// Constraint No-op Proof
use ledger_proof_statements::constraint::ConstraintPublic;
use risc0_zkvm::guest::env;
fn main() {
let public: ConstraintPublic = env::read();
env::commit(&public);
}

View File

@ -1,8 +0,0 @@
/// Death Constraint No-op Proof
use ledger_proof_statements::death_constraint::DeathConstraintPublic;
use risc0_zkvm::guest::env;
fn main() {
let public: DeathConstraintPublic = env::read();
env::commit(&public);
}