goas: reuse ZoneNotes struct in the deposit/withdrawal scenarios

This commit is contained in:
David Rusu 2024-08-11 23:08:40 +04:00
parent d73508a43c
commit 034fe8eda5
5 changed files with 142 additions and 224 deletions

View File

@ -28,8 +28,8 @@ pub struct ZoneMetadata {
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_vk);
hasher.update(&self.funds_vk); hasher.update(self.funds_vk);
hasher.update(self.unit.compress().as_bytes()); hasher.update(self.unit.compress().as_bytes());
hasher.finalize().into() hasher.finalize().into()
} }
@ -118,7 +118,7 @@ impl StateWitness {
pub fn evolve_nonce(self) -> Self { pub fn evolve_nonce(self) -> Self {
let updated_nonce = { let updated_nonce = {
let mut hasher = Sha256::new(); let mut hasher = Sha256::new();
hasher.update(&self.nonce); hasher.update(self.nonce);
hasher.update(b"NOMOS_ZONE_NONCE_EVOLVE"); hasher.update(b"NOMOS_ZONE_NONCE_EVOLVE");
hasher.finalize().into() hasher.finalize().into()
}; };

View File

@ -1,10 +1,104 @@
use std::collections::BTreeMap;
use cl::{PartialTxInputWitness, PartialTxOutputWitness}; use cl::{PartialTxInputWitness, PartialTxOutputWitness};
use common::{BoundTx, IncludedTxWitness, StateRoots, StateWitness, ZoneMetadata}; use common::{BoundTx, IncludedTxWitness, StateRoots, StateWitness, Tx, ZoneMetadata};
use goas_proof_statements::{ use goas_proof_statements::{
user_note::{UserAtomicTransfer, UserIntent}, user_note::{UserAtomicTransfer, UserIntent},
zone_funds::SpendFundsPrivate, zone_funds::SpendFundsPrivate,
zone_state::ZoneStatePrivate, zone_state::ZoneStatePrivate,
}; };
use rand_core::CryptoRngCore;
#[derive(Debug, Clone)]
pub struct ZoneNotes {
pub state: StateWitness,
pub state_note: cl::OutputWitness,
pub fund_note: cl::OutputWitness,
}
impl ZoneNotes {
pub fn new_with_balances(
zone_name: &str,
balances: BTreeMap<u32, u64>,
mut rng: impl CryptoRngCore,
) -> Self {
let state = StateWitness {
balances,
included_txs: vec![],
zone_metadata: zone_metadata(zone_name),
nonce: [0; 32],
};
let state_note = zone_state_utxo(&state, &mut rng);
let fund_note = zone_fund_utxo(state.total_balance(), state.zone_metadata, &mut rng);
Self {
state,
state_note,
fund_note,
}
}
pub fn state_input_witness(&self) -> cl::InputWitness {
cl::InputWitness::public(self.state_note)
}
pub fn fund_input_witness(&self) -> cl::InputWitness {
cl::InputWitness::public(self.fund_note)
}
pub fn run(mut self, txs: impl IntoIterator<Item = Tx>) -> Self {
for tx in txs {
self.state = self.state.apply(tx);
}
self.state = self.state.evolve_nonce();
let state_in = self.state_input_witness();
self.state_note = cl::OutputWitness::public(
cl::NoteWitness {
state: self.state.commit().0,
..state_in.note
},
state_in.evolved_nonce(),
);
let fund_in = self.fund_input_witness();
self.fund_note = cl::OutputWitness::public(
cl::NoteWitness {
value: self.state.total_balance(),
..fund_in.note
},
cl::NullifierNonce::from_bytes(self.state.nonce),
);
self
}
}
fn zone_fund_utxo(
value: u64,
zone_meta: ZoneMetadata,
mut rng: impl CryptoRngCore,
) -> cl::OutputWitness {
cl::OutputWitness::public(
cl::NoteWitness {
value,
unit: *common::ZONE_CL_FUNDS_UNIT,
death_constraint: zone_meta.funds_vk,
state: zone_meta.id(),
},
cl::NullifierNonce::random(&mut rng),
)
}
fn zone_state_utxo(zone: &StateWitness, mut rng: impl CryptoRngCore) -> cl::OutputWitness {
cl::OutputWitness::public(
cl::NoteWitness {
value: 1,
unit: zone.zone_metadata.unit,
death_constraint: zone.zone_metadata.zone_vk,
state: zone.commit().0,
},
cl::NullifierNonce::random(&mut rng),
)
}
pub fn user_atomic_transfer_death_constraint() -> [u8; 32] { pub fn user_atomic_transfer_death_constraint() -> [u8; 32] {
ledger::death_constraint::risc0_id_to_cl_death_constraint( ledger::death_constraint::risc0_id_to_cl_death_constraint(

View File

@ -1,100 +1,9 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use cl::{BundleWitness, NoteWitness, NullifierNonce}; use cl::{BundleWitness, NoteWitness, NullifierNonce};
use common::{BoundTx, Deposit, StateWitness, Tx, Withdraw, ZoneMetadata}; use common::{BoundTx, Deposit, Tx, Withdraw};
use executor::ZoneNotes;
use goas_proof_statements::user_note::UserIntent; use goas_proof_statements::user_note::UserIntent;
use rand_core::CryptoRngCore;
fn zone_fund_utxo(
value: u64,
zone_meta: ZoneMetadata,
mut rng: impl CryptoRngCore,
) -> cl::OutputWitness {
cl::OutputWitness::public(
cl::NoteWitness {
value,
unit: *common::ZONE_CL_FUNDS_UNIT,
death_constraint: zone_meta.funds_vk,
state: zone_meta.id(),
},
NullifierNonce::random(&mut rng),
)
}
fn zone_state_utxo(zone: &StateWitness, mut rng: impl CryptoRngCore) -> cl::OutputWitness {
cl::OutputWitness::public(
cl::NoteWitness {
value: 1,
unit: zone.zone_metadata.unit,
death_constraint: zone.zone_metadata.zone_vk,
state: zone.commit().0,
},
NullifierNonce::random(&mut rng),
)
}
#[derive(Debug, Clone)]
struct ZoneNotes {
state: StateWitness,
state_note: cl::OutputWitness,
fund_note: cl::OutputWitness,
}
impl ZoneNotes {
fn new_with_balances(
zone_name: &str,
balances: BTreeMap<u32, u64>,
mut rng: impl CryptoRngCore,
) -> Self {
let state = StateWitness {
balances,
included_txs: vec![],
zone_metadata: executor::zone_metadata(zone_name),
nonce: [0; 32],
};
let state_note = zone_state_utxo(&state, &mut rng);
let fund_note = zone_fund_utxo(state.total_balance(), state.zone_metadata, &mut rng);
Self {
state,
state_note,
fund_note,
}
}
fn state_input_witness(&self) -> cl::InputWitness {
cl::InputWitness::public(self.state_note)
}
fn fund_input_witness(&self) -> cl::InputWitness {
cl::InputWitness::public(self.fund_note)
}
fn run(mut self, txs: Vec<Tx>) -> Self {
for tx in txs {
self.state = self.state.apply(tx);
}
self.state = self.state.evolve_nonce();
let state_in = self.state_input_witness();
self.state_note = cl::OutputWitness::public(
cl::NoteWitness {
state: self.state.commit().0,
..state_in.note
},
state_in.evolved_nonce(),
);
let fund_in = self.fund_input_witness();
self.fund_note = cl::OutputWitness::public(
cl::NoteWitness {
value: self.state.total_balance(),
..fund_in.note
},
NullifierNonce::from_bytes(self.state.nonce),
);
self
}
}
#[test] #[test]
fn test_atomic_transfer() { fn test_atomic_transfer() {
@ -137,11 +46,11 @@ fn test_atomic_transfer() {
let zone_a_end = zone_a_start let zone_a_end = zone_a_start
.clone() .clone()
.run(vec![Tx::Withdraw(alice_intent.withdraw)]); .run([Tx::Withdraw(alice_intent.withdraw)]);
let zone_b_end = zone_b_start let zone_b_end = zone_b_start
.clone() .clone()
.run(vec![Tx::Deposit(alice_intent.deposit)]); .run([Tx::Deposit(alice_intent.deposit)]);
let alice_intent_in = cl::InputWitness::public(alice_intent_out); let alice_intent_in = cl::InputWitness::public(alice_intent_out);
let atomic_transfer_ptx = cl::PartialTxWitness { let atomic_transfer_ptx = cl::PartialTxWitness {

View File

@ -1,30 +1,9 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use cl::{NoteWitness, NullifierNonce, NullifierSecret}; use cl::{NoteWitness, NullifierSecret};
use common::{BoundTx, StateWitness, Tx, ZoneMetadata, ZONE_CL_FUNDS_UNIT}; use common::{BoundTx, StateWitness, Tx, ZONE_CL_FUNDS_UNIT};
use executor::ZoneNotes;
use ledger::death_constraint::DeathProof; use ledger::death_constraint::DeathProof;
use rand_core::CryptoRngCore;
fn zone_fund_note(value: u64, zone_meta: ZoneMetadata) -> cl::NoteWitness {
cl::NoteWitness {
value,
unit: *common::ZONE_CL_FUNDS_UNIT,
death_constraint: zone_meta.funds_vk,
state: zone_meta.id(),
}
}
fn zone_state_utxo(zone: &StateWitness, mut rng: impl CryptoRngCore) -> cl::OutputWitness {
cl::OutputWitness::public(
cl::NoteWitness {
value: 1,
unit: zone.zone_metadata.unit,
death_constraint: zone.zone_metadata.zone_vk,
state: zone.commit().0,
},
NullifierNonce::random(&mut rng),
)
}
#[test] #[test]
fn test_deposit() { fn test_deposit() {
@ -33,36 +12,15 @@ fn test_deposit() {
let alice = 42; let alice = 42;
let alice_sk = NullifierSecret::random(&mut rng); let alice_sk = NullifierSecret::random(&mut rng);
let init_state = StateWitness { let zone_start = ZoneNotes::new_with_balances("ZONE", BTreeMap::new(), &mut rng);
balances: BTreeMap::new(),
included_txs: vec![],
zone_metadata: executor::zone_metadata("ZONE"),
nonce: [0; 32],
};
let zone_state_in = cl::InputWitness::public(zone_state_utxo(&init_state, &mut rng));
let deposit = common::Deposit { let deposit = common::Deposit {
to: alice, to: alice,
amount: 78, amount: 78,
}; };
let end_state = init_state let zone_end = zone_start.clone().run([Tx::Deposit(deposit)]);
.clone()
.apply(Tx::Deposit(deposit))
.evolve_nonce();
let zone_state_out = cl::OutputWitness::public(
cl::NoteWitness {
state: end_state.commit().0,
..zone_state_in.note
},
zone_state_in.evolved_nonce(),
);
let zone_fund_out = cl::OutputWitness::public(
zone_fund_note(78, init_state.zone_metadata),
NullifierNonce::from_bytes(end_state.nonce),
);
let alice_deposit = cl::InputWitness::random( let alice_deposit = cl::InputWitness::random(
cl::OutputWitness::random( cl::OutputWitness::random(
NoteWitness::stateless( NoteWitness::stateless(
@ -78,15 +36,15 @@ fn test_deposit() {
); );
let deposit_ptx = cl::PartialTxWitness { let deposit_ptx = cl::PartialTxWitness {
inputs: vec![zone_state_in, alice_deposit], inputs: vec![zone_start.state_input_witness(), alice_deposit],
outputs: vec![zone_state_out, zone_fund_out], outputs: vec![zone_end.state_note, zone_end.fund_note],
}; };
let death_proofs = BTreeMap::from_iter([ let death_proofs = BTreeMap::from_iter([
( (
zone_state_in.nullifier(), zone_start.state_input_witness().nullifier(),
executor::prove_zone_stf( executor::prove_zone_stf(
init_state.clone(), zone_start.state.clone(),
vec![BoundTx { vec![BoundTx {
tx: Tx::Deposit(deposit), tx: Tx::Deposit(deposit),
bind: deposit_ptx.input_witness(1), // bind it to the deposit note bind: deposit_ptx.input_witness(1), // bind it to the deposit note
@ -103,7 +61,7 @@ fn test_deposit() {
]); ]);
let note_commitments = vec![ let note_commitments = vec![
zone_state_in.note_commitment(), zone_start.state_note.commit_note(),
alice_deposit.note_commitment(), alice_deposit.note_commitment(),
]; ];
@ -113,14 +71,17 @@ fn test_deposit() {
assert!(deposit_proof.verify()); assert!(deposit_proof.verify());
assert_eq!(deposit_proof.outputs[0].output, zone_state_out.commit());
assert_eq!( assert_eq!(
zone_state_out.note.state, deposit_proof.outputs[0].output,
zone_end.state_note.commit()
);
assert_eq!(
zone_end.state_note.note.state,
StateWitness { StateWitness {
balances: BTreeMap::from_iter([(alice, 78)]), balances: BTreeMap::from_iter([(alice, 78)]),
included_txs: vec![Tx::Deposit(deposit)], included_txs: vec![Tx::Deposit(deposit)],
zone_metadata: init_state.zone_metadata, zone_metadata: zone_start.state.zone_metadata,
nonce: init_state.evolve_nonce().nonce, nonce: zone_start.state.evolve_nonce().nonce,
} }
.commit() .commit()
.0 .0

View File

@ -2,37 +2,10 @@ use std::collections::BTreeMap;
use cl::{NoteWitness, NullifierNonce, NullifierSecret}; use cl::{NoteWitness, NullifierNonce, NullifierSecret};
use common::{BoundTx, StateWitness, Tx, ZoneMetadata, ZONE_CL_FUNDS_UNIT}; use common::{BoundTx, StateWitness, Tx, ZoneMetadata, ZONE_CL_FUNDS_UNIT};
use executor::ZoneNotes;
use ledger::death_constraint::DeathProof; use ledger::death_constraint::DeathProof;
use rand_core::CryptoRngCore; use rand_core::CryptoRngCore;
fn zone_fund_utxo(
value: u64,
zone_meta: ZoneMetadata,
mut rng: impl CryptoRngCore,
) -> cl::OutputWitness {
cl::OutputWitness::public(
cl::NoteWitness {
value,
unit: *common::ZONE_CL_FUNDS_UNIT,
death_constraint: zone_meta.funds_vk,
state: zone_meta.id(),
},
NullifierNonce::random(&mut rng),
)
}
fn zone_state_utxo(zone: &StateWitness, mut rng: impl CryptoRngCore) -> cl::OutputWitness {
cl::OutputWitness::public(
cl::NoteWitness {
value: 1,
unit: zone.zone_metadata.unit,
death_constraint: zone.zone_metadata.zone_vk,
state: zone.commit().0,
},
NullifierNonce::random(&mut rng),
)
}
#[test] #[test]
fn test_withdrawal() { fn test_withdrawal() {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
@ -40,16 +13,8 @@ fn test_withdrawal() {
let alice = 42; let alice = 42;
let alice_sk = NullifierSecret::random(&mut rng); let alice_sk = NullifierSecret::random(&mut rng);
let init_state = StateWitness { let zone_start =
balances: BTreeMap::from_iter([(alice, 100)]), ZoneNotes::new_with_balances("ZONE", BTreeMap::from_iter([(alice, 100)]), &mut rng);
included_txs: vec![],
zone_metadata: executor::zone_metadata("ZONE"),
nonce: [0; 32],
};
let zone_fund_in =
cl::InputWitness::public(zone_fund_utxo(100, init_state.zone_metadata, &mut rng));
let zone_state_in = cl::InputWitness::public(zone_state_utxo(&init_state, &mut rng));
let alice_intent = cl::InputWitness::random( let alice_intent = cl::InputWitness::random(
cl::OutputWitness::random( cl::OutputWitness::random(
@ -66,25 +31,7 @@ fn test_withdrawal() {
amount: 78, amount: 78,
}; };
let end_state = init_state let zone_end = zone_start.clone().run([Tx::Withdraw(withdraw)]);
.clone()
.apply(Tx::Withdraw(withdraw.clone()))
.evolve_nonce();
let zone_state_out = cl::OutputWitness::public(
cl::NoteWitness {
state: end_state.commit().0,
..zone_state_in.note
},
zone_state_in.evolved_nonce(),
);
let zone_fund_out = cl::OutputWitness::public(
cl::NoteWitness {
value: zone_fund_in.note.value - withdraw.amount,
..zone_fund_in.note
},
NullifierNonce::from_bytes(end_state.nonce),
);
let alice_withdrawal = cl::OutputWitness::random( let alice_withdrawal = cl::OutputWitness::random(
NoteWitness::stateless( NoteWitness::stateless(
@ -97,15 +44,19 @@ fn test_withdrawal() {
); );
let withdraw_ptx = cl::PartialTxWitness { let withdraw_ptx = cl::PartialTxWitness {
inputs: vec![zone_state_in, zone_fund_in, alice_intent], inputs: vec![
outputs: vec![zone_state_out, zone_fund_out, alice_withdrawal], zone_start.state_input_witness(),
zone_start.fund_input_witness(),
alice_intent,
],
outputs: vec![zone_end.state_note, zone_end.fund_note, alice_withdrawal],
}; };
let death_proofs = BTreeMap::from_iter([ let death_proofs = BTreeMap::from_iter([
( (
zone_state_in.nullifier(), zone_start.state_input_witness().nullifier(),
executor::prove_zone_stf( executor::prove_zone_stf(
init_state.clone(), zone_start.state.clone(),
vec![BoundTx { vec![BoundTx {
tx: Tx::Withdraw(withdraw.clone()), tx: Tx::Withdraw(withdraw.clone()),
bind: withdraw_ptx.input_witness(2), bind: withdraw_ptx.input_witness(2),
@ -116,11 +67,11 @@ fn test_withdrawal() {
), ),
), ),
( (
zone_fund_in.nullifier(), zone_start.fund_input_witness().nullifier(),
executor::prove_zone_fund_withdraw( executor::prove_zone_fund_withdraw(
withdraw_ptx.input_witness(1), // input fund note (input #1) withdraw_ptx.input_witness(1), // input fund note (input #1)
withdraw_ptx.output_witness(0), // output state note (output #0) withdraw_ptx.output_witness(0), // output state note (output #0)
&end_state, &zone_end.state,
), ),
), ),
( (
@ -130,8 +81,8 @@ fn test_withdrawal() {
]); ]);
let note_commitments = vec![ let note_commitments = vec![
zone_state_in.note_commitment(), zone_start.state_note.commit_note(),
zone_fund_in.note_commitment(), zone_start.fund_note.commit_note(),
alice_intent.note_commitment(), alice_intent.note_commitment(),
]; ];
@ -141,14 +92,17 @@ fn test_withdrawal() {
assert!(withdraw_proof.verify()); assert!(withdraw_proof.verify());
assert_eq!(withdraw_proof.outputs[0].output, zone_state_out.commit());
assert_eq!( assert_eq!(
zone_state_out.note.state, withdraw_proof.outputs[0].output,
zone_end.state_note.commit()
);
assert_eq!(
zone_end.state_note.note.state,
StateWitness { StateWitness {
balances: BTreeMap::from_iter([(alice, 22)]), balances: BTreeMap::from_iter([(alice, 22)]),
included_txs: vec![Tx::Withdraw(withdraw)], included_txs: vec![Tx::Withdraw(withdraw)],
zone_metadata: init_state.zone_metadata, zone_metadata: zone_start.state.zone_metadata,
nonce: init_state.evolve_nonce().nonce, nonce: zone_start.state.evolve_nonce().nonce,
} }
.commit() .commit()
.0 .0