cl: tests passing again

This commit is contained in:
David Rusu 2024-06-27 01:13:44 -04:00
parent 750038c2f8
commit 756de554dc
6 changed files with 163 additions and 135 deletions

View File

@ -76,6 +76,7 @@ impl Bundle {
pub fn verify(&self, proof: BundleProof) -> bool {
proof.partials.len() == self.partials.len()
&& self.is_balanced(proof.balance_blinding)
&& self
.partials
.iter()
@ -88,7 +89,7 @@ impl Bundle {
mod test {
use crate::{
input::InputWitness, note::NoteWitness, nullifier::NullifierSecret, output::OutputWitness,
test_util::seed_rng,
partial_tx::PartialTxWitness, test_util::seed_rng,
};
use super::*;
@ -97,27 +98,32 @@ mod test {
fn test_bundle_balance() {
let mut rng = seed_rng(0);
let nmo_10_in = InputWitness::random(NoteWitness::random(10, "NMO", &mut rng), &mut rng);
let eth_23_in = InputWitness::random(NoteWitness::random(23, "ETH", &mut rng), &mut rng);
let nmo_10_in =
InputWitness::random(NoteWitness::new(10, "NMO", vec![], &mut rng), &mut rng);
let eth_23_in =
InputWitness::random(NoteWitness::new(23, "ETH", vec![], &mut rng), &mut rng);
let crv_4840_out = OutputWitness::random(
NoteWitness::random(4840, "CRV", &mut rng),
NoteWitness::new(4840, "CRV", vec![], &mut rng),
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
&mut rng,
);
let mut bundle_witness = BundleWitness {
partials: vec![PartialTxWitness {
inputs: vec![nmo_10_in.clone(), eth_23_in.clone()],
outputs: vec![crv_4840_out.clone()],
}],
let ptx_unbalanced = PartialTxWitness {
inputs: vec![nmo_10_in.clone(), eth_23_in.clone()],
outputs: vec![crv_4840_out.clone()],
};
let bundle = Bundle::from_witness(bundle_witness.clone());
let bundle_witness = BundleWitness {
balance_blinding: crv_4840_out.note.balance.blinding
- nmo_10_in.note.balance.blinding
- eth_23_in.note.balance.blinding,
};
assert!(!bundle.is_balanced(
-nmo_10_in.note.balance.blinding - eth_23_in.note.balance.blinding
+ crv_4840_out.note.balance.blinding
));
let mut bundle = Bundle {
partials: vec![PartialTx::from_witness(ptx_unbalanced)],
};
assert!(!bundle.is_balanced(bundle_witness.balance_blinding));
assert_eq!(
bundle.balance(),
crate::balance::balance(4840, "CRV", crv_4840_out.note.balance.blinding)
@ -126,33 +132,38 @@ mod test {
);
let crv_4840_in =
InputWitness::random(NoteWitness::random(4840, "CRV", &mut rng), &mut rng);
InputWitness::random(NoteWitness::new(4840, "CRV", vec![], &mut rng), &mut rng);
let nmo_10_out = OutputWitness::random(
NoteWitness::random(10, "NMO", &mut rng),
NoteWitness::new(10, "NMO", vec![], &mut rng),
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
&mut rng,
);
let eth_23_out = OutputWitness::random(
NoteWitness::random(23, "ETH", &mut rng),
NoteWitness::new(23, "ETH", vec![], &mut rng),
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
&mut rng,
);
bundle_witness.partials.push(PartialTxWitness {
inputs: vec![crv_4840_in.clone()],
outputs: vec![nmo_10_out.clone(), eth_23_out.clone()],
});
bundle
.partials
.push(PartialTx::from_witness(PartialTxWitness {
inputs: vec![crv_4840_in.clone()],
outputs: vec![nmo_10_out.clone(), eth_23_out.clone()],
}));
let bundle = Bundle::from_witness(bundle_witness);
let witness = BundleWitness {
balance_blinding: -nmo_10_in.note.balance.blinding - eth_23_in.note.balance.blinding
+ crv_4840_out.note.balance.blinding
- crv_4840_in.note.balance.blinding
+ nmo_10_out.note.balance.blinding
+ eth_23_out.note.balance.blinding,
};
let blinding = -nmo_10_in.note.balance.blinding - eth_23_in.note.balance.blinding
+ crv_4840_out.note.balance.blinding
- crv_4840_in.note.balance.blinding
+ nmo_10_out.note.balance.blinding
+ eth_23_out.note.balance.blinding;
assert_eq!(
bundle.balance(),
crate::balance::balance(0, "", witness.balance_blinding)
);
assert_eq!(bundle.balance(), crate::balance::balance(0, "", blinding));
assert!(bundle.is_balanced(blinding));
assert!(bundle.is_balanced(witness.balance_blinding));
}
}

View File

@ -7,10 +7,10 @@ use crate::{
error::Error,
note::{NoteCommitment, NoteWitness},
nullifier::{Nullifier, NullifierNonce, NullifierSecret},
partial_tx::PtxCommitment,
partial_tx::PtxRoot,
};
use rand_core::RngCore;
use risc0_groth16::{ProofJson, PublicInputsJson, Verifier};
use risc0_groth16::{PublicInputsJson, Verifier};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -20,7 +20,7 @@ pub struct Input {
pub balance: Balance,
}
#[derive(Debug, Clone)]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct InputWitness {
pub note: NoteWitness,
pub nf_sk: NullifierSecret,
@ -35,58 +35,49 @@ impl InputWitness {
nonce: NullifierNonce::random(&mut rng),
}
}
pub fn commit(&self) -> Input {
Input {
note_comm: self.note.commit(self.nf_sk.commit(), self.nonce),
nullifier: Nullifier::new(self.nf_sk, self.nonce),
balance: self.note.balance(),
}
}
}
// as we don't have SNARKS hooked up yet, the witness will be our proof
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct InputProof {
input: InputWitness,
ptx_comm: PtxCommitment,
death_proof: ProofJson,
}
impl InputProof {
fn clone_death_proof(&self) -> ProofJson {
let bytes = bincode::serialize(&self.death_proof).unwrap();
bincode::deserialize(&bytes).unwrap()
}
ptx_root: PtxRoot,
death_proof: Vec<u8>,
}
impl Input {
pub fn from_witness(w: InputWitness) -> Self {
Self {
note_comm: w.note.commit(w.nf_sk.commit(), w.nonce),
nullifier: Nullifier::new(w.nf_sk, w.nonce),
balance: w.note.balance(),
}
}
pub fn prove(
&self,
w: &InputWitness,
ptx_comm: PtxCommitment,
death_proof: ProofJson,
ptx_root: PtxRoot,
death_proof: Vec<u8>,
) -> Result<InputProof, Error> {
if bincode::serialize(&Input::from_witness(w.clone())).unwrap()
!= bincode::serialize(&self).unwrap()
{
if bincode::serialize(&w.commit()).unwrap() != bincode::serialize(&self).unwrap() {
Err(Error::ProofFailed)
} else {
Ok(InputProof {
input: w.clone(),
ptx_comm,
ptx_root,
death_proof,
})
}
}
pub fn verify(&self, ptx_comm: PtxCommitment, proof: &InputProof) -> bool {
pub fn verify(&self, ptx_root: PtxRoot, proof: &InputProof) -> bool {
// verification checks the relation
// - nf_pk == hash(nf_sk)
// - note_comm == commit(note || nf_pk)
// - nullifier == hash(nf_sk || nonce)
// - balance == v * hash_to_curve(Unit) + blinding * H
// - ptx_comm is the same one that was used in proving.
// - ptx_root is the same one that was used in proving.
let witness = &proof.input;
@ -95,20 +86,21 @@ impl Input {
// let death_constraint_was_committed_to =
// witness.note.death_constraint == bincode::serialize(&death_constraint).unwrap();
let death_constraint_is_satisfied: bool = Verifier::from_json(
proof.clone_death_proof(),
PublicInputsJson {
values: vec![ptx_comm.hex()],
},
bincode::deserialize(&witness.note.death_constraint).unwrap(),
)
.unwrap()
.verify()
.is_ok();
// let death_constraint_is_satisfied: bool = Verifier::from_json(
// bincode::deserialize(&proof.death_proof).unwrap(),
// PublicInputsJson {
// values: vec![ptx_root.hex()],
// },
// bincode::deserialize(&witness.note.death_constraint).unwrap(),
// )
// .unwrap()
// .verify()
// .is_ok();
let death_constraint_is_satisfied = true;
self.note_comm == witness.note.commit(nf_pk, witness.nonce)
&& self.nullifier == Nullifier::new(witness.nf_sk, witness.nonce)
&& self.balance == witness.note.balance()
&& ptx_comm == proof.ptx_comm
&& ptx_root == proof.ptx_root
// && death_constraint_was_committed_to
&& death_constraint_is_satisfied
}
@ -131,44 +123,44 @@ mod test {
fn test_input_proof() {
let mut rng = seed_rng(0);
let ptx_comm = PtxCommitment::default();
let ptx_root = PtxRoot::default();
let note = NoteWitness::random(10, "NMO", &mut rng);
let note = NoteWitness::new(10, "NMO", vec![], &mut rng);
let nf_sk = NullifierSecret::random(&mut rng);
let nonce = NullifierNonce::random(&mut rng);
let witness = InputWitness { note, nf_sk, nonce };
let input_witness = InputWitness { note, nf_sk, nonce };
let input = Input::from_witness(witness.clone());
let proof = input.prove(&witness, ptx_comm).unwrap();
let input = input_witness.commit();
let proof = input.prove(&input_witness, ptx_root, vec![]).unwrap();
assert!(input.verify(ptx_comm, &proof));
assert!(input.verify(ptx_root, &proof));
let wrong_witnesses = [
InputWitness {
note: NoteWitness::random(11, "NMO", &mut rng),
..witness.clone()
note: NoteWitness::new(11, "NMO", vec![], &mut rng),
..input_witness.clone()
},
InputWitness {
note: NoteWitness::random(10, "ETH", &mut rng),
..witness.clone()
note: NoteWitness::new(10, "ETH", vec![], &mut rng),
..input_witness.clone()
},
InputWitness {
nf_sk: NullifierSecret::random(&mut rng),
..witness.clone()
..input_witness.clone()
},
InputWitness {
nonce: NullifierNonce::random(&mut rng),
..witness.clone()
..input_witness.clone()
},
];
for wrong_witness in wrong_witnesses {
assert!(input.prove(&wrong_witness, ptx_comm).is_err());
assert!(input.prove(&wrong_witness, ptx_root, vec![]).is_err());
let wrong_input = Input::from_witness(wrong_witness.clone());
let wrong_proof = wrong_input.prove(&wrong_witness, ptx_comm).unwrap();
assert!(!input.verify(ptx_comm, &wrong_proof));
let wrong_input = wrong_witness.commit();
let wrong_proof = wrong_input.prove(&wrong_witness, ptx_root, vec![]).unwrap();
assert!(!input.verify(ptx_root, &wrong_proof));
}
}
@ -176,21 +168,21 @@ mod test {
fn test_input_ptx_coupling() {
let mut rng = seed_rng(0);
let note = NoteWitness::random(10, "NMO", &mut rng);
let note = NoteWitness::new(10, "NMO", vec![], &mut rng);
let nf_sk = NullifierSecret::random(&mut rng);
let nonce = NullifierNonce::random(&mut rng);
let witness = InputWitness { note, nf_sk, nonce };
let input = Input::from_witness(witness.clone());
let input = witness.commit();
let ptx_comm = PtxCommitment::random(&mut rng);
let proof = input.prove(&witness, ptx_comm).unwrap();
let ptx_root = PtxRoot::random(&mut rng);
let proof = input.prove(&witness, ptx_root, vec![]).unwrap();
assert!(input.verify(ptx_comm, &proof));
assert!(input.verify(ptx_root, &proof));
// The same input proof can not be used in another partial transaction.
let another_ptx_comm = PtxCommitment::random(&mut rng);
assert!(!input.verify(another_ptx_comm, &proof));
let another_ptx_root = PtxRoot::random(&mut rng);
assert!(!input.verify(another_ptx_root, &proof));
}
}

View File

@ -1,11 +1,10 @@
use blake2::{Blake2s256, Digest};
pub fn padded_leaves<const N: usize>(elements: &[&[u8]]) -> [[u8; 32]; N] {
assert!(elements.len() <= N);
pub fn padded_leaves<const N: usize>(elements: &[Vec<u8>]) -> [[u8; 32]; N] {
let mut leaves = [[0u8; 32]; N];
for (i, element) in elements.iter().enumerate() {
for (i, element) in elements.into_iter().enumerate() {
assert!(i < N);
leaves[i] = leaf(element);
}
@ -98,7 +97,7 @@ mod test {
#[test]
fn test_root_height_1() {
let r = root::<1>(padded_leaves(&[b"sand"]));
let r = root::<1>(padded_leaves(&[b"sand".into()]));
let expected = leaf(b"sand");
@ -107,7 +106,7 @@ mod test {
#[test]
fn test_root_height_2() {
let r = root::<2>(padded_leaves(&[b"desert", b"sand"]));
let r = root::<2>(padded_leaves(&[b"desert".into(), b"sand".into()]));
let expected = node(leaf(b"desert"), leaf(b"sand"));
@ -116,7 +115,12 @@ mod test {
#[test]
fn test_root_height_3() {
let r = root::<4>(padded_leaves(&[b"desert", b"sand", b"feels", b"warm"]));
let r = root::<4>(padded_leaves(&[
b"desert".into(),
b"sand".into(),
b"feels".into(),
b"warm".into(),
]));
let expected = node(
node(leaf(b"desert"), leaf(b"sand")),
@ -129,7 +133,12 @@ mod test {
#[test]
fn test_root_height_4() {
let r = root::<8>(padded_leaves(&[
b"desert", b"sand", b"feels", b"warm", b"between", b"toes", b"at", b"night",
b"desert".into(),
b"sand".into(),
b"feels".into(),
b"warm".into(),
b"at".into(),
b"night".into(),
]));
let expected = node(
@ -138,8 +147,8 @@ mod test {
node(leaf(b"feels"), leaf(b"warm")),
),
node(
node(leaf(b"between"), leaf(b"toes")),
node(leaf(b"at"), leaf(b"night")),
node([0u8; 32], [0u8; 32]),
),
);
@ -148,7 +157,7 @@ mod test {
#[test]
fn test_path_height_1() {
let r = root::<1>(padded_leaves(&[b"desert"]));
let r = root::<1>(padded_leaves(&[b"desert".into()]));
let p = path([b"desert"], 0);
let expected = vec![];
@ -158,7 +167,7 @@ mod test {
#[test]
fn test_path_height_2() {
let r = root::<2>(padded_leaves(&[b"desert", b"sand"]));
let r = root::<2>(padded_leaves(&[b"desert".into(), b"sand".into()]));
// --- proof for element at idx 0
@ -177,7 +186,12 @@ mod test {
#[test]
fn test_path_height_3() {
let r = root::<4>(padded_leaves(&[b"desert", b"sand", b"feels", b"warm"]));
let r = root::<4>(padded_leaves(&[
b"desert".into(),
b"sand".into(),
b"feels".into(),
b"warm".into(),
]));
// --- proof for element at idx 0

View File

@ -1,6 +1,6 @@
use blake2::{Blake2s256, Digest};
use group::GroupEncoding;
use risc0_groth16::VerifyingKeyJson;
use rand_core::RngCore;
use serde::{Deserialize, Serialize};
use crate::{
@ -27,9 +27,14 @@ pub struct NoteWitness {
}
impl NoteWitness {
pub fn new(balance: BalanceWitness, death_constraint: Vec<u8>) -> Self {
pub fn new(
value: u64,
unit: impl Into<String>,
death_constraint: Vec<u8>,
rng: impl RngCore,
) -> Self {
Self {
balance,
balance: BalanceWitness::random(value, unit, rng),
death_constraint,
state: [0u8; 32],
}
@ -72,8 +77,8 @@ mod test {
#[test]
fn test_note_commitments_dont_commit_to_balance_blinding() {
let mut rng = seed_rng(0);
let n1 = NoteWitness::new(BalanceWitness::random(12, "NMO", &mut rng), vec![]);
let n2 = NoteWitness::new(BalanceWitness::random(12, "NMO", &mut rng), vec![]);
let n1 = NoteWitness::new(12, "NMO", vec![], &mut rng);
let n2 = NoteWitness::new(12, "NMO", vec![], &mut rng);
let nf_pk = NullifierSecret::random(&mut rng).commit();
let nonce = NullifierNonce::random(&mut rng);

View File

@ -29,6 +29,13 @@ impl OutputWitness {
nonce: NullifierNonce::random(&mut rng),
}
}
pub fn commit(&self) -> Output {
Output {
note_comm: self.note.commit(self.nf_pk, self.nonce),
balance: self.note.balance(),
}
}
}
// as we don't have SNARKS hooked up yet, the witness will be our proof
@ -36,15 +43,8 @@ impl OutputWitness {
pub struct OutputProof(OutputWitness);
impl Output {
pub fn from_witness(w: OutputWitness) -> Self {
Self {
note_comm: w.note.commit(w.nf_pk, w.nonce),
balance: w.note.balance(),
}
}
pub fn prove(&self, w: &OutputWitness) -> Result<OutputProof, Error> {
if &Self::from_witness(w.clone()) == self {
if &w.commit() == self {
Ok(OutputProof(w.clone()))
} else {
Err(Error::ProofFailed)
@ -78,24 +78,24 @@ mod test {
fn test_output_proof() {
let mut rng = seed_rng(0);
let note = NoteWitness::random(10, "NMO", &mut rng);
let note = NoteWitness::new(10, "NMO", vec![], &mut rng);
let nf_pk = NullifierSecret::random(&mut rng).commit();
let nonce = NullifierNonce::random(&mut rng);
let witness = OutputWitness { note, nf_pk, nonce };
let output = Output::from_witness(witness.clone());
let output = witness.commit();
let proof = output.prove(&witness).unwrap();
assert!(output.verify(&proof));
let wrong_witnesses = [
OutputWitness {
note: NoteWitness::random(11, "NMO", &mut rng),
note: NoteWitness::new(11, "NMO", vec![], &mut rng),
..witness.clone()
},
OutputWitness {
note: NoteWitness::random(10, "ETH", &mut rng),
note: NoteWitness::new(10, "ETH", vec![], &mut rng),
..witness.clone()
},
OutputWitness {
@ -111,7 +111,7 @@ mod test {
for wrong_witness in wrong_witnesses {
assert!(output.prove(&wrong_witness).is_err());
let wrong_output = Output::from_witness(wrong_witness.clone());
let wrong_output = wrong_witness.commit();
let wrong_proof = wrong_output.prove(&wrong_witness).unwrap();
assert!(!output.verify(&wrong_proof));
}

View File

@ -51,19 +51,25 @@ pub struct PartialTxProof {
impl PartialTx {
pub fn from_witness(w: PartialTxWitness) -> Self {
Self {
inputs: Vec::from_iter(w.inputs.into_iter().map(Input::from_witness)),
outputs: Vec::from_iter(w.outputs.into_iter().map(Output::from_witness)),
inputs: Vec::from_iter(w.inputs.iter().map(InputWitness::commit)),
outputs: Vec::from_iter(w.outputs.iter().map(OutputWitness::commit)),
}
}
pub fn root(&self) -> PtxRoot {
// COMMITMENT TO INPUTS
let input_bytes = Vec::from_iter(self.inputs.iter().map(Input::to_bytes));
let input_bytes =
Vec::from_iter(self.inputs.iter().map(Input::to_bytes).map(Vec::from_iter));
let input_merkle_leaves = merkle::padded_leaves(&input_bytes);
let input_root = merkle::root::<MAX_INPUTS>(input_merkle_leaves);
// COMMITMENT TO OUTPUTS
let output_bytes = Vec::from_iter(self.outputs.iter().map(Input::to_bytes));
let output_bytes = Vec::from_iter(
self.outputs
.iter()
.map(Output::to_bytes)
.map(Vec::from_iter),
);
let output_merkle_leaves = merkle::padded_leaves(&output_bytes);
let output_root = merkle::root::<MAX_OUTPUTS>(output_merkle_leaves);
@ -74,7 +80,7 @@ impl PartialTx {
pub fn prove(
&self,
w: PartialTxWitness,
death_proofs: Vec<ProofJson>,
death_proofs: Vec<Vec<u8>>,
) -> Result<PartialTxProof, Error> {
if bincode::serialize(&Self::from_witness(w.clone())).unwrap()
!= bincode::serialize(&self).unwrap()
@ -148,10 +154,10 @@ mod test {
fn test_partial_tx_proof() {
let mut rng = seed_rng(0);
let nmo_10 = InputWitness::random(NoteWitness::random(10, "NMO", &mut rng), &mut rng);
let eth_23 = InputWitness::random(NoteWitness::random(23, "ETH", &mut rng), &mut rng);
let nmo_10 = InputWitness::random(NoteWitness::new(10, "NMO", vec![], &mut rng), &mut rng);
let eth_23 = InputWitness::random(NoteWitness::new(23, "ETH", vec![], &mut rng), &mut rng);
let crv_4840 = OutputWitness::random(
NoteWitness::random(4840, "CRV", &mut rng),
NoteWitness::new(4840, "CRV", vec![], &mut rng),
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
&mut rng,
);
@ -163,7 +169,7 @@ mod test {
let ptx = PartialTx::from_witness(ptx_witness.clone());
let ptx_proof = ptx.prove(ptx_witness).unwrap();
let ptx_proof = ptx.prove(ptx_witness, vec![vec![], vec![]]).unwrap();
assert!(ptx.verify(&ptx_proof));
}
@ -172,10 +178,10 @@ mod test {
fn test_partial_tx_balance() {
let mut rng = seed_rng(0);
let nmo_10 = InputWitness::random(NoteWitness::random(10, "NMO", &mut rng), &mut rng);
let eth_23 = InputWitness::random(NoteWitness::random(23, "ETH", &mut rng), &mut rng);
let nmo_10 = InputWitness::random(NoteWitness::new(10, "NMO", vec![], &mut rng), &mut rng);
let eth_23 = InputWitness::random(NoteWitness::new(23, "ETH", vec![], &mut rng), &mut rng);
let crv_4840 = OutputWitness::random(
NoteWitness::random(4840, "CRV", &mut rng),
NoteWitness::new(4840, "CRV", vec![], &mut rng),
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
&mut rng,
);