s/{pairing::bls12_381, zcash_primitives::jubjub}/{bls12_381, jubjub}

FINALLY.
This commit is contained in:
Jack Grigg 2020-07-02 08:26:54 +12:00
parent 315f00d6d4
commit fdf06032e3
43 changed files with 1342 additions and 1834 deletions

View File

@ -22,8 +22,9 @@
//! },
//! groth16, Circuit, ConstraintSystem, SynthesisError,
//! };
//! use bls12_381::Bls12;
//! use ff::PrimeField;
//! use pairing::{bls12_381::Bls12, Engine};
//! use pairing::Engine;
//! use rand::rngs::OsRng;
//! use sha2::{Digest, Sha256};
//!

View File

@ -13,9 +13,12 @@ edition = "2018"
[dependencies]
bech32 = "0.7"
bls12_381 = { version = "0.1", path = "../bls12_381" }
bs58 = { version = "0.3", features = ["check"] }
ff = { version = "0.6", path = "../ff" }
group = { version = "0.6", path = "../group" }
hex = "0.4"
jubjub = { version = "0.3", path = "../jubjub" }
pairing = { version = "0.16", path = "../pairing" }
protobuf = "=2.14.0" # 2.15 has MSRV of 1.44.1
subtle = "2"

View File

@ -1,11 +1,10 @@
use pairing::bls12_381::Bls12;
use group::cofactor::CofactorGroup;
use zcash_primitives::{
consensus,
note_encryption::{try_sapling_note_decryption, try_sapling_output_recovery, Memo},
primitives::{Note, PaymentAddress},
transaction::Transaction,
zip32::ExtendedFullViewingKey,
JUBJUB,
};
/// A decrypted shielded output.
@ -15,11 +14,11 @@ pub struct DecryptedOutput {
/// [`shielded_outputs`]: zcash_primitives::transaction::TransactionData
pub index: usize,
/// The note within the output.
pub note: Note<Bls12>,
pub note: Note,
/// The account that decrypted the note.
pub account: usize,
/// The address the note was sent to.
pub to: PaymentAddress<Bls12>,
pub to: PaymentAddress,
/// The memo included with the note.
pub memo: Memo,
/// True if this output was recovered using an [`OutgoingViewingKey`], meaning that
@ -45,10 +44,11 @@ pub fn decrypt_transaction<P: consensus::Parameters>(
.collect();
for (index, output) in tx.shielded_outputs.iter().enumerate() {
let epk = match output.ephemeral_key.as_prime_order(&JUBJUB) {
Some(p) => p,
None => continue,
};
let epk = output.ephemeral_key.into_subgroup();
if epk.is_none().into() {
continue;
}
let epk = epk.unwrap();
for (account, (ivk, ovk)) in vks.iter().enumerate() {
let ((note, to, memo), outgoing) = match try_sapling_note_decryption::<P>(

View File

@ -7,14 +7,12 @@
use bech32::{self, Error, FromBase32, ToBase32};
use bs58::{self, decode::Error as Bs58Error};
use pairing::bls12_381::Bls12;
use std::convert::TryInto;
use std::io::{self, Write};
use zcash_primitives::{
legacy::TransparentAddress,
primitives::PaymentAddress,
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
JUBJUB,
};
fn bech32_encode<F>(hrp: &str, write: F) -> String
@ -97,18 +95,15 @@ pub fn decode_extended_full_viewing_key(
/// # Examples
///
/// ```
/// use pairing::bls12_381::Bls12;
/// use group::Group;
/// use jubjub::SubgroupPoint;
/// use rand_core::SeedableRng;
/// use rand_xorshift::XorShiftRng;
/// use zcash_client_backend::{
/// constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS,
/// encoding::encode_payment_address,
/// };
/// use zcash_primitives::{
/// jubjub::edwards,
/// primitives::{Diversifier, PaymentAddress},
/// JUBJUB,
/// };
/// use zcash_primitives::primitives::{Diversifier, PaymentAddress};
///
/// let rng = &mut XorShiftRng::from_seed([
/// 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
@ -117,16 +112,16 @@ pub fn decode_extended_full_viewing_key(
///
/// let pa = PaymentAddress::from_parts(
/// Diversifier([0u8; 11]),
/// edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
/// SubgroupPoint::random(rng),
/// )
/// .unwrap();
///
/// assert_eq!(
/// encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &pa),
/// "ztestsapling1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j0ym7pe",
/// "ztestsapling1qqqqqqqqqqqqqqqqqqcguyvaw2vjk4sdyeg0lc970u659lvhqq7t0np6hlup5lusxle75ss7jnk",
/// );
/// ```
pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String {
pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress) -> String {
bech32_encode(hrp, |w| w.write_all(&addr.to_bytes()))
}
@ -135,18 +130,15 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String
/// # Examples
///
/// ```
/// use pairing::bls12_381::Bls12;
/// use group::Group;
/// use jubjub::SubgroupPoint;
/// use rand_core::SeedableRng;
/// use rand_xorshift::XorShiftRng;
/// use zcash_client_backend::{
/// constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS,
/// encoding::decode_payment_address,
/// };
/// use zcash_primitives::{
/// jubjub::edwards,
/// primitives::{Diversifier, PaymentAddress},
/// JUBJUB,
/// };
/// use zcash_primitives::primitives::{Diversifier, PaymentAddress};
///
/// let rng = &mut XorShiftRng::from_seed([
/// 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
@ -155,19 +147,19 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String
///
/// let pa = PaymentAddress::from_parts(
/// Diversifier([0u8; 11]),
/// edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
/// SubgroupPoint::random(rng),
/// )
/// .unwrap();
///
/// assert_eq!(
/// decode_payment_address(
/// HRP_SAPLING_PAYMENT_ADDRESS,
/// "ztestsapling1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j0ym7pe",
/// "ztestsapling1qqqqqqqqqqqqqqqqqqcguyvaw2vjk4sdyeg0lc970u659lvhqq7t0np6hlup5lusxle75ss7jnk",
/// ),
/// Ok(Some(pa)),
/// );
/// ```
pub fn decode_payment_address(hrp: &str, s: &str) -> Result<Option<PaymentAddress<Bls12>>, Error> {
pub fn decode_payment_address(hrp: &str, s: &str) -> Result<Option<PaymentAddress>, Error> {
bech32_decode(hrp, s, |data| {
if data.len() != 43 {
return None;
@ -175,7 +167,7 @@ pub fn decode_payment_address(hrp: &str, s: &str) -> Result<Option<PaymentAddres
let mut bytes = [0; 43];
bytes.copy_from_slice(&data);
PaymentAddress::<Bls12>::from_bytes(&bytes, &JUBJUB)
PaymentAddress::from_bytes(&bytes)
})
}
@ -283,12 +275,10 @@ pub fn decode_transparent_address(
#[cfg(test)]
mod tests {
use pairing::bls12_381::Bls12;
use group::Group;
use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng;
use zcash_primitives::JUBJUB;
use zcash_primitives::{
jubjub::edwards,
primitives::{Diversifier, PaymentAddress},
zip32::ExtendedSpendingKey,
};
@ -386,16 +376,14 @@ mod tests {
0xbc, 0xe5,
]);
let addr = PaymentAddress::from_parts(
Diversifier([0u8; 11]),
edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
)
.unwrap();
let addr =
PaymentAddress::from_parts(Diversifier([0u8; 11]), jubjub::SubgroupPoint::random(rng))
.unwrap();
let encoded_main =
"zs1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j8nfaxd";
"zs1qqqqqqqqqqqqqqqqqqcguyvaw2vjk4sdyeg0lc970u659lvhqq7t0np6hlup5lusxle75c8v35z";
let encoded_test =
"ztestsapling1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j0ym7pe";
"ztestsapling1qqqqqqqqqqqqqqqqqqcguyvaw2vjk4sdyeg0lc970u659lvhqq7t0np6hlup5lusxle75ss7jnk";
assert_eq!(
encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr),
@ -431,11 +419,9 @@ mod tests {
0xbc, 0xe5,
]);
let addr = PaymentAddress::from_parts(
Diversifier([1u8; 11]),
edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
)
.unwrap();
let addr =
PaymentAddress::from_parts(Diversifier([1u8; 11]), jubjub::SubgroupPoint::random(rng))
.unwrap();
let encoded_main =
encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr);

View File

@ -1,12 +1,9 @@
//! Generated code for handling light client protobuf structs.
use ff::PrimeField;
use pairing::bls12_381::{Bls12, Fr, FrRepr};
use zcash_primitives::{
block::{BlockHash, BlockHeader},
jubjub::{edwards, PrimeOrder},
JUBJUB,
};
use group::GroupEncoding;
use std::convert::TryInto;
use zcash_primitives::block::{BlockHash, BlockHeader};
pub mod compact_formats;
@ -65,10 +62,10 @@ impl compact_formats::CompactOutput {
/// A convenience method that parses [`CompactOutput.cmu`].
///
/// [`CompactOutput.cmu`]: #structfield.cmu
pub fn cmu(&self) -> Result<Fr, ()> {
let mut repr = FrRepr::default();
pub fn cmu(&self) -> Result<bls12_381::Scalar, ()> {
let mut repr = [0; 32];
repr.as_mut().copy_from_slice(&self.cmu[..]);
Fr::from_repr(repr).ok_or(())
bls12_381::Scalar::from_repr(repr).ok_or(())
}
/// Returns the ephemeral public key for this output.
@ -76,8 +73,12 @@ impl compact_formats::CompactOutput {
/// A convenience method that parses [`CompactOutput.epk`].
///
/// [`CompactOutput.epk`]: #structfield.epk
pub fn epk(&self) -> Result<edwards::Point<Bls12, PrimeOrder>, ()> {
let p = edwards::Point::<Bls12, _>::read(&self.epk[..], &JUBJUB).map_err(|_| ())?;
p.as_prime_order(&JUBJUB).ok_or(())
pub fn epk(&self) -> Result<jubjub::SubgroupPoint, ()> {
let p = jubjub::SubgroupPoint::from_bytes(&self.epk[..].try_into().map_err(|_| ())?);
if p.is_some().into() {
Ok(p.unwrap())
} else {
Err(())
}
}
}

View File

@ -1,9 +1,7 @@
//! Structs representing transaction data scanned from the block chain by a wallet or
//! light client.
use pairing::bls12_381::{Bls12, Fr};
use zcash_primitives::{
jubjub::{edwards, PrimeOrder},
merkle_tree::IncrementalWitness,
primitives::{Note, PaymentAddress},
sapling::Node,
@ -36,11 +34,11 @@ pub struct WalletShieldedSpend {
/// [`OutputDescription`]: zcash_primitives::transaction::components::OutputDescription
pub struct WalletShieldedOutput {
pub index: usize,
pub cmu: Fr,
pub epk: edwards::Point<Bls12, PrimeOrder>,
pub cmu: bls12_381::Scalar,
pub epk: jubjub::SubgroupPoint,
pub account: usize,
pub note: Note<Bls12>,
pub to: PaymentAddress<Bls12>,
pub note: Note,
pub to: PaymentAddress,
pub is_change: bool,
pub witness: IncrementalWitness<Node>,
}

View File

@ -5,7 +5,6 @@ use std::collections::HashSet;
use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption};
use zcash_primitives::{
consensus,
jubjub::fs::Fs,
merkle_tree::{CommitmentTree, IncrementalWitness},
note_encryption::try_sapling_compact_note_decryption,
sapling::Node,
@ -26,7 +25,7 @@ use crate::wallet::{WalletShieldedOutput, WalletShieldedSpend, WalletTx};
fn scan_output<P: consensus::Parameters>(
height: u32,
(index, output): (usize, CompactOutput),
ivks: &[Fs],
ivks: &[jubjub::Fr],
spent_from_accounts: &HashSet<usize>,
tree: &mut CommitmentTree<Node>,
existing_witnesses: &mut [&mut IncrementalWitness<Node>],
@ -188,18 +187,17 @@ pub fn scan_block<P: consensus::Parameters>(
#[cfg(test)]
mod tests {
use ff::{Field, PrimeField};
use pairing::bls12_381::{Bls12, Fr};
use group::GroupEncoding;
use rand_core::{OsRng, RngCore};
use zcash_primitives::{
consensus::TestNetwork,
jubjub::{fs::Fs, FixedGenerators, JubjubParams, ToUniform},
constants::SPENDING_KEY_GENERATOR,
merkle_tree::CommitmentTree,
note_encryption::{Memo, SaplingNoteEncryption},
primitives::Note,
transaction::components::Amount,
util::generate_random_rseed,
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
JUBJUB,
};
use super::scan_block;
@ -212,19 +210,15 @@ mod tests {
nf
};
let fake_cmu = {
let fake_cmu = Fr::random(rng);
let fake_cmu = bls12_381::Scalar::random(rng);
fake_cmu.to_repr().as_ref().to_owned()
};
let fake_epk = {
let mut buffer = vec![0; 64];
let mut buffer = [0; 64];
rng.fill_bytes(&mut buffer);
let fake_esk = Fs::to_uniform(&buffer[..]);
let fake_epk = JUBJUB
.generator(FixedGenerators::SpendingKeyGenerator)
.mul(fake_esk, &JUBJUB);
let mut bytes = vec![];
fake_epk.write(&mut bytes).unwrap();
bytes
let fake_esk = jubjub::Fr::from_bytes_wide(&buffer);
let fake_epk = SPENDING_KEY_GENERATOR * fake_esk;
fake_epk.to_bytes().to_vec()
};
let mut cspend = CompactSpend::new();
cspend.set_nf(fake_nf);
@ -257,7 +251,7 @@ mod tests {
let mut rng = OsRng;
let rseed = generate_random_rseed::<TestNetwork, OsRng>(height as u32, &mut rng);
let note = Note {
g_d: to.diversifier().g_d::<Bls12>(&JUBJUB).unwrap(),
g_d: to.diversifier().g_d().unwrap(),
pk_d: to.pk_d().clone(),
value: value.into(),
rseed,
@ -269,9 +263,8 @@ mod tests {
Memo::default(),
&mut rng,
);
let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_owned();
let mut epk = vec![];
encryptor.epk().write(&mut epk).unwrap();
let cmu = note.cm().to_repr().as_ref().to_owned();
let epk = encryptor.epk().to_bytes().to_vec();
let enc_ciphertext = encryptor.encrypt_note_plaintext();
// Create a fake CompactBlock containing the note

View File

@ -15,6 +15,8 @@ edition = "2018"
bech32 = "0.7"
bs58 = { version = "0.3", features = ["check"] }
ff = { version = "0.6", path = "../ff" }
group = { version = "0.6", path = "../group" }
jubjub = { version = "0.3", path = "../jubjub" }
pairing = { version = "0.16", path = "../pairing" }
protobuf = "2"
rand_core = "0.5.1"

View File

@ -1,6 +1,5 @@
//! Structs for handling supported address types.
use pairing::bls12_381::Bls12;
use zcash_client_backend::encoding::{
decode_payment_address, decode_transparent_address, encode_payment_address,
encode_transparent_address,
@ -19,12 +18,12 @@ use zcash_client_backend::constants::testnet::{
/// An address that funds can be sent to.
pub enum RecipientAddress {
Shielded(PaymentAddress<Bls12>),
Shielded(PaymentAddress),
Transparent(TransparentAddress),
}
impl From<PaymentAddress<Bls12>> for RecipientAddress {
fn from(addr: PaymentAddress<Bls12>) -> Self {
impl From<PaymentAddress> for RecipientAddress {
fn from(addr: PaymentAddress) -> Self {
RecipientAddress::Shielded(addr)
}
}

View File

@ -96,7 +96,7 @@ fn get_target_and_anchor_heights(data: &Connection) -> Result<(u32, u32), error:
mod tests {
use crate::Network;
use ff::PrimeField;
use pairing::bls12_381::Bls12;
use group::GroupEncoding;
use protobuf::Message;
use rand_core::{OsRng, RngCore};
use rusqlite::{types::ToSql, Connection};
@ -111,7 +111,6 @@ mod tests {
transaction::components::Amount,
util::generate_random_rseed,
zip32::ExtendedFullViewingKey,
JUBJUB,
};
/// Create a fake CompactBlock at the given height, containing a single output paying
@ -128,7 +127,7 @@ mod tests {
let mut rng = OsRng;
let rseed = generate_random_rseed::<Network, OsRng>(height as u32, &mut rng);
let note = Note {
g_d: to.diversifier().g_d::<Bls12>(&JUBJUB).unwrap(),
g_d: to.diversifier().g_d().unwrap(),
pk_d: to.pk_d().clone(),
value: value.into(),
rseed,
@ -140,9 +139,8 @@ mod tests {
Memo::default(),
&mut rng,
);
let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_vec();
let mut epk = vec![];
encryptor.epk().write(&mut epk).unwrap();
let cmu = note.cm().to_repr().as_ref().to_vec();
let epk = encryptor.epk().to_bytes().to_vec();
let enc_ciphertext = encryptor.encrypt_note_plaintext();
// Create a fake CompactBlock containing the note
@ -161,7 +159,7 @@ mod tests {
rng.fill_bytes(&mut cb.hash);
cb.prevHash.extend_from_slice(&prev_hash.0);
cb.vtx.push(ctx);
(cb, note.nf(&extfvk.fvk.vk, 0, &JUBJUB))
(cb, note.nf(&extfvk.fvk.vk, 0))
}
/// Create a fake CompactBlock at the given height, spending a single note from the
@ -171,7 +169,7 @@ mod tests {
prev_hash: BlockHash,
(nf, in_value): (Vec<u8>, Amount),
extfvk: ExtendedFullViewingKey,
to: PaymentAddress<Bls12>,
to: PaymentAddress,
value: Amount,
) -> CompactBlock {
let mut rng = OsRng;
@ -189,7 +187,7 @@ mod tests {
// Create a fake Note for the payment
ctx.outputs.push({
let note = Note {
g_d: to.diversifier().g_d::<Bls12>(&JUBJUB).unwrap(),
g_d: to.diversifier().g_d().unwrap(),
pk_d: to.pk_d().clone(),
value: value.into(),
rseed,
@ -201,9 +199,8 @@ mod tests {
Memo::default(),
&mut rng,
);
let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_vec();
let mut epk = vec![];
encryptor.epk().write(&mut epk).unwrap();
let cmu = note.cm().to_repr().as_ref().to_vec();
let epk = encryptor.epk().to_bytes().to_vec();
let enc_ciphertext = encryptor.encrypt_note_plaintext();
let mut cout = CompactOutput::new();
@ -218,7 +215,7 @@ mod tests {
let change_addr = extfvk.default_address().unwrap().1;
let rseed = generate_random_rseed::<Network, OsRng>(height as u32, &mut rng);
let note = Note {
g_d: change_addr.diversifier().g_d::<Bls12>(&JUBJUB).unwrap(),
g_d: change_addr.diversifier().g_d().unwrap(),
pk_d: change_addr.pk_d().clone(),
value: (in_value - value).into(),
rseed,
@ -230,9 +227,8 @@ mod tests {
Memo::default(),
&mut rng,
);
let cmu = note.cm(&JUBJUB).to_repr().as_ref().to_vec();
let mut epk = vec![];
encryptor.epk().write(&mut epk).unwrap();
let cmu = note.cm().to_repr().as_ref().to_vec();
let epk = encryptor.epk().to_bytes().to_vec();
let enc_ciphertext = encryptor.encrypt_note_plaintext();
let mut cout = CompactOutput::new();

View File

@ -12,7 +12,6 @@ use zcash_primitives::{
merkle_tree::{CommitmentTree, IncrementalWitness},
sapling::Node,
transaction::Transaction,
JUBJUB,
};
use crate::{
@ -273,7 +272,6 @@ pub fn scan_cached_blocks<P: AsRef<Path>, Q: AsRef<Path>>(
let nf = output.note.nf(
&extfvks[output.account].fvk.vk,
output.witness.position() as u64,
&JUBJUB,
);
// Assumptions:

View File

@ -1,16 +1,13 @@
//! Functions for creating transactions.
use ff::PrimeField;
use pairing::bls12_381::Bls12;
use rand_core::{OsRng, RngCore};
use rusqlite::{types::ToSql, Connection, NO_PARAMS};
use std::convert::TryInto;
use std::path::Path;
use zcash_client_backend::encoding::encode_extended_full_viewing_key;
use zcash_primitives::{
consensus,
consensus::{NetworkUpgrade, Parameters},
jubjub::fs::{Fs, FsRepr},
consensus::{self, NetworkUpgrade, Parameters},
keys::OutgoingViewingKey,
merkle_tree::{IncrementalWitness, MerklePath},
note_encryption::Memo,
@ -22,7 +19,6 @@ use zcash_primitives::{
components::{amount::DEFAULT_FEE, Amount},
},
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
JUBJUB,
};
use crate::{
@ -59,7 +55,7 @@ pub enum OvkPolicy {
struct SelectedNoteRow {
diversifier: Diversifier,
note: Note<Bls12>,
note: Note,
merkle_path: MerklePath<Node>,
}
@ -243,22 +239,18 @@ pub fn create_to_address<P: AsRef<Path>>(
r.copy_from_slice(&d[..]);
Rseed::AfterZip212(r)
} else {
let tmp = FsRepr(
let r = jubjub::Fr::from_repr(
d[..]
.try_into()
.map_err(|_| Error(ErrorKind::InvalidNote))?,
);
let r = Fs::from_repr(tmp).ok_or(Error(ErrorKind::InvalidNote))?;
)
.ok_or(Error(ErrorKind::InvalidNote))?;
Rseed::BeforeZip212(r)
}
};
let from = extfvk
.fvk
.vk
.to_payment_address(diversifier, &JUBJUB)
.unwrap();
let note = from.create_note(note_value as u64, rseed, &JUBJUB).unwrap();
let from = extfvk.fvk.vk.to_payment_address(diversifier).unwrap();
let note = from.create_note(note_value as u64, rseed).unwrap();
let merkle_path = {
let d: Vec<_> = row.get(3)?;
@ -381,6 +373,7 @@ pub fn create_to_address<P: AsRef<Path>>(
#[cfg(test)]
mod tests {
use group::cofactor::CofactorGroup;
use rusqlite::Connection;
use tempfile::NamedTempFile;
use zcash_primitives::{
@ -390,7 +383,6 @@ mod tests {
prover::TxProver,
transaction::{components::Amount, Transaction},
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
JUBJUB,
};
use zcash_proofs::prover::LocalTxProver;
@ -827,7 +819,7 @@ mod tests {
&extfvk.fvk.ovk,
&output.cv,
&output.cmu,
&output.ephemeral_key.as_prime_order(&JUBJUB).unwrap(),
&output.ephemeral_key.into_subgroup().unwrap(),
&output.enc_ciphertext,
&output.out_ciphertext,
)

View File

@ -1,11 +1,8 @@
use criterion::{criterion_group, criterion_main, Criterion};
use pairing::bls12_381::Bls12;
use rand_core::{OsRng, RngCore};
use zcash_primitives::jubjub::JubjubBls12;
use zcash_primitives::pedersen_hash::{pedersen_hash, Personalization};
fn bench_pedersen_hash(c: &mut Criterion) {
let params = JubjubBls12::new();
let rng = &mut OsRng;
let bits = (0..510)
.map(|_| (rng.next_u32() % 2) != 0)
@ -13,7 +10,7 @@ fn bench_pedersen_hash(c: &mut Criterion) {
let personalization = Personalization::MerkleTree(31);
c.bench_function("Pedersen hash", |b| {
b.iter(|| pedersen_hash::<Bls12, _>(personalization, bits.clone(), &params))
b.iter(|| pedersen_hash(personalization, bits.clone()))
});
}

View File

@ -273,30 +273,16 @@ fn generate_pedersen_hash_exp_table() -> Vec<Vec<Vec<SubgroupPoint>>> {
#[cfg(test)]
mod tests {
use group::{cofactor::CofactorGroup, GroupEncoding};
use jubjub::{ExtendedPoint, SubgroupPoint};
use pairing::bls12_381::Bls12;
use jubjub::SubgroupPoint;
use super::*;
use crate::{
jubjub::{edwards, FixedGenerators, JubjubParams, PrimeOrder},
jubjub::{FixedGenerators, JubjubParams},
JUBJUB,
};
fn check_edwards(expected: &edwards::Point<Bls12, PrimeOrder>, actual: SubgroupPoint) {
// Check that the generator is indeed in the subgroup.
assert!(bool::from(
ExtendedPoint::from(actual).into_subgroup().is_some()
));
// Check that the generator is correctly derived.
let mut expected_bytes = [0; 32];
expected.write(&mut expected_bytes[..]).unwrap();
assert_eq!(expected_bytes[..], actual.to_bytes()[..]);
}
fn check_generator(expected: FixedGenerators, actual: SubgroupPoint) {
check_edwards(JUBJUB.generator(expected), actual)
assert_eq!(JUBJUB.generator(expected), &actual)
}
#[test]
@ -354,7 +340,7 @@ mod tests {
assert_eq!(expected.len(), actual.len());
for (expected, actual) in expected.iter().zip(actual.iter()) {
check_edwards(expected, *actual);
assert_eq!(expected, actual);
}
}
@ -373,7 +359,7 @@ mod tests {
assert_eq!(expected.len(), actual.len());
for (expected, actual) in expected.iter().zip(actual) {
// Same table points.
check_edwards(expected, *actual);
assert_eq!(expected, actual);
}
}
}

View File

@ -2,9 +2,8 @@
//!
//! [grouphash]: https://zips.z.cash/protocol/protocol.pdf#concretegrouphashjubjub
use crate::jubjub::{edwards, JubjubEngine, PrimeOrder};
use ff::PrimeField;
use group::{cofactor::CofactorGroup, Group, GroupEncoding};
use crate::constants;
use blake2s_simd::Params;
@ -12,15 +11,11 @@ use blake2s_simd::Params;
/// Produces a random point in the Jubjub curve.
/// The point is guaranteed to be prime order
/// and not the identity.
pub fn group_hash<E: JubjubEngine>(
tag: &[u8],
personalization: &[u8],
params: &E::Params,
) -> Option<edwards::Point<E, PrimeOrder>> {
pub fn group_hash(tag: &[u8], personalization: &[u8]) -> Option<jubjub::SubgroupPoint> {
assert_eq!(personalization.len(), 8);
// Check to see that scalar field is 255 bits
assert!(E::Fr::NUM_BITS == 255);
assert!(bls12_381::Scalar::NUM_BITS == 255);
let h = Params::new()
.hash_length(32)
@ -30,16 +25,18 @@ pub fn group_hash<E: JubjubEngine>(
.update(tag)
.finalize();
match edwards::Point::<E, _>::read(h.as_ref(), params) {
Ok(p) => {
let p = p.mul_by_cofactor(params);
let p = jubjub::ExtendedPoint::from_bytes(h.as_array());
if p.is_some().into() {
// <ExtendedPoint as CofactorGroup>::clear_cofactor is implemented using
// ExtendedPoint::mul_by_cofactor in the jubjub crate.
let p = CofactorGroup::clear_cofactor(&p.unwrap());
if p != edwards::Point::zero() {
Some(p)
} else {
None
}
if p.is_identity().into() {
None
} else {
Some(p)
}
Err(_) => None,
} else {
None
}
}

View File

@ -23,15 +23,14 @@
//! [Jubjub]: https://zips.z.cash/protocol/protocol.pdf#jubjub
//! [BLS12-381]: pairing::bls12_381
use ff::{Field, PrimeField};
use ff::PrimeField;
use group::{Curve, Group};
use pairing::Engine;
use crate::group_hash::group_hash;
use crate::constants;
use pairing::bls12_381::{Bls12, Fr};
/// This is an implementation of the twisted Edwards Jubjub curve.
pub mod edwards;
@ -95,7 +94,7 @@ pub trait ToUniform {
/// and some pre-computed parameters.
pub trait JubjubEngine: Engine {
/// The scalar field of the Jubjub curve
type Fs: PrimeField + ToUniform;
type Fs: PrimeField;
/// The parameters of Jubjub and the Sapling protocol
type Params: JubjubParams<Self>;
}
@ -112,20 +111,17 @@ pub trait JubjubParams<E: JubjubEngine>: Sized {
/// The scaling factor used for conversion from the Montgomery form.
fn scale(&self) -> &E::Fr;
/// Returns the generators (for each segment) used in all Pedersen commitments.
fn pedersen_hash_generators(&self) -> &[edwards::Point<E, PrimeOrder>];
fn pedersen_hash_generators(&self) -> &[jubjub::SubgroupPoint];
/// Returns the exp table for Pedersen hashes.
fn pedersen_hash_exp_table(&self) -> &[Vec<Vec<edwards::Point<E, PrimeOrder>>>];
fn pedersen_hash_exp_table(&self) -> &[Vec<Vec<jubjub::SubgroupPoint>>];
/// Returns the maximum number of chunks per segment of the Pedersen hash.
fn pedersen_hash_chunks_per_generator(&self) -> usize;
/// Returns the pre-computed window tables [-4, 3, 2, 1, 1, 2, 3, 4] of different
/// magnitudes of the Pedersen hash segment generators.
fn pedersen_circuit_generators(&self) -> &[Vec<Vec<(E::Fr, E::Fr)>>];
/// Returns the number of chunks needed to represent a full scalar during fixed-base
/// exponentiation.
fn fixed_base_chunks_per_generator(&self) -> usize;
/// Returns a fixed generator.
fn generator(&self, base: FixedGenerators) -> &edwards::Point<E, PrimeOrder>;
fn generator(&self, base: FixedGenerators) -> &jubjub::SubgroupPoint;
/// Returns a window table [0, 1, ..., 8] for different magnitudes of some
/// fixed generator.
fn circuit_generators(&self, _: FixedGenerators) -> &[Vec<(E::Fr, E::Fr)>];
@ -134,42 +130,41 @@ pub trait JubjubParams<E: JubjubEngine>: Sized {
fn pedersen_hash_exp_window_size() -> u32;
}
impl JubjubEngine for Bls12 {
type Fs = self::fs::Fs;
impl JubjubEngine for bls12_381::Bls12 {
type Fs = jubjub::Fr;
type Params = JubjubBls12;
}
pub struct JubjubBls12 {
edwards_d: Fr,
montgomery_a: Fr,
montgomery_2a: Fr,
scale: Fr,
edwards_d: bls12_381::Scalar,
montgomery_a: bls12_381::Scalar,
montgomery_2a: bls12_381::Scalar,
scale: bls12_381::Scalar,
pedersen_hash_generators: Vec<edwards::Point<Bls12, PrimeOrder>>,
pedersen_hash_exp: Vec<Vec<Vec<edwards::Point<Bls12, PrimeOrder>>>>,
pedersen_circuit_generators: Vec<Vec<Vec<(Fr, Fr)>>>,
pedersen_hash_generators: Vec<jubjub::SubgroupPoint>,
pedersen_hash_exp: Vec<Vec<Vec<jubjub::SubgroupPoint>>>,
fixed_base_generators: Vec<edwards::Point<Bls12, PrimeOrder>>,
fixed_base_circuit_generators: Vec<Vec<Vec<(Fr, Fr)>>>,
fixed_base_generators: Vec<jubjub::SubgroupPoint>,
fixed_base_circuit_generators: Vec<Vec<Vec<(bls12_381::Scalar, bls12_381::Scalar)>>>,
}
impl JubjubParams<Bls12> for JubjubBls12 {
fn edwards_d(&self) -> &Fr {
impl JubjubParams<bls12_381::Bls12> for JubjubBls12 {
fn edwards_d(&self) -> &bls12_381::Scalar {
&self.edwards_d
}
fn montgomery_a(&self) -> &Fr {
fn montgomery_a(&self) -> &bls12_381::Scalar {
&self.montgomery_a
}
fn montgomery_2a(&self) -> &Fr {
fn montgomery_2a(&self) -> &bls12_381::Scalar {
&self.montgomery_2a
}
fn scale(&self) -> &Fr {
fn scale(&self) -> &bls12_381::Scalar {
&self.scale
}
fn pedersen_hash_generators(&self) -> &[edwards::Point<Bls12, PrimeOrder>] {
fn pedersen_hash_generators(&self) -> &[jubjub::SubgroupPoint] {
&self.pedersen_hash_generators
}
fn pedersen_hash_exp_table(&self) -> &[Vec<Vec<edwards::Point<Bls12, PrimeOrder>>>] {
fn pedersen_hash_exp_table(&self) -> &[Vec<Vec<jubjub::SubgroupPoint>>] {
&self.pedersen_hash_exp
}
fn pedersen_hash_chunks_per_generator(&self) -> usize {
@ -178,13 +173,13 @@ impl JubjubParams<Bls12> for JubjubBls12 {
fn fixed_base_chunks_per_generator(&self) -> usize {
84
}
fn pedersen_circuit_generators(&self) -> &[Vec<Vec<(Fr, Fr)>>] {
&self.pedersen_circuit_generators
}
fn generator(&self, base: FixedGenerators) -> &edwards::Point<Bls12, PrimeOrder> {
fn generator(&self, base: FixedGenerators) -> &jubjub::SubgroupPoint {
&self.fixed_base_generators[base as usize]
}
fn circuit_generators(&self, base: FixedGenerators) -> &[Vec<(Fr, Fr)>] {
fn circuit_generators(
&self,
base: FixedGenerators,
) -> &[Vec<(bls12_381::Scalar, bls12_381::Scalar)>] {
&self.fixed_base_circuit_generators[base as usize][..]
}
fn pedersen_hash_exp_window_size() -> u32 {
@ -194,12 +189,12 @@ impl JubjubParams<Bls12> for JubjubBls12 {
impl JubjubBls12 {
pub fn new() -> Self {
let montgomery_a = Fr::from_str("40962").unwrap();
let montgomery_a = bls12_381::Scalar::from_str("40962").unwrap();
let montgomery_2a = montgomery_a.double();
let mut tmp_params = JubjubBls12 {
// d = -(10240/10241)
edwards_d: Fr::from_str(
edwards_d: bls12_381::Scalar::from_str(
"19257038036680949359750312669786877991949435402254120286184196891950884077233",
)
.unwrap(),
@ -208,7 +203,7 @@ impl JubjubBls12 {
// 2A = 2.A
montgomery_2a,
// scaling factor = sqrt(4 / (a - d))
scale: Fr::from_str(
scale: bls12_381::Scalar::from_str(
"17814886934372412843466061268024708274627479829237077604635722030778476050649",
)
.unwrap(),
@ -216,7 +211,6 @@ impl JubjubBls12 {
// We'll initialize these below
pedersen_hash_generators: vec![],
pedersen_hash_exp: vec![],
pedersen_circuit_generators: vec![],
fixed_base_generators: vec![],
fixed_base_circuit_generators: vec![],
};
@ -236,14 +230,10 @@ impl JubjubBls12 {
pedersen_hash_generators.push(JubjubBls12::find_group_hash(
&segment_number,
constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION,
&tmp_params,
));
}
JubjubBls12::check_consistency_of_pedersen_hash_generators(
&tmp_params,
&pedersen_hash_generators,
);
JubjubBls12::check_consistency_of_pedersen_hash_generators(&pedersen_hash_generators);
tmp_params.pedersen_hash_generators = pedersen_hash_generators;
}
@ -259,21 +249,21 @@ impl JubjubBls12 {
let mut tables = vec![];
let mut num_bits = 0;
while num_bits <= fs::Fs::NUM_BITS {
while num_bits <= jubjub::Fr::NUM_BITS {
let mut table = Vec::with_capacity(1 << window);
let mut base = edwards::Point::zero();
let mut base = jubjub::SubgroupPoint::identity();
for _ in 0..(1 << window) {
table.push(base.clone());
base = base.add(&g, &tmp_params);
base += g;
}
tables.push(table);
num_bits += window;
for _ in 0..window {
g = g.double(&tmp_params);
g = g.double();
}
}
@ -286,53 +276,47 @@ impl JubjubBls12 {
// Create the bases for other parts of the protocol
{
let mut fixed_base_generators =
vec![edwards::Point::zero(); FixedGenerators::Max as usize];
vec![jubjub::SubgroupPoint::identity(); FixedGenerators::Max as usize];
fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] =
JubjubBls12::find_group_hash(
&[],
constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::NoteCommitmentRandomness as usize] =
JubjubBls12::find_group_hash(
b"r",
constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::NullifierPosition as usize] =
JubjubBls12::find_group_hash(
&[],
constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] =
JubjubBls12::find_group_hash(
b"v",
constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::ValueCommitmentRandomness as usize] =
JubjubBls12::find_group_hash(
b"r",
constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] =
JubjubBls12::find_group_hash(
&[],
constants::SPENDING_KEY_GENERATOR_PERSONALIZATION,
&tmp_params,
);
// Check for duplicates, far worse than spec inconsistencies!
for (i, p1) in fixed_base_generators.iter().enumerate() {
if p1 == &edwards::Point::zero() {
if p1.is_identity().into() {
panic!("Neutral element!");
}
@ -346,38 +330,6 @@ impl JubjubBls12 {
tmp_params.fixed_base_generators = fixed_base_generators;
}
// Create the 2-bit window table lookups for each 4-bit
// "chunk" in each segment of the Pedersen hash
{
let mut pedersen_circuit_generators = vec![];
// Process each segment
for gen in tmp_params.pedersen_hash_generators.iter().cloned() {
let mut gen = montgomery::Point::from_edwards(&gen, &tmp_params);
let mut windows = vec![];
for _ in 0..tmp_params.pedersen_hash_chunks_per_generator() {
// Create (x, y) coeffs for this chunk
let mut coeffs = vec![];
let mut g = gen.clone();
// coeffs = g, g*2, g*3, g*4
for _ in 0..4 {
coeffs.push(g.to_xy().expect("cannot produce O"));
g = g.add(&gen, &tmp_params);
}
windows.push(coeffs);
// Our chunks are separated by 2 bits to prevent overlap.
for _ in 0..4 {
gen = gen.double(&tmp_params);
}
}
pedersen_circuit_generators.push(windows);
}
tmp_params.pedersen_circuit_generators = pedersen_circuit_generators;
}
// Create the 3-bit window table lookups for fixed-base
// exp of each base in the protocol.
{
@ -386,11 +338,12 @@ impl JubjubBls12 {
for mut gen in tmp_params.fixed_base_generators.iter().cloned() {
let mut windows = vec![];
for _ in 0..tmp_params.fixed_base_chunks_per_generator() {
let mut coeffs = vec![(Fr::zero(), Fr::one())];
let mut coeffs = vec![(bls12_381::Scalar::zero(), bls12_381::Scalar::one())];
let mut g = gen.clone();
for _ in 0..7 {
coeffs.push(g.to_xy());
g = g.add(&gen, &tmp_params);
let g_affine = jubjub::ExtendedPoint::from(g).to_affine();
coeffs.push((g_affine.get_u(), g_affine.get_v()));
g += gen;
}
windows.push(coeffs);
@ -406,17 +359,13 @@ impl JubjubBls12 {
tmp_params
}
fn find_group_hash<E: JubjubEngine>(
m: &[u8],
personalization: &[u8; 8],
params: &E::Params,
) -> edwards::Point<E, PrimeOrder> {
fn find_group_hash(m: &[u8], personalization: &[u8; 8]) -> jubjub::SubgroupPoint {
let mut tag = m.to_vec();
let i = tag.len();
tag.push(0u8);
loop {
let gh = group_hash(&tag, personalization, params);
let gh = group_hash(&tag, personalization);
// We don't want to overflow and start reusing generators
assert!(tag[i] != u8::max_value());
@ -430,19 +379,18 @@ impl JubjubBls12 {
/// Check for simple relations between the generators, that make finding collisions easy;
/// far worse than spec inconsistencies!
fn check_consistency_of_pedersen_hash_generators<E: JubjubEngine>(
tmp_params: &E::Params,
pedersen_hash_generators: &[edwards::Point<E, PrimeOrder>],
fn check_consistency_of_pedersen_hash_generators(
pedersen_hash_generators: &[jubjub::SubgroupPoint],
) {
for (i, p1) in pedersen_hash_generators.iter().enumerate() {
if p1 == &edwards::Point::zero() {
if p1.is_identity().into() {
panic!("Neutral element!");
}
for p2 in pedersen_hash_generators.iter().skip(i + 1) {
if p1 == p2 {
panic!("Duplicate generator!");
}
if p1 == &p2.negate() {
if *p1 == -p2 {
panic!("Inverse generator!");
}
}
@ -456,8 +404,8 @@ impl JubjubBls12 {
if k == j || k == i {
continue;
}
let sum = &p2.add(&p3, &tmp_params);
if sum == p1 {
let sum = p2 + p3;
if sum == *p1 {
panic!("Linear relation between generators!");
}
}
@ -468,6 +416,7 @@ impl JubjubBls12 {
#[test]
fn test_jubjub_bls12() {
use bls12_381::Bls12;
use hex_literal::hex;
let params = JubjubBls12::new();
@ -477,7 +426,7 @@ fn test_jubjub_bls12() {
let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139d31");
let p = edwards::Point::<Bls12, _>::read(&test_repr[..], &params).unwrap();
let q = edwards::Point::<Bls12, _>::get_for_y(
Fr::from_str(
bls12_381::Scalar::from_str(
"22440861827555040311190986994816762244378363690614952020532787748720529117853",
)
.unwrap(),
@ -492,7 +441,7 @@ fn test_jubjub_bls12() {
let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139db1");
let p = edwards::Point::<Bls12, _>::read(&test_repr[..], &params).unwrap();
let q = edwards::Point::<Bls12, _>::get_for_y(
Fr::from_str(
bls12_381::Scalar::from_str(
"22440861827555040311190986994816762244378363690614952020532787748720529117853",
)
.unwrap(),
@ -507,9 +456,7 @@ fn test_jubjub_bls12() {
#[test]
#[should_panic(expected = "Linear relation between generators!")]
fn test_jubjub_bls12_pedersen_hash_generators_consistency_check_linear_relation() {
let params = JubjubBls12::new();
let mut pedersen_hash_generators: Vec<edwards::Point<Bls12, PrimeOrder>> = vec![];
let mut pedersen_hash_generators: Vec<jubjub::SubgroupPoint> = vec![];
use byteorder::{LittleEndian, WriteBytesExt};
@ -522,7 +469,6 @@ fn test_jubjub_bls12_pedersen_hash_generators_consistency_check_linear_relation(
let p = JubjubBls12::find_group_hash(
&segment_number,
constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION,
&params,
);
pedersen_hash_generators.push(p);
}
@ -531,7 +477,7 @@ fn test_jubjub_bls12_pedersen_hash_generators_consistency_check_linear_relation(
let p2 = pedersen_hash_generators[1].clone();
//test for linear relation
pedersen_hash_generators.push(p1.add(&p2, &params));
pedersen_hash_generators.push(p1 + p2);
JubjubBls12::check_consistency_of_pedersen_hash_generators(&params, &pedersen_hash_generators);
JubjubBls12::check_consistency_of_pedersen_hash_generators(&pedersen_hash_generators);
}

View File

@ -5,12 +5,14 @@
//! [section 4.2.2]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
use crate::{
jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, ToUniform, Unknown},
constants::{PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR},
primitives::{ProofGenerationKey, ViewingKey},
};
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
use ff::PrimeField;
use group::{Group, GroupEncoding};
use std::io::{self, Read, Write};
use subtle::CtOption;
pub const PRF_EXPAND_PERSONALIZATION: &[u8; 16] = b"Zcash_ExpandSeed";
@ -37,47 +39,45 @@ pub struct OutgoingViewingKey(pub [u8; 32]);
/// A Sapling expanded spending key
#[derive(Clone)]
pub struct ExpandedSpendingKey<E: JubjubEngine> {
pub ask: E::Fs,
pub nsk: E::Fs,
pub struct ExpandedSpendingKey {
pub ask: jubjub::Fr,
pub nsk: jubjub::Fr,
pub ovk: OutgoingViewingKey,
}
/// A Sapling full viewing key
#[derive(Debug)]
pub struct FullViewingKey<E: JubjubEngine> {
pub vk: ViewingKey<E>,
pub struct FullViewingKey {
pub vk: ViewingKey,
pub ovk: OutgoingViewingKey,
}
impl<E: JubjubEngine> ExpandedSpendingKey<E> {
impl ExpandedSpendingKey {
pub fn from_spending_key(sk: &[u8]) -> Self {
let ask = E::Fs::to_uniform(prf_expand(sk, &[0x00]).as_bytes());
let nsk = E::Fs::to_uniform(prf_expand(sk, &[0x01]).as_bytes());
let ask = jubjub::Fr::from_bytes_wide(prf_expand(sk, &[0x00]).as_array());
let nsk = jubjub::Fr::from_bytes_wide(prf_expand(sk, &[0x01]).as_array());
let mut ovk = OutgoingViewingKey([0u8; 32]);
ovk.0
.copy_from_slice(&prf_expand(sk, &[0x02]).as_bytes()[..32]);
ExpandedSpendingKey { ask, nsk, ovk }
}
pub fn proof_generation_key(&self, params: &E::Params) -> ProofGenerationKey<E> {
pub fn proof_generation_key(&self) -> ProofGenerationKey {
ProofGenerationKey {
ak: params
.generator(FixedGenerators::SpendingKeyGenerator)
.mul(self.ask, params),
ak: SPENDING_KEY_GENERATOR * self.ask,
nsk: self.nsk,
}
}
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let mut ask_repr = <E::Fs as PrimeField>::Repr::default();
let mut ask_repr = [0; 32];
reader.read_exact(ask_repr.as_mut())?;
let ask = E::Fs::from_repr(ask_repr)
let ask = jubjub::Fr::from_repr(ask_repr)
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "ask not in field"))?;
let mut nsk_repr = <E::Fs as PrimeField>::Repr::default();
let mut nsk_repr = [0; 32];
reader.read_exact(nsk_repr.as_mut())?;
let nsk = E::Fs::from_repr(nsk_repr)
let nsk = jubjub::Fr::from_repr(nsk_repr)
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "nsk not in field"))?;
let mut ovk = [0; 32];
@ -106,7 +106,7 @@ impl<E: JubjubEngine> ExpandedSpendingKey<E> {
}
}
impl<E: JubjubEngine> Clone for FullViewingKey<E> {
impl Clone for FullViewingKey {
fn clone(&self) -> Self {
FullViewingKey {
vk: ViewingKey {
@ -118,49 +118,42 @@ impl<E: JubjubEngine> Clone for FullViewingKey<E> {
}
}
impl<E: JubjubEngine> FullViewingKey<E> {
pub fn from_expanded_spending_key(expsk: &ExpandedSpendingKey<E>, params: &E::Params) -> Self {
impl FullViewingKey {
pub fn from_expanded_spending_key(expsk: &ExpandedSpendingKey) -> Self {
FullViewingKey {
vk: ViewingKey {
ak: params
.generator(FixedGenerators::SpendingKeyGenerator)
.mul(expsk.ask, params),
nk: params
.generator(FixedGenerators::ProofGenerationKey)
.mul(expsk.nsk, params),
ak: SPENDING_KEY_GENERATOR * expsk.ask,
nk: PROOF_GENERATION_KEY_GENERATOR * expsk.nsk,
},
ovk: expsk.ovk,
}
}
pub fn read<R: Read>(mut reader: R, params: &E::Params) -> io::Result<Self> {
let ak = edwards::Point::<E, Unknown>::read(&mut reader, params)?;
let ak = match ak.as_prime_order(params) {
Some(p) => p,
None => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"ak not in prime-order subgroup",
));
}
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let ak = {
let mut buf = [0; 32];
reader.read_exact(&mut buf)?;
jubjub::SubgroupPoint::from_bytes(&buf).and_then(|p| CtOption::new(p, !p.is_identity()))
};
if ak == edwards::Point::zero() {
let nk = {
let mut buf = [0; 32];
reader.read_exact(&mut buf)?;
jubjub::SubgroupPoint::from_bytes(&buf)
};
if ak.is_none().into() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
io::ErrorKind::InvalidInput,
"ak not of prime order",
));
}
let nk = edwards::Point::<E, Unknown>::read(&mut reader, params)?;
let nk = match nk.as_prime_order(params) {
Some(p) => p,
None => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"nk not in prime-order subgroup",
));
}
};
if nk.is_none().into() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"nk not in prime-order subgroup",
));
}
let ak = ak.unwrap();
let nk = nk.unwrap();
let mut ovk = [0; 32];
reader.read_exact(&mut ovk)?;
@ -172,8 +165,8 @@ impl<E: JubjubEngine> FullViewingKey<E> {
}
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
self.vk.ak.write(&mut writer)?;
self.vk.nk.write(&mut writer)?;
writer.write_all(&self.vk.ak.to_bytes())?;
writer.write_all(&self.vk.nk.to_bytes())?;
writer.write_all(&self.ovk.0)?;
Ok(())
@ -189,35 +182,31 @@ impl<E: JubjubEngine> FullViewingKey<E> {
#[cfg(test)]
mod tests {
use crate::jubjub::{edwards, FixedGenerators, JubjubParams, PrimeOrder};
use pairing::bls12_381::Bls12;
use std::error::Error;
use group::{Group, GroupEncoding};
use super::FullViewingKey;
use crate::JUBJUB;
use crate::constants::SPENDING_KEY_GENERATOR;
#[test]
fn ak_must_be_prime_order() {
let mut buf = [0; 96];
let identity = edwards::Point::<Bls12, PrimeOrder>::zero();
let identity = jubjub::SubgroupPoint::identity();
// Set both ak and nk to the identity.
identity.write(&mut buf[0..32]).unwrap();
identity.write(&mut buf[32..64]).unwrap();
buf[0..32].copy_from_slice(&identity.to_bytes());
buf[32..64].copy_from_slice(&identity.to_bytes());
// ak is not allowed to be the identity.
assert_eq!(
FullViewingKey::<Bls12>::read(&buf[..], &JUBJUB)
.unwrap_err()
.description(),
FullViewingKey::read(&buf[..]).unwrap_err().to_string(),
"ak not of prime order"
);
// Set ak to a basepoint.
let basepoint = JUBJUB.generator(FixedGenerators::SpendingKeyGenerator);
basepoint.write(&mut buf[0..32]).unwrap();
let basepoint = SPENDING_KEY_GENERATOR;
buf[0..32].copy_from_slice(&basepoint.to_bytes());
// nk is allowed to be the identity.
assert!(FullViewingKey::<Bls12>::read(&buf[..], &JUBJUB).is_ok());
assert!(FullViewingKey::read(&buf[..]).is_ok());
}
}

View File

@ -194,13 +194,7 @@ impl<Node: Hashable> CommitmentTree<Node> {
/// # Examples
///
/// ```
/// extern crate ff;
/// extern crate pairing;
/// extern crate rand_core;
/// extern crate zcash_primitives;
///
/// use ff::{Field, PrimeField};
/// use pairing::bls12_381::Fr;
/// use rand_core::OsRng;
/// use zcash_primitives::{
/// merkle_tree::{CommitmentTree, IncrementalWitness},
@ -211,13 +205,13 @@ impl<Node: Hashable> CommitmentTree<Node> {
///
/// let mut tree = CommitmentTree::<Node>::new();
///
/// tree.append(Node::new(Fr::random(&mut rng).to_repr()));
/// tree.append(Node::new(Fr::random(&mut rng).to_repr()));
/// tree.append(Node::new(bls12_381::Scalar::random(&mut rng).to_repr()));
/// tree.append(Node::new(bls12_381::Scalar::random(&mut rng).to_repr()));
/// let mut witness = IncrementalWitness::from_tree(&tree);
/// assert_eq!(witness.position(), 1);
/// assert_eq!(tree.root(), witness.root());
///
/// let cmu = Node::new(Fr::random(&mut rng).to_repr());
/// let cmu = Node::new(bls12_381::Scalar::random(&mut rng).to_repr());
/// tree.append(cmu);
/// witness.append(cmu);
/// assert_eq!(tree.root(), witness.root());
@ -512,7 +506,6 @@ mod tests {
use crate::sapling::Node;
use hex;
use pairing::bls12_381::FrRepr;
use std::convert::TryInto;
use std::io::{self, Read, Write};
@ -1016,9 +1009,9 @@ mod tests {
let mut paths_i = 0;
let mut witness_ser_i = 0;
for i in 0..16 {
let cm = FrRepr(hex::decode(commitments[i]).unwrap()[..].try_into().unwrap());
let cm = hex::decode(commitments[i]).unwrap();
let cm = Node::new(cm);
let cm = Node::new(cm[..].try_into().unwrap());
// Witness here
witnesses.push((TestIncrementalWitness::from_tree(&tree), last_cm));

View File

@ -1,26 +1,20 @@
//! Implementation of in-band secret distribution for Zcash transactions.
use crate::{
consensus,
consensus::{NetworkUpgrade, ZIP212_GRACE_PERIOD},
jubjub::{
edwards,
fs::{Fs, FsRepr},
PrimeOrder, Unknown,
},
consensus::{self, NetworkUpgrade, ZIP212_GRACE_PERIOD},
primitives::{Diversifier, Note, PaymentAddress, Rseed},
};
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crypto_api_chachapoly::{ChaCha20Ietf, ChachaPolyIetf};
use ff::PrimeField;
use pairing::bls12_381::{Bls12, Fr};
use group::{cofactor::CofactorGroup, GroupEncoding};
use rand_core::{CryptoRng, RngCore};
use std::convert::TryInto;
use std::fmt;
use std::str;
use crate::{keys::OutgoingViewingKey, JUBJUB};
use crate::keys::OutgoingViewingKey;
pub const KDF_SAPLING_PERSONALIZATION: &[u8; 16] = b"Zcash_SaplingKDF";
pub const PRF_OCK_PERSONALIZATION: &[u8; 16] = b"Zcash_Derive_ock";
@ -138,34 +132,24 @@ impl str::FromStr for Memo {
/// Sapling key agreement for note encryption.
///
/// Implements section 5.4.4.3 of the Zcash Protocol Specification.
pub fn sapling_ka_agree<'a, P>(esk: &Fs, pk_d: &'a P) -> edwards::Point<Bls12, PrimeOrder>
where
edwards::Point<Bls12, Unknown>: From<&'a P>,
{
let p: edwards::Point<Bls12, Unknown> = pk_d.into();
// Multiply by 8
let p = p.mul_by_cofactor(&JUBJUB);
// Multiply by esk
p.mul(*esk, &JUBJUB)
pub fn sapling_ka_agree(esk: &jubjub::Fr, pk_d: &jubjub::ExtendedPoint) -> jubjub::SubgroupPoint {
// [8 esk] pk_d
// <ExtendedPoint as CofactorGroup>::clear_cofactor is implemented using
// ExtendedPoint::mul_by_cofactor in the jubjub crate.
CofactorGroup::clear_cofactor(&(pk_d * esk))
}
/// Sapling KDF for note encryption.
///
/// Implements section 5.4.4.4 of the Zcash Protocol Specification.
fn kdf_sapling(
dhsecret: edwards::Point<Bls12, PrimeOrder>,
epk: &edwards::Point<Bls12, PrimeOrder>,
) -> Blake2bHash {
let mut input = [0u8; 64];
dhsecret.write(&mut input[0..32]).unwrap();
epk.write(&mut input[32..64]).unwrap();
fn kdf_sapling(dhsecret: jubjub::SubgroupPoint, epk: &jubjub::SubgroupPoint) -> Blake2bHash {
Blake2bParams::new()
.hash_length(32)
.personal(KDF_SAPLING_PERSONALIZATION)
.hash(&input)
.to_state()
.update(&dhsecret.to_bytes())
.update(&epk.to_bytes())
.finalize()
}
/// Sapling PRF^ock.
@ -173,20 +157,19 @@ fn kdf_sapling(
/// Implemented per section 5.4.2 of the Zcash Protocol Specification.
pub fn prf_ock(
ovk: &OutgoingViewingKey,
cv: &edwards::Point<Bls12, Unknown>,
cmu: &Fr,
epk: &edwards::Point<Bls12, PrimeOrder>,
cv: &jubjub::ExtendedPoint,
cmu: &bls12_381::Scalar,
epk: &jubjub::SubgroupPoint,
) -> Blake2bHash {
let mut ock_input = [0u8; 128];
ock_input[0..32].copy_from_slice(&ovk.0);
cv.write(&mut ock_input[32..64]).unwrap();
ock_input[64..96].copy_from_slice(cmu.to_repr().as_ref());
epk.write(&mut ock_input[96..128]).unwrap();
Blake2bParams::new()
.hash_length(32)
.personal(PRF_OCK_PERSONALIZATION)
.hash(&ock_input)
.to_state()
.update(&ovk.0)
.update(&cv.to_bytes())
.update(&cmu.to_repr())
.update(&epk.to_bytes())
.finalize()
}
/// An API for encrypting Sapling notes.
@ -207,42 +190,39 @@ pub fn prf_ock(
/// extern crate zcash_primitives;
///
/// use ff::Field;
/// use pairing::bls12_381::Bls12;
/// use rand_core::OsRng;
/// use zcash_primitives::{
/// jubjub::fs::Fs,
/// keys::{OutgoingViewingKey, prf_expand},
/// note_encryption::{Memo, SaplingNoteEncryption},
/// primitives::{Diversifier, PaymentAddress, Rseed, ValueCommitment},
/// JUBJUB,
/// };
///
/// let mut rng = OsRng;
///
/// let diversifier = Diversifier([0; 11]);
/// let pk_d = diversifier.g_d::<Bls12>(&JUBJUB).unwrap();
/// let pk_d = diversifier.g_d().unwrap();
/// let to = PaymentAddress::from_parts(diversifier, pk_d).unwrap();
/// let ovk = OutgoingViewingKey([0; 32]);
///
/// let value = 1000;
/// let rcv = Fs::random(&mut rng);
/// let cv = ValueCommitment::<Bls12> {
/// let rcv = jubjub::Fr::random(&mut rng);
/// let cv = ValueCommitment {
/// value,
/// randomness: rcv.clone(),
/// };
/// let rcm = Fs::random(&mut rng);
/// let note = to.create_note(value, Rseed::BeforeZip212(rcm), &JUBJUB).unwrap();
/// let cmu = note.cm(&JUBJUB);
/// let rcm = jubjub::Fr::random(&mut rng);
/// let note = to.create_note(value, Rseed::BeforeZip212(rcm)).unwrap();
/// let cmu = note.cm();
///
/// let enc = SaplingNoteEncryption::new(ovk, note, to, Memo::default(), &mut rng);
/// let encCiphertext = enc.encrypt_note_plaintext();
/// let outCiphertext = enc.encrypt_outgoing_plaintext(&cv.cm(&JUBJUB).into(), &cmu);
/// let outCiphertext = enc.encrypt_outgoing_plaintext(&cv.cm().into(), &cmu);
/// ```
pub struct SaplingNoteEncryption {
epk: edwards::Point<Bls12, PrimeOrder>,
esk: Fs,
note: Note<Bls12>,
to: PaymentAddress<Bls12>,
epk: jubjub::SubgroupPoint,
esk: jubjub::Fr,
note: Note,
to: PaymentAddress,
memo: Memo,
ovk: OutgoingViewingKey,
}
@ -251,13 +231,13 @@ impl SaplingNoteEncryption {
/// Creates a new encryption context for the given note.
pub fn new<R: RngCore + CryptoRng>(
ovk: OutgoingViewingKey,
note: Note<Bls12>,
to: PaymentAddress<Bls12>,
note: Note,
to: PaymentAddress,
memo: Memo,
rng: &mut R,
) -> SaplingNoteEncryption {
let esk = note.generate_or_derive_esk(rng);
let epk = note.g_d.mul(esk, &JUBJUB);
let epk = note.g_d * esk;
SaplingNoteEncryption {
epk,
@ -270,18 +250,18 @@ impl SaplingNoteEncryption {
}
/// Exposes the ephemeral secret key being used to encrypt this note.
pub fn esk(&self) -> &Fs {
pub fn esk(&self) -> &jubjub::Fr {
&self.esk
}
/// Exposes the ephemeral public key being used to encrypt this note.
pub fn epk(&self) -> &edwards::Point<Bls12, PrimeOrder> {
pub fn epk(&self) -> &jubjub::SubgroupPoint {
&self.epk
}
/// Generates `encCiphertext` for this note.
pub fn encrypt_note_plaintext(&self) -> [u8; ENC_CIPHERTEXT_SIZE] {
let shared_secret = sapling_ka_agree(&self.esk, self.to.pk_d());
let shared_secret = sapling_ka_agree(&self.esk, self.to.pk_d().into());
let key = kdf_sapling(shared_secret, &self.epk);
// Note plaintext encoding is defined in section 5.5 of the Zcash Protocol
@ -319,13 +299,13 @@ impl SaplingNoteEncryption {
/// Generates `outCiphertext` for this note.
pub fn encrypt_outgoing_plaintext(
&self,
cv: &edwards::Point<Bls12, Unknown>,
cmu: &Fr,
cv: &jubjub::ExtendedPoint,
cmu: &bls12_381::Scalar,
) -> [u8; OUT_CIPHERTEXT_SIZE] {
let key = prf_ock(&self.ovk, &cv, &cmu, &self.epk);
let mut input = [0u8; OUT_PLAINTEXT_SIZE];
self.note.pk_d.write(&mut input[0..32]).unwrap();
input[0..32].copy_from_slice(&self.note.pk_d.to_bytes());
input[32..OUT_PLAINTEXT_SIZE].copy_from_slice(self.esk.to_repr().as_ref());
let mut output = [0u8; OUT_CIPHERTEXT_SIZE];
@ -342,11 +322,11 @@ impl SaplingNoteEncryption {
fn parse_note_plaintext_without_memo<P: consensus::Parameters>(
height: u32,
ivk: &Fs,
epk: &edwards::Point<Bls12, PrimeOrder>,
cmu: &Fr,
ivk: &jubjub::Fr,
epk: &jubjub::SubgroupPoint,
cmu: &bls12_381::Scalar,
plaintext: &[u8],
) -> Option<(Note<Bls12>, PaymentAddress<Bls12>)> {
) -> Option<(Note, PaymentAddress)> {
// Check note plaintext version
if !plaintext_version_is_valid::<P>(height, plaintext[0]) {
return None;
@ -362,27 +342,25 @@ fn parse_note_plaintext_without_memo<P: consensus::Parameters>(
.expect("slice is the correct length");
let rseed = if plaintext[0] == 0x01 {
let rcm = Fs::from_repr(FsRepr(r))?;
let rcm = jubjub::Fr::from_repr(r)?;
Rseed::BeforeZip212(rcm)
} else {
Rseed::AfterZip212(r)
};
let diversifier = Diversifier(d);
let pk_d = diversifier
.g_d::<Bls12>(&JUBJUB)?
.mul(ivk.to_repr(), &JUBJUB);
let pk_d = diversifier.g_d()? * ivk;
let to = PaymentAddress::from_parts(diversifier, pk_d)?;
let note = to.create_note(v, rseed, &JUBJUB).unwrap();
let note = to.create_note(v, rseed).unwrap();
if note.cm(&JUBJUB) != *cmu {
if note.cm() != *cmu {
// Published commitment doesn't match calculated commitment
return None;
}
if let Some(derived_esk) = note.derive_esk() {
if note.g_d.mul(derived_esk, &JUBJUB) != *epk {
if (note.g_d * derived_esk) != *epk {
return None;
}
}
@ -420,14 +398,14 @@ pub fn plaintext_version_is_valid<P: consensus::Parameters>(height: u32, leadbyt
/// Implements section 4.17.2 of the Zcash Protocol Specification.
pub fn try_sapling_note_decryption<P: consensus::Parameters>(
height: u32,
ivk: &Fs,
epk: &edwards::Point<Bls12, PrimeOrder>,
cmu: &Fr,
ivk: &jubjub::Fr,
epk: &jubjub::SubgroupPoint,
cmu: &bls12_381::Scalar,
enc_ciphertext: &[u8],
) -> Option<(Note<Bls12>, PaymentAddress<Bls12>, Memo)> {
) -> Option<(Note, PaymentAddress, Memo)> {
assert_eq!(enc_ciphertext.len(), ENC_CIPHERTEXT_SIZE);
let shared_secret = sapling_ka_agree(ivk, epk);
let shared_secret = sapling_ka_agree(ivk, epk.into());
let key = kdf_sapling(shared_secret, &epk);
let mut plaintext = [0; ENC_CIPHERTEXT_SIZE];
@ -463,14 +441,14 @@ pub fn try_sapling_note_decryption<P: consensus::Parameters>(
/// [`ZIP 307`]: https://zips.z.cash/zip-0307
pub fn try_sapling_compact_note_decryption<P: consensus::Parameters>(
height: u32,
ivk: &Fs,
epk: &edwards::Point<Bls12, PrimeOrder>,
cmu: &Fr,
ivk: &jubjub::Fr,
epk: &jubjub::SubgroupPoint,
cmu: &bls12_381::Scalar,
enc_ciphertext: &[u8],
) -> Option<(Note<Bls12>, PaymentAddress<Bls12>)> {
) -> Option<(Note, PaymentAddress)> {
assert_eq!(enc_ciphertext.len(), COMPACT_NOTE_SIZE);
let shared_secret = sapling_ka_agree(ivk, epk);
let shared_secret = sapling_ka_agree(ivk, epk.into());
let key = kdf_sapling(shared_secret, &epk);
// Start from block 1 to skip over Poly1305 keying output
@ -492,11 +470,11 @@ pub fn try_sapling_compact_note_decryption<P: consensus::Parameters>(
pub fn try_sapling_output_recovery_with_ock<P: consensus::Parameters>(
height: u32,
ock: &[u8],
cmu: &Fr,
epk: &edwards::Point<Bls12, PrimeOrder>,
cmu: &bls12_381::Scalar,
epk: &jubjub::SubgroupPoint,
enc_ciphertext: &[u8],
out_ciphertext: &[u8],
) -> Option<(Note<Bls12>, PaymentAddress<Bls12>, Memo)> {
) -> Option<(Note, PaymentAddress, Memo)> {
assert_eq!(enc_ciphertext.len(), ENC_CIPHERTEXT_SIZE);
assert_eq!(out_ciphertext.len(), OUT_CIPHERTEXT_SIZE);
@ -508,17 +486,23 @@ pub fn try_sapling_output_recovery_with_ock<P: consensus::Parameters>(
OUT_PLAINTEXT_SIZE
);
let pk_d = edwards::Point::<Bls12, _>::read(&op[0..32], &JUBJUB)
.ok()?
.as_prime_order(&JUBJUB)?;
let pk_d = {
let pk_d = jubjub::SubgroupPoint::from_bytes(
op[0..32].try_into().expect("slice is the correct length"),
);
if pk_d.is_none().into() {
return None;
}
pk_d.unwrap()
};
let esk = Fs::from_repr(FsRepr(
let esk = jubjub::Fr::from_repr(
op[32..OUT_PLAINTEXT_SIZE]
.try_into()
.expect("slice is the correct length"),
))?;
)?;
let shared_secret = sapling_ka_agree(&esk, &pk_d);
let shared_secret = sapling_ka_agree(&esk, &pk_d.into());
let key = kdf_sapling(shared_secret, &epk);
let mut plaintext = [0; ENC_CIPHERTEXT_SIZE];
@ -550,7 +534,7 @@ pub fn try_sapling_output_recovery_with_ock<P: consensus::Parameters>(
.expect("slice is the correct length");
let rseed = if plaintext[0] == 0x01 {
let rcm = Fs::from_repr(FsRepr(r))?;
let rcm = jubjub::Fr::from_repr(r)?;
Rseed::BeforeZip212(rcm)
} else {
Rseed::AfterZip212(r)
@ -560,19 +544,15 @@ pub fn try_sapling_output_recovery_with_ock<P: consensus::Parameters>(
memo.copy_from_slice(&plaintext[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE]);
let diversifier = Diversifier(d);
if diversifier
.g_d::<Bls12>(&JUBJUB)?
.mul(esk.to_repr(), &JUBJUB)
!= *epk
{
if diversifier.g_d()? * esk != *epk {
// Published epk doesn't match calculated epk
return None;
}
let to = PaymentAddress::from_parts(diversifier, pk_d)?;
let note = to.create_note(v, rseed, &JUBJUB).unwrap();
let note = to.create_note(v, rseed).unwrap();
if note.cm(&JUBJUB) != *cmu {
if note.cm() != *cmu {
// Published commitment doesn't match calculated commitment
return None;
}
@ -596,12 +576,12 @@ pub fn try_sapling_output_recovery_with_ock<P: consensus::Parameters>(
pub fn try_sapling_output_recovery<P: consensus::Parameters>(
height: u32,
ovk: &OutgoingViewingKey,
cv: &edwards::Point<Bls12, Unknown>,
cmu: &Fr,
epk: &edwards::Point<Bls12, PrimeOrder>,
cv: &jubjub::ExtendedPoint,
cmu: &bls12_381::Scalar,
epk: &jubjub::SubgroupPoint,
enc_ciphertext: &[u8],
out_ciphertext: &[u8],
) -> Option<(Note<Bls12>, PaymentAddress<Bls12>, Memo)> {
) -> Option<(Note, PaymentAddress, Memo)> {
try_sapling_output_recovery_with_ock::<P>(
height,
prf_ock(&ovk, &cv, &cmu, &epk).as_bytes(),
@ -614,24 +594,11 @@ pub fn try_sapling_output_recovery<P: consensus::Parameters>(
#[cfg(test)]
mod tests {
use crate::{
consensus::{
NetworkUpgrade,
NetworkUpgrade::{Canopy, Sapling},
Parameters, TestNetwork, ZIP212_GRACE_PERIOD,
},
jubjub::{
edwards,
fs::{Fs, FsRepr},
PrimeOrder, Unknown,
},
primitives::{Diversifier, PaymentAddress, Rseed, ValueCommitment},
util::generate_random_rseed,
};
use blake2b_simd::Hash as Blake2bHash;
use crypto_api_chachapoly::ChachaPolyIetf;
use ff::{Field, PrimeField};
use pairing::bls12_381::{Bls12, Fr, FrRepr};
use group::Group;
use group::{cofactor::CofactorGroup, GroupEncoding};
use rand_core::OsRng;
use rand_core::{CryptoRng, RngCore};
use std::convert::TryInto;
@ -643,7 +610,16 @@ mod tests {
try_sapling_output_recovery_with_ock, Memo, SaplingNoteEncryption, COMPACT_NOTE_SIZE,
ENC_CIPHERTEXT_SIZE, NOTE_PLAINTEXT_SIZE, OUT_CIPHERTEXT_SIZE, OUT_PLAINTEXT_SIZE,
};
use crate::{keys::OutgoingViewingKey, JUBJUB};
use crate::{
consensus::{
NetworkUpgrade,
NetworkUpgrade::{Canopy, Sapling},
Parameters, TestNetwork, ZIP212_GRACE_PERIOD,
},
keys::OutgoingViewingKey,
primitives::{Diversifier, PaymentAddress, Rseed, ValueCommitment},
util::generate_random_rseed,
};
#[test]
fn memo_from_str() {
@ -767,14 +743,14 @@ mod tests {
) -> (
OutgoingViewingKey,
Blake2bHash,
Fs,
edwards::Point<Bls12, Unknown>,
Fr,
edwards::Point<Bls12, PrimeOrder>,
jubjub::Fr,
jubjub::ExtendedPoint,
bls12_381::Scalar,
jubjub::SubgroupPoint,
[u8; ENC_CIPHERTEXT_SIZE],
[u8; OUT_CIPHERTEXT_SIZE],
) {
let ivk = Fs::random(&mut rng);
let ivk = jubjub::Fr::random(&mut rng);
let (ovk, ock, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) =
random_enc_ciphertext_with(height, ivk, rng);
@ -822,34 +798,34 @@ mod tests {
fn random_enc_ciphertext_with<R: RngCore + CryptoRng>(
height: u32,
ivk: Fs,
ivk: jubjub::Fr,
mut rng: &mut R,
) -> (
OutgoingViewingKey,
Blake2bHash,
Fs,
edwards::Point<Bls12, Unknown>,
Fr,
edwards::Point<Bls12, PrimeOrder>,
jubjub::Fr,
jubjub::ExtendedPoint,
bls12_381::Scalar,
jubjub::SubgroupPoint,
[u8; ENC_CIPHERTEXT_SIZE],
[u8; OUT_CIPHERTEXT_SIZE],
) {
let diversifier = Diversifier([0; 11]);
let pk_d = diversifier.g_d::<Bls12>(&JUBJUB).unwrap().mul(ivk, &JUBJUB);
let pk_d = diversifier.g_d().unwrap() * ivk;
let pa = PaymentAddress::from_parts_unchecked(diversifier, pk_d);
// Construct the value commitment for the proof instance
let value = 100;
let value_commitment = ValueCommitment::<Bls12> {
let value_commitment = ValueCommitment {
value,
randomness: Fs::random(&mut rng),
randomness: jubjub::Fr::random(&mut rng),
};
let cv = value_commitment.cm(&JUBJUB).into();
let cv = value_commitment.cm().into();
let rseed = generate_random_rseed::<TestNetwork, R>(height, &mut rng);
let note = pa.create_note(value, rseed, &JUBJUB).unwrap();
let cmu = note.cm(&JUBJUB);
let note = pa.create_note(value, rseed).unwrap();
let cmu = note.cm();
let ovk = OutgoingViewingKey([0; 32]);
let ne = SaplingNoteEncryption::new(ovk, note, pa, Memo([0; 512]), &mut rng);
@ -872,9 +848,9 @@ mod tests {
fn reencrypt_enc_ciphertext(
ovk: &OutgoingViewingKey,
cv: &edwards::Point<Bls12, Unknown>,
cmu: &Fr,
epk: &edwards::Point<Bls12, PrimeOrder>,
cv: &jubjub::ExtendedPoint,
cmu: &bls12_381::Scalar,
epk: &jubjub::SubgroupPoint,
enc_ciphertext: &mut [u8; ENC_CIPHERTEXT_SIZE],
out_ciphertext: &[u8; OUT_CIPHERTEXT_SIZE],
modify_plaintext: impl Fn(&mut [u8; NOTE_PLAINTEXT_SIZE]),
@ -889,14 +865,11 @@ mod tests {
OUT_PLAINTEXT_SIZE
);
let pk_d = edwards::Point::<Bls12, _>::read(&op[0..32], &JUBJUB)
.unwrap()
.as_prime_order(&JUBJUB)
.unwrap();
let pk_d = jubjub::SubgroupPoint::from_bytes(&op[0..32].try_into().unwrap()).unwrap();
let esk = Fs::from_repr(FsRepr(op[32..OUT_PLAINTEXT_SIZE].try_into().unwrap())).unwrap();
let esk = jubjub::Fr::from_repr(op[32..OUT_PLAINTEXT_SIZE].try_into().unwrap()).unwrap();
let shared_secret = sapling_ka_agree(&esk, &pk_d);
let shared_secret = sapling_ka_agree(&esk, &pk_d.into());
let key = kdf_sapling(shared_secret, &epk);
let mut plaintext = {
@ -932,7 +905,7 @@ mod tests {
break;
}
}
if d.g_d::<Bls12>(&JUBJUB).is_none() {
if d.g_d().is_none() {
break;
}
}
@ -949,7 +922,7 @@ mod tests {
break;
}
}
if d.g_d::<Bls12>(&JUBJUB).is_some() {
if d.g_d().is_some() {
break;
}
}
@ -970,7 +943,7 @@ mod tests {
assert_eq!(
try_sapling_note_decryption::<TestNetwork>(
height,
&Fs::random(&mut rng),
&jubjub::Fr::random(&mut rng),
&epk,
&cmu,
&enc_ciphertext
@ -995,7 +968,7 @@ mod tests {
try_sapling_note_decryption::<TestNetwork>(
height,
&ivk,
&edwards::Point::<Bls12, _>::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
&jubjub::SubgroupPoint::random(&mut rng),
&cmu,
&enc_ciphertext
),
@ -1020,7 +993,7 @@ mod tests {
height,
&ivk,
&epk,
&Fr::random(&mut rng),
&bls12_381::Scalar::random(&mut rng),
&enc_ciphertext
),
None
@ -1173,7 +1146,7 @@ mod tests {
assert_eq!(
try_sapling_compact_note_decryption::<TestNetwork>(
height,
&Fs::random(&mut rng),
&jubjub::Fr::random(&mut rng),
&epk,
&cmu,
&enc_ciphertext[..COMPACT_NOTE_SIZE]
@ -1198,7 +1171,7 @@ mod tests {
try_sapling_compact_note_decryption::<TestNetwork>(
height,
&ivk,
&edwards::Point::<Bls12, _>::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
&jubjub::SubgroupPoint::random(&mut rng),
&cmu,
&enc_ciphertext[..COMPACT_NOTE_SIZE]
),
@ -1223,7 +1196,7 @@ mod tests {
height,
&ivk,
&epk,
&Fr::random(&mut rng),
&bls12_381::Scalar::random(&mut rng),
&enc_ciphertext[..COMPACT_NOTE_SIZE]
),
None
@ -1406,7 +1379,7 @@ mod tests {
try_sapling_output_recovery::<TestNetwork>(
height,
&ovk,
&edwards::Point::<Bls12, _>::rand(&mut rng, &JUBJUB),
&jubjub::ExtendedPoint::random(&mut rng),
&cmu,
&epk,
&enc_ciphertext,
@ -1434,7 +1407,7 @@ mod tests {
height,
&ovk,
&cv,
&Fr::random(&mut rng),
&bls12_381::Scalar::random(&mut rng),
&epk,
&enc_ctext,
&out_ctext
@ -1445,7 +1418,7 @@ mod tests {
try_sapling_output_recovery_with_ock::<TestNetwork>(
height,
&ock.as_bytes(),
&Fr::random(&mut rng),
&bls12_381::Scalar::random(&mut rng),
&epk,
&enc_ctext,
&out_ctext
@ -1473,7 +1446,7 @@ mod tests {
&ovk,
&cv,
&cmu,
&edwards::Point::<Bls12, _>::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
&jubjub::SubgroupPoint::random(&mut rng),
&enc_ciphertext,
&out_ciphertext
),
@ -1484,7 +1457,7 @@ mod tests {
height,
&ock.as_bytes(),
&cmu,
&edwards::Point::<Bls12, _>::rand(&mut rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
&jubjub::SubgroupPoint::random(&mut rng),
&enc_ciphertext,
&out_ciphertext
),
@ -1724,7 +1697,7 @@ mod tests {
];
for &height in heights.iter() {
let ivk = Fs::zero();
let ivk = jubjub::Fr::zero();
let (ovk, ock, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) =
random_enc_ciphertext_with(height, ivk, &mut rng);
@ -1760,19 +1733,19 @@ mod tests {
macro_rules! read_fr {
($field:expr) => {{
Fr::from_repr(FrRepr($field[..].try_into().unwrap())).unwrap()
bls12_381::Scalar::from_repr($field[..].try_into().unwrap()).unwrap()
}};
}
macro_rules! read_fs {
($field:expr) => {{
Fs::from_repr(FsRepr($field[..].try_into().unwrap())).unwrap()
jubjub::Fr::from_repr($field[..].try_into().unwrap()).unwrap()
}};
}
macro_rules! read_point {
($field:expr) => {
edwards::Point::<Bls12, _>::read(&$field[..], &JUBJUB).unwrap()
jubjub::ExtendedPoint::from_bytes(&$field).unwrap()
};
}
@ -1785,27 +1758,19 @@ mod tests {
//
let ivk = read_fs!(tv.ivk);
let pk_d = read_point!(tv.default_pk_d)
.as_prime_order(&JUBJUB)
.unwrap();
let pk_d = read_point!(tv.default_pk_d).into_subgroup().unwrap();
let rcm = read_fs!(tv.rcm);
let cv = read_point!(tv.cv);
let cmu = read_fr!(tv.cmu);
let esk = read_fs!(tv.esk);
let epk = read_point!(tv.epk).as_prime_order(&JUBJUB).unwrap();
let epk = read_point!(tv.epk).into_subgroup().unwrap();
//
// Test the individual components
//
let shared_secret = sapling_ka_agree(&esk, &pk_d);
{
let mut encoded = [0; 32];
shared_secret
.write(&mut encoded[..])
.expect("length is not 32 bytes");
assert_eq!(encoded, tv.shared_secret);
}
let shared_secret = sapling_ka_agree(&esk, &pk_d.into());
assert_eq!(shared_secret.to_bytes(), tv.shared_secret);
let k_enc = kdf_sapling(shared_secret, &epk);
assert_eq!(k_enc.as_bytes(), tv.k_enc);
@ -1815,10 +1780,8 @@ mod tests {
assert_eq!(ock.as_bytes(), tv.ock);
let to = PaymentAddress::from_parts(Diversifier(tv.default_d), pk_d).unwrap();
let note = to
.create_note(tv.v, Rseed::BeforeZip212(rcm), &JUBJUB)
.unwrap();
assert_eq!(note.cm(&JUBJUB), cmu);
let note = to.create_note(tv.v, Rseed::BeforeZip212(rcm)).unwrap();
assert_eq!(note.cm(), cmu);
//
// Test decryption

View File

@ -1,10 +1,14 @@
//! Implementation of the Pedersen hash function used in Sapling.
use crate::jubjub::*;
use byteorder::{ByteOrder, LittleEndian};
use ff::{Endianness, Field, PrimeField};
use ff::PrimeField;
use group::Group;
use std::ops::{AddAssign, Neg};
use crate::constants::{
PEDERSEN_HASH_CHUNKS_PER_GENERATOR, PEDERSEN_HASH_EXP_TABLE, PEDERSEN_HASH_EXP_WINDOW_SIZE,
};
#[derive(Copy, Clone)]
pub enum Personalization {
NoteCommitment,
@ -24,27 +28,22 @@ impl Personalization {
}
}
pub fn pedersen_hash<E, I>(
personalization: Personalization,
bits: I,
params: &E::Params,
) -> edwards::Point<E, PrimeOrder>
pub fn pedersen_hash<I>(personalization: Personalization, bits: I) -> jubjub::SubgroupPoint
where
I: IntoIterator<Item = bool>,
E: JubjubEngine,
{
let mut bits = personalization
.get_bits()
.into_iter()
.chain(bits.into_iter());
let mut result = edwards::Point::zero();
let mut generators = params.pedersen_hash_exp_table().iter();
let mut result = jubjub::SubgroupPoint::identity();
let mut generators = PEDERSEN_HASH_EXP_TABLE.iter();
loop {
let mut acc = E::Fs::zero();
let mut cur = E::Fs::one();
let mut chunks_remaining = params.pedersen_hash_chunks_per_generator();
let mut acc = jubjub::Fr::zero();
let mut cur = jubjub::Fr::one();
let mut chunks_remaining = PEDERSEN_HASH_CHUNKS_PER_GENERATOR;
let mut encountered_bits = false;
// Grab three bits from the input
@ -84,21 +83,20 @@ where
break;
}
let mut table: &[Vec<edwards::Point<E, _>>] =
let mut table: &[Vec<jubjub::SubgroupPoint>] =
&generators.next().expect("we don't have enough generators");
let window = JubjubBls12::pedersen_hash_exp_window_size() as usize;
let window = PEDERSEN_HASH_EXP_WINDOW_SIZE as usize;
let window_mask = (1u64 << window) - 1;
let mut acc = acc.to_repr();
<E::Fs as PrimeField>::ReprEndianness::toggle_little_endian(&mut acc);
let acc = acc.to_repr();
let num_limbs: usize = acc.as_ref().len() / 8;
let mut limbs = vec![0u64; num_limbs + 1];
LittleEndian::read_u64_into(acc.as_ref(), &mut limbs[..num_limbs]);
let mut tmp = edwards::Point::zero();
let mut tmp = jubjub::SubgroupPoint::identity();
let mut pos = 0;
while pos < E::Fs::NUM_BITS as usize {
while pos < jubjub::Fr::NUM_BITS as usize {
let u64_idx = pos / 64;
let bit_idx = pos % 64;
let i = (if bit_idx + window < 64 {
@ -109,13 +107,13 @@ where
(limbs[u64_idx] >> bit_idx) | (limbs[u64_idx + 1] << (64 - bit_idx))
} & window_mask) as usize;
tmp = tmp.add(&table[0][i], params);
tmp += table[0][i];
pos += window;
table = &table[1..];
}
result = result.add(&tmp, params);
result += tmp;
}
result
@ -123,10 +121,10 @@ where
#[cfg(test)]
pub mod test {
use group::Curve;
use super::*;
use crate::test_vectors::pedersen_hash_vectors;
use pairing::bls12_381::Bls12;
pub struct TestVector<'a> {
pub personalization: Personalization,
@ -142,22 +140,19 @@ pub mod test {
assert!(test_vectors.len() > 0);
for v in test_vectors.iter() {
let params = &JubjubBls12::new();
let input_bools: Vec<bool> = v.input_bits.iter().map(|&i| i == 1).collect();
// The 6 bits prefix is handled separately
assert_eq!(v.personalization.get_bits(), &input_bools[..6]);
let (x, y) = pedersen_hash::<Bls12, _>(
let p = jubjub::ExtendedPoint::from(pedersen_hash(
v.personalization,
input_bools.into_iter().skip(6),
params,
)
.to_xy();
))
.to_affine();
assert_eq!(x.to_string(), v.hash_x);
assert_eq!(y.to_string(), v.hash_y);
assert_eq!(p.get_u().to_string(), v.hash_x);
assert_eq!(p.get_v().to_string(), v.hash_y);
}
}
}

View File

@ -1,6 +1,8 @@
//! Structs for core Zcash primitives.
use ff::{Field, PrimeField};
use ff::PrimeField;
use group::{Curve, Group, GroupEncoding};
use std::convert::TryInto;
use crate::constants;
@ -10,8 +12,6 @@ use crate::pedersen_hash::{pedersen_hash, Personalization};
use byteorder::{LittleEndian, WriteBytesExt};
use crate::jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder, ToUniform};
use crate::keys::prf_expand;
use blake2s_simd::Params as Blake2sParams;
@ -19,89 +19,66 @@ use blake2s_simd::Params as Blake2sParams;
use rand_core::{CryptoRng, RngCore};
#[derive(Clone)]
pub struct ValueCommitment<E: JubjubEngine> {
pub struct ValueCommitment {
pub value: u64,
pub randomness: E::Fs,
pub randomness: jubjub::Fr,
}
impl<E: JubjubEngine> ValueCommitment<E> {
pub fn cm(&self, params: &E::Params) -> edwards::Point<E, PrimeOrder> {
params
.generator(FixedGenerators::ValueCommitmentValue)
.mul(E::Fs::from(self.value), params)
.add(
&params
.generator(FixedGenerators::ValueCommitmentRandomness)
.mul(self.randomness, params),
params,
)
impl ValueCommitment {
pub fn cm(&self) -> jubjub::SubgroupPoint {
(constants::VALUE_COMMITMENT_VALUE_GENERATOR * jubjub::Fr::from(self.value))
+ (constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR * self.randomness)
}
}
#[derive(Clone)]
pub struct ProofGenerationKey<E: JubjubEngine> {
pub ak: edwards::Point<E, PrimeOrder>,
pub nsk: E::Fs,
pub struct ProofGenerationKey {
pub ak: jubjub::SubgroupPoint,
pub nsk: jubjub::Fr,
}
impl<E: JubjubEngine> ProofGenerationKey<E> {
pub fn to_viewing_key(&self, params: &E::Params) -> ViewingKey<E> {
impl ProofGenerationKey {
pub fn to_viewing_key(&self) -> ViewingKey {
ViewingKey {
ak: self.ak.clone(),
nk: params
.generator(FixedGenerators::ProofGenerationKey)
.mul(self.nsk, params),
nk: constants::PROOF_GENERATION_KEY_GENERATOR * self.nsk,
}
}
}
#[derive(Debug)]
pub struct ViewingKey<E: JubjubEngine> {
pub ak: edwards::Point<E, PrimeOrder>,
pub nk: edwards::Point<E, PrimeOrder>,
pub struct ViewingKey {
pub ak: jubjub::SubgroupPoint,
pub nk: jubjub::SubgroupPoint,
}
impl<E: JubjubEngine> ViewingKey<E> {
pub fn rk(&self, ar: E::Fs, params: &E::Params) -> edwards::Point<E, PrimeOrder> {
self.ak.add(
&params
.generator(FixedGenerators::SpendingKeyGenerator)
.mul(ar, params),
params,
)
impl ViewingKey {
pub fn rk(&self, ar: jubjub::Fr) -> jubjub::SubgroupPoint {
self.ak + constants::SPENDING_KEY_GENERATOR * ar
}
pub fn ivk(&self) -> E::Fs {
let mut preimage = [0; 64];
self.ak.write(&mut preimage[0..32]).unwrap();
self.nk.write(&mut preimage[32..64]).unwrap();
pub fn ivk(&self) -> jubjub::Fr {
let mut h = [0; 32];
h.copy_from_slice(
Blake2sParams::new()
.hash_length(32)
.personal(constants::CRH_IVK_PERSONALIZATION)
.hash(&preimage)
.to_state()
.update(&self.ak.to_bytes())
.update(&self.nk.to_bytes())
.finalize()
.as_bytes(),
);
// Drop the most significant five bits, so it can be interpreted as a scalar.
h[31] &= 0b0000_0111;
let mut e = <E::Fs as PrimeField>::Repr::default();
e.as_mut().copy_from_slice(&h[..]);
E::Fs::from_repr(e).expect("should be a valid scalar")
jubjub::Fr::from_repr(h).expect("should be a valid scalar")
}
pub fn to_payment_address(
&self,
diversifier: Diversifier,
params: &E::Params,
) -> Option<PaymentAddress<E>> {
diversifier.g_d(params).and_then(|g_d| {
let pk_d = g_d.mul(self.ivk(), params);
pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
diversifier.g_d().and_then(|g_d| {
let pk_d = g_d * self.ivk();
PaymentAddress::from_parts(diversifier, pk_d)
})
@ -112,15 +89,8 @@ impl<E: JubjubEngine> ViewingKey<E> {
pub struct Diversifier(pub [u8; 11]);
impl Diversifier {
pub fn g_d<E: JubjubEngine>(
&self,
params: &E::Params,
) -> Option<edwards::Point<E, PrimeOrder>> {
group_hash::<E>(
&self.0,
constants::KEY_DIVERSIFICATION_PERSONALIZATION,
params,
)
pub fn g_d(&self) -> Option<jubjub::SubgroupPoint> {
group_hash(&self.0, constants::KEY_DIVERSIFICATION_PERSONALIZATION)
}
}
@ -131,26 +101,23 @@ impl Diversifier {
/// `pk_d` is guaranteed to be prime-order (i.e. in the prime-order subgroup of Jubjub,
/// and not the identity).
#[derive(Clone, Debug)]
pub struct PaymentAddress<E: JubjubEngine> {
pk_d: edwards::Point<E, PrimeOrder>,
pub struct PaymentAddress {
pk_d: jubjub::SubgroupPoint,
diversifier: Diversifier,
}
impl<E: JubjubEngine> PartialEq for PaymentAddress<E> {
impl PartialEq for PaymentAddress {
fn eq(&self, other: &Self) -> bool {
self.pk_d == other.pk_d && self.diversifier == other.diversifier
}
}
impl<E: JubjubEngine> PaymentAddress<E> {
impl PaymentAddress {
/// Constructs a PaymentAddress from a diversifier and a Jubjub point.
///
/// Returns None if `pk_d` is the identity.
pub fn from_parts(
diversifier: Diversifier,
pk_d: edwards::Point<E, PrimeOrder>,
) -> Option<Self> {
if pk_d == edwards::Point::zero() {
pub fn from_parts(diversifier: Diversifier, pk_d: jubjub::SubgroupPoint) -> Option<Self> {
if pk_d.is_identity().into() {
None
} else {
Some(PaymentAddress { pk_d, diversifier })
@ -163,34 +130,36 @@ impl<E: JubjubEngine> PaymentAddress<E> {
#[cfg(test)]
pub(crate) fn from_parts_unchecked(
diversifier: Diversifier,
pk_d: edwards::Point<E, PrimeOrder>,
pk_d: jubjub::SubgroupPoint,
) -> Self {
PaymentAddress { pk_d, diversifier }
}
/// Parses a PaymentAddress from bytes.
pub fn from_bytes(bytes: &[u8; 43], params: &E::Params) -> Option<Self> {
pub fn from_bytes(bytes: &[u8; 43]) -> Option<Self> {
let diversifier = {
let mut tmp = [0; 11];
tmp.copy_from_slice(&bytes[0..11]);
Diversifier(tmp)
};
// Check that the diversifier is valid
if diversifier.g_d::<E>(params).is_none() {
if diversifier.g_d().is_none() {
return None;
}
edwards::Point::<E, _>::read(&bytes[11..43], params)
.ok()?
.as_prime_order(params)
.and_then(|pk_d| PaymentAddress::from_parts(diversifier, pk_d))
let pk_d = jubjub::SubgroupPoint::from_bytes(bytes[11..43].try_into().unwrap());
if pk_d.is_some().into() {
PaymentAddress::from_parts(diversifier, pk_d.unwrap())
} else {
None
}
}
/// Returns the byte encoding of this `PaymentAddress`.
pub fn to_bytes(&self) -> [u8; 43] {
let mut bytes = [0; 43];
bytes[0..11].copy_from_slice(&self.diversifier.0);
self.pk_d.write(&mut bytes[11..]).unwrap();
bytes[11..].copy_from_slice(&self.pk_d.to_bytes());
bytes
}
@ -200,21 +169,16 @@ impl<E: JubjubEngine> PaymentAddress<E> {
}
/// Returns `pk_d` for this `PaymentAddress`.
pub fn pk_d(&self) -> &edwards::Point<E, PrimeOrder> {
pub fn pk_d(&self) -> &jubjub::SubgroupPoint {
&self.pk_d
}
pub fn g_d(&self, params: &E::Params) -> Option<edwards::Point<E, PrimeOrder>> {
self.diversifier.g_d(params)
pub fn g_d(&self) -> Option<jubjub::SubgroupPoint> {
self.diversifier.g_d()
}
pub fn create_note(
&self,
value: u64,
randomness: Rseed<E::Fs>,
params: &E::Params,
) -> Option<Note<E>> {
self.g_d(params).map(|g_d| Note {
pub fn create_note(&self, value: u64, randomness: Rseed) -> Option<Note> {
self.g_d().map(|g_d| Note {
value,
rseed: randomness,
g_d,
@ -229,24 +193,24 @@ impl<E: JubjubEngine> PaymentAddress<E> {
/// After ZIP 212, the note randomness `rseed` is a 32-byte sequence, used to derive
/// both the note commitment trapdoor `rcm` and the ephemeral private key `esk`.
#[derive(Copy, Clone, Debug)]
pub enum Rseed<Fs> {
BeforeZip212(Fs),
pub enum Rseed {
BeforeZip212(jubjub::Fr),
AfterZip212([u8; 32]),
}
#[derive(Clone, Debug)]
pub struct Note<E: JubjubEngine> {
pub struct Note {
/// The value of the note
pub value: u64,
/// The diversified base of the address, GH(d)
pub g_d: edwards::Point<E, PrimeOrder>,
pub g_d: jubjub::SubgroupPoint,
/// The public key of the address, g_d^ivk
pub pk_d: edwards::Point<E, PrimeOrder>,
pub pk_d: jubjub::SubgroupPoint,
/// rseed
pub rseed: Rseed<E::Fs>,
pub rseed: Rseed,
}
impl<E: JubjubEngine> PartialEq for Note<E> {
impl PartialEq for Note {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
&& self.g_d == other.g_d
@ -255,18 +219,18 @@ impl<E: JubjubEngine> PartialEq for Note<E> {
}
}
impl<E: JubjubEngine> Note<E> {
pub fn uncommitted() -> E::Fr {
impl Note {
pub fn uncommitted() -> bls12_381::Scalar {
// The smallest u-coordinate that is not on the curve
// is one.
// TODO: This should be relocated to JubjubEngine as
// it's specific to the curve we're using, not all
// twisted edwards curves.
E::Fr::one()
bls12_381::Scalar::one()
}
/// Computes the note commitment, returning the full point.
fn cm_full_point(&self, params: &E::Params) -> edwards::Point<E, PrimeOrder> {
fn cm_full_point(&self) -> jubjub::SubgroupPoint {
// Calculate the note contents, as bytes
let mut note_contents = vec![];
@ -276,10 +240,10 @@ impl<E: JubjubEngine> Note<E> {
.unwrap();
// Write g_d
self.g_d.write(&mut note_contents).unwrap();
note_contents.extend_from_slice(&self.g_d.to_bytes());
// Write pk_d
self.pk_d.write(&mut note_contents).unwrap();
note_contents.extend_from_slice(&self.pk_d.to_bytes());
assert_eq!(note_contents.len(), 32 + 32 + 8);
@ -289,74 +253,70 @@ impl<E: JubjubEngine> Note<E> {
note_contents
.into_iter()
.flat_map(|byte| (0..8).map(move |i| ((byte >> i) & 1) == 1)),
params,
);
// Compute final commitment
params
.generator(FixedGenerators::NoteCommitmentRandomness)
.mul(self.rcm(), params)
.add(&hash_of_contents, params)
(constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR * self.rcm()) + hash_of_contents
}
/// Computes the nullifier given the viewing key and
/// note position
pub fn nf(&self, viewing_key: &ViewingKey<E>, position: u64, params: &E::Params) -> Vec<u8> {
pub fn nf(&self, viewing_key: &ViewingKey, position: u64) -> Vec<u8> {
// Compute rho = cm + position.G
let rho = self.cm_full_point(params).add(
&params
.generator(FixedGenerators::NullifierPosition)
.mul(E::Fs::from(position), params),
params,
);
let rho = self.cm_full_point()
+ (constants::NULLIFIER_POSITION_GENERATOR * jubjub::Fr::from(position));
// Compute nf = BLAKE2s(nk | rho)
let mut nf_preimage = [0u8; 64];
viewing_key.nk.write(&mut nf_preimage[0..32]).unwrap();
rho.write(&mut nf_preimage[32..64]).unwrap();
Blake2sParams::new()
.hash_length(32)
.personal(constants::PRF_NF_PERSONALIZATION)
.hash(&nf_preimage)
.to_state()
.update(&viewing_key.nk.to_bytes())
.update(&rho.to_bytes())
.finalize()
.as_bytes()
.to_vec()
}
/// Computes the note commitment
pub fn cm(&self, params: &E::Params) -> E::Fr {
pub fn cm(&self) -> bls12_381::Scalar {
// The commitment is in the prime order subgroup, so mapping the
// commitment to the x-coordinate is an injective encoding.
self.cm_full_point(params).to_xy().0
// commitment to the u-coordinate is an injective encoding.
jubjub::ExtendedPoint::from(self.cm_full_point())
.to_affine()
.get_u()
}
pub fn rcm(&self) -> E::Fs {
pub fn rcm(&self) -> jubjub::Fr {
match self.rseed {
Rseed::BeforeZip212(rcm) => rcm,
Rseed::AfterZip212(rseed) => E::Fs::to_uniform(prf_expand(&rseed, &[0x04]).as_bytes()),
Rseed::AfterZip212(rseed) => {
jubjub::Fr::from_bytes_wide(prf_expand(&rseed, &[0x04]).as_array())
}
}
}
pub fn generate_or_derive_esk<R: RngCore + CryptoRng>(&self, rng: &mut R) -> E::Fs {
pub fn generate_or_derive_esk<R: RngCore + CryptoRng>(&self, rng: &mut R) -> jubjub::Fr {
match self.derive_esk() {
None => {
// create random 64 byte buffer
let mut buffer = [0u8; 64];
&rng.fill_bytes(&mut buffer);
rng.fill_bytes(&mut buffer);
// reduce to uniform value
E::Fs::to_uniform(&buffer[..])
jubjub::Fr::from_bytes_wide(&buffer)
}
Some(esk) => esk,
}
}
/// Returns the derived `esk` if this note was created after ZIP 212 activated.
pub fn derive_esk(&self) -> Option<E::Fs> {
pub fn derive_esk(&self) -> Option<jubjub::Fr> {
match self.rseed {
Rseed::BeforeZip212(_) => None,
Rseed::AfterZip212(rseed) => {
Some(E::Fs::to_uniform(prf_expand(&rseed, &[0x05]).as_bytes()))
}
Rseed::AfterZip212(rseed) => Some(jubjub::Fr::from_bytes_wide(
prf_expand(&rseed, &[0x05]).as_array(),
)),
}
}
}

View File

@ -1,10 +1,6 @@
//! Abstractions over the proving system and parameters.
use crate::{
jubjub::{edwards, fs::Fs, Unknown},
primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed},
};
use pairing::bls12_381::{Bls12, Fr};
use crate::primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed};
use crate::{
merkle_tree::MerklePath,
@ -29,21 +25,14 @@ pub trait TxProver {
fn spend_proof(
&self,
ctx: &mut Self::SaplingProvingContext,
proof_generation_key: ProofGenerationKey<Bls12>,
proof_generation_key: ProofGenerationKey,
diversifier: Diversifier,
rseed: Rseed<Fs>,
ar: Fs,
rseed: Rseed,
ar: jubjub::Fr,
value: u64,
anchor: Fr,
anchor: bls12_381::Scalar,
merkle_path: MerklePath<Node>,
) -> Result<
(
[u8; GROTH_PROOF_SIZE],
edwards::Point<Bls12, Unknown>,
PublicKey<Bls12>,
),
(),
>;
) -> Result<([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint, PublicKey), ()>;
/// Create the value commitment and proof for a Sapling [`OutputDescription`],
/// while accumulating its value commitment randomness inside the context for later
@ -53,11 +42,11 @@ pub trait TxProver {
fn output_proof(
&self,
ctx: &mut Self::SaplingProvingContext,
esk: Fs,
payment_address: PaymentAddress<Bls12>,
rcm: Fs,
esk: jubjub::Fr,
payment_address: PaymentAddress,
rcm: jubjub::Fr,
value: u64,
) -> ([u8; GROTH_PROOF_SIZE], edwards::Point<Bls12, Unknown>);
) -> ([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint);
/// Create the `bindingSig` for a Sapling transaction. All calls to
/// [`TxProver::spend_proof`] and [`TxProver::output_proof`] must be completed before
@ -73,11 +62,10 @@ pub trait TxProver {
#[cfg(test)]
pub(crate) mod mock {
use ff::Field;
use pairing::bls12_381::{Bls12, Fr};
use rand_core::OsRng;
use crate::{
jubjub::{edwards, fs::Fs, FixedGenerators, Unknown},
constants::SPENDING_KEY_GENERATOR,
primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed, ValueCommitment},
};
@ -86,7 +74,6 @@ pub(crate) mod mock {
redjubjub::{PublicKey, Signature},
sapling::Node,
transaction::components::{Amount, GROTH_PROOF_SIZE},
JUBJUB,
};
use super::TxProver;
@ -102,35 +89,25 @@ pub(crate) mod mock {
fn spend_proof(
&self,
_ctx: &mut Self::SaplingProvingContext,
proof_generation_key: ProofGenerationKey<Bls12>,
proof_generation_key: ProofGenerationKey,
_diversifier: Diversifier,
_rcm: Rseed<Fs>,
ar: Fs,
_rcm: Rseed,
ar: jubjub::Fr,
value: u64,
_anchor: Fr,
_anchor: bls12_381::Scalar,
_merkle_path: MerklePath<Node>,
) -> Result<
(
[u8; GROTH_PROOF_SIZE],
edwards::Point<Bls12, Unknown>,
PublicKey<Bls12>,
),
(),
> {
) -> Result<([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint, PublicKey), ()> {
let mut rng = OsRng;
let cv = ValueCommitment::<Bls12> {
let cv = ValueCommitment {
value,
randomness: Fs::random(&mut rng),
randomness: jubjub::Fr::random(&mut rng),
}
.cm(&JUBJUB)
.cm()
.into();
let rk = PublicKey::<Bls12>(proof_generation_key.ak.clone().into()).randomize(
ar,
FixedGenerators::SpendingKeyGenerator,
&JUBJUB,
);
let rk = PublicKey(proof_generation_key.ak.clone().into())
.randomize(ar, SPENDING_KEY_GENERATOR);
Ok(([0u8; GROTH_PROOF_SIZE], cv, rk))
}
@ -138,18 +115,18 @@ pub(crate) mod mock {
fn output_proof(
&self,
_ctx: &mut Self::SaplingProvingContext,
_esk: Fs,
_payment_address: PaymentAddress<Bls12>,
_rcm: Fs,
_esk: jubjub::Fr,
_payment_address: PaymentAddress,
_rcm: jubjub::Fr,
value: u64,
) -> ([u8; GROTH_PROOF_SIZE], edwards::Point<Bls12, Unknown>) {
) -> ([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint) {
let mut rng = OsRng;
let cv = ValueCommitment::<Bls12> {
let cv = ValueCommitment {
value,
randomness: Fs::random(&mut rng),
randomness: jubjub::Fr::random(&mut rng),
}
.cm(&JUBJUB)
.cm()
.into();
([0u8; GROTH_PROOF_SIZE], cv)

View File

@ -3,28 +3,29 @@
//!
//! [RedJubjub]: https://zips.z.cash/protocol/protocol.pdf#concretereddsa
use crate::jubjub::{edwards::Point, FixedGenerators, JubjubEngine, JubjubParams, Unknown};
use ff::{Field, PrimeField};
use group::GroupEncoding;
use jubjub::{ExtendedPoint, SubgroupPoint};
use rand_core::RngCore;
use std::io::{self, Read, Write};
use std::ops::{AddAssign, MulAssign, Neg};
use crate::util::hash_to_scalar;
fn read_scalar<E: JubjubEngine, R: Read>(mut reader: R) -> io::Result<E::Fs> {
let mut s_repr = <E::Fs as PrimeField>::Repr::default();
fn read_scalar<R: Read>(mut reader: R) -> io::Result<jubjub::Fr> {
let mut s_repr = [0; 32];
reader.read_exact(s_repr.as_mut())?;
E::Fs::from_repr(s_repr)
jubjub::Fr::from_repr(s_repr)
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "scalar is not in field"))
}
fn write_scalar<E: JubjubEngine, W: Write>(s: &E::Fs, mut writer: W) -> io::Result<()> {
fn write_scalar<W: Write>(s: &jubjub::Fr, mut writer: W) -> io::Result<()> {
writer.write_all(s.to_repr().as_ref())
}
fn h_star<E: JubjubEngine>(a: &[u8], b: &[u8]) -> E::Fs {
hash_to_scalar::<E>(b"Zcash_RedJubjubH", a, b)
fn h_star(a: &[u8], b: &[u8]) -> jubjub::Fr {
hash_to_scalar(b"Zcash_RedJubjubH", a, b)
}
#[derive(Copy, Clone, Debug)]
@ -33,10 +34,10 @@ pub struct Signature {
sbar: [u8; 32],
}
pub struct PrivateKey<E: JubjubEngine>(pub E::Fs);
pub struct PrivateKey(pub jubjub::Fr);
#[derive(Debug)]
pub struct PublicKey<E: JubjubEngine>(pub Point<E, Unknown>);
pub struct PublicKey(pub ExtendedPoint);
impl Signature {
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
@ -53,167 +54,156 @@ impl Signature {
}
}
impl<E: JubjubEngine> PrivateKey<E> {
pub fn randomize(&self, alpha: E::Fs) -> Self {
impl PrivateKey {
pub fn randomize(&self, alpha: jubjub::Fr) -> Self {
let mut tmp = self.0;
tmp.add_assign(&alpha);
PrivateKey(tmp)
}
pub fn read<R: Read>(reader: R) -> io::Result<Self> {
let pk = read_scalar::<E, R>(reader)?;
let pk = read_scalar::<R>(reader)?;
Ok(PrivateKey(pk))
}
pub fn write<W: Write>(&self, writer: W) -> io::Result<()> {
write_scalar::<E, W>(&self.0, writer)
write_scalar::<W>(&self.0, writer)
}
pub fn sign<R: RngCore>(
&self,
msg: &[u8],
rng: &mut R,
p_g: FixedGenerators,
params: &E::Params,
) -> Signature {
pub fn sign<R: RngCore>(&self, msg: &[u8], rng: &mut R, p_g: SubgroupPoint) -> Signature {
// T = (l_H + 128) bits of randomness
// For H*, l_H = 512 bits
let mut t = [0u8; 80];
rng.fill_bytes(&mut t[..]);
// r = H*(T || M)
let r = h_star::<E>(&t[..], msg);
let r = h_star(&t[..], msg);
// R = r . P_G
let r_g = params.generator(p_g).mul(r, params);
let mut rbar = [0u8; 32];
r_g.write(&mut rbar[..])
.expect("Jubjub points should serialize to 32 bytes");
let r_g = p_g * r;
let rbar = r_g.to_bytes();
// S = r + H*(Rbar || M) . sk
let mut s = h_star::<E>(&rbar[..], msg);
let mut s = h_star(&rbar[..], msg);
s.mul_assign(&self.0);
s.add_assign(&r);
let mut sbar = [0u8; 32];
write_scalar::<E, &mut [u8]>(&s, &mut sbar[..])
write_scalar::<&mut [u8]>(&s, &mut sbar[..])
.expect("Jubjub scalars should serialize to 32 bytes");
Signature { rbar, sbar }
}
}
impl<E: JubjubEngine> PublicKey<E> {
pub fn from_private(privkey: &PrivateKey<E>, p_g: FixedGenerators, params: &E::Params) -> Self {
let res = params.generator(p_g).mul(privkey.0, params).into();
PublicKey(res)
impl PublicKey {
pub fn from_private(privkey: &PrivateKey, p_g: SubgroupPoint) -> Self {
PublicKey((p_g * privkey.0).into())
}
pub fn randomize(&self, alpha: E::Fs, p_g: FixedGenerators, params: &E::Params) -> Self {
let res: Point<E, Unknown> = params.generator(p_g).mul(alpha, params).into();
let res = res.add(&self.0, params);
PublicKey(res)
pub fn randomize(&self, alpha: jubjub::Fr, p_g: SubgroupPoint) -> Self {
PublicKey(ExtendedPoint::from(p_g * alpha) + self.0)
}
pub fn read<R: Read>(reader: R, params: &E::Params) -> io::Result<Self> {
let p = Point::read(reader, params)?;
Ok(PublicKey(p))
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let mut bytes = [0; 32];
reader.read_exact(&mut bytes)?;
let p = ExtendedPoint::from_bytes(&bytes).map(PublicKey);
if p.is_some().into() {
Ok(p.unwrap())
} else {
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid RedJubjub public key",
))
}
}
pub fn write<W: Write>(&self, writer: W) -> io::Result<()> {
self.0.write(writer)
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
writer.write_all(&self.0.to_bytes())
}
pub fn verify(
&self,
msg: &[u8],
sig: &Signature,
p_g: FixedGenerators,
params: &E::Params,
) -> bool {
pub fn verify(&self, msg: &[u8], sig: &Signature, p_g: SubgroupPoint) -> bool {
// c = H*(Rbar || M)
let c = h_star::<E>(&sig.rbar[..], msg);
let c = h_star(&sig.rbar[..], msg);
// Signature checks:
// R != invalid
let r = match Point::read(&sig.rbar[..], params) {
Ok(r) => r,
Err(_) => return false,
let r = {
let r = ExtendedPoint::from_bytes(&sig.rbar);
if r.is_none().into() {
return false;
}
r.unwrap()
};
// S < order(G)
// (E::Fs guarantees its representation is in the field)
let s = match read_scalar::<E, &[u8]>(&sig.sbar[..]) {
// (jubjub::Scalar guarantees its representation is in the field)
let s = match read_scalar::<&[u8]>(&sig.sbar[..]) {
Ok(s) => s,
Err(_) => return false,
};
// 0 = h_G(-S . P_G + R + c . vk)
self.0
.mul(c, params)
.add(&r, params)
.add(
&params.generator(p_g).mul(s, params).negate().into(),
params,
)
.mul_by_cofactor(params)
.eq(&Point::zero())
((self.0 * c) + r - (p_g * s))
.mul_by_cofactor()
.is_identity()
.into()
}
}
pub struct BatchEntry<'a, E: JubjubEngine> {
vk: PublicKey<E>,
pub struct BatchEntry<'a> {
vk: PublicKey,
msg: &'a [u8],
sig: Signature,
}
// TODO: #82: This is a naive implementation currently,
// and doesn't use multiexp.
pub fn batch_verify<'a, E: JubjubEngine, R: RngCore>(
pub fn batch_verify<'a, R: RngCore>(
rng: &mut R,
batch: &[BatchEntry<'a, E>],
p_g: FixedGenerators,
params: &E::Params,
batch: &[BatchEntry<'a>],
p_g: SubgroupPoint,
) -> bool {
let mut acc = Point::<E, Unknown>::zero();
let mut acc = ExtendedPoint::identity();
for entry in batch {
let mut r = match Point::<E, Unknown>::read(&entry.sig.rbar[..], params) {
Ok(r) => r,
Err(_) => return false,
let mut r = {
let r = ExtendedPoint::from_bytes(&entry.sig.rbar);
if r.is_none().into() {
return false;
}
r.unwrap()
};
let mut s = match read_scalar::<E, &[u8]>(&entry.sig.sbar[..]) {
let mut s = match read_scalar::<&[u8]>(&entry.sig.sbar[..]) {
Ok(s) => s,
Err(_) => return false,
};
let mut c = h_star::<E>(&entry.sig.rbar[..], entry.msg);
let mut c = h_star(&entry.sig.rbar[..], entry.msg);
let z = E::Fs::random(rng);
let z = jubjub::Fr::random(rng);
s.mul_assign(&z);
s = s.neg();
r = r.mul(z, params);
r = r * z;
c.mul_assign(&z);
acc = acc.add(&r, params);
acc = acc.add(&entry.vk.0.mul(c, params), params);
acc = acc.add(&params.generator(p_g).mul(s, params).into(), params);
acc = acc + r + (&entry.vk.0 * c) + (p_g * s);
}
acc = acc.mul_by_cofactor(params).into();
acc = acc.mul_by_cofactor().into();
acc.eq(&Point::zero())
acc.is_identity().into()
}
#[cfg(test)]
mod tests {
use pairing::bls12_381::Bls12;
use group::Group;
use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng;
use crate::jubjub::{edwards, fs::Fs, JubjubBls12};
use super::*;
use crate::constants::SPENDING_KEY_GENERATOR;
#[test]
fn test_batch_verify() {
@ -221,20 +211,19 @@ mod tests {
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let params = &JubjubBls12::new();
let p_g = FixedGenerators::SpendingKeyGenerator;
let p_g = SPENDING_KEY_GENERATOR;
let sk1 = PrivateKey::<Bls12>(Fs::random(rng));
let vk1 = PublicKey::from_private(&sk1, p_g, params);
let sk1 = PrivateKey(jubjub::Fr::random(rng));
let vk1 = PublicKey::from_private(&sk1, p_g);
let msg1 = b"Foo bar";
let sig1 = sk1.sign(msg1, rng, p_g, params);
assert!(vk1.verify(msg1, &sig1, p_g, params));
let sig1 = sk1.sign(msg1, rng, p_g);
assert!(vk1.verify(msg1, &sig1, p_g));
let sk2 = PrivateKey::<Bls12>(Fs::random(rng));
let vk2 = PublicKey::from_private(&sk2, p_g, params);
let sk2 = PrivateKey(jubjub::Fr::random(rng));
let vk2 = PublicKey::from_private(&sk2, p_g);
let msg2 = b"Foo bar";
let sig2 = sk2.sign(msg2, rng, p_g, params);
assert!(vk2.verify(msg2, &sig2, p_g, params));
let sig2 = sk2.sign(msg2, rng, p_g);
assert!(vk2.verify(msg2, &sig2, p_g));
let mut batch = vec![
BatchEntry {
@ -249,11 +238,11 @@ mod tests {
},
];
assert!(batch_verify(rng, &batch, p_g, params));
assert!(batch_verify(rng, &batch, p_g));
batch[0].sig = sig2;
assert!(!batch_verify(rng, &batch, p_g, params));
assert!(!batch_verify(rng, &batch, p_g));
}
#[test]
@ -262,33 +251,34 @@ mod tests {
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let params = &JubjubBls12::new();
let zero = edwards::Point::zero();
let p_g = FixedGenerators::SpendingKeyGenerator;
let zero = jubjub::ExtendedPoint::identity();
let p_g = SPENDING_KEY_GENERATOR;
// Get a point of order 8
let p8 = loop {
let r = edwards::Point::<Bls12, _>::rand(rng, params).mul(Fs::char(), params);
let r = jubjub::ExtendedPoint::random(rng)
.to_niels()
.multiply_bits(&jubjub::Fr::char());
let r2 = r.double(params);
let r4 = r2.double(params);
let r8 = r4.double(params);
let r2 = r.double();
let r4 = r2.double();
let r8 = r4.double();
if r2 != zero && r4 != zero && r8 == zero {
break r;
}
};
let sk = PrivateKey::<Bls12>(Fs::random(rng));
let vk = PublicKey::from_private(&sk, p_g, params);
let sk = PrivateKey(jubjub::Fr::random(rng));
let vk = PublicKey::from_private(&sk, p_g);
// TODO: This test will need to change when #77 is fixed
let msg = b"Foo bar";
let sig = sk.sign(msg, rng, p_g, params);
assert!(vk.verify(msg, &sig, p_g, params));
let sig = sk.sign(msg, rng, p_g);
assert!(vk.verify(msg, &sig, p_g));
let vktorsion = PublicKey(vk.0.add(&p8, params));
assert!(vktorsion.verify(msg, &sig, p_g, params));
let vktorsion = PublicKey(vk.0 + p8);
assert!(vktorsion.verify(msg, &sig, p_g));
}
#[test]
@ -297,14 +287,13 @@ mod tests {
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let p_g = FixedGenerators::SpendingKeyGenerator;
let params = &JubjubBls12::new();
let p_g = SPENDING_KEY_GENERATOR;
for _ in 0..1000 {
let sk = PrivateKey::<Bls12>(Fs::random(rng));
let vk = PublicKey::from_private(&sk, p_g, params);
let sk = PrivateKey(jubjub::Fr::random(rng));
let vk = PublicKey::from_private(&sk, p_g);
let msg = b"Foo bar";
let sig = sk.sign(msg, rng, p_g, params);
let sig = sk.sign(msg, rng, p_g);
let mut sk_bytes = [0u8; 32];
let mut vk_bytes = [0u8; 32];
@ -313,17 +302,17 @@ mod tests {
vk.write(&mut vk_bytes[..]).unwrap();
sig.write(&mut sig_bytes[..]).unwrap();
let sk_2 = PrivateKey::<Bls12>::read(&sk_bytes[..]).unwrap();
let vk_2 = PublicKey::from_private(&sk_2, p_g, params);
let sk_2 = PrivateKey::read(&sk_bytes[..]).unwrap();
let vk_2 = PublicKey::from_private(&sk_2, p_g);
let mut vk_2_bytes = [0u8; 32];
vk_2.write(&mut vk_2_bytes[..]).unwrap();
assert!(vk_bytes == vk_2_bytes);
let vk_2 = PublicKey::<Bls12>::read(&vk_bytes[..], params).unwrap();
let vk_2 = PublicKey::read(&vk_bytes[..]).unwrap();
let sig_2 = Signature::read(&sig_bytes[..]).unwrap();
assert!(vk.verify(msg, &sig_2, p_g, params));
assert!(vk_2.verify(msg, &sig, p_g, params));
assert!(vk_2.verify(msg, &sig_2, p_g, params));
assert!(vk.verify(msg, &sig_2, p_g));
assert!(vk_2.verify(msg, &sig, p_g));
assert!(vk_2.verify(msg, &sig_2, p_g));
}
}
@ -333,35 +322,34 @@ mod tests {
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let p_g = FixedGenerators::SpendingKeyGenerator;
let params = &JubjubBls12::new();
let p_g = SPENDING_KEY_GENERATOR;
for _ in 0..1000 {
let sk = PrivateKey::<Bls12>(Fs::random(rng));
let vk = PublicKey::from_private(&sk, p_g, params);
let sk = PrivateKey(jubjub::Fr::random(rng));
let vk = PublicKey::from_private(&sk, p_g);
let msg1 = b"Foo bar";
let msg2 = b"Spam eggs";
let sig1 = sk.sign(msg1, rng, p_g, params);
let sig2 = sk.sign(msg2, rng, p_g, params);
let sig1 = sk.sign(msg1, rng, p_g);
let sig2 = sk.sign(msg2, rng, p_g);
assert!(vk.verify(msg1, &sig1, p_g, params));
assert!(vk.verify(msg2, &sig2, p_g, params));
assert!(!vk.verify(msg1, &sig2, p_g, params));
assert!(!vk.verify(msg2, &sig1, p_g, params));
assert!(vk.verify(msg1, &sig1, p_g));
assert!(vk.verify(msg2, &sig2, p_g));
assert!(!vk.verify(msg1, &sig2, p_g));
assert!(!vk.verify(msg2, &sig1, p_g));
let alpha = Fs::random(rng);
let alpha = jubjub::Fr::random(rng);
let rsk = sk.randomize(alpha);
let rvk = vk.randomize(alpha, p_g, params);
let rvk = vk.randomize(alpha, p_g);
let sig1 = rsk.sign(msg1, rng, p_g, params);
let sig2 = rsk.sign(msg2, rng, p_g, params);
let sig1 = rsk.sign(msg1, rng, p_g);
let sig2 = rsk.sign(msg2, rng, p_g);
assert!(rvk.verify(msg1, &sig1, p_g, params));
assert!(rvk.verify(msg2, &sig2, p_g, params));
assert!(!rvk.verify(msg1, &sig2, p_g, params));
assert!(!rvk.verify(msg2, &sig1, p_g, params));
assert!(rvk.verify(msg1, &sig1, p_g));
assert!(rvk.verify(msg2, &sig2, p_g));
assert!(!rvk.verify(msg1, &sig2, p_g));
assert!(!rvk.verify(msg2, &sig1, p_g));
}
}
}

View File

@ -1,24 +1,23 @@
//! Structs and constants specific to the Sapling shielded pool.
use crate::{
jubjub::{fs::Fs, FixedGenerators, JubjubBls12},
constants::SPENDING_KEY_GENERATOR,
pedersen_hash::{pedersen_hash, Personalization},
primitives::Note,
};
use ff::{BitIterator, PrimeField};
use group::{Curve, GroupEncoding};
use lazy_static::lazy_static;
use pairing::bls12_381::{Bls12, Fr, FrRepr};
use rand_core::{CryptoRng, RngCore};
use std::io::{self, Read, Write};
use crate::merkle_tree::Hashable;
use crate::redjubjub::{PrivateKey, PublicKey, Signature};
use crate::JUBJUB;
pub const SAPLING_COMMITMENT_TREE_DEPTH: usize = 32;
/// Compute a parent node in the Sapling commitment tree given its two children.
pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr {
pub fn merkle_hash(depth: usize, lhs: &[u8; 32], rhs: &[u8; 32]) -> [u8; 32] {
let lhs = {
let mut tmp = [false; 256];
for (a, b) in tmp.iter_mut().rev().zip(BitIterator::<u8, _>::new(lhs)) {
@ -35,35 +34,38 @@ pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr {
tmp
};
pedersen_hash::<Bls12, _>(
jubjub::ExtendedPoint::from(pedersen_hash(
Personalization::MerkleTree(depth),
lhs.iter()
.copied()
.take(Fr::NUM_BITS as usize)
.chain(rhs.iter().copied().take(Fr::NUM_BITS as usize)),
&JUBJUB,
)
.to_xy()
.0
.take(bls12_381::Scalar::NUM_BITS as usize)
.chain(
rhs.iter()
.copied()
.take(bls12_381::Scalar::NUM_BITS as usize),
),
))
.to_affine()
.get_u()
.to_repr()
}
/// A node within the Sapling commitment tree.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Node {
repr: FrRepr,
repr: [u8; 32],
}
impl Node {
pub fn new(repr: FrRepr) -> Self {
pub fn new(repr: [u8; 32]) -> Self {
Node { repr }
}
}
impl Hashable for Node {
fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let mut repr = FrRepr([0; 32]);
reader.read_exact(&mut repr.0)?;
let mut repr = [0; 32];
reader.read_exact(&mut repr)?;
Ok(Node::new(repr))
}
@ -79,7 +81,7 @@ impl Hashable for Node {
fn blank() -> Self {
Node {
repr: Note::<Bls12>::uncommitted().to_repr(),
repr: Note::uncommitted().to_repr(),
}
}
@ -88,9 +90,9 @@ impl Hashable for Node {
}
}
impl From<Node> for Fr {
impl From<Node> for bls12_381::Scalar {
fn from(node: Node) -> Self {
Fr::from_repr(node.repr).expect("Tree nodes should be in the prime field")
bls12_381::Scalar::from_repr(node.repr).expect("Tree nodes should be in the prime field")
}
}
@ -107,29 +109,22 @@ lazy_static! {
/// Create the spendAuthSig for a Sapling SpendDescription.
pub fn spend_sig<R: RngCore + CryptoRng>(
ask: PrivateKey<Bls12>,
ar: Fs,
ask: PrivateKey,
ar: jubjub::Fr,
sighash: &[u8; 32],
rng: &mut R,
params: &JubjubBls12,
) -> Signature {
// We compute `rsk`...
let rsk = ask.randomize(ar);
// We compute `rk` from there (needed for key prefixing)
let rk = PublicKey::from_private(&rsk, FixedGenerators::SpendingKeyGenerator, params);
let rk = PublicKey::from_private(&rsk, SPENDING_KEY_GENERATOR);
// Compute the signature's message for rk/spend_auth_sig
let mut data_to_be_signed = [0u8; 64];
rk.0.write(&mut data_to_be_signed[0..32])
.expect("message buffer should be 32 bytes");
data_to_be_signed[0..32].copy_from_slice(&rk.0.to_bytes());
(&mut data_to_be_signed[32..64]).copy_from_slice(&sighash[..]);
// Do the signing
rsk.sign(
&data_to_be_signed,
rng,
FixedGenerators::SpendingKeyGenerator,
params,
)
rsk.sign(&data_to_be_signed, rng, SPENDING_KEY_GENERATOR)
}

View File

@ -7,26 +7,26 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
TestVector {
personalization: Personalization::NoteCommitment,
input_bits: vec![1, 1, 1, 1, 1, 1],
hash_x: "Fr(0x06b1187c11ca4fb4383b2e0d0dbbde3ad3617338b5029187ec65a5eaed5e4d0b)",
hash_y: "Fr(0x3ce70f536652f0dea496393a1e55c4e08b9d55508e16d11e5db40d4810cbc982)",
hash_x: "0x06b1187c11ca4fb4383b2e0d0dbbde3ad3617338b5029187ec65a5eaed5e4d0b",
hash_y: "0x3ce70f536652f0dea496393a1e55c4e08b9d55508e16d11e5db40d4810cbc982",
},
TestVector {
personalization: Personalization::NoteCommitment,
input_bits: vec![1, 1, 1, 1, 1, 1, 0],
hash_x: "Fr(0x2fc3bc454c337f71d4f04f86304262fcbfc9ecd808716b92fc42cbe6827f7f1a)",
hash_y: "Fr(0x46d0d25bf1a654eedc6a9b1e5af398925113959feac31b7a2c036ff9b9ec0638)",
hash_x: "0x2fc3bc454c337f71d4f04f86304262fcbfc9ecd808716b92fc42cbe6827f7f1a",
hash_y: "0x46d0d25bf1a654eedc6a9b1e5af398925113959feac31b7a2c036ff9b9ec0638",
},
TestVector {
personalization: Personalization::NoteCommitment,
input_bits: vec![1, 1, 1, 1, 1, 1, 1],
hash_x: "Fr(0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97)",
hash_y: "Fr(0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346)",
hash_x: "0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97",
hash_y: "0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346",
},
TestVector {
personalization: Personalization::NoteCommitment,
input_bits: vec![1, 1, 1, 1, 1, 1, 1, 0, 0],
hash_x: "Fr(0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97)",
hash_y: "Fr(0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346)",
hash_x: "0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97",
hash_y: "0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346",
},
TestVector {
personalization: Personalization::NoteCommitment,
@ -39,8 +39,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1,
0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0,
],
hash_x: "Fr(0x599ab788360ae8c6d5bb7618aec37056d6227408d857fdc394078a3d7afdfe0f)",
hash_y: "Fr(0x4320c373da670e28d168f4ffd72b43208e8c815f40841682c57a3ee1d005a527)",
hash_x: "0x599ab788360ae8c6d5bb7618aec37056d6227408d857fdc394078a3d7afdfe0f",
hash_y: "0x4320c373da670e28d168f4ffd72b43208e8c815f40841682c57a3ee1d005a527",
},
TestVector {
personalization: Personalization::NoteCommitment,
@ -53,8 +53,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0,
1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0,
],
hash_x: "Fr(0x2da510317620f5dfdce1f31db6019f947eedcf02ff2972cff597a5c3ad21f5dd)",
hash_y: "Fr(0x198789969c0c33e6c359b9da4a51771f4d50863f36beef90436944fe568399f2)",
hash_x: "0x2da510317620f5dfdce1f31db6019f947eedcf02ff2972cff597a5c3ad21f5dd",
hash_y: "0x198789969c0c33e6c359b9da4a51771f4d50863f36beef90436944fe568399f2",
},
TestVector {
personalization: Personalization::NoteCommitment,
@ -67,8 +67,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0,
0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1,
],
hash_x: "Fr(0x601247c7e640992d193dfb51df6ed93446687a7f2bcd0e4a598e6feb1ef20c40)",
hash_y: "Fr(0x371931733b73e7b95c2cad55a6cebd15c83619f697c64283e54e5ef61442a743)",
hash_x: "0x601247c7e640992d193dfb51df6ed93446687a7f2bcd0e4a598e6feb1ef20c40",
hash_y: "0x371931733b73e7b95c2cad55a6cebd15c83619f697c64283e54e5ef61442a743",
},
TestVector {
personalization: Personalization::NoteCommitment,
@ -101,8 +101,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1,
1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
],
hash_x: "Fr(0x314192ecb1f2d8806a8108704c875a25d9fb7e444f9f373919adedebe8f2ae27)",
hash_y: "Fr(0x6b12b32f1372ad574799dee9eb591d961b704bf611f55fcc71f7e82cd3330b74)",
hash_x: "0x314192ecb1f2d8806a8108704c875a25d9fb7e444f9f373919adedebe8f2ae27",
hash_y: "0x6b12b32f1372ad574799dee9eb591d961b704bf611f55fcc71f7e82cd3330b74",
},
TestVector {
personalization: Personalization::NoteCommitment,
@ -136,8 +136,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1,
0,
],
hash_x: "Fr(0x0666c2bce7f362a2b807d212e9a577f116891a932affd7addec39fbf372c494e)",
hash_y: "Fr(0x6758bccfaf2e47c07756b96edea23aa8d10c33b38220bd1c411af612eeec18ab)",
hash_x: "0x0666c2bce7f362a2b807d212e9a577f116891a932affd7addec39fbf372c494e",
hash_y: "0x6758bccfaf2e47c07756b96edea23aa8d10c33b38220bd1c411af612eeec18ab",
},
TestVector {
personalization: Personalization::NoteCommitment,
@ -177,8 +177,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0,
1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1,
],
hash_x: "Fr(0x130afe02b99375484efb0998f5331d2178e1d00e803049bb0769099420624f5f)",
hash_y: "Fr(0x5e2fc6970554ffe358652aa7968ac4fcf3de0c830e6ea492e01a38fafb68cd71)",
hash_x: "0x130afe02b99375484efb0998f5331d2178e1d00e803049bb0769099420624f5f",
hash_y: "0x5e2fc6970554ffe358652aa7968ac4fcf3de0c830e6ea492e01a38fafb68cd71",
},
TestVector {
personalization: Personalization::NoteCommitment,
@ -218,32 +218,32 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1,
1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1,
],
hash_x: "Fr(0x67914ebd539961b70f468fa23d4cb42133693a8ac57cd35a1e6369fe34fbedf7)",
hash_y: "Fr(0x44770870c0f0cfe59a10df95d6c21e6f1514a2f464b66377599438c126052d9f)",
hash_x: "0x67914ebd539961b70f468fa23d4cb42133693a8ac57cd35a1e6369fe34fbedf7",
hash_y: "0x44770870c0f0cfe59a10df95d6c21e6f1514a2f464b66377599438c126052d9f",
},
TestVector {
personalization: Personalization::MerkleTree(0),
input_bits: vec![0, 0, 0, 0, 0, 0],
hash_x: "Fr(0x62454a957289b3930d10f3def0d512cfe0ef3de06421321221af3558de9d481d)",
hash_y: "Fr(0x0279f0aebfb66e53ff69fba16b6608dbf4319b944432f45c6e69a3dbd1f7b330)",
hash_x: "0x62454a957289b3930d10f3def0d512cfe0ef3de06421321221af3558de9d481d",
hash_y: "0x0279f0aebfb66e53ff69fba16b6608dbf4319b944432f45c6e69a3dbd1f7b330",
},
TestVector {
personalization: Personalization::MerkleTree(0),
input_bits: vec![0, 0, 0, 0, 0, 0, 0],
hash_x: "Fr(0x283c7880f35179e201161402d9c4556b255917dbbf0142ae60519787d36d4dea)",
hash_y: "Fr(0x648224408b4b83297cd0feb4cdc4eeb224237734931145432793bcd414228dc4)",
hash_x: "0x283c7880f35179e201161402d9c4556b255917dbbf0142ae60519787d36d4dea",
hash_y: "0x648224408b4b83297cd0feb4cdc4eeb224237734931145432793bcd414228dc4",
},
TestVector {
personalization: Personalization::MerkleTree(0),
input_bits: vec![0, 0, 0, 0, 0, 0, 1],
hash_x: "Fr(0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d)",
hash_y: "Fr(0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622)",
hash_x: "0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d",
hash_y: "0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622",
},
TestVector {
personalization: Personalization::MerkleTree(0),
input_bits: vec![0, 0, 0, 0, 0, 0, 1, 0, 0],
hash_x: "Fr(0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d)",
hash_y: "Fr(0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622)",
hash_x: "0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d",
hash_y: "0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622",
},
TestVector {
personalization: Personalization::MerkleTree(0),
@ -256,8 +256,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1,
1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0,
],
hash_x: "Fr(0x20d2b1b0551efe511755d564f8da4f5bf285fd6051331fa5f129ad95b318f6cd)",
hash_y: "Fr(0x2834d96950de67ae80e85545f8333c6e14b5cf5be7325dac768f401e6edd9544)",
hash_x: "0x20d2b1b0551efe511755d564f8da4f5bf285fd6051331fa5f129ad95b318f6cd",
hash_y: "0x2834d96950de67ae80e85545f8333c6e14b5cf5be7325dac768f401e6edd9544",
},
TestVector {
personalization: Personalization::MerkleTree(0),
@ -270,8 +270,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0,
0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0,
],
hash_x: "Fr(0x01f4850a0f40e07186fee1f0a276f52fb12cffe05c18eb2aa18170330a93c555)",
hash_y: "Fr(0x19b0807358e7c8cba9168815ec54c4cd76997c34c592607d172151c48d5377cb)",
hash_x: "0x01f4850a0f40e07186fee1f0a276f52fb12cffe05c18eb2aa18170330a93c555",
hash_y: "0x19b0807358e7c8cba9168815ec54c4cd76997c34c592607d172151c48d5377cb",
},
TestVector {
personalization: Personalization::MerkleTree(0),
@ -284,8 +284,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1,
0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0,
],
hash_x: "Fr(0x26dd81a3ffa37452c6a932d41eb4f2e0fedd531e9af8c2a7935b91dff653879d)",
hash_y: "Fr(0x2fc7aebb729ef5cabf0fb3f883bc2eb2603093850b0ec19c1a3c08b653e7f27f)",
hash_x: "0x26dd81a3ffa37452c6a932d41eb4f2e0fedd531e9af8c2a7935b91dff653879d",
hash_y: "0x2fc7aebb729ef5cabf0fb3f883bc2eb2603093850b0ec19c1a3c08b653e7f27f",
},
TestVector {
personalization: Personalization::MerkleTree(0),
@ -318,8 +318,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1,
1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1,
],
hash_x: "Fr(0x1111740552773b00aa6a2334575aa94102cfbd084290a430c90eb56d6db65b85)",
hash_y: "Fr(0x6560c44b11683c20030626f89456f78a53ae8a89f565956a98ffc554b48fbb1a)",
hash_x: "0x1111740552773b00aa6a2334575aa94102cfbd084290a430c90eb56d6db65b85",
hash_y: "0x6560c44b11683c20030626f89456f78a53ae8a89f565956a98ffc554b48fbb1a",
},
TestVector {
personalization: Personalization::MerkleTree(0),
@ -353,8 +353,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0,
0,
],
hash_x: "Fr(0x429349ea9b5f8163bcda3014b3e15554df5173353fd73f315a49360c97265f68)",
hash_y: "Fr(0x188774bb6de41eba669be5d368942783f937acf2f418385fc5c78479b0a405ee)",
hash_x: "0x429349ea9b5f8163bcda3014b3e15554df5173353fd73f315a49360c97265f68",
hash_y: "0x188774bb6de41eba669be5d368942783f937acf2f418385fc5c78479b0a405ee",
},
TestVector {
personalization: Personalization::MerkleTree(0),
@ -394,8 +394,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0,
0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0,
],
hash_x: "Fr(0x00e827f3ed136f3c91c61c97ab9b7cca0ea53c20e47abb5e226ede297bdd5f37)",
hash_y: "Fr(0x315cc00a54972df6a19f650d3fab5f2ad0fb07397bacb6944568618f2aa76bf6)",
hash_x: "0x00e827f3ed136f3c91c61c97ab9b7cca0ea53c20e47abb5e226ede297bdd5f37",
hash_y: "0x315cc00a54972df6a19f650d3fab5f2ad0fb07397bacb6944568618f2aa76bf6",
},
TestVector {
personalization: Personalization::MerkleTree(0),
@ -435,32 +435,32 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1,
1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0,
],
hash_x: "Fr(0x3ee50557c4aa9158c4bb9d5961208e6c62f55c73ad7c7695a0eba0bcb6d83d05)",
hash_y: "Fr(0x1b1a2be6e47688828aeadf2d37db298eac0c2736c2722b227871fdeeee29de33)",
hash_x: "0x3ee50557c4aa9158c4bb9d5961208e6c62f55c73ad7c7695a0eba0bcb6d83d05",
hash_y: "0x1b1a2be6e47688828aeadf2d37db298eac0c2736c2722b227871fdeeee29de33",
},
TestVector {
personalization: Personalization::MerkleTree(34),
input_bits: vec![0, 1, 0, 0, 0, 1],
hash_x: "Fr(0x61f8e2cb8e945631677b450d5e5669bc6b5f2ec69b321ac550dbe74525d7ac9a)",
hash_y: "Fr(0x4e11951ab9c9400ee38a18bd98cdb9453f1f67141ee9d9bf0c1c157d4fb34f9a)",
hash_x: "0x61f8e2cb8e945631677b450d5e5669bc6b5f2ec69b321ac550dbe74525d7ac9a",
hash_y: "0x4e11951ab9c9400ee38a18bd98cdb9453f1f67141ee9d9bf0c1c157d4fb34f9a",
},
TestVector {
personalization: Personalization::MerkleTree(34),
input_bits: vec![0, 1, 0, 0, 0, 1, 0],
hash_x: "Fr(0x27fa1e296c37dde8448483ce5485c2604d1d830e53812246299773a02ecd519c)",
hash_y: "Fr(0x08e499113675202cb42b4b681a31430814edebd72c5bb3bc3bfedf91fb0605df)",
hash_x: "0x27fa1e296c37dde8448483ce5485c2604d1d830e53812246299773a02ecd519c",
hash_y: "0x08e499113675202cb42b4b681a31430814edebd72c5bb3bc3bfedf91fb0605df",
},
TestVector {
personalization: Personalization::MerkleTree(34),
input_bits: vec![0, 1, 0, 0, 0, 1, 1],
hash_x: "Fr(0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc)",
hash_y: "Fr(0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c)",
hash_x: "0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc",
hash_y: "0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c",
},
TestVector {
personalization: Personalization::MerkleTree(34),
input_bits: vec![0, 1, 0, 0, 0, 1, 1, 0, 0],
hash_x: "Fr(0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc)",
hash_y: "Fr(0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c)",
hash_x: "0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc",
hash_y: "0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c",
},
TestVector {
personalization: Personalization::MerkleTree(34),
@ -473,8 +473,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0,
0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0,
],
hash_x: "Fr(0x544a0b44c35dca64ee806d1af70b7c44134e5d86efed413947657ffd71adf9b2)",
hash_y: "Fr(0x5ddc5dbf12abbbc5561defd3782a32f450b3c398f52ff4629677e59e86e3ab31)",
hash_x: "0x544a0b44c35dca64ee806d1af70b7c44134e5d86efed413947657ffd71adf9b2",
hash_y: "0x5ddc5dbf12abbbc5561defd3782a32f450b3c398f52ff4629677e59e86e3ab31",
},
TestVector {
personalization: Personalization::MerkleTree(34),
@ -487,8 +487,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1,
1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,
],
hash_x: "Fr(0x6cb6490ccb0ca9ccd657146f58a7b800bc4fb2556ee37861227ee8fda724acfb)",
hash_y: "Fr(0x05c6fe100926f5cc441e54e72f024b6b12c907f2ec5680335057896411984c9f)",
hash_x: "0x6cb6490ccb0ca9ccd657146f58a7b800bc4fb2556ee37861227ee8fda724acfb",
hash_y: "0x05c6fe100926f5cc441e54e72f024b6b12c907f2ec5680335057896411984c9f",
},
TestVector {
personalization: Personalization::MerkleTree(34),
@ -501,8 +501,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1,
0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0,
],
hash_x: "Fr(0x40901e2175cb7f06a00c676d54d90e59fd448f11cbbc5eb517f9fea74b795ce2)",
hash_y: "Fr(0x42d512891f91087310c9bc630c8d0ecc014596f884fd6df55dada8195ed726de)",
hash_x: "0x40901e2175cb7f06a00c676d54d90e59fd448f11cbbc5eb517f9fea74b795ce2",
hash_y: "0x42d512891f91087310c9bc630c8d0ecc014596f884fd6df55dada8195ed726de",
},
TestVector {
personalization: Personalization::MerkleTree(34),
@ -535,8 +535,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1,
0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1,
],
hash_x: "Fr(0x66a433542419f1a086ed0663b0e8df2ece9a04065f147896976baba1a916b6dc)",
hash_y: "Fr(0x203bd3672522e1d3c86fa6b9f3b58f20199a4216adfd40982add13a856f6f3de)",
hash_x: "0x66a433542419f1a086ed0663b0e8df2ece9a04065f147896976baba1a916b6dc",
hash_y: "0x203bd3672522e1d3c86fa6b9f3b58f20199a4216adfd40982add13a856f6f3de",
},
TestVector {
personalization: Personalization::MerkleTree(34),
@ -570,8 +570,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0,
1,
],
hash_x: "Fr(0x119db3b38086c1a3c6c6f53c529ee62d9311d69c2d8aeeafa6e172e650d3afda)",
hash_y: "Fr(0x72287540be7d2b0f58f5c73eaa53c55bea6b79dd79873b4e47cc11787bb9a15d)",
hash_x: "0x119db3b38086c1a3c6c6f53c529ee62d9311d69c2d8aeeafa6e172e650d3afda",
hash_y: "0x72287540be7d2b0f58f5c73eaa53c55bea6b79dd79873b4e47cc11787bb9a15d",
},
TestVector {
personalization: Personalization::MerkleTree(34),
@ -611,8 +611,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1,
1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0,
],
hash_x: "Fr(0x446efdcf89b70ba2b03427a0893008181d0fc4e76b84b1a500d7ee523c8e3666)",
hash_y: "Fr(0x125ee0048efb0372b92c3c15d51a7c5c77a712054cc4fdd0774563da46ec7289)",
hash_x: "0x446efdcf89b70ba2b03427a0893008181d0fc4e76b84b1a500d7ee523c8e3666",
hash_y: "0x125ee0048efb0372b92c3c15d51a7c5c77a712054cc4fdd0774563da46ec7289",
},
TestVector {
personalization: Personalization::MerkleTree(34),
@ -652,8 +652,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0,
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1,
],
hash_x: "Fr(0x72723bf0573bcb4b72d4184cfeb707d9556b7f705f56a4652707a36f2edf10f7)",
hash_y: "Fr(0x3a7f0999a6a1393bd49fc82302e7352e01176fbebb0192bf5e6ef39eb8c585ad)",
hash_x: "0x72723bf0573bcb4b72d4184cfeb707d9556b7f705f56a4652707a36f2edf10f7",
hash_y: "0x3a7f0999a6a1393bd49fc82302e7352e01176fbebb0192bf5e6ef39eb8c585ad",
},
TestVector {
personalization: Personalization::MerkleTree(27),
@ -666,8 +666,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0,
1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0,
],
hash_x: "Fr(0x414f6ba05f6b92da1f9051950769e1083d05615def32b016ae424309828a11f4)",
hash_y: "Fr(0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9)",
hash_x: "0x414f6ba05f6b92da1f9051950769e1083d05615def32b016ae424309828a11f4",
hash_y: "0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9",
},
TestVector {
personalization: Personalization::MerkleTree(36),
@ -680,8 +680,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
],
hash_x: "Fr(0x62d6fe1e373225a5695f3115aed8265c59e2d6275ceef6bbc53fde3fc6594024)",
hash_y: "Fr(0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1)",
hash_x: "0x62d6fe1e373225a5695f3115aed8265c59e2d6275ceef6bbc53fde3fc6594024",
hash_y: "0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1",
},
TestVector {
personalization: Personalization::MerkleTree(0),
@ -694,8 +694,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
],
hash_x: "Fr(0x1116a934f26b57a2c9daa6f25ac9b1a8f9dacddba30f65433ac021bf39a6bfdd)",
hash_y: "Fr(0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1)",
hash_x: "0x1116a934f26b57a2c9daa6f25ac9b1a8f9dacddba30f65433ac021bf39a6bfdd",
hash_y: "0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1",
},
TestVector {
personalization: Personalization::NoteCommitment,
@ -708,8 +708,8 @@ pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
],
hash_x: "Fr(0x329e3bb2ca31ea6e13a986730237f6fd16b842a510cbabe851bdbcf57d75ee0d)",
hash_y: "Fr(0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9)",
hash_x: "0x329e3bb2ca31ea6e13a986730237f6fd16b842a510cbabe851bdbcf57d75ee0d",
hash_y: "0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9",
},
];
}

View File

@ -1,12 +1,8 @@
//! Structs for building transactions.
use crate::primitives::{Diversifier, Note, PaymentAddress};
use crate::zip32::ExtendedSpendingKey;
use crate::{
jubjub::fs::Fs,
primitives::{Diversifier, Note, PaymentAddress},
};
use ff::Field;
use pairing::bls12_381::{Bls12, Fr};
use rand::{rngs::OsRng, seq::SliceRandom, CryptoRng, RngCore};
use std::error;
use std::fmt;
@ -26,7 +22,6 @@ use crate::{
signature_hash_data, Transaction, TransactionData, SIGHASH_ALL,
},
util::generate_random_rseed,
JUBJUB,
};
#[cfg(feature = "transparent-inputs")]
@ -75,15 +70,15 @@ impl error::Error for Error {}
struct SpendDescriptionInfo {
extsk: ExtendedSpendingKey,
diversifier: Diversifier,
note: Note<Bls12>,
alpha: Fs,
note: Note,
alpha: jubjub::Fr,
merkle_path: MerklePath<Node>,
}
pub struct SaplingOutput {
ovk: OutgoingViewingKey,
to: PaymentAddress<Bls12>,
note: Note<Bls12>,
to: PaymentAddress,
note: Note,
memo: Memo,
}
@ -92,11 +87,11 @@ impl SaplingOutput {
height: u32,
rng: &mut R,
ovk: OutgoingViewingKey,
to: PaymentAddress<Bls12>,
to: PaymentAddress,
value: Amount,
memo: Option<Memo>,
) -> Result<Self, Error> {
let g_d = match to.g_d(&JUBJUB) {
let g_d = match to.g_d() {
Some(g_d) => g_d,
None => return Err(Error::InvalidAddress),
};
@ -143,7 +138,7 @@ impl SaplingOutput {
self.note.value,
);
let cmu = self.note.cm(&JUBJUB);
let cmu = self.note.cm();
let enc_ciphertext = encryptor.encrypt_note_plaintext();
let out_ciphertext = encryptor.encrypt_outgoing_plaintext(&cv, &cmu);
@ -310,11 +305,11 @@ pub struct Builder<P: consensus::Parameters, R: RngCore + CryptoRng> {
height: u32,
mtx: TransactionData,
fee: Amount,
anchor: Option<Fr>,
anchor: Option<bls12_381::Scalar>,
spends: Vec<SpendDescriptionInfo>,
outputs: Vec<SaplingOutput>,
transparent_inputs: TransparentInputs,
change_address: Option<(OutgoingViewingKey, PaymentAddress<Bls12>)>,
change_address: Option<(OutgoingViewingKey, PaymentAddress)>,
phantom: PhantomData<P>,
}
@ -369,13 +364,13 @@ impl<P: consensus::Parameters, R: RngCore + CryptoRng> Builder<P, R> {
&mut self,
extsk: ExtendedSpendingKey,
diversifier: Diversifier,
note: Note<Bls12>,
note: Note,
merkle_path: MerklePath<Node>,
) -> Result<(), Error> {
// Consistency check: all anchors must equal the first one
let cm = Node::new(note.cm(&JUBJUB).into());
let cm = Node::new(note.cm().into());
if let Some(anchor) = self.anchor {
let path_root: Fr = merkle_path.root(cm).into();
let path_root: bls12_381::Scalar = merkle_path.root(cm).into();
if path_root != anchor {
return Err(Error::AnchorMismatch);
}
@ -383,7 +378,7 @@ impl<P: consensus::Parameters, R: RngCore + CryptoRng> Builder<P, R> {
self.anchor = Some(merkle_path.root(cm).into())
}
let alpha = Fs::random(&mut self.rng);
let alpha = jubjub::Fr::random(&mut self.rng);
self.mtx.value_balance += Amount::from_u64(note.value).map_err(|_| Error::InvalidAmount)?;
@ -402,7 +397,7 @@ impl<P: consensus::Parameters, R: RngCore + CryptoRng> Builder<P, R> {
pub fn add_sapling_output(
&mut self,
ovk: OutgoingViewingKey,
to: PaymentAddress<Bls12>,
to: PaymentAddress,
value: Amount,
memo: Option<Memo>,
) -> Result<(), Error> {
@ -448,7 +443,7 @@ impl<P: consensus::Parameters, R: RngCore + CryptoRng> Builder<P, R> {
///
/// By default, change is sent to the Sapling address corresponding to the first note
/// being spent (i.e. the first call to [`Builder::add_sapling_spend`]).
pub fn send_change_to(&mut self, ovk: OutgoingViewingKey, to: PaymentAddress<Bls12>) {
pub fn send_change_to(&mut self, ovk: OutgoingViewingKey, to: PaymentAddress) {
self.change_address = Some((ovk, to));
}
@ -548,13 +543,12 @@ impl<P: consensus::Parameters, R: RngCore + CryptoRng> Builder<P, R> {
let anchor = self.anchor.expect("anchor was set if spends were added");
for (i, (pos, spend)) in spends.iter().enumerate() {
let proof_generation_key = spend.extsk.expsk.proof_generation_key(&JUBJUB);
let proof_generation_key = spend.extsk.expsk.proof_generation_key();
let mut nullifier = [0u8; 32];
nullifier.copy_from_slice(&spend.note.nf(
&proof_generation_key.to_viewing_key(&JUBJUB),
&proof_generation_key.to_viewing_key(),
spend.merkle_path.position,
&JUBJUB,
));
let (zkproof, cv, rk) = prover
@ -601,7 +595,7 @@ impl<P: consensus::Parameters, R: RngCore + CryptoRng> Builder<P, R> {
let mut d = [0; 11];
self.rng.fill_bytes(&mut d);
diversifier = Diversifier(d);
if let Some(val) = diversifier.g_d::<Bls12>(&JUBJUB) {
if let Some(val) = diversifier.g_d() {
g_d = val;
break;
}
@ -610,8 +604,8 @@ impl<P: consensus::Parameters, R: RngCore + CryptoRng> Builder<P, R> {
};
let (pk_d, payment_address) = loop {
let dummy_ivk = Fs::random(&mut self.rng);
let pk_d = g_d.mul(dummy_ivk, &JUBJUB);
let dummy_ivk = jubjub::Fr::random(&mut self.rng);
let pk_d = g_d * dummy_ivk;
if let Some(addr) = PaymentAddress::from_parts(diversifier, pk_d.clone()) {
break (pk_d, addr);
}
@ -631,7 +625,7 @@ impl<P: consensus::Parameters, R: RngCore + CryptoRng> Builder<P, R> {
};
let esk = dummy_note.generate_or_derive_esk(&mut self.rng);
let epk = dummy_note.g_d.mul(esk, &JUBJUB);
let epk = dummy_note.g_d * esk;
let (zkproof, cv) = prover.output_proof(
&mut ctx,
@ -641,7 +635,7 @@ impl<P: consensus::Parameters, R: RngCore + CryptoRng> Builder<P, R> {
dummy_note.value,
);
let cmu = dummy_note.cm(&JUBJUB);
let cmu = dummy_note.cm();
let mut enc_ciphertext = [0u8; 580];
let mut out_ciphertext = [0u8; 80];
@ -680,7 +674,6 @@ impl<P: consensus::Parameters, R: RngCore + CryptoRng> Builder<P, R> {
spend.alpha,
&sighash,
&mut self.rng,
&JUBJUB,
));
}
@ -712,8 +705,6 @@ mod tests {
use rand_core::OsRng;
use std::marker::PhantomData;
use crate::jubjub::fs::Fs;
use super::{Builder, Error};
use crate::{
consensus,
@ -725,7 +716,6 @@ mod tests {
sapling::Node,
transaction::components::Amount,
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
JUBJUB,
};
#[test]
@ -788,9 +778,9 @@ mod tests {
let mut rng = OsRng;
let note1 = to
.create_note(50000, Rseed::BeforeZip212(Fs::random(&mut rng)), &JUBJUB)
.create_note(50000, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)))
.unwrap();
let cm1 = Node::new(note1.cm(&JUBJUB).to_repr());
let cm1 = Node::new(note1.cm().to_repr());
let mut tree = CommitmentTree::new();
tree.append(cm1).unwrap();
let witness1 = IncrementalWitness::from_tree(&tree);
@ -887,9 +877,9 @@ mod tests {
}
let note1 = to
.create_note(59999, Rseed::BeforeZip212(Fs::random(&mut rng)), &JUBJUB)
.create_note(59999, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)))
.unwrap();
let cm1 = Node::new(note1.cm(&JUBJUB).to_repr());
let cm1 = Node::new(note1.cm().to_repr());
let mut tree = CommitmentTree::new();
tree.append(cm1).unwrap();
let mut witness1 = IncrementalWitness::from_tree(&tree);
@ -927,9 +917,9 @@ mod tests {
}
let note2 = to
.create_note(1, Rseed::BeforeZip212(Fs::random(&mut rng)), &JUBJUB)
.create_note(1, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)))
.unwrap();
let cm2 = Node::new(note2.cm(&JUBJUB).to_repr());
let cm2 = Node::new(note2.cm().to_repr());
tree.append(cm2).unwrap();
witness1.append(cm2).unwrap();
let witness2 = IncrementalWitness::from_tree(&tree);

View File

@ -1,14 +1,12 @@
//! Structs representing the components within Zcash transactions.
use crate::jubjub::{edwards, Unknown};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use ff::PrimeField;
use pairing::bls12_381::{Bls12, Fr, FrRepr};
use group::GroupEncoding;
use std::io::{self, Read, Write};
use crate::legacy::Script;
use crate::redjubjub::{PublicKey, Signature};
use crate::JUBJUB;
pub mod amount;
pub use self::amount::Amount;
@ -118,10 +116,10 @@ impl TxOut {
}
pub struct SpendDescription {
pub cv: edwards::Point<Bls12, Unknown>,
pub anchor: Fr,
pub cv: jubjub::ExtendedPoint,
pub anchor: bls12_381::Scalar,
pub nullifier: [u8; 32],
pub rk: PublicKey<Bls12>,
pub rk: PublicKey,
pub zkproof: [u8; GROTH_PROOF_SIZE],
pub spend_auth_sig: Option<Signature>,
}
@ -142,13 +140,21 @@ impl SpendDescription {
// - Canonical encoding is enforced here.
// - "Not small order" is enforced in SaplingVerificationContext::check_spend()
// (located in zcash_proofs::sapling::verifier).
let cv = edwards::Point::<Bls12, Unknown>::read(&mut reader, &JUBJUB)?;
let cv = {
let mut bytes = [0; 32];
reader.read_exact(&mut bytes)?;
let cv = jubjub::ExtendedPoint::from_bytes(&bytes);
if cv.is_none().into() {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid cv"));
}
cv.unwrap()
};
// Consensus rule (§7.3): Canonical encoding is enforced here
let anchor = {
let mut f = FrRepr([0; 32]);
reader.read_exact(&mut f.0)?;
Fr::from_repr(f)
let mut f = [0; 32];
reader.read_exact(&mut f)?;
bls12_381::Scalar::from_repr(f)
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "anchor not in field"))?
};
@ -158,7 +164,7 @@ impl SpendDescription {
// Consensus rules (§4.4):
// - Canonical encoding is enforced here.
// - "Not small order" is enforced in SaplingVerificationContext::check_spend()
let rk = PublicKey::<Bls12>::read(&mut reader, &JUBJUB)?;
let rk = PublicKey::read(&mut reader)?;
// Consensus rules (§4.4):
// - Canonical encoding is enforced by the API of SaplingVerificationContext::check_spend()
@ -183,7 +189,7 @@ impl SpendDescription {
}
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
self.cv.write(&mut writer)?;
writer.write_all(&self.cv.to_bytes())?;
writer.write_all(self.anchor.to_repr().as_ref())?;
writer.write_all(&self.nullifier)?;
self.rk.write(&mut writer)?;
@ -199,9 +205,9 @@ impl SpendDescription {
}
pub struct OutputDescription {
pub cv: edwards::Point<Bls12, Unknown>,
pub cmu: Fr,
pub ephemeral_key: edwards::Point<Bls12, Unknown>,
pub cv: jubjub::ExtendedPoint,
pub cmu: bls12_381::Scalar,
pub ephemeral_key: jubjub::ExtendedPoint,
pub enc_ciphertext: [u8; 580],
pub out_ciphertext: [u8; 80],
pub zkproof: [u8; GROTH_PROOF_SIZE],
@ -218,25 +224,44 @@ impl std::fmt::Debug for OutputDescription {
}
impl OutputDescription {
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> {
// Consensus rules (§4.5):
// - Canonical encoding is enforced here.
// - "Not small order" is enforced in SaplingVerificationContext::check_output()
// (located in zcash_proofs::sapling::verifier).
let cv = edwards::Point::<Bls12, Unknown>::read(&mut reader, &JUBJUB)?;
let cv = {
let mut bytes = [0; 32];
reader.read_exact(&mut bytes)?;
let cv = jubjub::ExtendedPoint::from_bytes(&bytes);
if cv.is_none().into() {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid cv"));
}
cv.unwrap()
};
// Consensus rule (§7.4): Canonical encoding is enforced here
let cmu = {
let mut f = FrRepr([0; 32]);
reader.read_exact(&mut f.0)?;
Fr::from_repr(f)
let mut f = [0; 32];
reader.read_exact(&mut f)?;
bls12_381::Scalar::from_repr(f)
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "cmu not in field"))?
};
// Consensus rules (§4.5):
// - Canonical encoding is enforced here.
// - "Not small order" is enforced in SaplingVerificationContext::check_output()
let ephemeral_key = edwards::Point::<Bls12, Unknown>::read(&mut reader, &JUBJUB)?;
let ephemeral_key = {
let mut bytes = [0; 32];
reader.read_exact(&mut bytes)?;
let ephemeral_key = jubjub::ExtendedPoint::from_bytes(&bytes);
if ephemeral_key.is_none().into() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid ephemeral_key",
));
}
ephemeral_key.unwrap()
};
let mut enc_ciphertext = [0; 580];
let mut out_ciphertext = [0; 80];
@ -261,9 +286,9 @@ impl OutputDescription {
}
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
self.cv.write(&mut writer)?;
writer.write_all(&self.cv.to_bytes())?;
writer.write_all(self.cmu.to_repr().as_ref())?;
self.ephemeral_key.write(&mut writer)?;
writer.write_all(&self.ephemeral_key.to_bytes())?;
writer.write_all(&self.enc_ciphertext)?;
writer.write_all(&self.out_ciphertext)?;
writer.write_all(&self.zkproof)

View File

@ -1,6 +1,7 @@
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
use byteorder::{LittleEndian, WriteBytesExt};
use ff::PrimeField;
use group::GroupEncoding;
use super::{
components::{Amount, TxOut},
@ -127,7 +128,7 @@ fn joinsplits_hash(tx: &TransactionData) -> Blake2bHash {
fn shielded_spends_hash(tx: &TransactionData) -> Blake2bHash {
let mut data = Vec::with_capacity(tx.shielded_spends.len() * 384);
for s_spend in &tx.shielded_spends {
s_spend.cv.write(&mut data).unwrap();
data.extend_from_slice(&s_spend.cv.to_bytes());
data.extend_from_slice(s_spend.anchor.to_repr().as_ref());
data.extend_from_slice(&s_spend.nullifier);
s_spend.rk.write(&mut data).unwrap();

View File

@ -1,12 +1,8 @@
use ff::Field;
use pairing::bls12_381::Bls12;
use rand_core::OsRng;
use crate::jubjub::{fs::Fs, FixedGenerators};
use super::{components::Amount, sighash::signature_hash, Transaction, TransactionData};
use crate::redjubjub::PrivateKey;
use crate::JUBJUB;
use crate::{constants::SPENDING_KEY_GENERATOR, redjubjub::PrivateKey};
#[test]
fn tx_read_write() {
@ -56,13 +52,8 @@ fn tx_write_rejects_unexpected_binding_sig() {
// Fails with an unexpected binding signature
{
let rng = &mut OsRng;
let sk = PrivateKey::<Bls12>(Fs::random(rng));
let sig = sk.sign(
b"Foo bar",
rng,
FixedGenerators::SpendingKeyGenerator,
&JUBJUB,
);
let sk = PrivateKey(jubjub::Fr::random(rng));
let sig = sk.sign(b"Foo bar", rng, SPENDING_KEY_GENERATOR);
let mut tx = TransactionData::new();
tx.binding_sig = Some(sig);

View File

@ -1,31 +1,26 @@
use blake2b_simd::Params;
use crate::{
consensus,
consensus::NetworkUpgrade,
jubjub::{fs::Fs, JubjubEngine, ToUniform},
primitives::Rseed,
};
use crate::{consensus, consensus::NetworkUpgrade, primitives::Rseed};
use ff::Field;
use rand_core::{CryptoRng, RngCore};
pub fn hash_to_scalar<E: JubjubEngine>(persona: &[u8], a: &[u8], b: &[u8]) -> E::Fs {
pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> jubjub::Fr {
let mut hasher = Params::new().hash_length(64).personal(persona).to_state();
hasher.update(a);
hasher.update(b);
let ret = hasher.finalize();
E::Fs::to_uniform(ret.as_ref())
jubjub::Fr::from_bytes_wide(ret.as_array())
}
pub fn generate_random_rseed<P: consensus::Parameters, R: RngCore + CryptoRng>(
height: u32,
rng: &mut R,
) -> Rseed<Fs> {
) -> Rseed {
if P::is_nu_active(NetworkUpgrade::Canopy, height) {
let mut buffer = [0u8; 32];
&rng.fill_bytes(&mut buffer);
Rseed::AfterZip212(buffer)
} else {
Rseed::BeforeZip212(Fs::random(rng))
Rseed::BeforeZip212(jubjub::Fr::random(rng))
}
}

View File

@ -6,18 +6,16 @@ use aes::Aes256;
use blake2b_simd::Params as Blake2bParams;
use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt};
use fpe::ff1::{BinaryNumeralString, FF1};
use pairing::bls12_381::Bls12;
use std::ops::AddAssign;
use crate::{
jubjub::{fs::Fs, FixedGenerators, JubjubEngine, JubjubParams, ToUniform},
constants::{PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR},
primitives::{Diversifier, PaymentAddress, ViewingKey},
};
use std::io::{self, Read, Write};
use crate::{
keys::{prf_expand, prf_expand_vec, ExpandedSpendingKey, FullViewingKey, OutgoingViewingKey},
JUBJUB,
use crate::keys::{
prf_expand, prf_expand_vec, ExpandedSpendingKey, FullViewingKey, OutgoingViewingKey,
};
pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &[u8; 16] = b"ZcashIP32Sapling";
@ -36,8 +34,8 @@ fn derive_child_ovk(parent: &OutgoingViewingKey, i_l: &[u8]) -> OutgoingViewingK
/// A Sapling full viewing key fingerprint
struct FVKFingerprint([u8; 32]);
impl<E: JubjubEngine> From<&FullViewingKey<E>> for FVKFingerprint {
fn from(fvk: &FullViewingKey<E>) -> Self {
impl From<&FullViewingKey> for FVKFingerprint {
fn from(fvk: &FullViewingKey) -> Self {
let mut h = Blake2bParams::new()
.hash_length(32)
.personal(ZIP32_SAPLING_FVFP_PERSONALIZATION)
@ -154,7 +152,7 @@ impl DiversifierKey {
let d_j = Diversifier(d_j);
// Return (j, d_j) if valid, else increment j and try again
match d_j.g_d::<Bls12>(&JUBJUB) {
match d_j.g_d() {
Some(_) => return Ok((j, d_j)),
None => {
if j.increment().is_err() {
@ -173,7 +171,7 @@ pub struct ExtendedSpendingKey {
parent_fvk_tag: FVKTag,
child_index: ChildIndex,
chain_code: ChainCode,
pub expsk: ExpandedSpendingKey<Bls12>,
pub expsk: ExpandedSpendingKey,
dk: DiversifierKey,
}
@ -184,7 +182,7 @@ pub struct ExtendedFullViewingKey {
parent_fvk_tag: FVKTag,
child_index: ChildIndex,
chain_code: ChainCode,
pub fvk: FullViewingKey<Bls12>,
pub fvk: FullViewingKey,
dk: DiversifierKey,
}
@ -297,7 +295,7 @@ impl ExtendedSpendingKey {
}
pub fn derive_child(&self, i: ChildIndex) -> Self {
let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk, &JUBJUB);
let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk);
let tmp = match i {
ChildIndex::Hardened(i) => {
let mut le_i = [0; 4];
@ -326,8 +324,8 @@ impl ExtendedSpendingKey {
child_index: i,
chain_code: ChainCode(c_i),
expsk: {
let mut ask = Fs::to_uniform(prf_expand(i_l, &[0x13]).as_bytes());
let mut nsk = Fs::to_uniform(prf_expand(i_l, &[0x14]).as_bytes());
let mut ask = jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x13]).as_array());
let mut nsk = jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x14]).as_array());
ask.add_assign(&self.expsk.ask);
nsk.add_assign(&self.expsk.nsk);
let ovk = derive_child_ovk(&self.expsk.ovk, i_l);
@ -337,7 +335,7 @@ impl ExtendedSpendingKey {
}
}
pub fn default_address(&self) -> Result<(DiversifierIndex, PaymentAddress<Bls12>), ()> {
pub fn default_address(&self) -> Result<(DiversifierIndex, PaymentAddress), ()> {
ExtendedFullViewingKey::from(self).default_address()
}
}
@ -349,7 +347,7 @@ impl<'a> From<&'a ExtendedSpendingKey> for ExtendedFullViewingKey {
parent_fvk_tag: xsk.parent_fvk_tag,
child_index: xsk.child_index,
chain_code: xsk.chain_code,
fvk: FullViewingKey::from_expanded_spending_key(&xsk.expsk, &JUBJUB),
fvk: FullViewingKey::from_expanded_spending_key(&xsk.expsk),
dk: xsk.dk,
}
}
@ -363,7 +361,7 @@ impl ExtendedFullViewingKey {
let i = reader.read_u32::<LittleEndian>()?;
let mut c = [0; 32];
reader.read_exact(&mut c)?;
let fvk = FullViewingKey::read(&mut reader, &*JUBJUB)?;
let fvk = FullViewingKey::read(&mut reader)?;
let mut dk = [0; 32];
reader.read_exact(&mut dk)?;
@ -410,16 +408,10 @@ impl ExtendedFullViewingKey {
child_index: i,
chain_code: ChainCode(c_i),
fvk: {
let i_ask = Fs::to_uniform(prf_expand(i_l, &[0x13]).as_bytes());
let i_nsk = Fs::to_uniform(prf_expand(i_l, &[0x14]).as_bytes());
let ak = JUBJUB
.generator(FixedGenerators::SpendingKeyGenerator)
.mul(i_ask, &JUBJUB)
.add(&self.fvk.vk.ak, &JUBJUB);
let nk = JUBJUB
.generator(FixedGenerators::ProofGenerationKey)
.mul(i_nsk, &JUBJUB)
.add(&self.fvk.vk.nk, &JUBJUB);
let i_ask = jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x13]).as_array());
let i_nsk = jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x14]).as_array());
let ak = (SPENDING_KEY_GENERATOR * i_ask) + self.fvk.vk.ak;
let nk = (PROOF_GENERATION_KEY_GENERATOR * i_nsk) + self.fvk.vk.nk;
FullViewingKey {
vk: ViewingKey { ak, nk },
@ -430,21 +422,18 @@ impl ExtendedFullViewingKey {
})
}
pub fn address(
&self,
j: DiversifierIndex,
) -> Result<(DiversifierIndex, PaymentAddress<Bls12>), ()> {
pub fn address(&self, j: DiversifierIndex) -> Result<(DiversifierIndex, PaymentAddress), ()> {
let (j, d_j) = match self.dk.diversifier(j) {
Ok(ret) => ret,
Err(()) => return Err(()),
};
match self.fvk.vk.to_payment_address(d_j, &JUBJUB) {
match self.fvk.vk.to_payment_address(d_j) {
Some(addr) => Ok((j, addr)),
None => Err(()),
}
}
pub fn default_address(&self) -> Result<(DiversifierIndex, PaymentAddress<Bls12>), ()> {
pub fn default_address(&self) -> Result<(DiversifierIndex, PaymentAddress), ()> {
self.address(DiversifierIndex::new())
}
}
@ -454,6 +443,7 @@ mod tests {
use super::*;
use ff::PrimeField;
use group::GroupEncoding;
#[test]
fn derive_nonhardened_child() {
@ -1030,11 +1020,8 @@ mod tests {
let xfvk = &xfvks[j];
let tv = &test_vectors[j];
let mut buf = [0; 32];
xfvk.fvk.vk.ak.write(&mut buf[..]).unwrap();
assert_eq!(buf, tv.ak);
xfvk.fvk.vk.nk.write(&mut buf[..]).unwrap();
assert_eq!(buf, tv.nk);
assert_eq!(xfvk.fvk.vk.ak.to_bytes(), tv.ak);
assert_eq!(xfvk.fvk.vk.nk.to_bytes(), tv.nk);
assert_eq!(xfvk.fvk.ovk.0, tv.ovk);
assert_eq!(xfvk.dk.0, tv.dk);

View File

@ -1,17 +1,16 @@
use bellman::groth16::*;
use bls12_381::Bls12;
use ff::Field;
use pairing::bls12_381::{Bls12, Fr};
use group::Group;
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
use std::time::{Duration, Instant};
use zcash_primitives::jubjub::{edwards, fs, JubjubBls12};
use zcash_primitives::primitives::{Diversifier, ProofGenerationKey, ValueCommitment};
use zcash_proofs::circuit::sapling::Spend;
const TREE_DEPTH: usize = 32;
fn main() {
let jubjub_params = &JubjubBls12::new();
let rng = &mut XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
@ -19,8 +18,7 @@ fn main() {
println!("Creating sample parameters...");
let groth_params = generate_random_parameters::<Bls12, _, _>(
Spend::<Bls12> {
params: jubjub_params,
Spend {
value_commitment: None,
proof_generation_key: None,
payment_address: None,
@ -37,20 +35,20 @@ fn main() {
let mut total_time = Duration::new(0, 0);
for _ in 0..SAMPLES {
let value_commitment = ValueCommitment::<Bls12> {
let value_commitment = ValueCommitment {
value: 1,
randomness: fs::Fs::random(rng),
randomness: jubjub::Fr::random(rng),
};
let nsk = fs::Fs::random(rng);
let ak = edwards::Point::rand(rng, jubjub_params).mul_by_cofactor(jubjub_params);
let nsk = jubjub::Fr::random(rng);
let ak = jubjub::SubgroupPoint::random(rng);
let proof_generation_key = ProofGenerationKey {
ak: ak.clone(),
nsk: nsk.clone(),
};
let viewing_key = proof_generation_key.to_viewing_key(jubjub_params);
let viewing_key = proof_generation_key.to_viewing_key();
let payment_address;
@ -61,21 +59,21 @@ fn main() {
Diversifier(d)
};
if let Some(p) = viewing_key.to_payment_address(diversifier, jubjub_params) {
if let Some(p) = viewing_key.to_payment_address(diversifier) {
payment_address = p;
break;
}
}
let commitment_randomness = fs::Fs::random(rng);
let auth_path = vec![Some((Fr::random(rng), rng.next_u32() % 2 != 0)); TREE_DEPTH];
let ar = fs::Fs::random(rng);
let anchor = Fr::random(rng);
let commitment_randomness = jubjub::Fr::random(rng);
let auth_path =
vec![Some((bls12_381::Scalar::random(rng), rng.next_u32() % 2 != 0)); TREE_DEPTH];
let ar = jubjub::Fr::random(rng);
let anchor = bls12_381::Scalar::random(rng);
let start = Instant::now();
let _ = create_random_proof(
Spend {
params: jubjub_params,
value_commitment: Some(value_commitment),
proof_generation_key: Some(proof_generation_key),
payment_address: Some(payment_address),

File diff suppressed because it is too large Load Diff

View File

@ -4,9 +4,10 @@ use super::ecc::{EdwardsPoint, MontgomeryPoint};
use bellman::gadgets::boolean::Boolean;
use bellman::gadgets::lookup::*;
use bellman::{ConstraintSystem, SynthesisError};
use zcash_primitives::jubjub::*;
pub use zcash_primitives::pedersen_hash::Personalization;
use crate::constants::PEDERSEN_CIRCUIT_GENERATORS;
fn get_constant_bools(person: &Personalization) -> Vec<Boolean> {
person
.get_bits()
@ -15,21 +16,20 @@ fn get_constant_bools(person: &Personalization) -> Vec<Boolean> {
.collect()
}
pub fn pedersen_hash<E: JubjubEngine, CS>(
pub fn pedersen_hash<CS>(
mut cs: CS,
personalization: Personalization,
bits: &[Boolean],
params: &E::Params,
) -> Result<EdwardsPoint<E>, SynthesisError>
) -> Result<EdwardsPoint, SynthesisError>
where
CS: ConstraintSystem<E::Fr>,
CS: ConstraintSystem<bls12_381::Scalar>,
{
let personalization = get_constant_bools(&personalization);
assert_eq!(personalization.len(), 6);
let mut edwards_result = None;
let mut bits = personalization.iter().chain(bits.iter()).peekable();
let mut segment_generators = params.pedersen_circuit_generators().iter();
let mut segment_generators = PEDERSEN_CIRCUIT_GENERATORS.iter();
let boolean_false = Boolean::constant(false);
let mut segment_i = 0;
@ -60,7 +60,6 @@ where
format!("addition of segment {}, window {}", segment_i, window_i)
}),
segment_result,
params,
)?;
}
}
@ -83,7 +82,6 @@ where
// Convert this segment into twisted Edwards form.
let segment_result = segment_result.into_edwards(
cs.namespace(|| format!("conversion of segment {} into edwards", segment_i)),
params,
)?;
match edwards_result {
@ -91,7 +89,6 @@ where
*edwards_result = segment_result.add(
cs.namespace(|| format!("addition of segment {} to accumulator", segment_i)),
edwards_result,
params,
)?;
}
None => {
@ -111,7 +108,7 @@ mod test {
use bellman::gadgets::boolean::{AllocatedBit, Boolean};
use bellman::gadgets::test::*;
use ff::PrimeField;
use pairing::bls12_381::{Bls12, Fr};
use group::Curve;
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
use zcash_primitives::pedersen_hash;
@ -147,7 +144,6 @@ mod test {
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let params = &JubjubBls12::new();
let leaves_len = 2 * 255;
let note_len = 64 + 256 + 256;
@ -177,11 +173,10 @@ mod test {
})
.collect();
pedersen_hash::<Bls12, _>(
pedersen_hash(
cs.namespace(|| "pedersen hash"),
Personalization::NoteCommitment,
&input_bools,
params,
)
.unwrap();
@ -206,7 +201,6 @@ mod test {
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let params = &JubjubBls12::new();
for length in 0..751 {
for _ in 0..5 {
@ -225,36 +219,33 @@ mod test {
})
.collect();
let res = pedersen_hash::<Bls12, _>(
let res = pedersen_hash(
cs.namespace(|| "pedersen hash"),
Personalization::MerkleTree(1),
&input_bools,
params,
)
.unwrap();
assert!(cs.is_satisfied());
let expected = pedersen_hash::pedersen_hash::<Bls12, _>(
let expected = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash(
Personalization::MerkleTree(1),
input.clone().into_iter(),
params,
)
.to_xy();
))
.to_affine();
assert_eq!(res.get_x().get_value().unwrap(), expected.0);
assert_eq!(res.get_y().get_value().unwrap(), expected.1);
assert_eq!(res.get_x().get_value().unwrap(), expected.get_u());
assert_eq!(res.get_y().get_value().unwrap(), expected.get_v());
// Test against the output of a different personalization
let unexpected = pedersen_hash::pedersen_hash::<Bls12, _>(
let unexpected = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash(
Personalization::MerkleTree(0),
input.into_iter(),
params,
)
.to_xy();
))
.to_affine();
assert!(res.get_x().get_value().unwrap() != unexpected.0);
assert!(res.get_y().get_value().unwrap() != unexpected.1);
assert!(res.get_x().get_value().unwrap() != unexpected.get_u());
assert!(res.get_y().get_value().unwrap() != unexpected.get_v());
}
}
}
@ -265,7 +256,6 @@ mod test {
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
let params = &JubjubBls12::new();
let expected_xs = [
"28161926966428986673895580777285905189725480206811328272001879986576840909576",
@ -291,11 +281,10 @@ mod test {
})
.collect();
let res = pedersen_hash::<Bls12, _>(
let res = pedersen_hash(
cs.namespace(|| "pedersen hash"),
Personalization::MerkleTree(1),
&input_bools,
params,
)
.unwrap();
@ -303,11 +292,11 @@ mod test {
assert_eq!(
res.get_x().get_value().unwrap(),
Fr::from_str(expected_xs[length - 300]).unwrap()
bls12_381::Scalar::from_str(expected_xs[length - 300]).unwrap()
);
assert_eq!(
res.get_y().get_value().unwrap(),
Fr::from_str(expected_ys[length - 300]).unwrap()
bls12_381::Scalar::from_str(expected_ys[length - 300]).unwrap()
);
}
}

View File

@ -1,17 +1,21 @@
//! The Sapling circuits.
use ff::{Field, PrimeField};
use ff::PrimeField;
use group::Curve;
use bellman::{Circuit, ConstraintSystem, SynthesisError};
use zcash_primitives::jubjub::{FixedGenerators, JubjubEngine};
use zcash_primitives::constants;
use zcash_primitives::primitives::{PaymentAddress, ProofGenerationKey, ValueCommitment};
use super::ecc;
use super::pedersen_hash;
use crate::constants::{
NOTE_COMMITMENT_RANDOMNESS_GENERATOR, NULLIFIER_POSITION_GENERATOR,
PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
VALUE_COMMITMENT_VALUE_GENERATOR,
};
use bellman::gadgets::blake2s;
use bellman::gadgets::boolean;
use bellman::gadgets::multipack;
@ -21,60 +25,54 @@ use bellman::gadgets::Assignment;
pub const TREE_DEPTH: usize = zcash_primitives::sapling::SAPLING_COMMITMENT_TREE_DEPTH;
/// This is an instance of the `Spend` circuit.
pub struct Spend<'a, E: JubjubEngine> {
pub params: &'a E::Params,
pub struct Spend {
/// Pedersen commitment to the value being spent
pub value_commitment: Option<ValueCommitment<E>>,
pub value_commitment: Option<ValueCommitment>,
/// Key required to construct proofs for spending notes
/// for a particular spending key
pub proof_generation_key: Option<ProofGenerationKey<E>>,
pub proof_generation_key: Option<ProofGenerationKey>,
/// The payment address associated with the note
pub payment_address: Option<PaymentAddress<E>>,
pub payment_address: Option<PaymentAddress>,
/// The randomness of the note commitment
pub commitment_randomness: Option<E::Fs>,
pub commitment_randomness: Option<jubjub::Fr>,
/// Re-randomization of the public key
pub ar: Option<E::Fs>,
pub ar: Option<jubjub::Fr>,
/// The authentication path of the commitment in the tree
pub auth_path: Vec<Option<(E::Fr, bool)>>,
pub auth_path: Vec<Option<(bls12_381::Scalar, bool)>>,
/// The anchor; the root of the tree. If the note being
/// spent is zero-value, this can be anything.
pub anchor: Option<E::Fr>,
pub anchor: Option<bls12_381::Scalar>,
}
/// This is an output circuit instance.
pub struct Output<'a, E: JubjubEngine> {
pub params: &'a E::Params,
pub struct Output {
/// Pedersen commitment to the value being spent
pub value_commitment: Option<ValueCommitment<E>>,
pub value_commitment: Option<ValueCommitment>,
/// The payment address of the recipient
pub payment_address: Option<PaymentAddress<E>>,
pub payment_address: Option<PaymentAddress>,
/// The randomness used to hide the note commitment data
pub commitment_randomness: Option<E::Fs>,
pub commitment_randomness: Option<jubjub::Fr>,
/// The ephemeral secret key for DH with recipient
pub esk: Option<E::Fs>,
pub esk: Option<jubjub::Fr>,
}
/// Exposes a Pedersen commitment to the value as an
/// input to the circuit
fn expose_value_commitment<E, CS>(
fn expose_value_commitment<CS>(
mut cs: CS,
value_commitment: Option<ValueCommitment<E>>,
params: &E::Params,
value_commitment: Option<ValueCommitment>,
) -> Result<Vec<boolean::Boolean>, SynthesisError>
where
E: JubjubEngine,
CS: ConstraintSystem<E::Fr>,
CS: ConstraintSystem<bls12_381::Scalar>,
{
// Booleanize the value into little-endian bit order
let value_bits = boolean::u64_into_boolean_vec_le(
@ -83,11 +81,10 @@ where
)?;
// Compute the note value in the exponent
let value = ecc::fixed_base_multiplication::<E, _>(
let value = ecc::fixed_base_multiplication(
cs.namespace(|| "compute the value in the exponent"),
FixedGenerators::ValueCommitmentValue,
&VALUE_COMMITMENT_VALUE_GENERATOR,
&value_bits,
params,
)?;
// Booleanize the randomness. This does not ensure
@ -99,15 +96,14 @@ where
)?;
// Compute the randomness in the exponent
let rcv = ecc::fixed_base_multiplication::<E, _>(
let rcv = ecc::fixed_base_multiplication(
cs.namespace(|| "computation of rcv"),
FixedGenerators::ValueCommitmentRandomness,
&VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
&rcv,
params,
)?;
// Compute the Pedersen commitment to the value
let cv = value.add(cs.namespace(|| "computation of cv"), &rcv, params)?;
let cv = value.add(cs.namespace(|| "computation of cv"), &rcv)?;
// Expose the commitment as an input to the circuit
cv.inputize(cs.namespace(|| "commitment point"))?;
@ -115,33 +111,34 @@ where
Ok(value_bits)
}
impl<'a, E: JubjubEngine> Circuit<E::Fr> for Spend<'a, E> {
fn synthesize<CS: ConstraintSystem<E::Fr>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
impl Circuit<bls12_381::Scalar> for Spend {
fn synthesize<CS: ConstraintSystem<bls12_381::Scalar>>(
self,
cs: &mut CS,
) -> Result<(), SynthesisError> {
// Prover witnesses ak (ensures that it's on the curve)
let ak = ecc::EdwardsPoint::witness(
cs.namespace(|| "ak"),
self.proof_generation_key.as_ref().map(|k| k.ak.clone()),
self.params,
self.proof_generation_key.as_ref().map(|k| k.ak.into()),
)?;
// There are no sensible attacks on small order points
// of ak (that we're aware of!) but it's a cheap check,
// so we do it.
ak.assert_not_small_order(cs.namespace(|| "ak not small order"), self.params)?;
ak.assert_not_small_order(cs.namespace(|| "ak not small order"))?;
// Rerandomize ak and expose it as an input to the circuit
{
let ar = boolean::field_into_boolean_vec_le(cs.namespace(|| "ar"), self.ar)?;
// Compute the randomness in the exponent
let ar = ecc::fixed_base_multiplication::<E, _>(
let ar = ecc::fixed_base_multiplication(
cs.namespace(|| "computation of randomization for the signing key"),
FixedGenerators::SpendingKeyGenerator,
&SPENDING_KEY_GENERATOR,
&ar,
self.params,
)?;
let rk = ak.add(cs.namespace(|| "computation of rk"), &ar, self.params)?;
let rk = ak.add(cs.namespace(|| "computation of rk"), &ar)?;
rk.inputize(cs.namespace(|| "rk"))?;
}
@ -161,11 +158,10 @@ impl<'a, E: JubjubEngine> Circuit<E::Fr> for Spend<'a, E> {
// congruency then that's equivalent.
// Compute nk = [nsk] ProvingPublicKey
nk = ecc::fixed_base_multiplication::<E, _>(
nk = ecc::fixed_base_multiplication(
cs.namespace(|| "computation of nk"),
FixedGenerators::ProofGenerationKey,
&PROOF_GENERATION_KEY_GENERATOR,
&nsk,
self.params,
)?;
}
@ -198,21 +194,15 @@ impl<'a, E: JubjubEngine> Circuit<E::Fr> for Spend<'a, E> {
)?;
// drop_5 to ensure it's in the field
ivk.truncate(E::Fs::CAPACITY as usize);
ivk.truncate(jubjub::Fr::CAPACITY as usize);
// Witness g_d, checking that it's on the curve.
let g_d = {
// This binding is to avoid a weird edge case in Rust's
// ownership/borrowing rules. self is partially moved
// above, but the closure for and_then will have to
// move self (or a reference to self) to reference
// self.params, so we have to copy self.params here.
let params = self.params;
ecc::EdwardsPoint::witness(
cs.namespace(|| "witness g_d"),
self.payment_address.as_ref().and_then(|a| a.g_d(params)),
self.params,
self.payment_address
.as_ref()
.and_then(|a| a.g_d().map(jubjub::ExtendedPoint::from)),
)?
};
@ -220,10 +210,10 @@ impl<'a, E: JubjubEngine> Circuit<E::Fr> for Spend<'a, E> {
// is already done in the Output circuit, and this proof ensures
// g_d is bound to a product of that check, but for defense in
// depth let's check it anyway. It's cheap.
g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"), self.params)?;
g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"))?;
// Compute pk_d = g_d^ivk
let pk_d = g_d.mul(cs.namespace(|| "compute pk_d"), &ivk, self.params)?;
let pk_d = g_d.mul(cs.namespace(|| "compute pk_d"), &ivk)?;
// Compute note contents:
// value (in big endian) followed by g_d and pk_d
@ -237,12 +227,11 @@ impl<'a, E: JubjubEngine> Circuit<E::Fr> for Spend<'a, E> {
let value_bits = expose_value_commitment(
cs.namespace(|| "value commitment"),
self.value_commitment,
self.params,
)?;
// Compute the note's value as a linear combination
// of the bits.
let mut coeff = E::Fr::one();
let mut coeff = bls12_381::Scalar::one();
for bit in &value_bits {
value_num = value_num.add_bool_with_coeff(CS::one(), bit, coeff);
coeff = coeff.double();
@ -266,11 +255,10 @@ impl<'a, E: JubjubEngine> Circuit<E::Fr> for Spend<'a, E> {
);
// Compute the hash of the note contents
let mut cm = pedersen_hash::pedersen_hash::<E, _>(
let mut cm = pedersen_hash::pedersen_hash(
cs.namespace(|| "note content hash"),
pedersen_hash::Personalization::NoteCommitment,
&note_contents,
self.params,
)?;
{
@ -281,20 +269,15 @@ impl<'a, E: JubjubEngine> Circuit<E::Fr> for Spend<'a, E> {
)?;
// Compute the note commitment randomness in the exponent
let rcm = ecc::fixed_base_multiplication::<E, _>(
let rcm = ecc::fixed_base_multiplication(
cs.namespace(|| "computation of commitment randomness"),
FixedGenerators::NoteCommitmentRandomness,
&NOTE_COMMITMENT_RANDOMNESS_GENERATOR,
&rcm,
self.params,
)?;
// Randomize the note commitment. Pedersen hashes are not
// themselves hiding commitments.
cm = cm.add(
cs.namespace(|| "randomization of note commitment"),
&rcm,
self.params,
)?;
cm = cm.add(cs.namespace(|| "randomization of note commitment"), &rcm)?;
}
// This will store (least significant bit first)
@ -342,11 +325,10 @@ impl<'a, E: JubjubEngine> Circuit<E::Fr> for Spend<'a, E> {
preimage.extend(xr.to_bits_le(cs.namespace(|| "xr into bits"))?);
// Compute the new subtree value
cur = pedersen_hash::pedersen_hash::<E, _>(
cur = pedersen_hash::pedersen_hash(
cs.namespace(|| "computation of pedersen hash"),
pedersen_hash::Personalization::MerkleTree(i),
&preimage,
self.params,
)?
.get_x()
.clone(); // Injective encoding
@ -366,7 +348,7 @@ impl<'a, E: JubjubEngine> Circuit<E::Fr> for Spend<'a, E> {
cs.enforce(
|| "conditionally enforce correct root",
|lc| lc + cur.get_variable() - rt.get_variable(),
|lc| lc + &value_num.lc(E::Fr::one()),
|lc| lc + &value_num.lc(bls12_381::Scalar::one()),
|lc| lc,
);
@ -379,19 +361,14 @@ impl<'a, E: JubjubEngine> Circuit<E::Fr> for Spend<'a, E> {
let mut rho = cm;
{
// Compute the position in the exponent
let position = ecc::fixed_base_multiplication::<E, _>(
let position = ecc::fixed_base_multiplication(
cs.namespace(|| "g^position"),
FixedGenerators::NullifierPosition,
&NULLIFIER_POSITION_GENERATOR,
&position_bits,
self.params,
)?;
// Add the position to the commitment
rho = rho.add(
cs.namespace(|| "faerie gold prevention"),
&position,
self.params,
)?;
rho = rho.add(cs.namespace(|| "faerie gold prevention"), &position)?;
}
// Let's compute nf = BLAKE2s(nk || rho)
@ -410,8 +387,11 @@ impl<'a, E: JubjubEngine> Circuit<E::Fr> for Spend<'a, E> {
}
}
impl<'a, E: JubjubEngine> Circuit<E::Fr> for Output<'a, E> {
fn synthesize<CS: ConstraintSystem<E::Fr>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
impl Circuit<bls12_381::Scalar> for Output {
fn synthesize<CS: ConstraintSystem<bls12_381::Scalar>>(
self,
cs: &mut CS,
) -> Result<(), SynthesisError> {
// Let's start to construct our note, which contains
// value (big endian)
let mut note_contents = vec![];
@ -421,19 +401,17 @@ impl<'a, E: JubjubEngine> Circuit<E::Fr> for Output<'a, E> {
note_contents.extend(expose_value_commitment(
cs.namespace(|| "value commitment"),
self.value_commitment,
self.params,
)?);
// Let's deal with g_d
{
let params = self.params;
// Prover witnesses g_d, ensuring it's on the
// curve.
let g_d = ecc::EdwardsPoint::witness(
cs.namespace(|| "witness g_d"),
self.payment_address.as_ref().and_then(|a| a.g_d(params)),
self.params,
self.payment_address
.as_ref()
.and_then(|a| a.g_d().map(jubjub::ExtendedPoint::from)),
)?;
// g_d is ensured to be large order. The relationship
@ -445,7 +423,7 @@ impl<'a, E: JubjubEngine> Circuit<E::Fr> for Output<'a, E> {
//
// Further, if it were small order, epk would be
// small order too!
g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"), self.params)?;
g_d.assert_not_small_order(cs.namespace(|| "g_d not small order"))?;
// Extend our note contents with the representation of
// g_d.
@ -455,7 +433,7 @@ impl<'a, E: JubjubEngine> Circuit<E::Fr> for Output<'a, E> {
let esk = boolean::field_into_boolean_vec_le(cs.namespace(|| "esk"), self.esk)?;
// Create the ephemeral public key from g_d.
let epk = g_d.mul(cs.namespace(|| "epk computation"), &esk, self.params)?;
let epk = g_d.mul(cs.namespace(|| "epk computation"), &esk)?;
// Expose epk publicly.
epk.inputize(cs.namespace(|| "epk"))?;
@ -466,19 +444,22 @@ impl<'a, E: JubjubEngine> Circuit<E::Fr> for Output<'a, E> {
// they would like.
{
// Just grab pk_d from the witness
let pk_d = self.payment_address.as_ref().map(|e| e.pk_d().to_xy());
let pk_d = self
.payment_address
.as_ref()
.map(|e| jubjub::ExtendedPoint::from(*e.pk_d()).to_affine());
// Witness the y-coordinate, encoded as little
// endian bits (to match the representation)
let y_contents = boolean::field_into_boolean_vec_le(
cs.namespace(|| "pk_d bits of y"),
pk_d.map(|e| e.1),
pk_d.map(|e| e.get_v()),
)?;
// Witness the sign bit
let sign_bit = boolean::Boolean::from(boolean::AllocatedBit::alloc(
cs.namespace(|| "pk_d bit of x"),
pk_d.map(|e| e.0.is_odd()),
pk_d.map(|e| e.get_u().is_odd()),
)?);
// Extend the note with pk_d representation
@ -494,11 +475,10 @@ impl<'a, E: JubjubEngine> Circuit<E::Fr> for Output<'a, E> {
);
// Compute the hash of the note contents
let mut cm = pedersen_hash::pedersen_hash::<E, _>(
let mut cm = pedersen_hash::pedersen_hash(
cs.namespace(|| "note content hash"),
pedersen_hash::Personalization::NoteCommitment,
&note_contents,
self.params,
)?;
{
@ -509,19 +489,14 @@ impl<'a, E: JubjubEngine> Circuit<E::Fr> for Output<'a, E> {
)?;
// Compute the note commitment randomness in the exponent
let rcm = ecc::fixed_base_multiplication::<E, _>(
let rcm = ecc::fixed_base_multiplication(
cs.namespace(|| "computation of commitment randomness"),
FixedGenerators::NoteCommitmentRandomness,
&NOTE_COMMITMENT_RANDOMNESS_GENERATOR,
&rcm,
self.params,
)?;
// Randomize our note commitment
cm = cm.add(
cs.namespace(|| "randomization of note commitment"),
&rcm,
self.params,
)?;
cm = cm.add(cs.namespace(|| "randomization of note commitment"), &rcm)?;
}
// Only the x-coordinate of the output is revealed,
@ -538,16 +513,14 @@ impl<'a, E: JubjubEngine> Circuit<E::Fr> for Output<'a, E> {
fn test_input_circuit_with_bls12_381() {
use bellman::gadgets::test::*;
use ff::{BitIterator, Field};
use pairing::bls12_381::*;
use group::Group;
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
use zcash_primitives::{
jubjub::{edwards, fs, JubjubBls12},
pedersen_hash,
primitives::{Diversifier, Note, ProofGenerationKey, Rseed},
};
let params = &JubjubBls12::new();
let rng = &mut XorShiftRng::from_seed([
0x58, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
@ -556,20 +529,20 @@ fn test_input_circuit_with_bls12_381() {
let tree_depth = 32;
for _ in 0..10 {
let value_commitment = ValueCommitment::<Bls12> {
let value_commitment = ValueCommitment {
value: rng.next_u64(),
randomness: fs::Fs::random(rng),
randomness: jubjub::Fr::random(rng),
};
let nsk = fs::Fs::random(rng);
let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params);
let nsk = jubjub::Fr::random(rng);
let ak = jubjub::SubgroupPoint::random(rng);
let proof_generation_key = ProofGenerationKey {
ak: ak.clone(),
nsk: nsk.clone(),
};
let viewing_key = proof_generation_key.to_viewing_key(params);
let viewing_key = proof_generation_key.to_viewing_key();
let payment_address;
@ -580,20 +553,21 @@ fn test_input_circuit_with_bls12_381() {
Diversifier(d)
};
if let Some(p) = viewing_key.to_payment_address(diversifier, params) {
if let Some(p) = viewing_key.to_payment_address(diversifier) {
payment_address = p;
break;
}
}
let g_d = payment_address.diversifier().g_d(params).unwrap();
let commitment_randomness = fs::Fs::random(rng);
let auth_path = vec![Some((Fr::random(rng), rng.next_u32() % 2 != 0)); tree_depth];
let ar = fs::Fs::random(rng);
let g_d = payment_address.diversifier().g_d().unwrap();
let commitment_randomness = jubjub::Fr::random(rng);
let auth_path =
vec![Some((bls12_381::Scalar::random(rng), rng.next_u32() % 2 != 0)); tree_depth];
let ar = jubjub::Fr::random(rng);
{
let rk = viewing_key.rk(ar, params).to_xy();
let expected_value_cm = value_commitment.cm(params).to_xy();
let rk = jubjub::ExtendedPoint::from(viewing_key.rk(ar)).to_affine();
let expected_value_cm = jubjub::ExtendedPoint::from(value_commitment.cm()).to_affine();
let note = Note {
value: value_commitment.value,
g_d: g_d.clone(),
@ -602,7 +576,7 @@ fn test_input_circuit_with_bls12_381() {
};
let mut position = 0u64;
let cm: Fr = note.cm(params);
let cm = note.cm();
let mut cur = cm.clone();
for (i, val) in auth_path.clone().into_iter().enumerate() {
@ -621,22 +595,21 @@ fn test_input_circuit_with_bls12_381() {
lhs.reverse();
rhs.reverse();
cur = pedersen_hash::pedersen_hash::<Bls12, _>(
cur = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash(
pedersen_hash::Personalization::MerkleTree(i),
lhs.into_iter()
.take(Fr::NUM_BITS as usize)
.chain(rhs.into_iter().take(Fr::NUM_BITS as usize)),
params,
)
.to_xy()
.0;
.take(bls12_381::Scalar::NUM_BITS as usize)
.chain(rhs.into_iter().take(bls12_381::Scalar::NUM_BITS as usize)),
))
.to_affine()
.get_u();
if b {
position |= 1 << i;
}
}
let expected_nf = note.nf(&viewing_key, position, params);
let expected_nf = note.nf(&viewing_key, position);
let expected_nf = multipack::bytes_to_bits_le(&expected_nf);
let expected_nf = multipack::compute_multipacking(&expected_nf);
assert_eq!(expected_nf.len(), 2);
@ -644,7 +617,6 @@ fn test_input_circuit_with_bls12_381() {
let mut cs = TestConstraintSystem::new();
let instance = Spend {
params,
value_commitment: Some(value_commitment.clone()),
proof_generation_key: Some(proof_generation_key.clone()),
payment_address: Some(payment_address.clone()),
@ -666,16 +638,16 @@ fn test_input_circuit_with_bls12_381() {
assert_eq!(cs.get("randomization of note commitment/x3/num"), cm);
assert_eq!(cs.num_inputs(), 8);
assert_eq!(cs.get_input(0, "ONE"), Fr::one());
assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.0);
assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.1);
assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one());
assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.get_u());
assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.get_v());
assert_eq!(
cs.get_input(3, "value commitment/commitment point/x/input variable"),
expected_value_cm.0
expected_value_cm.get_u()
);
assert_eq!(
cs.get_input(4, "value commitment/commitment point/y/input variable"),
expected_value_cm.1
expected_value_cm.get_v()
);
assert_eq!(cs.get_input(5, "anchor/input variable"), cur);
assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]);
@ -688,16 +660,14 @@ fn test_input_circuit_with_bls12_381() {
fn test_input_circuit_with_bls12_381_external_test_vectors() {
use bellman::gadgets::test::*;
use ff::{BitIterator, Field};
use pairing::bls12_381::*;
use group::Group;
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
use zcash_primitives::{
jubjub::{edwards, fs, JubjubBls12},
pedersen_hash,
primitives::{Diversifier, Note, ProofGenerationKey, Rseed},
};
let params = &JubjubBls12::new();
let rng = &mut XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
@ -732,20 +702,20 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() {
];
for i in 0..10 {
let value_commitment = ValueCommitment::<Bls12> {
let value_commitment = ValueCommitment {
value: i,
randomness: fs::Fs::from_str(&(1000 * (i + 1)).to_string()).unwrap(),
randomness: jubjub::Fr::from_str(&(1000 * (i + 1)).to_string()).unwrap(),
};
let nsk = fs::Fs::random(rng);
let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params);
let nsk = jubjub::Fr::random(rng);
let ak = jubjub::SubgroupPoint::random(rng);
let proof_generation_key = ProofGenerationKey {
ak: ak.clone(),
nsk: nsk.clone(),
};
let viewing_key = proof_generation_key.to_viewing_key(params);
let viewing_key = proof_generation_key.to_viewing_key();
let payment_address;
@ -756,27 +726,28 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() {
Diversifier(d)
};
if let Some(p) = viewing_key.to_payment_address(diversifier, params) {
if let Some(p) = viewing_key.to_payment_address(diversifier) {
payment_address = p;
break;
}
}
let g_d = payment_address.diversifier().g_d(params).unwrap();
let commitment_randomness = fs::Fs::random(rng);
let auth_path = vec![Some((Fr::random(rng), rng.next_u32() % 2 != 0)); tree_depth];
let ar = fs::Fs::random(rng);
let g_d = payment_address.diversifier().g_d().unwrap();
let commitment_randomness = jubjub::Fr::random(rng);
let auth_path =
vec![Some((bls12_381::Scalar::random(rng), rng.next_u32() % 2 != 0)); tree_depth];
let ar = jubjub::Fr::random(rng);
{
let rk = viewing_key.rk(ar, params).to_xy();
let expected_value_cm = value_commitment.cm(params).to_xy();
let rk = jubjub::ExtendedPoint::from(viewing_key.rk(ar)).to_affine();
let expected_value_cm = jubjub::ExtendedPoint::from(value_commitment.cm()).to_affine();
assert_eq!(
expected_value_cm.0,
Fr::from_str(&expected_cm_xs[i as usize]).unwrap()
expected_value_cm.get_u(),
bls12_381::Scalar::from_str(&expected_cm_xs[i as usize]).unwrap()
);
assert_eq!(
expected_value_cm.1,
Fr::from_str(&expected_cm_ys[i as usize]).unwrap()
expected_value_cm.get_v(),
bls12_381::Scalar::from_str(&expected_cm_ys[i as usize]).unwrap()
);
let note = Note {
value: value_commitment.value,
@ -786,7 +757,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() {
};
let mut position = 0u64;
let cm: Fr = note.cm(params);
let cm = note.cm();
let mut cur = cm.clone();
for (i, val) in auth_path.clone().into_iter().enumerate() {
@ -805,22 +776,21 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() {
lhs.reverse();
rhs.reverse();
cur = pedersen_hash::pedersen_hash::<Bls12, _>(
cur = jubjub::ExtendedPoint::from(pedersen_hash::pedersen_hash(
pedersen_hash::Personalization::MerkleTree(i),
lhs.into_iter()
.take(Fr::NUM_BITS as usize)
.chain(rhs.into_iter().take(Fr::NUM_BITS as usize)),
params,
)
.to_xy()
.0;
.take(bls12_381::Scalar::NUM_BITS as usize)
.chain(rhs.into_iter().take(bls12_381::Scalar::NUM_BITS as usize)),
))
.to_affine()
.get_u();
if b {
position |= 1 << i;
}
}
let expected_nf = note.nf(&viewing_key, position, params);
let expected_nf = note.nf(&viewing_key, position);
let expected_nf = multipack::bytes_to_bits_le(&expected_nf);
let expected_nf = multipack::compute_multipacking(&expected_nf);
assert_eq!(expected_nf.len(), 2);
@ -828,7 +798,6 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() {
let mut cs = TestConstraintSystem::new();
let instance = Spend {
params: params,
value_commitment: Some(value_commitment.clone()),
proof_generation_key: Some(proof_generation_key.clone()),
payment_address: Some(payment_address.clone()),
@ -850,16 +819,16 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() {
assert_eq!(cs.get("randomization of note commitment/x3/num"), cm);
assert_eq!(cs.num_inputs(), 8);
assert_eq!(cs.get_input(0, "ONE"), Fr::one());
assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.0);
assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.1);
assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one());
assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.get_u());
assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.get_v());
assert_eq!(
cs.get_input(3, "value commitment/commitment point/x/input variable"),
expected_value_cm.0
expected_value_cm.get_u()
);
assert_eq!(
cs.get_input(4, "value commitment/commitment point/y/input variable"),
expected_value_cm.1
expected_value_cm.get_v()
);
assert_eq!(cs.get_input(5, "anchor/input variable"), cur);
assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]);
@ -872,35 +841,31 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() {
fn test_output_circuit_with_bls12_381() {
use bellman::gadgets::test::*;
use ff::Field;
use pairing::bls12_381::*;
use group::Group;
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
use zcash_primitives::{
jubjub::{edwards, fs, JubjubBls12},
primitives::{Diversifier, ProofGenerationKey, Rseed},
};
use zcash_primitives::primitives::{Diversifier, ProofGenerationKey, Rseed};
let params = &JubjubBls12::new();
let rng = &mut XorShiftRng::from_seed([
0x58, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
for _ in 0..100 {
let value_commitment = ValueCommitment::<Bls12> {
let value_commitment = ValueCommitment {
value: rng.next_u64(),
randomness: fs::Fs::random(rng),
randomness: jubjub::Fr::random(rng),
};
let nsk = fs::Fs::random(rng);
let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params);
let nsk = jubjub::Fr::random(rng);
let ak = jubjub::SubgroupPoint::random(rng);
let proof_generation_key = ProofGenerationKey {
ak: ak.clone(),
nsk: nsk.clone(),
};
let viewing_key = proof_generation_key.to_viewing_key(params);
let viewing_key = proof_generation_key.to_viewing_key();
let payment_address;
@ -911,20 +876,19 @@ fn test_output_circuit_with_bls12_381() {
Diversifier(d)
};
if let Some(p) = viewing_key.to_payment_address(diversifier, params) {
if let Some(p) = viewing_key.to_payment_address(diversifier) {
payment_address = p;
break;
}
}
let commitment_randomness = fs::Fs::random(rng);
let esk = fs::Fs::random(rng);
let commitment_randomness = jubjub::Fr::random(rng);
let esk = jubjub::Fr::random(rng);
{
let mut cs = TestConstraintSystem::new();
let instance = Output {
params,
value_commitment: Some(value_commitment.clone()),
payment_address: Some(payment_address.clone()),
commitment_randomness: Some(commitment_randomness),
@ -944,31 +908,34 @@ fn test_output_circuit_with_bls12_381() {
.create_note(
value_commitment.value,
Rseed::BeforeZip212(commitment_randomness),
params,
)
.expect("should be valid")
.cm(params);
.cm();
let expected_value_cm = value_commitment.cm(params).to_xy();
let expected_value_cm = jubjub::ExtendedPoint::from(value_commitment.cm()).to_affine();
let expected_epk = payment_address
.g_d(params)
.expect("should be valid")
.mul(esk, params);
let expected_epk_xy = expected_epk.to_xy();
let expected_epk =
jubjub::ExtendedPoint::from(payment_address.g_d().expect("should be valid") * esk)
.to_affine();
assert_eq!(cs.num_inputs(), 6);
assert_eq!(cs.get_input(0, "ONE"), Fr::one());
assert_eq!(cs.get_input(0, "ONE"), bls12_381::Scalar::one());
assert_eq!(
cs.get_input(1, "value commitment/commitment point/x/input variable"),
expected_value_cm.0
expected_value_cm.get_u()
);
assert_eq!(
cs.get_input(2, "value commitment/commitment point/y/input variable"),
expected_value_cm.1
expected_value_cm.get_v()
);
assert_eq!(
cs.get_input(3, "epk/x/input variable"),
expected_epk.get_u()
);
assert_eq!(
cs.get_input(4, "epk/y/input variable"),
expected_epk.get_v()
);
assert_eq!(cs.get_input(3, "epk/x/input variable"), expected_epk_xy.0);
assert_eq!(cs.get_input(4, "epk/y/input variable"), expected_epk_xy.1);
assert_eq!(cs.get_input(5, "commitment/input variable"), expected_cm);
}
}

View File

@ -337,7 +337,7 @@ where
#[ignore]
fn test_sprout_constraints() {
use bellman::gadgets::test::*;
use pairing::bls12_381::Fr;
use bls12_381::Scalar;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
@ -355,7 +355,7 @@ fn test_sprout_constraints() {
}
while test_vector.len() != 0 {
let mut cs = TestConstraintSystem::<Fr>::new();
let mut cs = TestConstraintSystem::<Scalar>::new();
let phi = Some(get_u256(&mut test_vector));
let rt = Some(get_u256(&mut test_vector));

View File

@ -168,9 +168,6 @@ fn generate_pedersen_circuit_generators() -> Vec<Vec<Vec<(Scalar, Scalar)>>> {
#[cfg(test)]
mod tests {
use bls12_381::Scalar;
use ff::PrimeField;
use pairing::bls12_381::Fr;
use zcash_primitives::{
jubjub::{FixedGenerators, JubjubParams},
JUBJUB,
@ -178,10 +175,6 @@ mod tests {
use super::*;
fn check_scalar(expected: Fr, actual: Scalar) {
assert_eq!(expected.to_repr().0, actual.to_bytes());
}
fn check_generator(expected: FixedGenerators, actual: FixedGenerator) {
let expected = JUBJUB.circuit_generators(expected);
@ -192,25 +185,25 @@ mod tests {
assert_eq!(expected.len(), actual.len());
for (expected, actual) in expected.iter().zip(actual) {
// Same coordinates.
check_scalar(expected.0, actual.0);
check_scalar(expected.1, actual.1);
assert_eq!(expected.0, actual.0);
assert_eq!(expected.1, actual.1);
}
}
}
#[test]
fn edwards_d() {
check_scalar(*JUBJUB.edwards_d(), EDWARDS_D);
assert_eq!(*JUBJUB.edwards_d(), EDWARDS_D);
}
#[test]
fn montgomery_a() {
check_scalar(*JUBJUB.montgomery_a(), MONTGOMERY_A);
assert_eq!(*JUBJUB.montgomery_a(), MONTGOMERY_A);
}
#[test]
fn montgomery_scale() {
check_scalar(*JUBJUB.scale(), MONTGOMERY_SCALE);
assert_eq!(*JUBJUB.scale(), MONTGOMERY_SCALE);
}
#[test]
@ -268,26 +261,4 @@ mod tests {
&SPENDING_KEY_GENERATOR,
);
}
#[test]
fn pedersen_circuit_generators() {
let expected = JUBJUB.pedersen_circuit_generators();
let actual = &PEDERSEN_CIRCUIT_GENERATORS;
// Same number of Pedersen hash generators.
assert_eq!(expected.len(), actual.len());
for (expected, actual) in expected.iter().zip(actual.iter()) {
// Same number of windows per generator.
assert_eq!(expected.len(), actual.len());
for (expected, actual) in expected.iter().zip(actual) {
// Same size table per window.
assert_eq!(expected.len(), actual.len());
for (expected, actual) in expected.iter().zip(actual) {
// Same coordinates.
check_scalar(expected.0, actual.0);
check_scalar(expected.1, actual.1);
}
}
}
}
}

View File

@ -7,7 +7,7 @@
#![deny(intra_doc_link_resolution_failure)]
use bellman::groth16::{prepare_verifying_key, Parameters, PreparedVerifyingKey, VerifyingKey};
use pairing::bls12_381::Bls12;
use bls12_381::Bls12;
use std::fs::File;
use std::io::{self, BufReader};
use std::path::Path;

View File

@ -1,18 +1,14 @@
//! Abstractions over the proving system and parameters for ease of use.
use bellman::groth16::{Parameters, PreparedVerifyingKey};
use pairing::bls12_381::{Bls12, Fr};
use zcash_primitives::{
jubjub::{edwards, fs::Fs, Unknown},
primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed},
};
use bls12_381::Bls12;
use zcash_primitives::{
merkle_tree::MerklePath,
primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed},
prover::TxProver,
redjubjub::{PublicKey, Signature},
sapling::Node,
transaction::components::{Amount, GROTH_PROOF_SIZE},
JUBJUB,
};
use crate::sapling::SaplingProvingContext;
@ -130,21 +126,14 @@ impl TxProver for LocalTxProver {
fn spend_proof(
&self,
ctx: &mut Self::SaplingProvingContext,
proof_generation_key: ProofGenerationKey<Bls12>,
proof_generation_key: ProofGenerationKey,
diversifier: Diversifier,
rseed: Rseed<Fs>,
ar: Fs,
rseed: Rseed,
ar: jubjub::Fr,
value: u64,
anchor: Fr,
anchor: bls12_381::Scalar,
merkle_path: MerklePath<Node>,
) -> Result<
(
[u8; GROTH_PROOF_SIZE],
edwards::Point<Bls12, Unknown>,
PublicKey<Bls12>,
),
(),
> {
) -> Result<([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint, PublicKey), ()> {
let (proof, cv, rk) = ctx.spend_proof(
proof_generation_key,
diversifier,
@ -155,7 +144,6 @@ impl TxProver for LocalTxProver {
merkle_path,
&self.spend_params,
&self.spend_vk,
&JUBJUB,
)?;
let mut zkproof = [0u8; GROTH_PROOF_SIZE];
@ -169,19 +157,12 @@ impl TxProver for LocalTxProver {
fn output_proof(
&self,
ctx: &mut Self::SaplingProvingContext,
esk: Fs,
payment_address: PaymentAddress<Bls12>,
rcm: Fs,
esk: jubjub::Fr,
payment_address: PaymentAddress,
rcm: jubjub::Fr,
value: u64,
) -> ([u8; GROTH_PROOF_SIZE], edwards::Point<Bls12, Unknown>) {
let (proof, cv) = ctx.output_proof(
esk,
payment_address,
rcm,
value,
&self.output_params,
&JUBJUB,
);
) -> ([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint) {
let (proof, cv) = ctx.output_proof(esk, payment_address, rcm, value, &self.output_params);
let mut zkproof = [0u8; GROTH_PROOF_SIZE];
proof
@ -197,6 +178,6 @@ impl TxProver for LocalTxProver {
value_balance: Amount,
sighash: &[u8; 32],
) -> Result<Signature, ()> {
ctx.binding_sig(value_balance, sighash, &JUBJUB)
ctx.binding_sig(value_balance, sighash)
}
}

View File

@ -1,10 +1,8 @@
//! Helpers for creating Sapling proofs.
use pairing::bls12_381::Bls12;
use zcash_primitives::jubjub::{
edwards, fs::Fs, FixedGenerators, JubjubBls12, JubjubParams, Unknown,
use zcash_primitives::{
constants::VALUE_COMMITMENT_VALUE_GENERATOR, transaction::components::Amount,
};
use zcash_primitives::transaction::components::Amount;
mod prover;
mod verifier;
@ -13,10 +11,7 @@ pub use self::prover::SaplingProvingContext;
pub use self::verifier::SaplingVerificationContext;
// This function computes `value` in the exponent of the value commitment base
fn compute_value_balance(
value: Amount,
params: &JubjubBls12,
) -> Option<edwards::Point<Bls12, Unknown>> {
fn compute_value_balance(value: Amount) -> Option<jubjub::ExtendedPoint> {
// Compute the absolute value (failing if -i64::MAX is
// the value)
let abs = match i64::from(value).checked_abs() {
@ -28,13 +23,11 @@ fn compute_value_balance(
let is_negative = value.is_negative();
// Compute it in the exponent
let mut value_balance = params
.generator(FixedGenerators::ValueCommitmentValue)
.mul(Fs::from(abs), params);
let mut value_balance = VALUE_COMMITMENT_VALUE_GENERATOR * jubjub::Fr::from(abs);
// Negate if necessary
if is_negative {
value_balance = value_balance.negate();
value_balance = -value_balance;
}
// Convert to unknown order point

View File

@ -2,16 +2,15 @@ use bellman::{
gadgets::multipack,
groth16::{create_random_proof, verify_proof, Parameters, PreparedVerifyingKey, Proof},
};
use bls12_381::Bls12;
use ff::Field;
use pairing::bls12_381::{Bls12, Fr};
use group::{Curve, GroupEncoding};
use rand_core::OsRng;
use std::ops::{AddAssign, Neg};
use zcash_primitives::{
jubjub::{edwards, fs::Fs, FixedGenerators, JubjubBls12, Unknown},
primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, Rseed, ValueCommitment},
};
use zcash_primitives::{
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
merkle_tree::MerklePath,
primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, Rseed, ValueCommitment},
redjubjub::{PrivateKey, PublicKey, Signature},
sapling::Node,
transaction::components::Amount,
@ -22,17 +21,17 @@ use crate::circuit::sapling::{Output, Spend};
/// A context object for creating the Sapling components of a Zcash transaction.
pub struct SaplingProvingContext {
bsk: Fs,
bsk: jubjub::Fr,
// (sum of the Spend value commitments) - (sum of the Output value commitments)
cv_sum: edwards::Point<Bls12, Unknown>,
cv_sum: jubjub::ExtendedPoint,
}
impl SaplingProvingContext {
/// Construct a new context to be used with a single transaction.
pub fn new() -> Self {
SaplingProvingContext {
bsk: Fs::zero(),
cv_sum: edwards::Point::zero(),
bsk: jubjub::Fr::zero(),
cv_sum: jubjub::ExtendedPoint::identity(),
}
}
@ -41,29 +40,21 @@ impl SaplingProvingContext {
/// inside the context for later use.
pub fn spend_proof(
&mut self,
proof_generation_key: ProofGenerationKey<Bls12>,
proof_generation_key: ProofGenerationKey,
diversifier: Diversifier,
rseed: Rseed<Fs>,
ar: Fs,
rseed: Rseed,
ar: jubjub::Fr,
value: u64,
anchor: Fr,
anchor: bls12_381::Scalar,
merkle_path: MerklePath<Node>,
proving_key: &Parameters<Bls12>,
verifying_key: &PreparedVerifyingKey<Bls12>,
params: &JubjubBls12,
) -> Result<
(
Proof<Bls12>,
edwards::Point<Bls12, Unknown>,
PublicKey<Bls12>,
),
(),
> {
) -> Result<(Proof<Bls12>, jubjub::ExtendedPoint, PublicKey), ()> {
// Initialize secure RNG
let mut rng = OsRng;
// We create the randomness of the value commitment
let rcv = Fs::random(&mut rng);
let rcv = jubjub::Fr::random(&mut rng);
// Accumulate the value commitment randomness in the context
{
@ -75,41 +66,33 @@ impl SaplingProvingContext {
}
// Construct the value commitment
let value_commitment = ValueCommitment::<Bls12> {
let value_commitment = ValueCommitment {
value,
randomness: rcv,
};
// Construct the viewing key
let viewing_key = proof_generation_key.to_viewing_key(params);
let viewing_key = proof_generation_key.to_viewing_key();
// Construct the payment address with the viewing key / diversifier
let payment_address = viewing_key
.to_payment_address(diversifier, params)
.ok_or(())?;
let payment_address = viewing_key.to_payment_address(diversifier).ok_or(())?;
// This is the result of the re-randomization, we compute it for the caller
let rk = PublicKey::<Bls12>(proof_generation_key.ak.clone().into()).randomize(
ar,
FixedGenerators::SpendingKeyGenerator,
params,
);
let rk =
PublicKey(proof_generation_key.ak.clone().into()).randomize(ar, SPENDING_KEY_GENERATOR);
// Let's compute the nullifier while we have the position
let note = Note {
value,
g_d: diversifier
.g_d::<Bls12>(params)
.expect("was a valid diversifier before"),
g_d: diversifier.g_d().expect("was a valid diversifier before"),
pk_d: payment_address.pk_d().clone(),
rseed,
};
let nullifier = note.nf(&viewing_key, merkle_path.position, params);
let nullifier = note.nf(&viewing_key, merkle_path.position);
// We now have the full witness for our circuit
let instance = Spend {
params,
value_commitment: Some(value_commitment.clone()),
proof_generation_key: Some(proof_generation_key),
payment_address: Some(payment_address),
@ -129,14 +112,16 @@ impl SaplingProvingContext {
// Try to verify the proof:
// Construct public input for circuit
let mut public_input = [Fr::zero(); 7];
let mut public_input = [bls12_381::Scalar::zero(); 7];
{
let (x, y) = rk.0.to_xy();
let affine = rk.0.to_affine();
let (x, y) = (affine.get_u(), affine.get_v());
public_input[0] = x;
public_input[1] = y;
}
{
let (x, y) = value_commitment.cm(params).to_xy();
let affine = jubjub::ExtendedPoint::from(value_commitment.cm()).to_affine();
let (x, y) = (affine.get_u(), affine.get_v());
public_input[2] = x;
public_input[3] = y;
}
@ -157,16 +142,10 @@ impl SaplingProvingContext {
verify_proof(verifying_key, &proof, &public_input[..]).map_err(|_| ())?;
// Compute value commitment
let value_commitment: edwards::Point<Bls12, Unknown> = value_commitment.cm(params).into();
let value_commitment: jubjub::ExtendedPoint = value_commitment.cm().into();
// Accumulate the value commitment in the context
{
let mut tmp = value_commitment.clone();
tmp = tmp.add(&self.cv_sum, params);
// Update the context
self.cv_sum = tmp;
}
self.cv_sum += value_commitment;
Ok((proof, value_commitment, rk))
}
@ -176,20 +155,19 @@ impl SaplingProvingContext {
/// for later use.
pub fn output_proof(
&mut self,
esk: Fs,
payment_address: PaymentAddress<Bls12>,
rcm: Fs,
esk: jubjub::Fr,
payment_address: PaymentAddress,
rcm: jubjub::Fr,
value: u64,
proving_key: &Parameters<Bls12>,
params: &JubjubBls12,
) -> (Proof<Bls12>, edwards::Point<Bls12, Unknown>) {
) -> (Proof<Bls12>, jubjub::ExtendedPoint) {
// Initialize secure RNG
let mut rng = OsRng;
// We construct ephemeral randomness for the value commitment. This
// randomness is not given back to the caller, but the synthetic
// blinding factor `bsk` is accumulated in the context.
let rcv = Fs::random(&mut rng);
let rcv = jubjub::Fr::random(&mut rng);
// Accumulate the value commitment randomness in the context
{
@ -201,14 +179,13 @@ impl SaplingProvingContext {
}
// Construct the value commitment for the proof instance
let value_commitment = ValueCommitment::<Bls12> {
let value_commitment = ValueCommitment {
value,
randomness: rcv,
};
// We now have a full witness for the output proof.
let instance = Output {
params,
value_commitment: Some(value_commitment.clone()),
payment_address: Some(payment_address.clone()),
commitment_randomness: Some(rcm),
@ -220,69 +197,52 @@ impl SaplingProvingContext {
create_random_proof(instance, proving_key, &mut rng).expect("proving should not fail");
// Compute the actual value commitment
let value_commitment: edwards::Point<Bls12, Unknown> = value_commitment.cm(params).into();
let value_commitment: jubjub::ExtendedPoint = value_commitment.cm().into();
// Accumulate the value commitment in the context. We do this to check internal consistency.
{
let mut tmp = value_commitment.clone();
tmp = tmp.negate(); // Outputs subtract from the total.
tmp = tmp.add(&self.cv_sum, params);
// Update the context
self.cv_sum = tmp;
}
self.cv_sum -= value_commitment; // Outputs subtract from the total.
(proof, value_commitment)
}
/// Create the bindingSig for a Sapling transaction. All calls to spend_proof()
/// and output_proof() must be completed before calling this function.
pub fn binding_sig(
&self,
value_balance: Amount,
sighash: &[u8; 32],
params: &JubjubBls12,
) -> Result<Signature, ()> {
pub fn binding_sig(&self, value_balance: Amount, sighash: &[u8; 32]) -> Result<Signature, ()> {
// Initialize secure RNG
let mut rng = OsRng;
// Grab the current `bsk` from the context
let bsk = PrivateKey::<Bls12>(self.bsk);
let bsk = PrivateKey(self.bsk);
// Grab the `bvk` using DerivePublic.
let bvk = PublicKey::from_private(&bsk, FixedGenerators::ValueCommitmentRandomness, params);
let bvk = PublicKey::from_private(&bsk, VALUE_COMMITMENT_RANDOMNESS_GENERATOR);
// In order to check internal consistency, let's use the accumulated value
// commitments (as the verifier would) and apply value_balance to compare
// against our derived bvk.
{
// Compute value balance
let mut value_balance = compute_value_balance(value_balance, params).ok_or(())?;
let value_balance = compute_value_balance(value_balance).ok_or(())?;
// Subtract value_balance from cv_sum to get final bvk
value_balance = value_balance.negate();
let mut tmp = self.cv_sum.clone();
tmp = tmp.add(&value_balance, params);
let final_bvk = self.cv_sum - value_balance;
// The result should be the same, unless the provided valueBalance is wrong.
if bvk.0 != tmp {
if bvk.0 != final_bvk {
return Err(());
}
}
// Construct signature message
let mut data_to_be_signed = [0u8; 64];
bvk.0
.write(&mut data_to_be_signed[0..32])
.expect("message buffer should be 32 bytes");
data_to_be_signed[0..32].copy_from_slice(&bvk.0.to_bytes());
(&mut data_to_be_signed[32..64]).copy_from_slice(&sighash[..]);
// Sign
Ok(bsk.sign(
&data_to_be_signed,
&mut rng,
FixedGenerators::ValueCommitmentRandomness,
params,
VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
))
}
}

View File

@ -2,31 +2,27 @@ use bellman::{
gadgets::multipack,
groth16::{verify_proof, PreparedVerifyingKey, Proof},
};
use ff::Field;
use pairing::bls12_381::{Bls12, Fr};
use zcash_primitives::jubjub::{edwards, FixedGenerators, JubjubBls12, Unknown};
use bls12_381::Bls12;
use group::{Curve, GroupEncoding};
use zcash_primitives::{
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
redjubjub::{PublicKey, Signature},
transaction::components::Amount,
};
use super::compute_value_balance;
fn is_small_order<Order>(p: &edwards::Point<Bls12, Order>, params: &JubjubBls12) -> bool {
p.double(params).double(params).double(params) == edwards::Point::zero()
}
/// A context object for verifying the Sapling components of a Zcash transaction.
pub struct SaplingVerificationContext {
// (sum of the Spend value commitments) - (sum of the Output value commitments)
cv_sum: edwards::Point<Bls12, Unknown>,
cv_sum: jubjub::ExtendedPoint,
}
impl SaplingVerificationContext {
/// Construct a new context to be used with a single transaction.
pub fn new() -> Self {
SaplingVerificationContext {
cv_sum: edwards::Point::zero(),
cv_sum: jubjub::ExtendedPoint::identity(),
}
}
@ -34,61 +30,46 @@ impl SaplingVerificationContext {
/// accumulating its value commitment inside the context for later use.
pub fn check_spend(
&mut self,
cv: edwards::Point<Bls12, Unknown>,
anchor: Fr,
cv: jubjub::ExtendedPoint,
anchor: bls12_381::Scalar,
nullifier: &[u8; 32],
rk: PublicKey<Bls12>,
rk: PublicKey,
sighash_value: &[u8; 32],
spend_auth_sig: Signature,
zkproof: Proof<Bls12>,
verifying_key: &PreparedVerifyingKey<Bls12>,
params: &JubjubBls12,
) -> bool {
if is_small_order(&cv, params) {
return false;
}
if is_small_order(&rk.0, params) {
if (cv.is_small_order() | rk.0.is_small_order()).into() {
return false;
}
// Accumulate the value commitment in the context
{
let mut tmp = cv.clone();
tmp = tmp.add(&self.cv_sum, params);
// Update the context
self.cv_sum = tmp;
}
self.cv_sum += cv;
// Grab the nullifier as a sequence of bytes
let nullifier = &nullifier[..];
// Compute the signature's message for rk/spend_auth_sig
let mut data_to_be_signed = [0u8; 64];
rk.0.write(&mut data_to_be_signed[0..32])
.expect("message buffer should be 32 bytes");
data_to_be_signed[0..32].copy_from_slice(&rk.0.to_bytes());
(&mut data_to_be_signed[32..64]).copy_from_slice(&sighash_value[..]);
// Verify the spend_auth_sig
if !rk.verify(
&data_to_be_signed,
&spend_auth_sig,
FixedGenerators::SpendingKeyGenerator,
params,
) {
if !rk.verify(&data_to_be_signed, &spend_auth_sig, SPENDING_KEY_GENERATOR) {
return false;
}
// Construct public input for circuit
let mut public_input = [Fr::zero(); 7];
let mut public_input = [bls12_381::Scalar::zero(); 7];
{
let (x, y) = rk.0.to_xy();
let affine = rk.0.to_affine();
let (x, y) = (affine.get_u(), affine.get_v());
public_input[0] = x;
public_input[1] = y;
}
{
let (x, y) = cv.to_xy();
let affine = cv.to_affine();
let (x, y) = (affine.get_u(), affine.get_v());
public_input[2] = x;
public_input[3] = y;
}
@ -113,40 +94,30 @@ impl SaplingVerificationContext {
/// accumulating its value commitment inside the context for later use.
pub fn check_output(
&mut self,
cv: edwards::Point<Bls12, Unknown>,
cm: Fr,
epk: edwards::Point<Bls12, Unknown>,
cv: jubjub::ExtendedPoint,
cm: bls12_381::Scalar,
epk: jubjub::ExtendedPoint,
zkproof: Proof<Bls12>,
verifying_key: &PreparedVerifyingKey<Bls12>,
params: &JubjubBls12,
) -> bool {
if is_small_order(&cv, params) {
return false;
}
if is_small_order(&epk, params) {
if (cv.is_small_order() | epk.is_small_order()).into() {
return false;
}
// Accumulate the value commitment in the context
{
let mut tmp = cv.clone();
tmp = tmp.negate(); // Outputs subtract from the total.
tmp = tmp.add(&self.cv_sum, params);
// Update the context
self.cv_sum = tmp;
}
self.cv_sum -= cv;
// Construct public input for circuit
let mut public_input = [Fr::zero(); 5];
let mut public_input = [bls12_381::Scalar::zero(); 5];
{
let (x, y) = cv.to_xy();
let affine = cv.to_affine();
let (x, y) = (affine.get_u(), affine.get_v());
public_input[0] = x;
public_input[1] = y;
}
{
let (x, y) = epk.to_xy();
let affine = epk.to_affine();
let (x, y) = (affine.get_u(), affine.get_v());
public_input[2] = x;
public_input[3] = y;
}
@ -164,34 +135,29 @@ impl SaplingVerificationContext {
value_balance: Amount,
sighash_value: &[u8; 32],
binding_sig: Signature,
params: &JubjubBls12,
) -> bool {
// Obtain current cv_sum from the context
let mut bvk = PublicKey(self.cv_sum.clone());
// Compute value balance
let mut value_balance = match compute_value_balance(value_balance, params) {
let value_balance = match compute_value_balance(value_balance) {
Some(a) => a,
None => return false,
};
// Subtract value_balance from current cv_sum to get final bvk
value_balance = value_balance.negate();
bvk.0 = bvk.0.add(&value_balance, params);
bvk.0 -= value_balance;
// Compute the signature's message for bvk/binding_sig
let mut data_to_be_signed = [0u8; 64];
bvk.0
.write(&mut data_to_be_signed[0..32])
.expect("bvk is 32 bytes");
data_to_be_signed[0..32].copy_from_slice(&bvk.0.to_bytes());
(&mut data_to_be_signed[32..64]).copy_from_slice(&sighash_value[..]);
// Verify the binding_sig
bvk.verify(
&data_to_be_signed,
&binding_sig,
FixedGenerators::ValueCommitmentRandomness,
params,
VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
)
}
}

View File

@ -4,7 +4,7 @@ use bellman::{
gadgets::multipack,
groth16::{self, create_random_proof, Parameters, PreparedVerifyingKey, Proof},
};
use pairing::bls12_381::Bls12;
use bls12_381::Bls12;
use rand_core::OsRng;
use crate::circuit::sprout::*;