move zone_id into {input,output}witness

This commit is contained in:
Giacomo Pasini 2024-11-27 17:17:27 +01:00
parent 3646971fd9
commit 720836e7f2
No known key found for this signature in database
GPG Key ID: FC08489D2D895D4B
11 changed files with 140 additions and 163 deletions

View File

@ -1,9 +1,6 @@
use serde::{Deserialize, Serialize};
use crate::{
cl::{partial_tx::PartialTx, BalanceWitness, PartialTxWitness},
zone_layer::notes::ZoneId,
};
use crate::{cl::partial_tx::PartialTx, zone_layer::notes::ZoneId};
use sha2::{Digest, Sha256};
use std::collections::HashSet;
@ -45,27 +42,10 @@ impl Bundle {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BundleWitness {
pub partials: Vec<PartialTxWitness>,
}
impl BundleWitness {
pub fn balance(&self) -> BalanceWitness {
BalanceWitness::combine(self.partials.iter().map(|ptx| ptx.balance()), [0u8; 16])
}
// pub fn commit(&self) -> Bundle {
// Bundle {
// partials: Vec::from_iter(self.partials.iter().map(|ptx| ptx.commit())),
// }
// }
}
#[cfg(test)]
mod test {
use crate::cl::{
balance::UnitBalance,
balance::{BalanceWitness, UnitBalance},
input::InputWitness,
note::{derive_unit, NoteWitness},
nullifier::NullifierSecret,
@ -73,25 +53,35 @@ mod test {
partial_tx::PartialTxWitness,
};
use super::*;
#[test]
fn test_bundle_balance() {
let mut rng = rand::thread_rng();
let zone_id = [0; 32];
let (nmo, eth, crv) = (derive_unit("NMO"), derive_unit("ETH"), derive_unit("CRV"));
let nf_a = NullifierSecret::random(&mut rng);
let nf_b = NullifierSecret::random(&mut rng);
let nf_c = NullifierSecret::random(&mut rng);
let nmo_10_utxo = OutputWitness::new(NoteWitness::basic(10, nmo, &mut rng), nf_a.commit());
let nmo_10_utxo = OutputWitness::new(
NoteWitness::basic(10, nmo, &mut rng),
nf_a.commit(),
zone_id,
);
let nmo_10_in = InputWitness::from_output(nmo_10_utxo, nf_a);
let eth_23_utxo = OutputWitness::new(NoteWitness::basic(23, eth, &mut rng), nf_b.commit());
let eth_23_utxo = OutputWitness::new(
NoteWitness::basic(23, eth, &mut rng),
nf_b.commit(),
zone_id,
);
let eth_23_in = InputWitness::from_output(eth_23_utxo, nf_b);
let crv_4840_out =
OutputWitness::new(NoteWitness::basic(4840, crv, &mut rng), nf_c.commit());
let crv_4840_out = OutputWitness::new(
NoteWitness::basic(4840, crv, &mut rng),
nf_c.commit(),
zone_id,
);
let ptx_unbalanced = PartialTxWitness {
inputs: vec![nmo_10_in, eth_23_in],
@ -99,13 +89,9 @@ mod test {
balance_blinding: BalanceWitness::random_blinding(&mut rng),
};
let bundle_witness = BundleWitness {
partials: vec![ptx_unbalanced.clone()],
};
assert!(!bundle_witness.balance().is_zero());
assert!(!ptx_unbalanced.balance().is_zero());
assert_eq!(
bundle_witness.balance().balances,
ptx_unbalanced.balance().balances,
vec![
UnitBalance {
unit: nmo,
@ -129,10 +115,12 @@ mod test {
let nmo_10_out = OutputWitness::new(
NoteWitness::basic(10, nmo, &mut rng),
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
zone_id,
);
let eth_23_out = OutputWitness::new(
NoteWitness::basic(23, eth, &mut rng),
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
zone_id,
);
let ptx_solved = PartialTxWitness {
@ -141,11 +129,10 @@ mod test {
balance_blinding: BalanceWitness::random_blinding(&mut rng),
};
let witness = BundleWitness {
partials: vec![ptx_unbalanced, ptx_solved],
};
let bundle_balance =
BalanceWitness::combine([ptx_unbalanced.balance(), ptx_solved.balance()], [0; 16]);
assert!(witness.balance().is_zero());
assert_eq!(witness.balance().balances, vec![]);
assert!(bundle_balance.is_zero());
assert_eq!(bundle_balance.balances, vec![]);
}
}

View File

@ -24,59 +24,65 @@ pub struct Input {
pub struct InputWitness {
pub note: NoteWitness,
pub nf_sk: NullifierSecret,
pub zone_id: ZoneId,
}
impl InputWitness {
pub fn new(note: NoteWitness, nf_sk: NullifierSecret) -> Self {
Self { note, nf_sk }
pub fn new(note: NoteWitness, nf_sk: NullifierSecret, zone_id: ZoneId) -> Self {
Self {
note,
nf_sk,
zone_id,
}
}
pub fn from_output(output: OutputWitness, nf_sk: NullifierSecret) -> Self {
assert_eq!(nf_sk.commit(), output.nf_pk);
Self::new(output.note, nf_sk)
Self::new(output.note, nf_sk, output.zone_id)
}
pub fn public(output: OutputWitness) -> Self {
let nf_sk = NullifierSecret::zero();
assert_eq!(nf_sk.commit(), output.nf_pk); // ensure the output was a public UTXO
Self::new(output.note, nf_sk)
Self::new(output.note, nf_sk, output.zone_id)
}
pub fn evolved_nonce(&self, tag: &dyn AsRef<[u8]>, domain: &[u8]) -> Nonce {
pub fn evolved_nonce(&self, domain: &[u8]) -> Nonce {
let mut hasher = Sha256::new();
hasher.update(b"NOMOS_COIN_EVOLVE");
hasher.update(domain);
hasher.update(self.nf_sk.0);
hasher.update(self.note.commit(tag, self.nf_sk.commit()).0);
hasher.update(self.note.commit(&self.zone_id, self.nf_sk.commit()).0);
let nonce_bytes: [u8; 32] = hasher.finalize().into();
Nonce::from_bytes(nonce_bytes)
}
pub fn evolve_output(&self, tag: &dyn AsRef<[u8]>, domain: &[u8]) -> OutputWitness {
pub fn evolve_output(&self, domain: &[u8]) -> OutputWitness {
OutputWitness {
note: NoteWitness {
nonce: self.evolved_nonce(tag, domain),
nonce: self.evolved_nonce(domain),
..self.note
},
nf_pk: self.nf_sk.commit(),
zone_id: self.zone_id,
}
}
pub fn nullifier(&self, tag: &dyn AsRef<[u8]>) -> Nullifier {
Nullifier::new(tag, self.nf_sk, self.note_commitment(tag))
pub fn nullifier(&self) -> Nullifier {
Nullifier::new(&self.zone_id, self.nf_sk, self.note_commitment())
}
pub fn commit(&self, zone_id: ZoneId) -> Input {
pub fn commit(&self) -> Input {
Input {
nullifier: self.nullifier(&zone_id),
nullifier: self.nullifier(),
constraint: self.note.constraint,
zone_id,
zone_id: self.zone_id,
}
}
pub fn note_commitment(&self, tag: &dyn AsRef<[u8]>) -> NoteCommitment {
self.note.commit(tag, self.nf_sk.commit())
pub fn note_commitment(&self) -> NoteCommitment {
self.note.commit(&self.zone_id, self.nf_sk.commit())
}
}

View File

@ -65,10 +65,11 @@ impl MMR {
}
pub fn commit(&self) -> [u8; 32] {
// todo: baggin the peaks
let mut hasher = Sha256::new();
for mrr_root in self.roots.iter() {
hasher.update(mrr_root.root);
hasher.update(mrr_root.height.to_le_bytes());
for mmr_root in self.roots.iter() {
hasher.update(mmr_root.root);
hasher.update(mmr_root.height.to_le_bytes());
}
hasher.finalize().into()
}

View File

@ -11,7 +11,7 @@ pub mod output;
pub mod partial_tx;
pub use balance::{Balance, BalanceWitness};
pub use bundle::{Bundle, BundleWitness};
pub use bundle::Bundle;
pub use input::{Input, InputWitness};
pub use note::{Constraint, Nonce, NoteCommitment, NoteWitness};
pub use nullifier::{Nullifier, NullifierCommitment, NullifierSecret};

View File

@ -19,26 +19,35 @@ pub struct Output {
pub struct OutputWitness {
pub note: NoteWitness,
pub nf_pk: NullifierCommitment,
pub zone_id: ZoneId,
}
impl OutputWitness {
pub fn new(note: NoteWitness, nf_pk: NullifierCommitment) -> Self {
Self { note, nf_pk }
}
pub fn public(note: NoteWitness) -> Self {
let nf_pk = NullifierSecret::zero().commit();
Self { note, nf_pk }
}
pub fn commit_note(&self, tag: &dyn AsRef<[u8]>) -> NoteCommitment {
self.note.commit(tag, self.nf_pk)
}
pub fn commit(&self, zone_id: ZoneId) -> Output {
Output {
pub fn new(note: NoteWitness, nf_pk: NullifierCommitment, zone_id: ZoneId) -> Self {
Self {
note,
nf_pk,
zone_id,
note_comm: self.commit_note(&zone_id),
}
}
pub fn public(note: NoteWitness, zone_id: ZoneId) -> Self {
let nf_pk = NullifierSecret::zero().commit();
Self {
note,
nf_pk,
zone_id,
}
}
pub fn commit_note(&self) -> NoteCommitment {
self.note.commit(&self.zone_id, self.nf_pk)
}
pub fn commit(&self) -> Output {
Output {
zone_id: self.zone_id,
note_comm: self.commit_note(),
}
}
}

View File

@ -1,14 +1,11 @@
use rand_core::{CryptoRngCore, RngCore};
use serde::{Deserialize, Serialize};
use crate::{
cl::{
use crate::cl::{
balance::{Balance, BalanceWitness},
input::{Input, InputWitness},
merkle,
output::{Output, OutputWitness},
},
zone_layer::notes::ZoneId,
};
pub const MAX_INPUTS: usize = 8;
@ -68,33 +65,17 @@ impl PartialTxWitness {
BalanceWitness::from_ptx(self, self.balance_blinding)
}
pub fn commit(&self, input_zones: &[ZoneId], output_zones: &[ZoneId]) -> PartialTx {
assert_eq!(self.inputs.len(), input_zones.len());
assert_eq!(self.outputs.len(), output_zones.len());
pub fn commit(&self) -> PartialTx {
PartialTx {
inputs: self
.inputs
.iter()
.zip(input_zones.iter())
.map(|(i, z)| i.commit(*z))
.collect(),
outputs: self
.outputs
.iter()
.zip(output_zones.iter())
.map(|(o, z)| o.commit(*z))
.collect(),
inputs: self.inputs.iter().map(InputWitness::commit).collect(),
outputs: self.outputs.iter().map(OutputWitness::commit).collect(),
balance: self.balance().commit(),
}
}
pub fn input_witness(&self, zone_id: ZoneId, idx: usize) -> PartialTxInputWitness {
let input_bytes = Vec::from_iter(
self.inputs
.iter()
.map(|i| i.commit(zone_id).to_bytes().to_vec()),
);
pub fn input_witness(&self, idx: usize) -> PartialTxInputWitness {
let input_bytes =
Vec::from_iter(self.inputs.iter().map(|i| i.commit().to_bytes().to_vec()));
let input_merkle_leaves = merkle::padded_leaves::<MAX_INPUTS>(&input_bytes);
let path = merkle::path(input_merkle_leaves, idx);
@ -102,12 +83,9 @@ impl PartialTxWitness {
PartialTxInputWitness { input, path }
}
pub fn output_witness(&self, zone_id: ZoneId, idx: usize) -> PartialTxOutputWitness {
let output_bytes = Vec::from_iter(
self.outputs
.iter()
.map(|o| o.commit(zone_id).to_bytes().to_vec()),
);
pub fn output_witness(&self, idx: usize) -> PartialTxOutputWitness {
let output_bytes =
Vec::from_iter(self.outputs.iter().map(|o| o.commit().to_bytes().to_vec()));
let output_merkle_leaves = merkle::padded_leaves::<MAX_OUTPUTS>(&output_bytes);
let path = merkle::path(output_merkle_leaves, idx);
@ -151,8 +129,8 @@ pub struct PartialTxInputWitness {
}
impl PartialTxInputWitness {
pub fn input_root(&self, zone_id: ZoneId) -> [u8; 32] {
let leaf = merkle::leaf(&self.input.commit(zone_id).to_bytes());
pub fn input_root(&self) -> [u8; 32] {
let leaf = merkle::leaf(&self.input.commit().to_bytes());
merkle::path_root(leaf, &self.path)
}
}
@ -165,8 +143,8 @@ pub struct PartialTxOutputWitness {
}
impl PartialTxOutputWitness {
pub fn output_root(&self, zone_id: ZoneId) -> [u8; 32] {
let leaf = merkle::leaf(&self.output.commit(zone_id).to_bytes());
pub fn output_root(&self) -> [u8; 32] {
let leaf = merkle::leaf(&self.output.commit().to_bytes());
merkle::path_root(leaf, &self.path)
}
}

View File

@ -1,16 +1,20 @@
use cl::cl::{
note::derive_unit, BalanceWitness, BundleWitness, InputWitness, NoteWitness,
NullifierCommitment, NullifierSecret, OutputWitness, PartialTxWitness,
use cl::{
cl::{
note::derive_unit, BalanceWitness, InputWitness, NoteWitness, NullifierCommitment,
NullifierSecret, OutputWitness, PartialTxWitness,
},
zone_layer::notes::ZoneId,
};
fn receive_utxo(note: NoteWitness, nf_pk: NullifierCommitment) -> OutputWitness {
OutputWitness::new(note, nf_pk)
fn receive_utxo(note: NoteWitness, nf_pk: NullifierCommitment, zone_id: ZoneId) -> OutputWitness {
OutputWitness::new(note, nf_pk, zone_id)
}
#[test]
fn test_simple_transfer() {
let nmo = derive_unit("NMO");
let mut rng = rand::thread_rng();
let zone_id = [0; 32];
let sender_nf_sk = NullifierSecret::random(&mut rng);
let sender_nf_pk = sender_nf_sk.commit();
@ -18,12 +22,16 @@ fn test_simple_transfer() {
let recipient_nf_pk = NullifierSecret::random(&mut rng).commit();
// Assume the sender has received an unspent output from somewhere
let utxo = receive_utxo(NoteWitness::basic(10, nmo, &mut rng), sender_nf_pk);
let utxo = receive_utxo(NoteWitness::basic(10, nmo, &mut rng), sender_nf_pk, zone_id);
// and wants to send 8 NMO to some recipient and return 2 NMO to itself.
let recipient_output =
OutputWitness::new(NoteWitness::basic(8, nmo, &mut rng), recipient_nf_pk);
let change_output = OutputWitness::new(NoteWitness::basic(2, nmo, &mut rng), sender_nf_pk);
let recipient_output = OutputWitness::new(
NoteWitness::basic(8, nmo, &mut rng),
recipient_nf_pk,
zone_id,
);
let change_output =
OutputWitness::new(NoteWitness::basic(2, nmo, &mut rng), sender_nf_pk, zone_id);
let ptx_witness = PartialTxWitness {
inputs: vec![InputWitness::from_output(utxo, sender_nf_sk)],
@ -31,10 +39,5 @@ fn test_simple_transfer() {
balance_blinding: BalanceWitness::random_blinding(&mut rng),
};
let bundle = BundleWitness {
partials: vec![ptx_witness],
};
assert!(bundle.balance().is_zero())
assert!(ptx_witness.balance().is_zero())
}

View File

@ -20,7 +20,7 @@ pub struct ProvedLedgerTransition {
// TODO: find a better name
#[derive(Debug, Clone)]
pub struct ProvedBundle {
pub bundle: ProvedBalance,
pub balance: ProvedBalance,
pub ptxs: Vec<ProvedPartialTx>,
}
@ -30,7 +30,7 @@ impl ProvedBundle {
}
fn proofs(&self) -> Vec<risc0_zkvm::Receipt> {
let mut proofs = vec![self.bundle.risc0_receipt.clone()];
let mut proofs = vec![self.balance.risc0_receipt.clone()];
proofs.extend(self.ptxs.iter().map(|p| p.risc0_receipt.clone()));
proofs
}

View File

@ -2,7 +2,6 @@ use ledger_proof_statements::ptx::{PtxPrivate, PtxPublic};
use crate::error::{Error, Result};
use cl::cl::{merkle, PartialTxWitness};
use cl::zone_layer::notes::ZoneId;
#[derive(Debug, Clone)]
pub struct ProvedPartialTx {
@ -15,15 +14,11 @@ impl ProvedPartialTx {
ptx_witness: PartialTxWitness,
input_cm_paths: Vec<Vec<merkle::PathNode>>,
cm_roots: Vec<[u8; 32]>,
from: Vec<ZoneId>,
to: Vec<ZoneId>,
) -> Result<ProvedPartialTx> {
let ptx_private = PtxPrivate {
ptx: ptx_witness,
input_cm_paths,
cm_roots: cm_roots.clone(),
from,
to,
};
let env = risc0_zkvm::ExecutorEnv::builder()

View File

@ -1,12 +1,11 @@
use cl::{
cl::{
balance::Unit, merkle, mmr::MMR, note::derive_unit, BalanceWitness, BundleWitness,
InputWitness, NoteWitness, NullifierCommitment, NullifierSecret, OutputWitness,
PartialTxWitness,
balance::Unit, merkle, mmr::MMR, note::derive_unit, BalanceWitness, InputWitness,
NoteWitness, NullifierCommitment, NullifierSecret, OutputWitness, PartialTxWitness,
},
zone_layer::{
ledger::LedgerWitness,
notes::ZoneNote,
notes::{ZoneId, ZoneNote},
tx::{UpdateBundle, ZoneUpdate},
},
};
@ -43,8 +42,8 @@ impl User {
}
}
fn receive_utxo(note: NoteWitness, nf_pk: NullifierCommitment) -> OutputWitness {
OutputWitness::new(note, nf_pk)
fn receive_utxo(note: NoteWitness, nf_pk: NullifierCommitment, zone_id: ZoneId) -> OutputWitness {
OutputWitness::new(note, nf_pk, zone_id)
}
fn cross_transfer_transition(
@ -52,18 +51,23 @@ fn cross_transfer_transition(
input_path: Vec<merkle::PathNode>,
to: User,
amount: u64,
zone_a: [u8; 32],
zone_b: [u8; 32],
zone_a: ZoneId,
zone_b: ZoneId,
mut ledger_a: LedgerWitness,
mut ledger_b: LedgerWitness,
) -> (ProvedLedgerTransition, ProvedLedgerTransition) {
let mut rng = rand::thread_rng();
assert!(amount <= input.note.value);
let change = input.note.value - amount;
let transfer = OutputWitness::new(NoteWitness::basic(amount, *nmo(), &mut rng), to.pk());
let transfer = OutputWitness::new(
NoteWitness::basic(amount, *nmo(), &mut rng),
to.pk(),
zone_b,
);
let change = OutputWitness::new(
NoteWitness::basic(change, *nmo(), &mut rng),
input.nf_sk.commit(),
zone_a,
);
// Construct the ptx consuming the input and producing the two outputs.
@ -76,24 +80,22 @@ fn cross_transfer_transition(
ptx_witness.clone(),
vec![input_path],
vec![ledger_a.commitments.roots[0].root],
vec![zone_a],
vec![zone_b, zone_a],
)
.unwrap();
let bundle = ProvedBalance::prove(&BalancePrivate {
let balance = ProvedBalance::prove(&BalancePrivate {
balances: vec![ptx_witness.balance()],
})
.unwrap();
let zone_tx = ProvedBundle {
ptxs: vec![proved_ptx.clone()],
bundle,
balance,
};
// Prove the constraints for alices input (she uses the no-op constraint)
let constraint_proof =
ConstraintProof::prove_nop(input.nullifier(&zone_a), proved_ptx.public.ptx.root());
ConstraintProof::prove_nop(input.nullifier(), proved_ptx.public.ptx.root());
let ledger_a_transition = ProvedLedgerTransition::prove(
ledger_a.clone(),
@ -106,10 +108,10 @@ fn cross_transfer_transition(
let ledger_b_transition =
ProvedLedgerTransition::prove(ledger_b.clone(), zone_b, vec![zone_tx], vec![]).unwrap();
ledger_a.commitments.push(&change.commit_note(&zone_a).0);
ledger_a.nullifiers.push(input.nullifier(&zone_a));
ledger_a.commitments.push(&change.commit_note().0);
ledger_a.nullifiers.push(input.nullifier());
ledger_b.commitments.push(&transfer.commit_note(&zone_b).0);
ledger_b.commitments.push(&transfer.commit_note().0);
assert_eq!(ledger_a_transition.public.ledger, ledger_a.commit());
assert_eq!(ledger_b_transition.public.ledger, ledger_b.commit());
@ -121,6 +123,9 @@ fn cross_transfer_transition(
fn zone_update_cross() {
let mut rng = rand::thread_rng();
let zone_a_id = [0; 32];
let zone_b_id = [1; 32];
// alice is sending 8 NMO to bob.
let alice = User::random(&mut rng);
@ -130,15 +135,13 @@ fn zone_update_cross() {
let utxo = receive_utxo(
NoteWitness::stateless(10, *nmo(), ConstraintProof::nop_constraint(), &mut rng),
alice.pk(),
zone_a_id,
);
let alice_input = InputWitness::from_output(utxo, alice.sk());
let zone_a_id = [0; 32];
let zone_b_id = [1; 32];
let mut mmr = MMR::new();
let input_cm_path = mmr.push(&utxo.commit_note(&zone_a_id).0).path;
let input_cm_path = mmr.push(&utxo.commit_note().0).path;
let ledger_a = LedgerWitness {
commitments: mmr,

View File

@ -1,7 +1,4 @@
use cl::{
cl::{merkle, PartialTx, PartialTxWitness},
zone_layer::notes::ZoneId,
};
use cl::cl::{merkle, PartialTx, PartialTxWitness};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -15,6 +12,4 @@ pub struct PtxPrivate {
pub ptx: PartialTxWitness,
pub input_cm_paths: Vec<Vec<merkle::PathNode>>,
pub cm_roots: Vec<[u8; 32]>,
pub from: Vec<ZoneId>,
pub to: Vec<ZoneId>,
}