cl: swap out ExtendedPoint for SubgroupPoint

This commit is contained in:
David Rusu 2024-06-15 18:21:51 -04:00
parent 913064da39
commit 993ecf13b5
7 changed files with 192 additions and 146 deletions

View File

@ -1,16 +1,123 @@
use jubjub::{ExtendedPoint, Scalar};
use group::{ff::Field, GroupEncoding};
use jubjub::{Scalar, SubgroupPoint};
use lazy_static::lazy_static;
use rand_core::RngCore;
lazy_static! {
static ref PEDERSON_COMMITMENT_BLINDING_POINT: ExtendedPoint =
static ref PEDERSON_COMMITMENT_BLINDING_POINT: SubgroupPoint =
crate::crypto::hash_to_curve(b"NOMOS_CL_PEDERSON_COMMITMENT_BLINDING");
}
pub fn unit_point(unit: &str) -> ExtendedPoint {
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Balance(pub SubgroupPoint);
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct BalanceWitness {
pub value: u64,
pub unit: String,
pub blinding: Scalar,
}
impl Balance {
pub fn from_witness(w: BalanceWitness) -> Self {
Self(balance(w.value, &w.unit, w.blinding))
}
pub fn to_bytes(&self) -> [u8; 32] {
self.0.to_bytes()
}
}
impl BalanceWitness {
pub fn new(value: u64, unit: impl Into<String>, blinding: Scalar) -> Self {
Self {
value,
unit: unit.into(),
blinding,
}
}
pub fn random(value: u64, unit: impl Into<String>, rng: impl RngCore) -> Self {
Self::new(value, unit, Scalar::random(rng))
}
pub fn unit_point(&self) -> SubgroupPoint {
unit_point(&self.unit)
}
}
pub fn unit_point(unit: &str) -> SubgroupPoint {
crate::crypto::hash_to_curve(unit.as_bytes())
}
pub fn balance(value: u64, unit: &str, blinding: Scalar) -> ExtendedPoint {
pub fn balance(value: u64, unit: &str, blinding: Scalar) -> SubgroupPoint {
let value_scalar = Scalar::from(value);
unit_point(unit) * value_scalar + *PEDERSON_COMMITMENT_BLINDING_POINT * blinding
}
#[cfg(test)]
mod test {
use crate::test_util::seed_rng;
use super::*;
#[test]
fn test_balance_zero_unitless() {
// Zero is the same across all units
let rng = seed_rng(0);
let r = Scalar::random(rng);
assert_eq!(
Balance::from_witness(BalanceWitness::new(0, "NMO", r)),
Balance::from_witness(BalanceWitness::new(0, "ETH", r)),
);
}
#[test]
fn test_balance_blinding() {
// balances are blinded
let r1 = Scalar::from(12);
let r2 = Scalar::from(8);
let a_w = BalanceWitness::new(10, "NMO", r1);
let b_w = BalanceWitness::new(10, "NMO", r2);
let a = Balance::from_witness(a_w);
let b = Balance::from_witness(b_w);
assert_ne!(a, b);
assert_eq!(
a.0 - b.0,
Balance::from_witness(BalanceWitness::new(0, "NMO", r1 - r2)).0
);
}
#[test]
fn test_balance_units() {
// Unit's differentiate between values.
let r = Scalar::from(1337);
let nmo = BalanceWitness::new(10, "NMO", r);
let eth = BalanceWitness::new(10, "ETH", r);
assert_ne!(Balance::from_witness(nmo), Balance::from_witness(eth));
}
#[test]
fn test_balance_homomorphism() {
let mut rng = seed_rng(0);
let r1 = Scalar::random(&mut rng);
let r2 = Scalar::random(&mut rng);
let ten = BalanceWitness::new(10, "NMO", 0.into());
let eight = BalanceWitness::new(8, "NMO", 0.into());
let two = BalanceWitness::new(2, "NMO", 0.into());
// Values of same unit are homomorphic
assert_eq!(
Balance::from_witness(ten).0 - Balance::from_witness(eight).0,
Balance::from_witness(two).0
);
// Blinding factors are also homomorphic.
assert_eq!(
Balance::from_witness(BalanceWitness::new(10, "NMO", r1)).0
- Balance::from_witness(BalanceWitness::new(10, "NMO", r2)).0,
Balance::from_witness(BalanceWitness::new(0, "NMO", r1 - r2)).0
);
}
}

View File

@ -1,6 +1,6 @@
use std::collections::BTreeSet;
use jubjub::{ExtendedPoint, Scalar};
use jubjub::{Scalar, SubgroupPoint};
use crate::{
error::Error,
@ -34,7 +34,7 @@ impl Bundle {
}
}
pub fn balance(&self) -> ExtendedPoint {
pub fn balance(&self) -> SubgroupPoint {
self.partials.iter().map(|ptx| ptx.balance()).sum()
}
@ -102,10 +102,10 @@ mod test {
fn test_bundle_balance() {
let mut rng = seed_rng(0);
let nmo_10_in = InputWitness::random(Note::new(10, "NMO"), &mut rng);
let eth_23_in = InputWitness::random(Note::new(23, "ETH"), &mut rng);
let nmo_10_in = InputWitness::random(Note::random(10, "NMO", &mut rng), &mut rng);
let eth_23_in = InputWitness::random(Note::random(23, "ETH", &mut rng), &mut rng);
let crv_4840_out = OutputWitness::random(
Note::new(4840, "CRV"),
Note::random(4840, "CRV", &mut rng),
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
&mut rng,
);
@ -120,24 +120,24 @@ mod test {
let bundle = Bundle::from_witness(bundle_witness.clone());
assert!(!bundle.is_balanced(
-nmo_10_in.balance_blinding - eth_23_in.balance_blinding
+ crv_4840_out.balance_blinding
-nmo_10_in.note.balance.blinding - eth_23_in.note.balance.blinding
+ crv_4840_out.note.balance.blinding
));
assert_eq!(
bundle.balance(),
crate::balance::balance(4840, "CRV", crv_4840_out.balance_blinding)
- (crate::balance::balance(10, "NMO", nmo_10_in.balance_blinding)
+ crate::balance::balance(23, "ETH", eth_23_in.balance_blinding))
crate::balance::balance(4840, "CRV", crv_4840_out.note.balance.blinding)
- (crate::balance::balance(10, "NMO", nmo_10_in.note.balance.blinding)
+ crate::balance::balance(23, "ETH", eth_23_in.note.balance.blinding))
);
let crv_4840_in = InputWitness::random(Note::new(4840, "CRV"), &mut rng);
let crv_4840_in = InputWitness::random(Note::random(4840, "CRV", &mut rng), &mut rng);
let nmo_10_out = OutputWitness::random(
Note::new(10, "NMO"),
Note::random(10, "NMO", &mut rng),
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
&mut rng,
);
let eth_23_out = OutputWitness::random(
Note::new(23, "ETH"),
Note::random(23, "ETH", &mut rng),
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
&mut rng,
);
@ -149,11 +149,11 @@ mod test {
let bundle = Bundle::from_witness(bundle_witness);
let blinding = -nmo_10_in.balance_blinding - eth_23_in.balance_blinding
+ crv_4840_out.balance_blinding
- crv_4840_in.balance_blinding
+ nmo_10_out.balance_blinding
+ eth_23_out.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, "", blinding));

View File

@ -1,13 +1,13 @@
use blake2::{Blake2s256, Digest};
use group::Group;
use jubjub::ExtendedPoint;
use jubjub::SubgroupPoint;
use rand_chacha::ChaCha20Rng;
use rand_core::SeedableRng;
pub fn hash_to_curve(bytes: &[u8]) -> ExtendedPoint {
pub fn hash_to_curve(bytes: &[u8]) -> SubgroupPoint {
let mut hasher = Blake2s256::new();
hasher.update(b"NOMOS_HASH_TO_CURVE");
hasher.update(bytes);
let seed: [u8; 32] = hasher.finalize().into();
ExtendedPoint::random(ChaCha20Rng::from_seed(seed))
SubgroupPoint::random(ChaCha20Rng::from_seed(seed))
}

View File

@ -3,20 +3,19 @@
/// Partial transactions, as the name suggests, are transactions
/// which on their own may not balance (i.e. \sum inputs != \sum outputs)
use crate::{
balance::Balance,
error::Error,
note::{Note, NoteCommitment},
nullifier::{Nullifier, NullifierNonce, NullifierSecret},
partial_tx::PtxCommitment,
};
use group::{ff::Field, GroupEncoding};
use jubjub::{ExtendedPoint, Scalar};
use rand_core::RngCore;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Input {
pub note_comm: NoteCommitment,
pub nullifier: Nullifier,
pub balance: ExtendedPoint,
pub balance: Balance,
}
#[derive(Debug, Clone, PartialEq, Eq)]
@ -24,7 +23,6 @@ pub struct InputWitness {
pub note: Note,
pub nf_sk: NullifierSecret,
pub nonce: NullifierNonce,
pub balance_blinding: Scalar,
}
impl InputWitness {
@ -33,7 +31,6 @@ impl InputWitness {
note,
nf_sk: NullifierSecret::random(&mut rng),
nonce: NullifierNonce::random(&mut rng),
balance_blinding: Scalar::random(&mut rng),
}
}
}
@ -47,7 +44,7 @@ impl Input {
Self {
note_comm: w.note.commit(w.nf_sk.commit(), w.nonce),
nullifier: Nullifier::new(w.nf_sk, w.nonce),
balance: w.note.balance(w.balance_blinding),
balance: w.note.balance(),
}
}
@ -72,7 +69,7 @@ impl Input {
let nf_pk = witness.nf_sk.commit();
self.note_comm == witness.note.commit(nf_pk, witness.nonce)
&& self.nullifier == Nullifier::new(witness.nf_sk, witness.nonce)
&& self.balance == witness.note.balance(witness.balance_blinding)
&& self.balance == witness.note.balance()
&& ptx_comm == proof.1
}
@ -87,8 +84,6 @@ impl Input {
#[cfg(test)]
mod test {
use group::ff::Field;
use super::*;
use crate::{nullifier::NullifierNonce, test_util::seed_rng};
@ -98,17 +93,11 @@ mod test {
let ptx_comm = PtxCommitment::default();
let note = Note::new(10, "NMO");
let note = Note::random(10, "NMO", &mut rng);
let nf_sk = NullifierSecret::random(&mut rng);
let nonce = NullifierNonce::random(&mut rng);
let balance_blinding = Scalar::random(&mut rng);
let witness = InputWitness {
note,
nf_sk,
nonce,
balance_blinding,
};
let witness = InputWitness { note, nf_sk, nonce };
let input = Input::from_witness(witness.clone());
let proof = input.prove(&witness, ptx_comm).unwrap();
@ -117,11 +106,11 @@ mod test {
let wrong_witnesses = [
InputWitness {
note: Note::new(11, "NMO"),
note: Note::random(11, "NMO", &mut rng),
..witness.clone()
},
InputWitness {
note: Note::new(10, "ETH"),
note: Note::random(10, "ETH", &mut rng),
..witness.clone()
},
InputWitness {
@ -132,10 +121,6 @@ mod test {
nonce: NullifierNonce::random(&mut rng),
..witness.clone()
},
InputWitness {
balance_blinding: Scalar::random(&mut rng),
..witness.clone()
},
];
for wrong_witness in wrong_witnesses {
@ -151,17 +136,11 @@ mod test {
fn test_input_ptx_coupling() {
let mut rng = seed_rng(0);
let note = Note::new(10, "NMO");
let note = Note::random(10, "NMO", &mut rng);
let nf_sk = NullifierSecret::random(&mut rng);
let nonce = NullifierNonce::random(&mut rng);
let balance_blinding = Scalar::random(&mut rng);
let witness = InputWitness {
note,
nf_sk,
nonce,
balance_blinding,
};
let witness = InputWitness { note, nf_sk, nonce };
let input = Input::from_witness(witness.clone());

View File

@ -1,8 +1,11 @@
use blake2::{Blake2s256, Digest};
use group::GroupEncoding;
use jubjub::{ExtendedPoint, Scalar};
use rand_core::RngCore;
use crate::nullifier::{NullifierCommitment, NullifierNonce};
use crate::{
balance::{Balance, BalanceWitness},
nullifier::{NullifierCommitment, NullifierNonce},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NoteCommitment([u8; 32]);
@ -13,85 +16,57 @@ impl NoteCommitment {
}
}
// TODO: Rename Note to NoteWitness and NoteCommitment to Note
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Note {
pub value: u64,
pub unit: String,
pub balance: BalanceWitness,
}
impl Note {
pub fn new(value: u64, unit: impl Into<String>) -> Self {
pub fn random(value: u64, unit: impl Into<String>, rng: impl RngCore) -> Self {
Self {
value,
unit: unit.into(),
balance: BalanceWitness::random(value, unit, rng),
}
}
pub fn unit_point(&self) -> ExtendedPoint {
crate::balance::unit_point(&self.unit)
}
pub fn balance(&self, blinding: Scalar) -> ExtendedPoint {
crate::balance::balance(self.value, &self.unit, blinding)
}
pub fn commit(&self, nf_pk: NullifierCommitment, nonce: NullifierNonce) -> NoteCommitment {
let mut hasher = Blake2s256::new();
hasher.update(b"NOMOS_CL_NOTE_COMMIT");
hasher.update(self.value.to_le_bytes());
hasher.update(self.unit_point().to_bytes());
hasher.update(self.balance.value.to_le_bytes());
hasher.update(self.balance.unit_point().to_bytes());
// Important! we don't commit to the balance blinding factor as that may make the notes linkable.
hasher.update(nf_pk.as_bytes());
hasher.update(nonce.as_bytes());
let commit_bytes: [u8; 32] = hasher.finalize().into();
NoteCommitment(commit_bytes)
}
pub fn balance(&self) -> Balance {
Balance::from_witness(self.balance.clone())
}
}
#[cfg(test)]
mod test {
use crate::{nullifier::NullifierSecret, test_util::seed_rng};
use super::*;
#[test]
fn test_balance_zero_unitless() {
// Zero is the same across all units
let r = Scalar::from(32);
assert_eq!(
Note::new(0, "NMO").balance(r),
Note::new(0, "ETH").balance(r)
);
}
fn test_note_commitments_dont_commit_to_balance_blinding() {
let mut rng = seed_rng(0);
let n1 = Note::random(12, "NMO", &mut rng);
let n2 = Note::random(12, "NMO", &mut rng);
#[test]
fn test_balance_blinding() {
// balances are blinded
let r1 = Scalar::from(12);
let r2 = Scalar::from(8);
let a = Note::new(10, "NMO");
assert_ne!(a.balance(r1), a.balance(r2));
assert_eq!(a.balance(r1), a.balance(r1));
}
let nf_pk = NullifierSecret::random(&mut rng).commit();
let nonce = NullifierNonce::random(&mut rng);
#[test]
fn test_balance_units() {
// Unit's differentiate between values.
let nmo = Note::new(10, "NMO");
let eth = Note::new(10, "ETH");
let r = Scalar::from(1337);
assert_ne!(nmo.balance(r), eth.balance(r));
}
// Balance blinding factors are different.
assert_ne!(n1.balance.blinding, n2.balance.blinding);
#[test]
fn test_balance_homomorphism() {
let r = Scalar::from(32);
let ten = Note::new(10, "NMO");
let eight = Note::new(8, "NMO");
let two = Note::new(2, "NMO");
assert_eq!(ten.balance(r) - eight.balance(r), two.balance(0.into()));
assert_eq!(
ten.balance(54.into()) - ten.balance(48.into()),
Note::new(0, "NMO").balance(6.into())
);
// But their commitments are the same.
assert_eq!(n1.commit(nf_pk, nonce), n2.commit(nf_pk, nonce));
}
}

View File

@ -1,8 +1,7 @@
use group::{ff::Field, GroupEncoding};
use jubjub::{ExtendedPoint, Scalar};
use rand_core::RngCore;
use crate::{
balance::Balance,
error::Error,
note::{Note, NoteCommitment},
nullifier::{NullifierCommitment, NullifierNonce},
@ -11,7 +10,7 @@ use crate::{
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Output {
pub note_comm: NoteCommitment,
pub balance: ExtendedPoint,
pub balance: Balance,
}
#[derive(Debug, Clone, PartialEq, Eq)]
@ -19,7 +18,6 @@ pub struct OutputWitness {
pub note: Note,
pub nf_pk: NullifierCommitment,
pub nonce: NullifierNonce,
pub balance_blinding: Scalar,
}
impl OutputWitness {
@ -28,7 +26,6 @@ impl OutputWitness {
note,
nf_pk: owner,
nonce: NullifierNonce::random(&mut rng),
balance_blinding: Scalar::random(&mut rng),
}
}
}
@ -41,7 +38,7 @@ impl Output {
pub fn from_witness(w: OutputWitness) -> Self {
Self {
note_comm: w.note.commit(w.nf_pk, w.nonce),
balance: w.note.balance(w.balance_blinding),
balance: w.note.balance(),
}
}
@ -60,7 +57,7 @@ impl Output {
let witness = &proof.0;
self.note_comm == witness.note.commit(witness.nf_pk, witness.nonce)
&& self.balance == witness.note.balance(witness.balance_blinding)
&& self.balance == witness.note.balance()
}
pub(crate) fn to_bytes(&self) -> [u8; 64] {
@ -73,8 +70,6 @@ impl Output {
#[cfg(test)]
mod test {
use group::ff::Field;
use super::*;
use crate::{nullifier::NullifierSecret, test_util::seed_rng};
@ -82,17 +77,11 @@ mod test {
fn test_output_proof() {
let mut rng = seed_rng(0);
let note = Note::new(10, "NMO");
let note = Note::random(10, "NMO", &mut rng);
let nf_pk = NullifierSecret::random(&mut rng).commit();
let nonce = NullifierNonce::random(&mut rng);
let balance_blinding = Scalar::random(&mut rng);
let witness = OutputWitness {
note,
nf_pk,
nonce,
balance_blinding,
};
let witness = OutputWitness { note, nf_pk, nonce };
let output = Output::from_witness(witness.clone());
let proof = output.prove(&witness).unwrap();
@ -101,11 +90,11 @@ mod test {
let wrong_witnesses = [
OutputWitness {
note: Note::new(11, "NMO"),
note: Note::random(11, "NMO", &mut rng),
..witness.clone()
},
OutputWitness {
note: Note::new(10, "ETH"),
note: Note::random(10, "ETH", &mut rng),
..witness.clone()
},
OutputWitness {
@ -116,10 +105,6 @@ mod test {
nonce: NullifierNonce::random(&mut rng),
..witness.clone()
},
OutputWitness {
balance_blinding: Scalar::random(&mut rng),
..witness.clone()
},
];
for wrong_witness in wrong_witnesses {

View File

@ -1,7 +1,7 @@
use std::collections::BTreeSet;
use blake2::{Blake2s256, Digest};
use jubjub::ExtendedPoint;
use jubjub::SubgroupPoint;
use rand_core::RngCore;
use crate::error::Error;
@ -114,9 +114,9 @@ impl PartialTx {
.all(|(o, p)| o.verify(p))
}
pub fn balance(&self) -> ExtendedPoint {
let in_sum: ExtendedPoint = self.inputs.iter().map(|i| i.balance).sum();
let out_sum: ExtendedPoint = self.outputs.iter().map(|o| o.balance).sum();
pub fn balance(&self) -> SubgroupPoint {
let in_sum: SubgroupPoint = self.inputs.iter().map(|i| i.balance.0).sum();
let out_sum: SubgroupPoint = self.outputs.iter().map(|o| o.balance.0).sum();
out_sum - in_sum
}
@ -133,10 +133,10 @@ mod test {
fn test_partial_tx_proof() {
let mut rng = seed_rng(0);
let nmo_10 = InputWitness::random(Note::new(10, "NMO"), &mut rng);
let eth_23 = InputWitness::random(Note::new(23, "ETH"), &mut rng);
let nmo_10 = InputWitness::random(Note::random(10, "NMO", &mut rng), &mut rng);
let eth_23 = InputWitness::random(Note::random(23, "ETH", &mut rng), &mut rng);
let crv_4840 = OutputWitness::random(
Note::new(4840, "CRV"),
Note::random(4840, "CRV", &mut rng),
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
&mut rng,
);
@ -157,10 +157,10 @@ mod test {
fn test_partial_tx_balance() {
let mut rng = seed_rng(0);
let nmo_10 = InputWitness::random(Note::new(10, "NMO"), &mut rng);
let eth_23 = InputWitness::random(Note::new(23, "ETH"), &mut rng);
let nmo_10 = InputWitness::random(Note::random(10, "NMO", &mut rng), &mut rng);
let eth_23 = InputWitness::random(Note::random(23, "ETH", &mut rng), &mut rng);
let crv_4840 = OutputWitness::random(
Note::new(4840, "CRV"),
Note::random(4840, "CRV", &mut rng),
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
&mut rng,
);
@ -174,9 +174,9 @@ mod test {
assert_eq!(
ptx.balance(),
crate::balance::balance(4840, "CRV", crv_4840.balance_blinding)
- (crate::balance::balance(10, "NMO", nmo_10.balance_blinding)
+ crate::balance::balance(23, "ETH", eth_23.balance_blinding))
crate::balance::balance(4840, "CRV", crv_4840.note.balance.blinding)
- (crate::balance::balance(10, "NMO", nmo_10.note.balance.blinding)
+ crate::balance::balance(23, "ETH", eth_23.note.balance.blinding))
);
}
}