s/{pairing::bls12_381, zcash_primitives::jubjub}/{bls12_381, jubjub}
FINALLY.
This commit is contained in:
parent
315f00d6d4
commit
fdf06032e3
|
@ -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};
|
||||
//!
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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(), ¶ms))
|
||||
b.iter(|| pedersen_hash(personalization, bits.clone()))
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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[..], ¶ms).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[..], ¶ms).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,
|
||||
¶ms,
|
||||
);
|
||||
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, ¶ms));
|
||||
pedersen_hash_generators.push(p1 + p2);
|
||||
|
||||
JubjubBls12::check_consistency_of_pedersen_hash_generators(¶ms, &pedersen_hash_generators);
|
||||
JubjubBls12::check_consistency_of_pedersen_hash_generators(&pedersen_hash_generators);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
¶ms
|
||||
.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(
|
||||
¶ms
|
||||
.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(
|
||||
¶ms
|
||||
.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(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(
|
||||
¶ms.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(¶ms.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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
¬e_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,
|
||||
¬e_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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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::*;
|
||||
|
|
Loading…
Reference in New Issue