Integrate zone withdrawal with CL (#17)
* aat: integrate withdraw with CL * aat: withdrawal passes! * aat: cleanup withdrawals a bit * aat: move Ptx{Input|Output|Private to cl::partial_tx * aat: zone_state zone transition validation coded w.r.t. metadata * aat: rename meta to in_meta in zone transition validation
This commit is contained in:
parent
5547b739c5
commit
a320c20d25
|
@ -6,7 +6,6 @@ edition = "2021"
|
|||
[dependencies]
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
cl = { path = "../../cl/cl" }
|
||||
goas_proof_statements = { path = "../proof_statements" }
|
||||
proof_statements = { path = "../../cl/proof_statements" }
|
||||
ledger_proof_statements = { path = "../../cl/ledger_proof_statements" }
|
||||
once_cell = "1"
|
||||
sha2 = "0.10"
|
|
@ -0,0 +1,36 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Event {
|
||||
Spend(Spend),
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
// TODO: add variant tag to byte encoding
|
||||
match self {
|
||||
Event::Spend(spend) => spend.to_bytes().to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An event that authorizes spending zone funds
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Spend {
|
||||
pub amount: u64,
|
||||
/// The public key of the recipient
|
||||
pub to: cl::NullifierCommitment,
|
||||
/// The nullifier of note that is being spent, this is to avoid using the spend event to
|
||||
/// for multiple notes
|
||||
pub fund_nf: cl::Nullifier,
|
||||
}
|
||||
|
||||
impl Spend {
|
||||
pub fn to_bytes(&self) -> [u8; 72] {
|
||||
let mut bytes = [0; 72];
|
||||
bytes[0..8].copy_from_slice(&self.amount.to_le_bytes());
|
||||
bytes[8..40].copy_from_slice(self.to.as_bytes());
|
||||
bytes[40..72].copy_from_slice(self.fund_nf.as_bytes());
|
||||
bytes
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
pub mod events;
|
||||
|
||||
use cl::{
|
||||
balance::Unit,
|
||||
crypto,
|
||||
input::InputWitness,
|
||||
nullifier::{Nullifier, NullifierCommitment},
|
||||
output::OutputWitness,
|
||||
|
@ -17,14 +18,14 @@ pub const MAX_EVENTS: usize = 1 << 8;
|
|||
|
||||
// state of the zone
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct StateCommitment([u8; 32]);
|
||||
pub struct StateCommitment(pub [u8; 32]);
|
||||
|
||||
pub type AccountId = u32;
|
||||
|
||||
// PLACEHOLDER: this is probably going to be NMO?
|
||||
pub static ZONE_CL_FUNDS_UNIT: Lazy<Unit> = Lazy::new(|| crypto::hash_to_curve(b"NMO"));
|
||||
pub static ZONE_CL_FUNDS_UNIT: Lazy<Unit> = Lazy::new(|| cl::note::unit_point("NMO"));
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ZoneMetadata {
|
||||
pub zone_vk: [u8; 32],
|
||||
pub funds_vk: [u8; 32],
|
||||
|
@ -41,11 +42,11 @@ impl ZoneMetadata {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct StateWitness {
|
||||
pub balances: BTreeMap<u32, u32>,
|
||||
pub balances: BTreeMap<AccountId, u64>,
|
||||
pub included_txs: Vec<Input>,
|
||||
pub output_events: Vec<Event>,
|
||||
pub output_events: Vec<events::Event>,
|
||||
pub zone_metadata: ZoneMetadata,
|
||||
}
|
||||
|
||||
|
@ -67,34 +68,61 @@ impl StateWitness {
|
|||
StateCommitment(root)
|
||||
}
|
||||
|
||||
fn events_root(&self) -> [u8; 32] {
|
||||
let event_bytes = Vec::from_iter(
|
||||
self.output_events
|
||||
.iter()
|
||||
.map(Event::to_bytes)
|
||||
.map(Vec::from_iter),
|
||||
);
|
||||
pub fn withdraw(mut self, w: Withdraw) -> Self {
|
||||
self.included_txs.push(Input::Withdraw(w));
|
||||
|
||||
let Withdraw {
|
||||
from,
|
||||
amount,
|
||||
to,
|
||||
fund_nf,
|
||||
} = w;
|
||||
|
||||
let from_balance = self.balances.entry(from).or_insert(0);
|
||||
*from_balance = from_balance
|
||||
.checked_sub(amount)
|
||||
.expect("insufficient funds in account");
|
||||
|
||||
let spend_auth = events::Spend {
|
||||
amount,
|
||||
to,
|
||||
fund_nf,
|
||||
};
|
||||
|
||||
self.output_events.push(events::Event::Spend(spend_auth));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn events_root(&self) -> [u8; 32] {
|
||||
let event_bytes = Vec::from_iter(self.output_events.iter().map(events::Event::to_bytes));
|
||||
let event_merkle_leaves = cl::merkle::padded_leaves(&event_bytes);
|
||||
cl::merkle::root::<MAX_EVENTS>(event_merkle_leaves)
|
||||
}
|
||||
|
||||
fn included_txs_root(&self) -> [u8; 32] {
|
||||
pub fn included_txs_root(&self) -> [u8; 32] {
|
||||
// this is a placeholder
|
||||
let tx_bytes = [vec![0u8; 32]];
|
||||
let tx_merkle_leaves = cl::merkle::padded_leaves(&tx_bytes);
|
||||
cl::merkle::root::<MAX_TXS>(tx_merkle_leaves)
|
||||
}
|
||||
|
||||
fn balances_root(&self) -> [u8; 32] {
|
||||
let balance_bytes = Vec::from_iter(self.balances.iter().map(|(k, v)| {
|
||||
let mut bytes = [0; 8];
|
||||
bytes.copy_from_slice(&k.to_le_bytes());
|
||||
bytes[8..].copy_from_slice(&v.to_le_bytes());
|
||||
bytes.to_vec()
|
||||
pub fn balances_root(&self) -> [u8; 32] {
|
||||
let balance_bytes = Vec::from_iter(self.balances.iter().map(|(owner, balance)| {
|
||||
let mut bytes: Vec<u8> = vec![];
|
||||
bytes.extend(owner.to_le_bytes());
|
||||
bytes.extend(balance.to_le_bytes());
|
||||
bytes
|
||||
}));
|
||||
let balance_merkle_leaves = cl::merkle::padded_leaves(&balance_bytes);
|
||||
cl::merkle::root::<MAX_BALANCES>(balance_merkle_leaves)
|
||||
}
|
||||
|
||||
pub fn event_merkle_path(&self, event: events::Event) -> Vec<cl::merkle::PathNode> {
|
||||
let idx = self.output_events.iter().position(|e| e == &event).unwrap();
|
||||
let event_bytes = Vec::from_iter(self.output_events.iter().map(events::Event::to_bytes));
|
||||
let event_merkle_leaves = cl::merkle::padded_leaves(&event_bytes);
|
||||
cl::merkle::path::<MAX_EVENTS>(event_merkle_leaves, idx)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StateCommitment> for [u8; 32] {
|
||||
|
@ -103,27 +131,35 @@ impl From<StateCommitment> for [u8; 32] {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Withdraw {
|
||||
pub from: AccountId,
|
||||
pub amount: AccountId,
|
||||
pub amount: u64,
|
||||
pub to: NullifierCommitment,
|
||||
pub nf: Nullifier,
|
||||
pub fund_nf: Nullifier,
|
||||
}
|
||||
|
||||
impl Withdraw {
|
||||
pub fn to_event(&self) -> events::Spend {
|
||||
events::Spend {
|
||||
amount: self.amount,
|
||||
to: self.to,
|
||||
fund_nf: self.fund_nf,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> [u8; 72] {
|
||||
let mut bytes = [0; 72];
|
||||
bytes[0..4].copy_from_slice(&self.from.to_le_bytes());
|
||||
bytes[4..8].copy_from_slice(&self.amount.to_le_bytes());
|
||||
bytes[8..40].copy_from_slice(self.to.as_bytes());
|
||||
bytes[40..72].copy_from_slice(self.nf.as_bytes());
|
||||
bytes[40..72].copy_from_slice(self.fund_nf.as_bytes());
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
/// A deposit of funds into the zone
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Deposit {
|
||||
/// The note that is used to deposit funds into the zone
|
||||
pub deposit: InputWitness,
|
||||
|
@ -137,21 +173,8 @@ pub struct Deposit {
|
|||
pub zone_funds_out: OutputWitness,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Input {
|
||||
Withdraw(Withdraw),
|
||||
Deposit(Deposit),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub enum Event {
|
||||
Spend(goas_proof_statements::zone_funds::Spend),
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
match self {
|
||||
Event::Spend(spend) => spend.to_bytes().to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,4 +16,8 @@ common = { path = "../common" }
|
|||
tempfile = "3"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
rand = "0.8.5"
|
||||
rand_core = "0.6.0"
|
||||
cl = { path = "../../cl/cl" }
|
||||
ledger = { path = "../../cl/ledger" }
|
||||
ledger_proof_statements = { path = "../../cl/ledger_proof_statements" }
|
||||
goas_proof_statements = { path = "../proof_statements" }
|
|
@ -4,10 +4,9 @@
|
|||
/// This workaround manually calls into docker after creating a directory with the required permissions.
|
||||
/// In addition, splitting the process in different stages highlights better the different work that
|
||||
/// needs to be done which could be split across different actors.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use clap::Parser;
|
||||
use risc0_zkvm::{get_prover_server, ProverOpts, Receipt};
|
||||
use std::path::PathBuf;
|
||||
|
||||
const WORK_DIR_ENV: &str = "RISC0_WORK_DIR";
|
||||
|
||||
|
@ -47,8 +46,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
risc0_groth16::docker::stark_to_snark(&converted.get_seal_bytes())?;
|
||||
|
||||
std::fs::create_dir_all(&args.output_dir)?;
|
||||
std::fs::copy(work_dir_path.join("proof.json"), args.output_dir.join("proof.json"))?;
|
||||
std::fs::copy(work_dir_path.join("public.json"), args.output_dir.join("public.json"))?;
|
||||
std::fs::copy(
|
||||
work_dir_path.join("proof.json"),
|
||||
args.output_dir.join("proof.json"),
|
||||
)?;
|
||||
std::fs::copy(
|
||||
work_dir_path.join("public.json"),
|
||||
args.output_dir.join("public.json"),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
use common::{events::Event, Input, StateWitness};
|
||||
use goas_proof_statements::{zone_funds::SpendFundsPrivate, zone_state::ZoneStatePrivate};
|
||||
|
||||
pub fn prove_zone_stf(
|
||||
state: StateWitness,
|
||||
inputs: Vec<Input>,
|
||||
zone_in: cl::PartialTxInputWitness,
|
||||
zone_out: cl::PartialTxOutputWitness,
|
||||
) -> ledger::DeathProof {
|
||||
let private_inputs = ZoneStatePrivate {
|
||||
state,
|
||||
inputs,
|
||||
zone_in,
|
||||
zone_out,
|
||||
};
|
||||
|
||||
let env = risc0_zkvm::ExecutorEnv::builder()
|
||||
.write(&private_inputs)
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let prover = risc0_zkvm::default_prover();
|
||||
|
||||
use std::time::Instant;
|
||||
let start_t = Instant::now();
|
||||
let opts = risc0_zkvm::ProverOpts::succinct();
|
||||
let prove_info = prover
|
||||
.prove_with_opts(env, goas_risc0_proofs::ZONE_STATE_ELF, &opts)
|
||||
.unwrap();
|
||||
println!("STARK 'zone_stf' prover time: {:.2?}", start_t.elapsed());
|
||||
let receipt = prove_info.receipt;
|
||||
ledger::DeathProof::from_risc0(goas_risc0_proofs::ZONE_STATE_ID, receipt)
|
||||
}
|
||||
|
||||
pub fn prove_zone_fund_withdraw(
|
||||
in_zone_funds: cl::PartialTxInputWitness,
|
||||
zone_note: cl::PartialTxOutputWitness,
|
||||
out_zone_funds: cl::PartialTxOutputWitness,
|
||||
spent_note: cl::PartialTxOutputWitness,
|
||||
out_zone_state: &StateWitness,
|
||||
withdraw: common::Withdraw,
|
||||
) -> ledger::DeathProof {
|
||||
let spend_event = withdraw.to_event();
|
||||
let private_inputs = SpendFundsPrivate {
|
||||
in_zone_funds,
|
||||
zone_note,
|
||||
out_zone_funds,
|
||||
spent_note,
|
||||
spend_event,
|
||||
spend_event_state_path: out_zone_state.event_merkle_path(Event::Spend(spend_event)),
|
||||
balances_root: out_zone_state.balances_root(),
|
||||
txs_root: out_zone_state.included_txs_root(),
|
||||
};
|
||||
|
||||
let env = risc0_zkvm::ExecutorEnv::builder()
|
||||
.write(&private_inputs)
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let prover = risc0_zkvm::default_prover();
|
||||
|
||||
use std::time::Instant;
|
||||
let start_t = Instant::now();
|
||||
let opts = risc0_zkvm::ProverOpts::succinct();
|
||||
let prove_info = prover
|
||||
.prove_with_opts(env, goas_risc0_proofs::SPEND_ZONE_FUNDS_ELF, &opts)
|
||||
.unwrap();
|
||||
println!("STARK 'zone_fund' prover time: {:.2?}", start_t.elapsed());
|
||||
let receipt = prove_info.receipt;
|
||||
ledger::DeathProof::from_risc0(goas_risc0_proofs::SPEND_ZONE_FUNDS_ID, receipt)
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use cl::{NoteWitness, NullifierNonce, NullifierSecret};
|
||||
use common::{events::Event, Input, StateWitness, ZoneMetadata, ZONE_CL_FUNDS_UNIT};
|
||||
use ledger::death_constraint::DeathProof;
|
||||
use rand_core::CryptoRngCore;
|
||||
|
||||
fn zone_state_death_constraint() -> [u8; 32] {
|
||||
ledger::death_constraint::risc0_id_to_cl_death_constraint(goas_risc0_proofs::ZONE_STATE_ID)
|
||||
}
|
||||
|
||||
fn zone_fund_death_constraint() -> [u8; 32] {
|
||||
ledger::death_constraint::risc0_id_to_cl_death_constraint(
|
||||
goas_risc0_proofs::SPEND_ZONE_FUNDS_ID,
|
||||
)
|
||||
}
|
||||
|
||||
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]
|
||||
fn test_withdrawal() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let alice = 42;
|
||||
let alice_sk = NullifierSecret::random(&mut rng);
|
||||
|
||||
let init_state = StateWitness {
|
||||
balances: BTreeMap::from_iter([(alice, 100)]),
|
||||
included_txs: vec![],
|
||||
output_events: vec![],
|
||||
zone_metadata: ZoneMetadata {
|
||||
zone_vk: zone_state_death_constraint(),
|
||||
funds_vk: zone_fund_death_constraint(),
|
||||
unit: cl::note::unit_point("ZONE_STATE"),
|
||||
},
|
||||
};
|
||||
|
||||
let zone_fund_in =
|
||||
cl::InputWitness::public(zone_fund_utxo(35240, init_state.zone_metadata, &mut rng));
|
||||
let zone_state_in = cl::InputWitness::public(zone_state_utxo(&init_state, &mut rng));
|
||||
|
||||
let withdraw = common::Withdraw {
|
||||
from: alice,
|
||||
amount: 78,
|
||||
to: alice_sk.commit(),
|
||||
fund_nf: zone_fund_in.nullifier(),
|
||||
};
|
||||
|
||||
let end_state = init_state.clone().withdraw(withdraw);
|
||||
|
||||
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
|
||||
},
|
||||
zone_fund_in.evolved_nonce(),
|
||||
);
|
||||
|
||||
let alice_withdrawal = cl::OutputWitness::random(
|
||||
NoteWitness::stateless(
|
||||
withdraw.amount,
|
||||
*ZONE_CL_FUNDS_UNIT,
|
||||
DeathProof::nop_constraint(),
|
||||
),
|
||||
alice_sk.commit(),
|
||||
&mut rng,
|
||||
);
|
||||
|
||||
let withdraw_ptx = cl::PartialTxWitness {
|
||||
inputs: vec![zone_state_in, zone_fund_in],
|
||||
outputs: vec![zone_state_out, zone_fund_out, alice_withdrawal],
|
||||
};
|
||||
|
||||
let death_proofs = BTreeMap::from_iter([
|
||||
(
|
||||
zone_state_in.nullifier(),
|
||||
executor::prove_zone_stf(
|
||||
init_state.clone(),
|
||||
vec![Input::Withdraw(withdraw)],
|
||||
withdraw_ptx.input_witness(0), // input state note (input #0)
|
||||
withdraw_ptx.output_witness(0), // output state note (output #0)
|
||||
),
|
||||
),
|
||||
(
|
||||
zone_fund_in.nullifier(),
|
||||
executor::prove_zone_fund_withdraw(
|
||||
withdraw_ptx.input_witness(1), // input fund note (input #1)
|
||||
withdraw_ptx.output_witness(0), // output state note (output #0)
|
||||
withdraw_ptx.output_witness(1), // output state note (output #0)
|
||||
withdraw_ptx.output_witness(2), // output state note (output #0)
|
||||
&end_state,
|
||||
withdraw,
|
||||
),
|
||||
),
|
||||
]);
|
||||
|
||||
let note_commitments = vec![
|
||||
zone_state_in.note_commitment(),
|
||||
zone_fund_in.note_commitment(),
|
||||
];
|
||||
|
||||
let withdraw_proof =
|
||||
ledger::partial_tx::ProvedPartialTx::prove(&withdraw_ptx, death_proofs, ¬e_commitments)
|
||||
.expect("withdraw proof failed");
|
||||
|
||||
assert!(withdraw_proof.verify());
|
||||
|
||||
assert_eq!(withdraw_proof.outputs[0].output, zone_state_out.commit());
|
||||
assert_eq!(
|
||||
zone_state_out.note.state,
|
||||
StateWitness {
|
||||
balances: BTreeMap::from_iter([(alice, 22)]),
|
||||
included_txs: vec![Input::Withdraw(withdraw)],
|
||||
output_events: vec![Event::Spend(common::events::Spend {
|
||||
amount: 78,
|
||||
to: alice_sk.commit(),
|
||||
fund_nf: zone_fund_in.nullifier()
|
||||
})],
|
||||
zone_metadata: init_state.zone_metadata
|
||||
}
|
||||
.commit()
|
||||
.0
|
||||
)
|
||||
}
|
|
@ -4,6 +4,6 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
common = { path = "../common" }
|
||||
cl = { path = "../../cl/cl" }
|
||||
proof_statements = { path = "../../cl/proof_statements" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
pub mod zone_funds;
|
||||
pub mod zone_state;
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/// The User Note encodes the logic of the atomic asset transfer
|
||||
///
|
||||
/// The scenario is as follows:
|
||||
/// The user, let's call her Alice has 100 NMO in Zone A and she wants to move it to
|
||||
/// Zone B. She wants to arrange this transfer so that both the withdrawal from Zone
|
||||
/// A and the deposit to Zone B occur atomically.
|
||||
///
|
||||
/// The Alice will create a partial tx that looks like this:
|
||||
///
|
||||
/// [fee note] -> [user note]
|
||||
///
|
||||
/// The User Note will encode the logic that orchestrates the withdrawal from zone A
|
||||
/// and deposit to zone B.
|
||||
///
|
||||
/// The User Notes death constraint requires the following statements to be satisfied
|
||||
/// in order for the fee to be captured.
|
||||
///
|
||||
/// 1. w_tx = withdraw(amt=100 NMO, from=Alice) tx was included in Zone A.
|
||||
/// 2. d_tx = deposit(amt=100 NMO, to=Alice) tx was included in Zone B.
|
||||
/// 3. w_tx is included in Zone A iff d_tx is included in Zone B
|
||||
///
|
||||
/// Details:
|
||||
/// - 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.
|
|
@ -1,39 +1,17 @@
|
|||
use proof_statements::{ptx::PartialTxInputPrivate, ptx::PartialTxOutputPrivate};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// An event that authorizes spending zone funds
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Spend {
|
||||
pub amount: u64,
|
||||
/// The public key of the recipient
|
||||
pub to: cl::NullifierCommitment,
|
||||
/// The nullifier of note that is being spent, this is to avoid using the spend event to
|
||||
/// for multiple notes
|
||||
pub nf: cl::Nullifier,
|
||||
}
|
||||
|
||||
impl Spend {
|
||||
pub fn to_bytes(&self) -> [u8; 72] {
|
||||
let mut bytes = [0; 72];
|
||||
bytes[0..8].copy_from_slice(&self.amount.to_le_bytes());
|
||||
bytes[8..40].copy_from_slice(self.to.as_bytes());
|
||||
bytes[40..72].copy_from_slice(self.nf.as_bytes());
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct SpendFundsPrivate {
|
||||
/// The note we're spending
|
||||
pub in_zone_funds: PartialTxInputPrivate,
|
||||
pub in_zone_funds: cl::PartialTxInputWitness,
|
||||
/// The zone note that is authorizing the spend
|
||||
pub zone_note: PartialTxOutputPrivate,
|
||||
pub zone_note: cl::PartialTxOutputWitness,
|
||||
/// The note that is being created to send the change back to the zone
|
||||
pub out_zone_funds: PartialTxOutputPrivate,
|
||||
pub out_zone_funds: cl::PartialTxOutputWitness,
|
||||
/// The spent funds note
|
||||
pub spent_note: PartialTxOutputPrivate,
|
||||
pub spent_note: cl::PartialTxOutputWitness,
|
||||
/// The event emitted by the zone that authorizes the spend
|
||||
pub spend_event: Spend,
|
||||
pub spend_event: common::events::Spend,
|
||||
/// Path to the zone output events root
|
||||
pub spend_event_state_path: Vec<cl::merkle::PathNode>,
|
||||
/// Merkle root of txs included in the zone
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
use common::{Input, StateWitness};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ZoneStatePrivate {
|
||||
pub state: StateWitness,
|
||||
pub inputs: Vec<Input>,
|
||||
pub zone_in: cl::PartialTxInputWitness,
|
||||
pub zone_out: cl::PartialTxOutputWitness,
|
||||
}
|
|
@ -10,7 +10,7 @@ risc0-zkvm = { version = "1.0", default-features = false, features = ['std'] }
|
|||
serde = { version = "1.0", features = ["derive"] }
|
||||
cl = { path = "../../../cl/cl" }
|
||||
goas_proof_statements = { path = "../../proof_statements" }
|
||||
proof_statements = { path = "../../../cl/proof_statements" }
|
||||
ledger_proof_statements = { path = "../../../cl/ledger_proof_statements" }
|
||||
sha2 = "0.10"
|
||||
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
///
|
||||
/// Our goal: prove the zone authorized spending of funds
|
||||
use cl::merkle;
|
||||
use cl::nullifier::{Nullifier, NullifierSecret};
|
||||
use cl::partial_tx::PtxRoot;
|
||||
use goas_proof_statements::zone_funds::SpendFundsPrivate;
|
||||
use proof_statements::death_constraint::DeathConstraintPublic;
|
||||
use ledger_proof_statements::death_constraint::DeathConstraintPublic;
|
||||
use risc0_zkvm::guest::env;
|
||||
|
||||
fn main() {
|
||||
|
@ -19,11 +19,12 @@ fn main() {
|
|||
balances_root,
|
||||
} = env::read();
|
||||
|
||||
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!(nf, spend_event.nf);
|
||||
assert_eq!(ptx_root, zone_note.ptx_root());
|
||||
let input_root = in_zone_funds.input_root();
|
||||
let output_root = out_zone_funds.output_root();
|
||||
|
||||
assert_eq!(output_root, zone_note.output_root());
|
||||
assert_eq!(output_root, spent_note.output_root());
|
||||
assert_eq!(output_root, out_zone_funds.output_root());
|
||||
|
||||
// ** Assert the spent event was an output of the correct zone stf **
|
||||
// The zone state field is a merkle tree over:
|
||||
|
@ -45,8 +46,6 @@ fn main() {
|
|||
zone_note.output.note.state
|
||||
);
|
||||
|
||||
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
|
||||
|
@ -57,39 +56,41 @@ fn main() {
|
|||
assert_eq!(out_zone_funds.output.note.value, change);
|
||||
// zone funds output should have the same death constraints as the zone funds input
|
||||
assert_eq!(
|
||||
out_zone_funds.output.note.death_constraint,
|
||||
in_zone_funds.input.note.death_constraint
|
||||
in_zone_funds.input.note.death_constraint,
|
||||
out_zone_funds.output.note.death_constraint
|
||||
);
|
||||
assert_eq!(
|
||||
out_zone_funds.output.note.unit,
|
||||
in_zone_funds.input.note.unit
|
||||
in_zone_funds.input.note.unit,
|
||||
out_zone_funds.output.note.unit
|
||||
);
|
||||
// zone funds nullifier, nonce and value blinding should be public so that everybody can spend it
|
||||
// ensure zone fund sk's, blindings and nonces are propagated correctly.
|
||||
assert_eq!(
|
||||
out_zone_funds.output.nf_pk,
|
||||
NullifierSecret::from_bytes([0; 16]).commit()
|
||||
in_zone_funds.input.nf_sk.commit(),
|
||||
out_zone_funds.output.nf_pk
|
||||
);
|
||||
assert_eq!(
|
||||
out_zone_funds.output.balance_blinding,
|
||||
in_zone_funds.input.balance_blinding
|
||||
in_zone_funds.input.balance_blinding,
|
||||
out_zone_funds.output.balance_blinding
|
||||
);
|
||||
assert_eq!(
|
||||
in_zone_funds.input.evolved_nonce(),
|
||||
out_zone_funds.output.nonce,
|
||||
in_zone_funds.input.evolved_nonce()
|
||||
);
|
||||
// the state is propagated
|
||||
assert_eq!(
|
||||
in_zone_funds.input.note.state,
|
||||
out_zone_funds.output.note.state,
|
||||
in_zone_funds.input.note.state
|
||||
);
|
||||
|
||||
assert_eq!(ptx_root, spent_note.ptx_root());
|
||||
|
||||
// check the correct amount of funds is being spent
|
||||
assert_eq!(spent_note.output.note.value, spend_event.amount);
|
||||
assert_eq!(spent_note.output.note.unit, in_zone_funds.input.note.unit);
|
||||
// check the correct recipient is being paid
|
||||
assert_eq!(spent_note.output.nf_pk, spend_event.to);
|
||||
|
||||
let nf = in_zone_funds.input.nullifier();
|
||||
assert_eq!(nf, spend_event.fund_nf); // ensure this event was meant for this note.
|
||||
|
||||
let ptx_root = PtxRoot(merkle::node(input_root, output_root));
|
||||
env::commit(&DeathConstraintPublic { ptx_root, nf });
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ bincode = "1"
|
|||
common = { path = "../../common" }
|
||||
cl = { path = "../../../cl/cl" }
|
||||
goas_proof_statements = { path = "../../proof_statements" }
|
||||
proof_statements = { path = "../../../cl/proof_statements" }
|
||||
ledger_proof_statements = { path = "../../../cl/ledger_proof_statements" }
|
||||
sha2 = "0.10"
|
||||
|
||||
[patch.crates-io]
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use cl::{
|
||||
input::InputWitness,
|
||||
merkle,
|
||||
nullifier::{Nullifier, NullifierSecret},
|
||||
partial_tx::{MAX_INPUTS, MAX_OUTPUTS},
|
||||
|
@ -7,37 +6,10 @@ use cl::{
|
|||
};
|
||||
|
||||
use common::*;
|
||||
use goas_proof_statements::zone_funds::Spend;
|
||||
use proof_statements::{
|
||||
death_constraint::DeathConstraintPublic,
|
||||
ptx::{PartialTxInputPrivate, PartialTxOutputPrivate},
|
||||
};
|
||||
use goas_proof_statements::zone_state::ZoneStatePrivate;
|
||||
use ledger_proof_statements::death_constraint::DeathConstraintPublic;
|
||||
use risc0_zkvm::guest::env;
|
||||
|
||||
fn withdraw(mut state: StateWitness, withdraw: Withdraw) -> StateWitness {
|
||||
state.included_txs.push(Input::Withdraw(withdraw));
|
||||
|
||||
let Withdraw {
|
||||
from,
|
||||
amount,
|
||||
to,
|
||||
nf,
|
||||
} = withdraw;
|
||||
|
||||
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,
|
||||
nf,
|
||||
};
|
||||
|
||||
state.output_events.push(Event::Spend(spend_auth));
|
||||
state
|
||||
}
|
||||
|
||||
fn deposit(
|
||||
mut state: StateWitness,
|
||||
deposit: Deposit,
|
||||
|
@ -114,7 +86,7 @@ fn deposit(
|
|||
assert_eq!(nullifier, pub_inputs.nf);
|
||||
|
||||
// 6) We're now ready to do the deposit!
|
||||
let amount = deposit.note.value as u32;
|
||||
let amount = deposit.note.value;
|
||||
let to = AccountId::from_be_bytes(<[u8; 4]>::try_from(&deposit.note.state[0..4]).unwrap());
|
||||
|
||||
let to_balance = state.balances.entry(to).or_insert(0);
|
||||
|
@ -125,59 +97,68 @@ fn deposit(
|
|||
state
|
||||
}
|
||||
|
||||
fn validate_zone_input(
|
||||
input: &PartialTxInputPrivate,
|
||||
state: &StateWitness,
|
||||
) -> (PtxRoot, Nullifier) {
|
||||
let ptx_root = input.ptx_root();
|
||||
let nf = Nullifier::new(input.input.nf_sk, input.input.nonce);
|
||||
fn validate_zone_transition(
|
||||
in_note: cl::PartialTxInputWitness,
|
||||
out_note: cl::PartialTxOutputWitness,
|
||||
in_meta: ZoneMetadata,
|
||||
in_state_cm: StateCommitment,
|
||||
out_state: StateWitness,
|
||||
) {
|
||||
// Ensure input/output notes are committing to the expected states.
|
||||
assert_eq!(in_note.input.note.state, in_state_cm.0);
|
||||
assert_eq!(out_note.output.note.state, out_state.commit().0);
|
||||
|
||||
assert_eq!(input.input.note.state, <[u8; 32]>::from(state.commit()));
|
||||
// should not be possible to create one but let's put this check here just in case
|
||||
debug_assert_eq!(
|
||||
input.input.note.death_constraint,
|
||||
state.zone_metadata.zone_vk
|
||||
// zone metadata is propagated
|
||||
assert_eq!(out_state.zone_metadata.id(), in_meta.id());
|
||||
|
||||
// ensure units match metadata
|
||||
assert_eq!(in_note.input.note.unit, in_meta.unit);
|
||||
assert_eq!(out_note.output.note.unit, in_meta.unit);
|
||||
|
||||
// ensure constraints match metadata
|
||||
assert_eq!(in_note.input.note.death_constraint, in_meta.zone_vk);
|
||||
assert_eq!(out_note.output.note.death_constraint, in_meta.zone_vk);
|
||||
|
||||
// nullifier secret is propagated
|
||||
assert_eq!(in_note.input.nf_sk.commit(), out_note.output.nf_pk);
|
||||
|
||||
// balance blinding is propagated
|
||||
assert_eq!(
|
||||
in_note.input.balance_blinding,
|
||||
out_note.output.balance_blinding
|
||||
);
|
||||
|
||||
(ptx_root, nf)
|
||||
}
|
||||
|
||||
fn validate_zone_output(
|
||||
ptx: PtxRoot,
|
||||
input: InputWitness,
|
||||
output: PartialTxOutputPrivate,
|
||||
state: &StateWitness,
|
||||
) {
|
||||
assert_eq!(ptx, output.ptx_root()); // the ptx root is the same as in the input
|
||||
let output = output.output;
|
||||
assert_eq!(output.note.state, <[u8; 32]>::from(state.commit())); // the state in the output is as calculated by this function
|
||||
assert_eq!(output.note.death_constraint, state.zone_metadata.zone_vk); // the death constraint is the correct one
|
||||
assert_eq!(output.nf_pk, NullifierSecret::from_bytes([0; 16]).commit()); // the nullifier secret is public
|
||||
assert_eq!(output.balance_blinding, input.balance_blinding); // the balance blinding is the same as in the input
|
||||
assert_eq!(output.note.unit, state.zone_metadata.unit); // the balance unit is the same as in the input
|
||||
|
||||
// the nonce is correctly evolved
|
||||
assert_eq!(output.nonce, input.evolved_nonce());
|
||||
assert_eq!(in_note.input.evolved_nonce(), out_note.output.nonce);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let zone_in: PartialTxInputPrivate = env::read();
|
||||
let mut state: StateWitness = env::read();
|
||||
let zone_out: PartialTxOutputPrivate = env::read();
|
||||
let ZoneStatePrivate {
|
||||
mut state,
|
||||
inputs,
|
||||
zone_in,
|
||||
zone_out,
|
||||
} = env::read();
|
||||
|
||||
let (ptx_root, nf) = validate_zone_input(&zone_in, &state);
|
||||
let pub_inputs = DeathConstraintPublic {
|
||||
ptx_root: PtxRoot(cl::merkle::node(
|
||||
zone_in.input_root(),
|
||||
zone_out.output_root(),
|
||||
)),
|
||||
nf: zone_in.input.nullifier(),
|
||||
};
|
||||
|
||||
let pub_inputs = DeathConstraintPublic { ptx_root, nf };
|
||||
|
||||
let inputs: Vec<Input> = env::read();
|
||||
let in_meta = state.zone_metadata;
|
||||
let in_state_cm = state.commit();
|
||||
|
||||
for input in inputs {
|
||||
match input {
|
||||
Input::Withdraw(input) => state = withdraw(state, input),
|
||||
Input::Deposit(input) => state = deposit(state, input, pub_inputs),
|
||||
state = match input {
|
||||
Input::Withdraw(input) => state.withdraw(input),
|
||||
Input::Deposit(input) => deposit(state, input, pub_inputs),
|
||||
}
|
||||
}
|
||||
|
||||
validate_zone_output(ptx_root, zone_in.input, zone_out, &state);
|
||||
validate_zone_transition(zone_in, zone_out, in_meta, in_state_cm, state);
|
||||
|
||||
env::commit(&pub_inputs);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[workspace]
|
||||
resolver = "2"
|
||||
members = [ "cl", "ledger", "proof_statements", "risc0_proofs"]
|
||||
members = [ "cl", "ledger", "ledger_proof_statements", "risc0_proofs"]
|
||||
|
||||
# Always optimize; building and running the risc0_proofs takes much longer without optimization.
|
||||
[profile.dev]
|
||||
|
|
|
@ -40,6 +40,10 @@ impl BalanceWitness {
|
|||
Self(blinding)
|
||||
}
|
||||
|
||||
pub fn unblinded() -> Self {
|
||||
Self::new(Scalar::ZERO)
|
||||
}
|
||||
|
||||
pub fn random(mut rng: impl CryptoRngCore) -> Self {
|
||||
Self::new(Scalar::random(&mut rng))
|
||||
}
|
||||
|
@ -60,8 +64,8 @@ pub fn balance(value: u64, unit: Unit, blinding: Scalar) -> Unit {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::*;
|
||||
use crate::note::unit_point;
|
||||
|
||||
#[test]
|
||||
fn test_pederson_blinding_point_pre_compute() {
|
||||
|
@ -77,30 +81,34 @@ mod test {
|
|||
#[test]
|
||||
fn test_balance_zero_unitless() {
|
||||
// Zero is the same across all units
|
||||
let (nmo, eth) = (unit_point("NMO"), unit_point("ETH"));
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let b = BalanceWitness::random(&mut rng);
|
||||
assert_eq!(
|
||||
b.commit(&NoteWitness::basic(0, "NMO")),
|
||||
b.commit(&NoteWitness::basic(0, "ETH")),
|
||||
b.commit(&NoteWitness::basic(0, nmo)),
|
||||
b.commit(&NoteWitness::basic(0, eth)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_balance_blinding() {
|
||||
// balances are blinded
|
||||
let nmo = unit_point("NMO");
|
||||
|
||||
let r_a = Scalar::from(12u32);
|
||||
let r_b = Scalar::from(8u32);
|
||||
let bal_a = BalanceWitness::new(r_a);
|
||||
let bal_b = BalanceWitness::new(r_b);
|
||||
|
||||
let note = NoteWitness::basic(10, "NMO");
|
||||
let note = NoteWitness::basic(10, nmo);
|
||||
|
||||
let a = bal_a.commit(¬e);
|
||||
let b = bal_b.commit(¬e);
|
||||
|
||||
assert_ne!(a, b);
|
||||
|
||||
let diff_note = NoteWitness::basic(0, "NMO");
|
||||
let diff_note = NoteWitness::basic(0, nmo);
|
||||
assert_eq!(
|
||||
a.0 - b.0,
|
||||
BalanceWitness::new(r_a - r_b).commit(&diff_note).0
|
||||
|
@ -110,24 +118,28 @@ mod test {
|
|||
#[test]
|
||||
fn test_balance_units() {
|
||||
// Unit's differentiate between values.
|
||||
let (nmo, eth) = (unit_point("NMO"), unit_point("ETH"));
|
||||
|
||||
let b = BalanceWitness::new(Scalar::from(1337u32));
|
||||
|
||||
let nmo = NoteWitness::basic(10, "NMO");
|
||||
let eth = NoteWitness::basic(10, "ETH");
|
||||
let nmo = NoteWitness::basic(10, nmo);
|
||||
let eth = NoteWitness::basic(10, eth);
|
||||
assert_ne!(b.commit(&nmo), b.commit(ð));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_balance_homomorphism() {
|
||||
let nmo = unit_point("NMO");
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let b1 = BalanceWitness::random(&mut rng);
|
||||
let b2 = BalanceWitness::random(&mut rng);
|
||||
let b_zero = BalanceWitness::new(Scalar::ZERO);
|
||||
|
||||
let ten = NoteWitness::basic(10, "NMO");
|
||||
let eight = NoteWitness::basic(8, "NMO");
|
||||
let two = NoteWitness::basic(2, "NMO");
|
||||
let zero = NoteWitness::basic(0, "NMO");
|
||||
let ten = NoteWitness::basic(10, nmo);
|
||||
let eight = NoteWitness::basic(8, nmo);
|
||||
let two = NoteWitness::basic(2, nmo);
|
||||
let zero = NoteWitness::basic(0, nmo);
|
||||
|
||||
// Values of same unit are homomorphic
|
||||
assert_eq!(
|
||||
|
|
|
@ -29,7 +29,10 @@ impl Bundle {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{
|
||||
input::InputWitness, note::NoteWitness, nullifier::NullifierSecret, output::OutputWitness,
|
||||
input::InputWitness,
|
||||
note::{unit_point, NoteWitness},
|
||||
nullifier::NullifierSecret,
|
||||
output::OutputWitness,
|
||||
partial_tx::PartialTxWitness,
|
||||
};
|
||||
|
||||
|
@ -38,21 +41,22 @@ mod test {
|
|||
#[test]
|
||||
fn test_bundle_balance() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let (nmo, eth, crv) = (unit_point("NMO"), unit_point("ETH"), unit_point("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::random(NoteWitness::basic(10, "NMO"), nf_a.commit(), &mut rng);
|
||||
OutputWitness::random(NoteWitness::basic(10, nmo), nf_a.commit(), &mut rng);
|
||||
let nmo_10_in = InputWitness::random(nmo_10_utxo, nf_a, &mut rng);
|
||||
|
||||
let eth_23_utxo =
|
||||
OutputWitness::random(NoteWitness::basic(23, "ETH"), nf_b.commit(), &mut rng);
|
||||
OutputWitness::random(NoteWitness::basic(23, eth), nf_b.commit(), &mut rng);
|
||||
let eth_23_in = InputWitness::random(eth_23_utxo, nf_b, &mut rng);
|
||||
|
||||
let crv_4840_out =
|
||||
OutputWitness::random(NoteWitness::basic(4840, "CRV"), nf_c.commit(), &mut rng);
|
||||
OutputWitness::random(NoteWitness::basic(4840, crv), nf_c.commit(), &mut rng);
|
||||
|
||||
let ptx_unbalanced = PartialTxWitness {
|
||||
inputs: vec![nmo_10_in, eth_23_in],
|
||||
|
@ -80,12 +84,12 @@ mod test {
|
|||
|
||||
let crv_4840_in = InputWitness::random(crv_4840_out, nf_c, &mut rng);
|
||||
let nmo_10_out = OutputWitness::random(
|
||||
NoteWitness::basic(10, "NMO"),
|
||||
NoteWitness::basic(10, nmo),
|
||||
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
|
||||
&mut rng,
|
||||
);
|
||||
let eth_23_out = OutputWitness::random(
|
||||
NoteWitness::basic(23, "ETH"),
|
||||
NoteWitness::basic(23, eth),
|
||||
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
|
||||
&mut rng,
|
||||
);
|
||||
|
|
|
@ -41,6 +41,17 @@ impl InputWitness {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn public(output: crate::OutputWitness) -> Self {
|
||||
let nf_sk = NullifierSecret::zero();
|
||||
assert_eq!(nf_sk.commit(), output.nf_pk); // ensure the output was a public UTXO
|
||||
Self {
|
||||
note: output.note,
|
||||
balance_blinding: BalanceWitness::unblinded(),
|
||||
nf_sk,
|
||||
nonce: output.nonce,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn evolved_nonce(&self) -> NullifierNonce {
|
||||
self.nonce.evolve(&self.nf_sk)
|
||||
}
|
||||
|
|
|
@ -15,4 +15,6 @@ pub use input::{Input, InputWitness};
|
|||
pub use note::{DeathCommitment, NoteCommitment, NoteWitness};
|
||||
pub use nullifier::{Nullifier, NullifierCommitment, NullifierNonce, NullifierSecret};
|
||||
pub use output::{Output, OutputWitness};
|
||||
pub use partial_tx::{PartialTx, PartialTxWitness, PtxRoot};
|
||||
pub use partial_tx::{
|
||||
PartialTx, PartialTxInputWitness, PartialTxOutputWitness, PartialTxWitness, PtxRoot,
|
||||
};
|
||||
|
|
|
@ -19,7 +19,7 @@ pub fn death_commitment(death_constraint: &[u8]) -> DeathCommitment {
|
|||
}
|
||||
|
||||
pub fn unit_point(unit: &str) -> Unit {
|
||||
crate::crypto::hash_to_curve(unit.as_bytes())
|
||||
crate::crypto::hash_to_curve(format!("NOMOS_CL_UNIT{unit}").as_bytes())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
|
@ -42,25 +42,20 @@ pub struct NoteWitness {
|
|||
}
|
||||
|
||||
impl NoteWitness {
|
||||
pub fn new(
|
||||
value: u64,
|
||||
unit: impl Into<String>,
|
||||
death_constraint: [u8; 32],
|
||||
state: [u8; 32],
|
||||
) -> Self {
|
||||
pub fn new(value: u64, unit: Unit, death_constraint: [u8; 32], state: [u8; 32]) -> Self {
|
||||
Self {
|
||||
value,
|
||||
unit: unit_point(&unit.into()),
|
||||
unit,
|
||||
death_constraint,
|
||||
state,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn basic(value: u64, unit: impl Into<String>) -> Self {
|
||||
pub fn basic(value: u64, unit: Unit) -> Self {
|
||||
Self::new(value, unit, [0u8; 32], [0u8; 32])
|
||||
}
|
||||
|
||||
pub fn stateless(value: u64, unit: impl Into<String>, death_constraint: [u8; 32]) -> Self {
|
||||
pub fn stateless(value: u64, unit: Unit, death_constraint: [u8; 32]) -> Self {
|
||||
Self::new(value, unit, death_constraint, [0u8; 32])
|
||||
}
|
||||
|
||||
|
@ -94,18 +89,19 @@ impl NoteWitness {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::nullifier::NullifierSecret;
|
||||
|
||||
use super::*;
|
||||
use crate::nullifier::NullifierSecret;
|
||||
|
||||
#[test]
|
||||
fn test_note_commit_permutations() {
|
||||
let (nmo, eth) = (unit_point("NMO"), unit_point("ETH"));
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let nf_pk = NullifierSecret::random(&mut rng).commit();
|
||||
let nf_nonce = NullifierNonce::random(&mut rng);
|
||||
|
||||
let reference_note = NoteWitness::basic(32, "NMO");
|
||||
let reference_note = NoteWitness::basic(32, nmo);
|
||||
|
||||
// different notes under same nullifier produce different commitments
|
||||
let mutation_tests = [
|
||||
|
@ -114,7 +110,7 @@ mod test {
|
|||
..reference_note
|
||||
},
|
||||
NoteWitness {
|
||||
unit: unit_point("ETH"),
|
||||
unit: eth,
|
||||
..reference_note
|
||||
},
|
||||
NoteWitness {
|
||||
|
|
|
@ -43,6 +43,10 @@ impl NullifierSecret {
|
|||
Self(sk)
|
||||
}
|
||||
|
||||
pub const fn zero() -> Self {
|
||||
Self([0u8; 16])
|
||||
}
|
||||
|
||||
pub fn commit(&self) -> NullifierCommitment {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(b"NOMOS_CL_NULL_COMMIT");
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
error::Error,
|
||||
note::{NoteCommitment, NoteWitness},
|
||||
nullifier::{NullifierCommitment, NullifierNonce},
|
||||
BalanceWitness,
|
||||
BalanceWitness, NullifierSecret,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
@ -37,6 +37,15 @@ impl OutputWitness {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn public(note: NoteWitness, nonce: NullifierNonce) -> Self {
|
||||
Self {
|
||||
note,
|
||||
balance_blinding: BalanceWitness::unblinded(),
|
||||
nf_pk: NullifierSecret::zero().commit(),
|
||||
nonce,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn commit_note(&self) -> NoteCommitment {
|
||||
self.note.commit(self.nf_pk, self.nonce)
|
||||
}
|
||||
|
@ -86,14 +95,15 @@ impl Output {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::nullifier::NullifierSecret;
|
||||
use crate::{note::unit_point, nullifier::NullifierSecret};
|
||||
|
||||
#[test]
|
||||
fn test_output_proof() {
|
||||
let (nmo, eth) = (unit_point("NMO"), unit_point("ETH"));
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let witness = OutputWitness {
|
||||
note: NoteWitness::basic(10, "NMO"),
|
||||
note: NoteWitness::basic(10, nmo),
|
||||
balance_blinding: BalanceWitness::random(&mut rng),
|
||||
nf_pk: NullifierSecret::random(&mut rng).commit(),
|
||||
nonce: NullifierNonce::random(&mut rng),
|
||||
|
@ -106,11 +116,11 @@ mod test {
|
|||
|
||||
let wrong_witnesses = [
|
||||
OutputWitness {
|
||||
note: NoteWitness::basic(11, "NMO"),
|
||||
note: NoteWitness::basic(11, nmo),
|
||||
..witness
|
||||
},
|
||||
OutputWitness {
|
||||
note: NoteWitness::basic(10, "ETH"),
|
||||
note: NoteWitness::basic(10, eth),
|
||||
..witness
|
||||
},
|
||||
OutputWitness {
|
||||
|
|
|
@ -60,6 +60,26 @@ impl PartialTxWitness {
|
|||
|
||||
BalanceWitness(out_sum - in_sum)
|
||||
}
|
||||
|
||||
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);
|
||||
let input = self.inputs[idx];
|
||||
PartialTxInputWitness { input, path }
|
||||
}
|
||||
|
||||
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);
|
||||
let output = self.outputs[idx];
|
||||
PartialTxOutputWitness { output, path }
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialTx {
|
||||
|
@ -81,24 +101,6 @@ impl PartialTx {
|
|||
merkle::root::<MAX_OUTPUTS>(output_merkle_leaves)
|
||||
}
|
||||
|
||||
pub fn input_merkle_path(&self, idx: usize) -> Vec<merkle::PathNode> {
|
||||
let input_bytes =
|
||||
Vec::from_iter(self.inputs.iter().map(Input::to_bytes).map(Vec::from_iter));
|
||||
let input_merkle_leaves = merkle::padded_leaves::<MAX_INPUTS>(&input_bytes);
|
||||
merkle::path(input_merkle_leaves, idx)
|
||||
}
|
||||
|
||||
pub fn output_merkle_path(&self, idx: usize) -> Vec<merkle::PathNode> {
|
||||
let output_bytes = Vec::from_iter(
|
||||
self.outputs
|
||||
.iter()
|
||||
.map(Output::to_bytes)
|
||||
.map(Vec::from_iter),
|
||||
);
|
||||
let output_merkle_leaves = merkle::padded_leaves::<MAX_OUTPUTS>(&output_bytes);
|
||||
merkle::path(output_merkle_leaves, idx)
|
||||
}
|
||||
|
||||
pub fn root(&self) -> PtxRoot {
|
||||
let input_root = self.input_root();
|
||||
let output_root = self.output_root();
|
||||
|
@ -114,15 +116,47 @@ impl PartialTx {
|
|||
}
|
||||
}
|
||||
|
||||
/// An input to a partial transaction
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PartialTxInputWitness {
|
||||
pub input: InputWitness,
|
||||
pub path: Vec<merkle::PathNode>,
|
||||
}
|
||||
|
||||
impl PartialTxInputWitness {
|
||||
pub fn input_root(&self) -> [u8; 32] {
|
||||
let leaf = merkle::leaf(&self.input.commit().to_bytes());
|
||||
merkle::path_root(leaf, &self.path)
|
||||
}
|
||||
}
|
||||
|
||||
/// An output to a partial transaction
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PartialTxOutputWitness {
|
||||
pub output: OutputWitness,
|
||||
pub path: Vec<merkle::PathNode>,
|
||||
}
|
||||
|
||||
impl PartialTxOutputWitness {
|
||||
pub fn output_root(&self) -> [u8; 32] {
|
||||
let leaf = merkle::leaf(&self.output.commit().to_bytes());
|
||||
merkle::path_root(leaf, &self.path)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use crate::{note::NoteWitness, nullifier::NullifierSecret};
|
||||
use crate::{
|
||||
note::{unit_point, NoteWitness},
|
||||
nullifier::NullifierSecret,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_partial_tx_balance() {
|
||||
let (nmo, eth, crv) = (unit_point("NMO"), unit_point("ETH"), unit_point("CRV"));
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let nf_a = NullifierSecret::random(&mut rng);
|
||||
|
@ -130,15 +164,15 @@ mod test {
|
|||
let nf_c = NullifierSecret::random(&mut rng);
|
||||
|
||||
let nmo_10_utxo =
|
||||
OutputWitness::random(NoteWitness::basic(10, "NMO"), nf_a.commit(), &mut rng);
|
||||
OutputWitness::random(NoteWitness::basic(10, nmo), nf_a.commit(), &mut rng);
|
||||
let nmo_10 = InputWitness::random(nmo_10_utxo, nf_a, &mut rng);
|
||||
|
||||
let eth_23_utxo =
|
||||
OutputWitness::random(NoteWitness::basic(23, "ETH"), nf_b.commit(), &mut rng);
|
||||
OutputWitness::random(NoteWitness::basic(23, eth), nf_b.commit(), &mut rng);
|
||||
let eth_23 = InputWitness::random(eth_23_utxo, nf_b, &mut rng);
|
||||
|
||||
let crv_4840 =
|
||||
OutputWitness::random(NoteWitness::basic(4840, "CRV"), nf_c.commit(), &mut rng);
|
||||
OutputWitness::random(NoteWitness::basic(4840, crv), nf_c.commit(), &mut rng);
|
||||
|
||||
let ptx_witness = PartialTxWitness {
|
||||
inputs: vec![nmo_10, eth_23],
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use cl::note::unit_point;
|
||||
use rand_core::CryptoRngCore;
|
||||
|
||||
fn receive_utxo(
|
||||
|
@ -10,6 +11,7 @@ fn receive_utxo(
|
|||
|
||||
#[test]
|
||||
fn test_simple_transfer() {
|
||||
let nmo = unit_point("NMO");
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let sender_nf_sk = cl::NullifierSecret::random(&mut rng);
|
||||
|
@ -18,13 +20,13 @@ fn test_simple_transfer() {
|
|||
let recipient_nf_pk = cl::NullifierSecret::random(&mut rng).commit();
|
||||
|
||||
// Assume the sender has received an unspent output from somewhere
|
||||
let utxo = receive_utxo(cl::NoteWitness::basic(10, "NMO"), sender_nf_pk, &mut rng);
|
||||
let utxo = receive_utxo(cl::NoteWitness::basic(10, nmo), sender_nf_pk, &mut rng);
|
||||
|
||||
// and wants to send 8 NMO to some recipient and return 2 NMO to itself.
|
||||
let recipient_output =
|
||||
cl::OutputWitness::random(cl::NoteWitness::basic(8, "NMO"), recipient_nf_pk, &mut rng);
|
||||
cl::OutputWitness::random(cl::NoteWitness::basic(8, nmo), recipient_nf_pk, &mut rng);
|
||||
let change_output =
|
||||
cl::OutputWitness::random(cl::NoteWitness::basic(2, "NMO"), sender_nf_pk, &mut rng);
|
||||
cl::OutputWitness::random(cl::NoteWitness::basic(2, nmo), sender_nf_pk, &mut rng);
|
||||
|
||||
let ptx_witness = cl::PartialTxWitness {
|
||||
inputs: vec![cl::InputWitness::random(utxo, sender_nf_sk, &mut rng)],
|
||||
|
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
cl = { path = "../cl" }
|
||||
proof_statements = { path = "../proof_statements" }
|
||||
ledger_proof_statements = { path = "../ledger_proof_statements" }
|
||||
nomos_cl_risc0_proofs = { path = "../risc0_proofs" }
|
||||
risc0-zkvm = { version = "1.0", features = ["prove", "metal"] }
|
||||
risc0-groth16 = { version = "1.0" }
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use proof_statements::death_constraint::DeathConstraintPublic;
|
||||
use ledger_proof_statements::death_constraint::DeathConstraintPublic;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::error::Result;
|
||||
|
@ -7,11 +7,11 @@ pub type Risc0DeathConstraintId = [u32; 8];
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DeathProof {
|
||||
constraint: Risc0DeathConstraintId,
|
||||
risc0_receipt: risc0_zkvm::Receipt,
|
||||
pub constraint: Risc0DeathConstraintId,
|
||||
pub risc0_receipt: risc0_zkvm::Receipt,
|
||||
}
|
||||
|
||||
fn risc0_id_to_cl_death_constraint(risc0_id: Risc0DeathConstraintId) -> [u8; 32] {
|
||||
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.
|
||||
|
@ -26,6 +26,16 @@ fn risc0_id_to_cl_death_constraint(risc0_id: Risc0DeathConstraintId) -> [u8; 32]
|
|||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
@ -75,9 +85,6 @@ impl DeathProof {
|
|||
// extract the receipt.
|
||||
let receipt = prove_info.receipt;
|
||||
|
||||
Self {
|
||||
constraint: nomos_cl_risc0_proofs::DEATH_CONSTRAINT_NOP_ID,
|
||||
risc0_receipt: receipt,
|
||||
}
|
||||
Self::from_risc0(nomos_cl_risc0_proofs::DEATH_CONSTRAINT_NOP_ID, receipt)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use proof_statements::input::{InputPrivate, InputPublic};
|
||||
use ledger_proof_statements::input::{InputPrivate, InputPublic};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
|
@ -89,16 +89,18 @@ fn note_commitment_leaves(note_commitments: &[cl::NoteCommitment]) -> [[u8; 32];
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use rand::thread_rng;
|
||||
|
||||
use super::*;
|
||||
use cl::note::unit_point;
|
||||
use rand::thread_rng;
|
||||
|
||||
#[test]
|
||||
fn test_input_prover() {
|
||||
let nmo = unit_point("NMO");
|
||||
|
||||
let mut rng = thread_rng();
|
||||
|
||||
let input = cl::InputWitness {
|
||||
note: cl::NoteWitness::basic(32, "NMO"),
|
||||
note: cl::NoteWitness::basic(32, nmo),
|
||||
balance_blinding: cl::BalanceWitness::random(&mut rng),
|
||||
nf_sk: cl::NullifierSecret::random(&mut rng),
|
||||
nonce: cl::NullifierNonce::random(&mut rng),
|
||||
|
@ -141,7 +143,7 @@ mod test {
|
|||
InputPublic {
|
||||
input: cl::Input {
|
||||
balance: cl::BalanceWitness::random(&mut rng)
|
||||
.commit(&cl::NoteWitness::basic(32, "NMO")),
|
||||
.commit(&cl::NoteWitness::basic(32, nmo)),
|
||||
..expected_public_inputs.input
|
||||
},
|
||||
..expected_public_inputs
|
||||
|
|
|
@ -4,3 +4,5 @@ pub mod error;
|
|||
pub mod input;
|
||||
pub mod output;
|
||||
pub mod partial_tx;
|
||||
|
||||
pub use death_constraint::DeathProof;
|
||||
|
|
|
@ -55,16 +55,18 @@ impl ProvedOutput {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use rand::thread_rng;
|
||||
|
||||
use super::*;
|
||||
use cl::note::unit_point;
|
||||
use rand::thread_rng;
|
||||
|
||||
#[test]
|
||||
fn test_output_prover() {
|
||||
let nmo = unit_point("NMO");
|
||||
|
||||
let mut rng = thread_rng();
|
||||
|
||||
let output = cl::OutputWitness {
|
||||
note: cl::NoteWitness::basic(32, "NMO"),
|
||||
note: cl::NoteWitness::basic(32, nmo),
|
||||
balance_blinding: cl::BalanceWitness::random(&mut rng),
|
||||
nf_pk: cl::NullifierSecret::random(&mut rng).commit(),
|
||||
nonce: cl::NullifierNonce::random(&mut rng),
|
||||
|
@ -79,19 +81,19 @@ mod test {
|
|||
|
||||
let wrong_output_cms = [
|
||||
cl::Output {
|
||||
note_comm: cl::NoteWitness::basic(100, "NMO").commit(
|
||||
note_comm: cl::NoteWitness::basic(100, nmo).commit(
|
||||
cl::NullifierSecret::random(&mut rng).commit(),
|
||||
cl::NullifierNonce::random(&mut rng),
|
||||
),
|
||||
..expected_output_cm
|
||||
},
|
||||
cl::Output {
|
||||
note_comm: cl::NoteWitness::basic(100, "NMO").commit(
|
||||
note_comm: cl::NoteWitness::basic(100, nmo).commit(
|
||||
cl::NullifierSecret::random(&mut rng).commit(),
|
||||
cl::NullifierNonce::random(&mut rng),
|
||||
),
|
||||
balance: cl::BalanceWitness::random(&mut rng)
|
||||
.commit(&cl::NoteWitness::basic(100, "NMO")),
|
||||
.commit(&cl::NoteWitness::basic(100, nmo)),
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -103,10 +105,11 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_zero_output_is_rejected() {
|
||||
let nmo = unit_point("NMO");
|
||||
let mut rng = thread_rng();
|
||||
|
||||
let output = cl::OutputWitness::random(
|
||||
cl::NoteWitness::basic(0, "NMO"),
|
||||
cl::NoteWitness::basic(0, nmo),
|
||||
cl::NullifierSecret::random(&mut rng).commit(),
|
||||
&mut rng,
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use proof_statements::death_constraint::DeathConstraintPublic;
|
||||
use ledger_proof_statements::death_constraint::DeathConstraintPublic;
|
||||
|
||||
use crate::{
|
||||
death_constraint::DeathProof, error::Result, input::ProvedInput, output::ProvedOutput,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use cl::note::unit_point;
|
||||
use ledger::{bundle::ProvedBundle, death_constraint::DeathProof, partial_tx::ProvedPartialTx};
|
||||
use rand_core::CryptoRngCore;
|
||||
|
||||
|
@ -29,6 +30,8 @@ fn receive_utxo(
|
|||
|
||||
#[test]
|
||||
fn test_simple_transfer() {
|
||||
let nmo = unit_point("NMO");
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
// alice is sending 8 NMO to bob.
|
||||
|
@ -38,19 +41,18 @@ fn test_simple_transfer() {
|
|||
|
||||
// Alice has an unspent note worth 10 NMO
|
||||
let utxo = receive_utxo(
|
||||
cl::NoteWitness::stateless(10, "NMO", DeathProof::nop_constraint()),
|
||||
cl::NoteWitness::stateless(10, nmo, DeathProof::nop_constraint()),
|
||||
alice.pk(),
|
||||
&mut rng,
|
||||
);
|
||||
let alices_input = cl::InputWitness::random(utxo, alice.sk(), &mut rng);
|
||||
|
||||
// Alice wants to send 8 NMO to bob
|
||||
let bobs_output =
|
||||
cl::OutputWitness::random(cl::NoteWitness::basic(8, "NMO"), bob.pk(), &mut rng);
|
||||
let bobs_output = cl::OutputWitness::random(cl::NoteWitness::basic(8, nmo), bob.pk(), &mut rng);
|
||||
|
||||
// .. and return the 2 NMO in change to herself.
|
||||
let change_output =
|
||||
cl::OutputWitness::random(cl::NoteWitness::basic(2, "NMO"), alice.pk(), &mut rng);
|
||||
cl::OutputWitness::random(cl::NoteWitness::basic(2, nmo), alice.pk(), &mut rng);
|
||||
|
||||
// Construct the ptx consuming Alices inputs and producing the two outputs.
|
||||
let ptx_witness = cl::PartialTxWitness {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "proof_statements"
|
||||
name = "ledger_proof_statements"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
|
@ -1,3 +1,2 @@
|
|||
pub mod death_constraint;
|
||||
pub mod input;
|
||||
pub mod ptx;
|
|
@ -1,36 +0,0 @@
|
|||
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<merkle::PathNode>,
|
||||
pub ptx_path: Vec<merkle::PathNode>,
|
||||
}
|
||||
|
||||
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.note_commitment().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<merkle::PathNode>,
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ edition = "2021"
|
|||
risc0-zkvm = { version = "1.0", default-features = false, features = ['std'] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
cl = { path = "../../cl" }
|
||||
proof_statements = { path = "../../proof_statements" }
|
||||
ledger_proof_statements = { path = "../../ledger_proof_statements" }
|
||||
|
||||
|
||||
[patch.crates-io]
|
||||
|
|
|
@ -9,7 +9,7 @@ edition = "2021"
|
|||
risc0-zkvm = { version = "1.0", default-features = false, features = ['std'] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
cl = { path = "../../cl" }
|
||||
proof_statements = { path = "../../proof_statements" }
|
||||
ledger_proof_statements = { path = "../../ledger_proof_statements" }
|
||||
|
||||
|
||||
[patch.crates-io]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/// Death Constraint No-op Proof
|
||||
use proof_statements::death_constraint::DeathConstraintPublic;
|
||||
use ledger_proof_statements::death_constraint::DeathConstraintPublic;
|
||||
use risc0_zkvm::guest::env;
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -9,7 +9,7 @@ edition = "2021"
|
|||
risc0-zkvm = { version = "1.0", default-features = false, features = ['std'] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
cl = { path = "../../cl" }
|
||||
proof_statements = { path = "../../proof_statements" }
|
||||
ledger_proof_statements = { path = "../../ledger_proof_statements" }
|
||||
|
||||
|
||||
[patch.crates-io]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/// Input Proof
|
||||
use cl::merkle;
|
||||
use proof_statements::input::{InputPrivate, InputPublic};
|
||||
use ledger_proof_statements::input::{InputPrivate, InputPublic};
|
||||
use risc0_zkvm::guest::env;
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -9,7 +9,7 @@ edition = "2021"
|
|||
risc0-zkvm = { version = "1.0", default-features = false, features = ['std'] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
cl = { path = "../../cl" }
|
||||
proof_statements = { path = "../../proof_statements" }
|
||||
ledger_proof_statements = { path = "../../ledger_proof_statements" }
|
||||
|
||||
|
||||
[patch.crates-io]
|
||||
|
|
Loading…
Reference in New Issue