zcash_primitives: Replace `sapling::redjubjub` with `redjubjub` crate
As a side-effect, we remove the ability to verify individual transactions with pre-ZIP 216 rules (which we already removed from `zcashd` consensus nodes in zcash/zcash#6000 and zcash/zcash#6399, as all pre-ZIP 216 transactions on mainnet are also valid under ZIP 216).
This commit is contained in:
parent
764127f952
commit
de1ed21051
|
@ -100,6 +100,10 @@ and this library adheres to Rust's notion of
|
||||||
- `zcash_primitives::sapling`:
|
- `zcash_primitives::sapling`:
|
||||||
- `BatchValidator::validate` now takes the `SpendVerifyingKey` and
|
- `BatchValidator::validate` now takes the `SpendVerifyingKey` and
|
||||||
`OutputVerifyingKey` newtypes.
|
`OutputVerifyingKey` newtypes.
|
||||||
|
- `SaplingVerificationContext::new` now always creates a context with ZIP 216
|
||||||
|
rules enforced, and no longer has a boolean for configuring this.
|
||||||
|
- `SaplingVerificationContext::{check_spend, final_check}` now use the
|
||||||
|
`redjubjub` crate types for `rk`, `spend_auth_sig`, and `binding_sig`.
|
||||||
- `SaplingVerificationContext::{check_spend, check_output}` now take
|
- `SaplingVerificationContext::{check_spend, check_output}` now take
|
||||||
the `PreparedSpendVerifyingKey` and `PreparedOutputVerifyingKey`
|
the `PreparedSpendVerifyingKey` and `PreparedOutputVerifyingKey`
|
||||||
newtypes.
|
newtypes.
|
||||||
|
@ -127,6 +131,15 @@ and this library adheres to Rust's notion of
|
||||||
- `Error::MissingSignatures`
|
- `Error::MissingSignatures`
|
||||||
- `bundle::Bundle` now has a second generic parameter `V`.
|
- `bundle::Bundle` now has a second generic parameter `V`.
|
||||||
- `bundle::Bundle::value_balance` now returns `&V` instead of `&Amount`.
|
- `bundle::Bundle::value_balance` now returns `&V` instead of `&Amount`.
|
||||||
|
- `bundle::Authorized::binding_sig` now has type `redjubjub::Signature<Binding>`.
|
||||||
|
- `bundle::Authorized::AuthSig` now has type `redjubjub::Signature<SpendAuth>`.
|
||||||
|
- `bundle::SpendDescription::temporary_zcashd_from_parts` now takes `rk` as
|
||||||
|
`redjubjub::VerificationKey<SpendAuth>` instead of
|
||||||
|
`zcash_primitives::sapling::redjubjub::PublicKey`.
|
||||||
|
- `bundle::SpendDescription::rk` now returns `&redjubjub::VerificationKey<SpendAuth>`.
|
||||||
|
- `bundle::SpendDescriptionV5::into_spend_description` now takes
|
||||||
|
`spend_auth_sig` as `redjubjub::Signature<SpendAuth>` instead of
|
||||||
|
`zcash_primitives::sapling::redjubjub::Signature`.
|
||||||
- `bundle::testing::arb_bundle` now takes a `value_balance: V` argument.
|
- `bundle::testing::arb_bundle` now takes a `value_balance: V` argument.
|
||||||
- `bundle::MapAuth` trait methods now take `&mut self` instead of `&self`.
|
- `bundle::MapAuth` trait methods now take `&mut self` instead of `&self`.
|
||||||
- `circuit::ValueCommitmentOpening::value` is now represented as a `NoteValue`
|
- `circuit::ValueCommitmentOpening::value` is now represented as a `NoteValue`
|
||||||
|
@ -150,6 +163,11 @@ and this library adheres to Rust's notion of
|
||||||
- `try_sapling_output_recovery`
|
- `try_sapling_output_recovery`
|
||||||
- `util::generate_random_rseed` now takes a `Zip212Enforcement` argument
|
- `util::generate_random_rseed` now takes a `Zip212Enforcement` argument
|
||||||
instead of a `P: consensus::Parameters` argument and a height.
|
instead of a `P: consensus::Parameters` argument and a height.
|
||||||
|
- `value::TrapdoorSum::into_bsk` now returns `redjubjub::SigningKey<Binding>`
|
||||||
|
instead of `zcash_primitives::sapling::redjubjub::PrivateKey`.
|
||||||
|
- `value::CommitmentSum::into_bvk` now returns
|
||||||
|
`redjubjub::VerificationKey<Binding>` instead of
|
||||||
|
`zcash_primitives::sapling::redjubjub::PublicKey`.
|
||||||
- `zcash_primitives::transaction`:
|
- `zcash_primitives::transaction`:
|
||||||
- `builder::Builder::{build, build_zfuture}` now take
|
- `builder::Builder::{build, build_zfuture}` now take
|
||||||
`&impl SpendProver, &impl OutputProver` instead of `&impl TxProver`.
|
`&impl SpendProver, &impl OutputProver` instead of `&impl TxProver`.
|
||||||
|
@ -192,6 +210,8 @@ and this library adheres to Rust's notion of
|
||||||
- `OutputDescriptionV5::read`
|
- `OutputDescriptionV5::read`
|
||||||
- `note_encryption::SaplingDomain::for_height` (use `SaplingDomain::new`
|
- `note_encryption::SaplingDomain::for_height` (use `SaplingDomain::new`
|
||||||
instead).
|
instead).
|
||||||
|
- `redjubjub` module (use the `redjubjub` crate instead).
|
||||||
|
- `spend_sig` (use `redjubjub::SigningKey::{randomize, sign}` instead).
|
||||||
- `zcash_primitives::transaction::components::sapling`:
|
- `zcash_primitives::transaction::components::sapling`:
|
||||||
- The following types were removed from this module (moved into
|
- The following types were removed from this module (moved into
|
||||||
`zcash_primitives::sapling::bundle`):
|
`zcash_primitives::sapling::bundle`):
|
||||||
|
|
|
@ -11,7 +11,6 @@ pub mod note;
|
||||||
pub mod note_encryption;
|
pub mod note_encryption;
|
||||||
pub mod pedersen_hash;
|
pub mod pedersen_hash;
|
||||||
pub mod prover;
|
pub mod prover;
|
||||||
pub mod redjubjub;
|
|
||||||
mod spec;
|
mod spec;
|
||||||
mod tree;
|
mod tree;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
@ -19,13 +18,6 @@ pub mod value;
|
||||||
mod verifier;
|
mod verifier;
|
||||||
pub mod zip32;
|
pub mod zip32;
|
||||||
|
|
||||||
use group::GroupEncoding;
|
|
||||||
use rand_core::{CryptoRng, RngCore};
|
|
||||||
|
|
||||||
use constants::SPENDING_KEY_GENERATOR;
|
|
||||||
|
|
||||||
use self::redjubjub::{PrivateKey, PublicKey, Signature};
|
|
||||||
|
|
||||||
pub use address::PaymentAddress;
|
pub use address::PaymentAddress;
|
||||||
pub use bundle::Bundle;
|
pub use bundle::Bundle;
|
||||||
pub use keys::{Diversifier, NullifierDerivingKey, ProofGenerationKey, SaplingIvk, ViewingKey};
|
pub use keys::{Diversifier, NullifierDerivingKey, ProofGenerationKey, SaplingIvk, ViewingKey};
|
||||||
|
@ -35,60 +27,6 @@ pub use tree::{
|
||||||
};
|
};
|
||||||
pub use verifier::{BatchValidator, SaplingVerificationContext};
|
pub use verifier::{BatchValidator, SaplingVerificationContext};
|
||||||
|
|
||||||
/// Create the spendAuthSig for a Sapling SpendDescription.
|
|
||||||
pub fn spend_sig<R: RngCore + CryptoRng>(
|
|
||||||
ask: PrivateKey,
|
|
||||||
ar: jubjub::Fr,
|
|
||||||
sighash: &[u8; 32],
|
|
||||||
rng: &mut R,
|
|
||||||
) -> Signature {
|
|
||||||
spend_sig_internal(&ask, ar, sighash, rng)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn spend_sig_internal<R: RngCore>(
|
|
||||||
ask: &PrivateKey,
|
|
||||||
ar: jubjub::Fr,
|
|
||||||
sighash: &[u8; 32],
|
|
||||||
rng: &mut R,
|
|
||||||
) -> Signature {
|
|
||||||
// We compute `rsk`...
|
|
||||||
let rsk = ask.randomize(ar);
|
|
||||||
|
|
||||||
// We compute `rk` from there (needed for key prefixing)
|
|
||||||
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];
|
|
||||||
data_to_be_signed[0..32].copy_from_slice(&rk.0.to_bytes());
|
|
||||||
data_to_be_signed[32..64].copy_from_slice(&sighash[..]);
|
|
||||||
|
|
||||||
// Do the signing
|
|
||||||
rsk.sign(&data_to_be_signed, rng, SPENDING_KEY_GENERATOR)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verifies a spendAuthSig.
|
|
||||||
///
|
|
||||||
/// This only exists because the RedJubjub implementation inside `zcash_primitives` does
|
|
||||||
/// not implement key prefixing (which was added in response to a Sapling audit). This
|
|
||||||
/// will be fixed by migrating to the redjubjub crate.
|
|
||||||
pub(crate) fn verify_spend_sig(
|
|
||||||
ak: &PublicKey,
|
|
||||||
alpha: jubjub::Fr,
|
|
||||||
sighash: &[u8; 32],
|
|
||||||
sig: &Signature,
|
|
||||||
) -> bool {
|
|
||||||
// We compute `rk` (needed for key prefixing)
|
|
||||||
let rk = ak.randomize(alpha, SPENDING_KEY_GENERATOR);
|
|
||||||
|
|
||||||
// Compute the signature's message for rk/spend_auth_sig
|
|
||||||
let mut data_to_be_signed = [0u8; 64];
|
|
||||||
data_to_be_signed[0..32].copy_from_slice(&rk.0.to_bytes());
|
|
||||||
data_to_be_signed[32..64].copy_from_slice(&sighash[..]);
|
|
||||||
|
|
||||||
// Do the verifying
|
|
||||||
rk.verify(&data_to_be_signed, sig, SPENDING_KEY_GENERATOR)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-dependencies"))]
|
#[cfg(any(test, feature = "test-dependencies"))]
|
||||||
pub mod testing {
|
pub mod testing {
|
||||||
pub use super::{
|
pub use super::{
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use std::{marker::PhantomData, sync::mpsc::Sender};
|
use std::{marker::PhantomData, sync::mpsc::Sender};
|
||||||
|
|
||||||
use ff::Field;
|
use group::{ff::Field, GroupEncoding};
|
||||||
use rand::{seq::SliceRandom, RngCore};
|
use rand::{seq::SliceRandom, RngCore};
|
||||||
use rand_core::CryptoRng;
|
use rand_core::CryptoRng;
|
||||||
|
use redjubjub::{Binding, SpendAuth};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
sapling::{
|
sapling::{
|
||||||
|
@ -14,17 +15,13 @@ use crate::{
|
||||||
Authorization, Authorized, Bundle, GrothProofBytes, MapAuth, OutputDescription,
|
Authorization, Authorized, Bundle, GrothProofBytes, MapAuth, OutputDescription,
|
||||||
SpendDescription,
|
SpendDescription,
|
||||||
},
|
},
|
||||||
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
|
|
||||||
keys::OutgoingViewingKey,
|
keys::OutgoingViewingKey,
|
||||||
note_encryption::{sapling_note_encryption, Zip212Enforcement},
|
note_encryption::{sapling_note_encryption, Zip212Enforcement},
|
||||||
prover::{OutputProver, SpendProver},
|
prover::{OutputProver, SpendProver},
|
||||||
redjubjub::{PrivateKey, PublicKey, Signature},
|
|
||||||
spend_sig_internal,
|
|
||||||
util::generate_random_rseed_internal,
|
util::generate_random_rseed_internal,
|
||||||
value::{
|
value::{
|
||||||
CommitmentSum, NoteValue, TrapdoorSum, ValueCommitTrapdoor, ValueCommitment, ValueSum,
|
CommitmentSum, NoteValue, TrapdoorSum, ValueCommitTrapdoor, ValueCommitment, ValueSum,
|
||||||
},
|
},
|
||||||
verify_spend_sig,
|
|
||||||
zip32::ExtendedSpendingKey,
|
zip32::ExtendedSpendingKey,
|
||||||
Diversifier, MerklePath, Node, Note, PaymentAddress, ProofGenerationKey, SaplingIvk,
|
Diversifier, MerklePath, Node, Note, PaymentAddress, ProofGenerationKey, SaplingIvk,
|
||||||
},
|
},
|
||||||
|
@ -117,10 +114,11 @@ impl SpendDescriptionInfo {
|
||||||
// Construct the value commitment.
|
// Construct the value commitment.
|
||||||
let cv = ValueCommitment::derive(self.note.value(), self.rcv.clone());
|
let cv = ValueCommitment::derive(self.note.value(), self.rcv.clone());
|
||||||
|
|
||||||
let ak = PublicKey(self.proof_generation_key.ak.into());
|
let ak = redjubjub::VerificationKey::try_from(self.proof_generation_key.ak.to_bytes())
|
||||||
|
.expect("valid points are valid verification keys");
|
||||||
|
|
||||||
// This is the result of the re-randomization, we compute it for the caller
|
// This is the result of the re-randomization, we compute it for the caller
|
||||||
let rk = ak.randomize(self.alpha, SPENDING_KEY_GENERATOR);
|
let rk = ak.randomize(&self.alpha);
|
||||||
|
|
||||||
let nullifier = self.note.nf(
|
let nullifier = self.note.nf(
|
||||||
&self.proof_generation_key.to_viewing_key().nk,
|
&self.proof_generation_key.to_viewing_key().nk,
|
||||||
|
@ -507,10 +505,7 @@ impl SaplingBuilder {
|
||||||
(spends - outputs)
|
(spends - outputs)
|
||||||
.into_bvk(i64::try_from(self.value_balance).map_err(|_| Error::InvalidAmount)?)
|
.into_bvk(i64::try_from(self.value_balance).map_err(|_| Error::InvalidAmount)?)
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(redjubjub::VerificationKey::from(&bsk), bvk);
|
||||||
PublicKey::from_private(&bsk, VALUE_COMMITMENT_RANDOMNESS_GENERATOR).0,
|
|
||||||
bvk.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
let bundle = if shielded_spends.is_empty() && shielded_outputs.is_empty() {
|
let bundle = if shielded_spends.is_empty() && shielded_outputs.is_empty() {
|
||||||
None
|
None
|
||||||
|
@ -676,16 +671,9 @@ impl<S: InProgressSignatures, V> Bundle<InProgress<Unproven, S>, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marker for an unauthorized bundle with no signatures.
|
/// Marker for an unauthorized bundle with no signatures.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Unsigned {
|
pub struct Unsigned {
|
||||||
bsk: PrivateKey,
|
bsk: redjubjub::SigningKey<Binding>,
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for Unsigned {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
bsk: PrivateKey(self.bsk.0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Unsigned {
|
impl fmt::Debug for Unsigned {
|
||||||
|
@ -703,7 +691,7 @@ impl InProgressSignatures for Unsigned {
|
||||||
pub struct SigningParts {
|
pub struct SigningParts {
|
||||||
/// The spend validating key for this spend description. Used to match spend
|
/// The spend validating key for this spend description. Used to match spend
|
||||||
/// authorizing keys to spend descriptions they can create signatures for.
|
/// authorizing keys to spend descriptions they can create signatures for.
|
||||||
ak: PublicKey,
|
ak: redjubjub::VerificationKey<SpendAuth>,
|
||||||
/// The randomization needed to derive the actual signing key for this note.
|
/// The randomization needed to derive the actual signing key for this note.
|
||||||
alpha: jubjub::Scalar,
|
alpha: jubjub::Scalar,
|
||||||
}
|
}
|
||||||
|
@ -711,7 +699,7 @@ pub struct SigningParts {
|
||||||
/// Marker for a partially-authorized bundle, in the process of being signed.
|
/// Marker for a partially-authorized bundle, in the process of being signed.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PartiallyAuthorized {
|
pub struct PartiallyAuthorized {
|
||||||
binding_signature: Signature,
|
binding_signature: redjubjub::Signature<Binding>,
|
||||||
sighash: [u8; 32],
|
sighash: [u8; 32],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -720,16 +708,18 @@ impl InProgressSignatures for PartiallyAuthorized {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A heisen[`Signature`] for a particular [`SpendDescription`].
|
/// A heisen[`Signature`] for a particular [`SpendDescription`].
|
||||||
|
///
|
||||||
|
/// [`Signature`]: redjubjub::Signature
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum MaybeSigned {
|
pub enum MaybeSigned {
|
||||||
/// The information needed to sign this [`SpendDescription`].
|
/// The information needed to sign this [`SpendDescription`].
|
||||||
SigningMetadata(SigningParts),
|
SigningMetadata(SigningParts),
|
||||||
/// The signature for this [`SpendDescription`].
|
/// The signature for this [`SpendDescription`].
|
||||||
Signature(Signature),
|
Signature(redjubjub::Signature<SpendAuth>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaybeSigned {
|
impl MaybeSigned {
|
||||||
fn finalize(self) -> Result<Signature, Error> {
|
fn finalize(self) -> Result<redjubjub::Signature<SpendAuth>, Error> {
|
||||||
match self {
|
match self {
|
||||||
Self::Signature(sig) => Ok(sig),
|
Self::Signature(sig) => Ok(sig),
|
||||||
_ => Err(Error::MissingSignatures),
|
_ => Err(Error::MissingSignatures),
|
||||||
|
@ -752,11 +742,7 @@ impl<P: InProgressProofs, V> Bundle<InProgress<P, Unsigned>, V> {
|
||||||
MaybeSigned::SigningMetadata,
|
MaybeSigned::SigningMetadata,
|
||||||
|auth: InProgress<P, Unsigned>| InProgress {
|
|auth: InProgress<P, Unsigned>| InProgress {
|
||||||
sigs: PartiallyAuthorized {
|
sigs: PartiallyAuthorized {
|
||||||
binding_signature: auth.sigs.bsk.sign(
|
binding_signature: auth.sigs.bsk.sign(&mut rng, &sighash),
|
||||||
&sighash,
|
|
||||||
&mut rng,
|
|
||||||
VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
|
|
||||||
),
|
|
||||||
sighash,
|
sighash,
|
||||||
},
|
},
|
||||||
_proof_state: PhantomData::default(),
|
_proof_state: PhantomData::default(),
|
||||||
|
@ -774,7 +760,7 @@ impl<V> Bundle<InProgress<Proven, Unsigned>, V> {
|
||||||
self,
|
self,
|
||||||
mut rng: R,
|
mut rng: R,
|
||||||
sighash: [u8; 32],
|
sighash: [u8; 32],
|
||||||
signing_keys: &[PrivateKey],
|
signing_keys: &[redjubjub::SigningKey<SpendAuth>],
|
||||||
) -> Result<Bundle<Authorized, V>, Error> {
|
) -> Result<Bundle<Authorized, V>, Error> {
|
||||||
signing_keys
|
signing_keys
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -786,18 +772,23 @@ impl<V> Bundle<InProgress<Proven, Unsigned>, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: InProgressProofs, V> Bundle<InProgress<P, PartiallyAuthorized>, V> {
|
impl<P: InProgressProofs, V> Bundle<InProgress<P, PartiallyAuthorized>, V> {
|
||||||
/// Signs this bundle with the given [`PrivateKey`].
|
/// Signs this bundle with the given [`redjubjub::SigningKey`].
|
||||||
///
|
///
|
||||||
/// This will apply signatures for all notes controlled by this spending key.
|
/// This will apply signatures for all notes controlled by this spending key.
|
||||||
pub fn sign<R: RngCore + CryptoRng>(self, mut rng: R, ask: &PrivateKey) -> Self {
|
pub fn sign<R: RngCore + CryptoRng>(
|
||||||
let expected_ak = PublicKey::from_private(ask, SPENDING_KEY_GENERATOR);
|
self,
|
||||||
|
mut rng: R,
|
||||||
|
ask: &redjubjub::SigningKey<SpendAuth>,
|
||||||
|
) -> Self {
|
||||||
|
let expected_ak = redjubjub::VerificationKey::from(ask);
|
||||||
let sighash = self.authorization().sigs.sighash;
|
let sighash = self.authorization().sigs.sighash;
|
||||||
self.map_authorization((
|
self.map_authorization((
|
||||||
|proof| proof,
|
|proof| proof,
|
||||||
|proof| proof,
|
|proof| proof,
|
||||||
|maybe| match maybe {
|
|maybe| match maybe {
|
||||||
MaybeSigned::SigningMetadata(parts) if parts.ak.0 == expected_ak.0 => {
|
MaybeSigned::SigningMetadata(parts) if parts.ak == expected_ak => {
|
||||||
MaybeSigned::Signature(spend_sig_internal(ask, parts.alpha, &sighash, &mut rng))
|
let rsk = ask.randomize(&parts.alpha);
|
||||||
|
MaybeSigned::Signature(rsk.sign(&mut rng, &sighash))
|
||||||
}
|
}
|
||||||
s => s,
|
s => s,
|
||||||
},
|
},
|
||||||
|
@ -805,16 +796,19 @@ impl<P: InProgressProofs, V> Bundle<InProgress<P, PartiallyAuthorized>, V> {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Appends externally computed [`Signature`]s.
|
/// Appends externally computed [`redjubjub::Signature`]s.
|
||||||
///
|
///
|
||||||
/// Each signature will be applied to the one input for which it is valid. An error
|
/// Each signature will be applied to the one input for which it is valid. An error
|
||||||
/// will be returned if the signature is not valid for any inputs, or if it is valid
|
/// will be returned if the signature is not valid for any inputs, or if it is valid
|
||||||
/// for more than one input.
|
/// for more than one input.
|
||||||
pub fn append_signatures(self, signatures: &[Signature]) -> Result<Self, Error> {
|
pub fn append_signatures(
|
||||||
|
self,
|
||||||
|
signatures: &[redjubjub::Signature<SpendAuth>],
|
||||||
|
) -> Result<Self, Error> {
|
||||||
signatures.iter().try_fold(self, Self::append_signature)
|
signatures.iter().try_fold(self, Self::append_signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_signature(self, signature: &Signature) -> Result<Self, Error> {
|
fn append_signature(self, signature: &redjubjub::Signature<SpendAuth>) -> Result<Self, Error> {
|
||||||
let sighash = self.authorization().sigs.sighash;
|
let sighash = self.authorization().sigs.sighash;
|
||||||
let mut signature_valid_for = 0usize;
|
let mut signature_valid_for = 0usize;
|
||||||
let bundle = self.map_authorization((
|
let bundle = self.map_authorization((
|
||||||
|
@ -822,7 +816,8 @@ impl<P: InProgressProofs, V> Bundle<InProgress<P, PartiallyAuthorized>, V> {
|
||||||
|proof| proof,
|
|proof| proof,
|
||||||
|maybe| match maybe {
|
|maybe| match maybe {
|
||||||
MaybeSigned::SigningMetadata(parts) => {
|
MaybeSigned::SigningMetadata(parts) => {
|
||||||
if verify_spend_sig(&parts.ak, parts.alpha, &sighash, signature) {
|
let rk = parts.ak.randomize(&parts.alpha);
|
||||||
|
if rk.verify(&sighash, signature).is_ok() {
|
||||||
signature_valid_for += 1;
|
signature_valid_for += 1;
|
||||||
MaybeSigned::Signature(*signature)
|
MaybeSigned::Signature(*signature)
|
||||||
} else {
|
} else {
|
||||||
|
@ -872,7 +867,6 @@ pub mod testing {
|
||||||
bundle::{Authorized, Bundle},
|
bundle::{Authorized, Bundle},
|
||||||
note_encryption::Zip212Enforcement,
|
note_encryption::Zip212Enforcement,
|
||||||
prover::mock::{MockOutputProver, MockSpendProver},
|
prover::mock::{MockOutputProver, MockSpendProver},
|
||||||
redjubjub::PrivateKey,
|
|
||||||
testing::{arb_node, arb_note},
|
testing::{arb_node, arb_note},
|
||||||
value::testing::arb_positive_note_value,
|
value::testing::arb_positive_note_value,
|
||||||
zip32::testing::arb_extended_spending_key,
|
zip32::testing::arb_extended_spending_key,
|
||||||
|
@ -929,7 +923,8 @@ pub mod testing {
|
||||||
.apply_signatures(
|
.apply_signatures(
|
||||||
&mut rng,
|
&mut rng,
|
||||||
fake_sighash_bytes,
|
fake_sighash_bytes,
|
||||||
&[PrivateKey(extsk.expsk.ask)],
|
&[redjubjub::SigningKey::try_from(extsk.expsk.ask.to_bytes())
|
||||||
|
.expect("valid scalars are valid signing keys")],
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
|
||||||
use memuse::DynamicUsage;
|
use memuse::DynamicUsage;
|
||||||
|
use redjubjub::{Binding, SpendAuth};
|
||||||
|
|
||||||
use zcash_note_encryption::{
|
use zcash_note_encryption::{
|
||||||
EphemeralKeyBytes, ShieldedOutput, COMPACT_NOTE_SIZE, ENC_CIPHERTEXT_SIZE, OUT_CIPHERTEXT_SIZE,
|
EphemeralKeyBytes, ShieldedOutput, COMPACT_NOTE_SIZE, ENC_CIPHERTEXT_SIZE, OUT_CIPHERTEXT_SIZE,
|
||||||
|
@ -10,7 +11,6 @@ use crate::sapling::{
|
||||||
circuit::GROTH_PROOF_SIZE,
|
circuit::GROTH_PROOF_SIZE,
|
||||||
note::ExtractedNoteCommitment,
|
note::ExtractedNoteCommitment,
|
||||||
note_encryption::{CompactOutputDescription, SaplingDomain},
|
note_encryption::{CompactOutputDescription, SaplingDomain},
|
||||||
redjubjub::{self, PublicKey, Signature},
|
|
||||||
value::ValueCommitment,
|
value::ValueCommitment,
|
||||||
Nullifier,
|
Nullifier,
|
||||||
};
|
};
|
||||||
|
@ -29,13 +29,13 @@ pub trait Authorization: Debug {
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct Authorized {
|
pub struct Authorized {
|
||||||
// TODO: Make this private.
|
// TODO: Make this private.
|
||||||
pub binding_sig: redjubjub::Signature,
|
pub binding_sig: redjubjub::Signature<Binding>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Authorization for Authorized {
|
impl Authorization for Authorized {
|
||||||
type SpendProof = GrothProofBytes;
|
type SpendProof = GrothProofBytes;
|
||||||
type OutputProof = GrothProofBytes;
|
type OutputProof = GrothProofBytes;
|
||||||
type AuthSig = redjubjub::Signature;
|
type AuthSig = redjubjub::Signature<SpendAuth>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A map from one bundle authorization to another.
|
/// A map from one bundle authorization to another.
|
||||||
|
@ -315,7 +315,7 @@ pub struct SpendDescription<A: Authorization> {
|
||||||
cv: ValueCommitment,
|
cv: ValueCommitment,
|
||||||
anchor: bls12_381::Scalar,
|
anchor: bls12_381::Scalar,
|
||||||
nullifier: Nullifier,
|
nullifier: Nullifier,
|
||||||
rk: PublicKey,
|
rk: redjubjub::VerificationKey<SpendAuth>,
|
||||||
zkproof: A::SpendProof,
|
zkproof: A::SpendProof,
|
||||||
spend_auth_sig: A::AuthSig,
|
spend_auth_sig: A::AuthSig,
|
||||||
}
|
}
|
||||||
|
@ -336,7 +336,7 @@ impl<A: Authorization> SpendDescription<A> {
|
||||||
cv: ValueCommitment,
|
cv: ValueCommitment,
|
||||||
anchor: bls12_381::Scalar,
|
anchor: bls12_381::Scalar,
|
||||||
nullifier: Nullifier,
|
nullifier: Nullifier,
|
||||||
rk: PublicKey,
|
rk: redjubjub::VerificationKey<SpendAuth>,
|
||||||
zkproof: A::SpendProof,
|
zkproof: A::SpendProof,
|
||||||
spend_auth_sig: A::AuthSig,
|
spend_auth_sig: A::AuthSig,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -347,7 +347,7 @@ impl<A: Authorization> SpendDescription<A> {
|
||||||
cv: ValueCommitment,
|
cv: ValueCommitment,
|
||||||
anchor: bls12_381::Scalar,
|
anchor: bls12_381::Scalar,
|
||||||
nullifier: Nullifier,
|
nullifier: Nullifier,
|
||||||
rk: PublicKey,
|
rk: redjubjub::VerificationKey<SpendAuth>,
|
||||||
zkproof: A::SpendProof,
|
zkproof: A::SpendProof,
|
||||||
spend_auth_sig: A::AuthSig,
|
spend_auth_sig: A::AuthSig,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -377,7 +377,7 @@ impl<A: Authorization> SpendDescription<A> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the randomized verification key for the note being spent.
|
/// Returns the randomized verification key for the note being spent.
|
||||||
pub fn rk(&self) -> &PublicKey {
|
pub fn rk(&self) -> &redjubjub::VerificationKey<SpendAuth> {
|
||||||
&self.rk
|
&self.rk
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,11 +406,15 @@ impl DynamicUsage for SpendDescription<Authorized> {
|
||||||
pub struct SpendDescriptionV5 {
|
pub struct SpendDescriptionV5 {
|
||||||
cv: ValueCommitment,
|
cv: ValueCommitment,
|
||||||
nullifier: Nullifier,
|
nullifier: Nullifier,
|
||||||
rk: PublicKey,
|
rk: redjubjub::VerificationKey<SpendAuth>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpendDescriptionV5 {
|
impl SpendDescriptionV5 {
|
||||||
pub(crate) fn from_parts(cv: ValueCommitment, nullifier: Nullifier, rk: PublicKey) -> Self {
|
pub(crate) fn from_parts(
|
||||||
|
cv: ValueCommitment,
|
||||||
|
nullifier: Nullifier,
|
||||||
|
rk: redjubjub::VerificationKey<SpendAuth>,
|
||||||
|
) -> Self {
|
||||||
Self { cv, nullifier, rk }
|
Self { cv, nullifier, rk }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,7 +422,7 @@ impl SpendDescriptionV5 {
|
||||||
self,
|
self,
|
||||||
anchor: bls12_381::Scalar,
|
anchor: bls12_381::Scalar,
|
||||||
zkproof: GrothProofBytes,
|
zkproof: GrothProofBytes,
|
||||||
spend_auth_sig: Signature,
|
spend_auth_sig: redjubjub::Signature<SpendAuth>,
|
||||||
) -> SpendDescription<Authorized> {
|
) -> SpendDescription<Authorized> {
|
||||||
SpendDescription {
|
SpendDescription {
|
||||||
cv: self.cv,
|
cv: self.cv,
|
||||||
|
@ -627,9 +631,7 @@ pub mod testing {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
sapling::{
|
sapling::{
|
||||||
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
|
|
||||||
note::ExtractedNoteCommitment,
|
note::ExtractedNoteCommitment,
|
||||||
redjubjub::{PrivateKey, PublicKey},
|
|
||||||
value::{
|
value::{
|
||||||
testing::{arb_note_value_bounded, arb_trapdoor},
|
testing::{arb_note_value_bounded, arb_trapdoor},
|
||||||
ValueCommitment, MAX_NOTE_VALUE,
|
ValueCommitment, MAX_NOTE_VALUE,
|
||||||
|
@ -669,8 +671,8 @@ pub mod testing {
|
||||||
fake_sighash_bytes in prop::array::uniform32(prop::num::u8::ANY),
|
fake_sighash_bytes in prop::array::uniform32(prop::num::u8::ANY),
|
||||||
) -> SpendDescription<Authorized> {
|
) -> SpendDescription<Authorized> {
|
||||||
let mut rng = StdRng::from_seed(rng_seed);
|
let mut rng = StdRng::from_seed(rng_seed);
|
||||||
let sk1 = PrivateKey(jubjub::Fr::random(&mut rng));
|
let sk1 = redjubjub::SigningKey::new(&mut rng);
|
||||||
let rk = PublicKey::from_private(&sk1, SPENDING_KEY_GENERATOR);
|
let rk = redjubjub::VerificationKey::from(&sk1);
|
||||||
let cv = ValueCommitment::derive(value, rcv);
|
let cv = ValueCommitment::derive(value, rcv);
|
||||||
SpendDescription {
|
SpendDescription {
|
||||||
cv,
|
cv,
|
||||||
|
@ -678,7 +680,7 @@ pub mod testing {
|
||||||
nullifier,
|
nullifier,
|
||||||
rk,
|
rk,
|
||||||
zkproof,
|
zkproof,
|
||||||
spend_auth_sig: sk1.sign(&fake_sighash_bytes, &mut rng, SPENDING_KEY_GENERATOR),
|
spend_auth_sig: sk1.sign(&mut rng, &fake_sighash_bytes),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -731,18 +733,14 @@ pub mod testing {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let mut rng = StdRng::from_seed(rng_seed);
|
let mut rng = StdRng::from_seed(rng_seed);
|
||||||
let bsk = PrivateKey(jubjub::Fr::random(&mut rng));
|
let bsk = redjubjub::SigningKey::new(&mut rng);
|
||||||
|
|
||||||
Some(Bundle {
|
Some(Bundle {
|
||||||
shielded_spends,
|
shielded_spends,
|
||||||
shielded_outputs,
|
shielded_outputs,
|
||||||
value_balance,
|
value_balance,
|
||||||
authorization: Authorized {
|
authorization: Authorized {
|
||||||
binding_sig: bsk.sign(
|
binding_sig: bsk.sign(&mut rng, &fake_bvk_bytes),
|
||||||
&fake_bvk_bytes,
|
|
||||||
&mut rng,
|
|
||||||
VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,386 +0,0 @@
|
||||||
//! Implementation of [RedJubjub], a specialization of RedDSA to the Jubjub
|
|
||||||
//! curve.
|
|
||||||
//!
|
|
||||||
//! [RedJubjub]: https://zips.z.cash/protocol/protocol.pdf#concretereddsa
|
|
||||||
|
|
||||||
use ff::{Field, PrimeField};
|
|
||||||
use group::GroupEncoding;
|
|
||||||
use jubjub::{AffinePoint, ExtendedPoint, SubgroupPoint};
|
|
||||||
use rand_core::RngCore;
|
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
use std::io::{self, Read, Write};
|
|
||||||
use std::ops::{AddAssign, MulAssign, Neg};
|
|
||||||
|
|
||||||
use super::util::hash_to_scalar;
|
|
||||||
|
|
||||||
fn read_scalar<R: Read>(mut reader: R) -> io::Result<jubjub::Fr> {
|
|
||||||
let mut s_repr = [0u8; 32];
|
|
||||||
reader.read_exact(s_repr.as_mut())?;
|
|
||||||
|
|
||||||
Option::from(jubjub::Fr::from_repr(s_repr))
|
|
||||||
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "scalar is not in field"))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_scalar<W: Write>(s: &jubjub::Fr, mut writer: W) -> io::Result<()> {
|
|
||||||
writer.write_all(s.to_repr().as_ref())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn h_star(a: &[u8], b: &[u8]) -> jubjub::Fr {
|
|
||||||
hash_to_scalar(b"Zcash_RedJubjubH", a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Signature {
|
|
||||||
rbar: [u8; 32],
|
|
||||||
sbar: [u8; 32],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Signature {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("Signature")
|
|
||||||
.field("rbar", &hex::encode(self.rbar))
|
|
||||||
.field("sbar", &hex::encode(self.sbar))
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PrivateKey(pub jubjub::Fr);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct PublicKey(pub ExtendedPoint);
|
|
||||||
|
|
||||||
impl Signature {
|
|
||||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
|
||||||
let mut rbar = [0u8; 32];
|
|
||||||
let mut sbar = [0u8; 32];
|
|
||||||
reader.read_exact(&mut rbar)?;
|
|
||||||
reader.read_exact(&mut sbar)?;
|
|
||||||
Ok(Signature { rbar, sbar })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
|
||||||
writer.write_all(&self.rbar)?;
|
|
||||||
writer.write_all(&self.sbar)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PrivateKey {
|
|
||||||
#[must_use]
|
|
||||||
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::<R>(reader)?;
|
|
||||||
Ok(PrivateKey(pk))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write<W: Write>(&self, writer: W) -> io::Result<()> {
|
|
||||||
write_scalar::<W>(&self.0, writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
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(&t[..], msg);
|
|
||||||
|
|
||||||
// R = r . P_G
|
|
||||||
let r_g = p_g * r;
|
|
||||||
let rbar = r_g.to_bytes();
|
|
||||||
|
|
||||||
// S = r + H*(Rbar || M) . sk
|
|
||||||
let mut s = h_star(&rbar[..], msg);
|
|
||||||
s.mul_assign(&self.0);
|
|
||||||
s.add_assign(&r);
|
|
||||||
let mut sbar = [0u8; 32];
|
|
||||||
write_scalar::<&mut [u8]>(&s, &mut sbar[..])
|
|
||||||
.expect("Jubjub scalars should serialize to 32 bytes");
|
|
||||||
|
|
||||||
Signature { rbar, sbar }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PublicKey {
|
|
||||||
pub fn from_private(privkey: &PrivateKey, p_g: SubgroupPoint) -> Self {
|
|
||||||
PublicKey((p_g * privkey.0).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn randomize(&self, alpha: jubjub::Fr, p_g: SubgroupPoint) -> Self {
|
|
||||||
PublicKey(ExtendedPoint::from(p_g * alpha) + self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
|
||||||
let mut bytes = [0u8; 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, mut writer: W) -> io::Result<()> {
|
|
||||||
writer.write_all(&self.0.to_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn verify(&self, msg: &[u8], sig: &Signature, p_g: SubgroupPoint) -> bool {
|
|
||||||
self.verify_with_zip216(msg, sig, p_g, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn verify_with_zip216(
|
|
||||||
&self,
|
|
||||||
msg: &[u8],
|
|
||||||
sig: &Signature,
|
|
||||||
p_g: SubgroupPoint,
|
|
||||||
zip216_enabled: bool,
|
|
||||||
) -> bool {
|
|
||||||
// c = H*(Rbar || M)
|
|
||||||
let c = h_star(&sig.rbar[..], msg);
|
|
||||||
|
|
||||||
// Signature checks:
|
|
||||||
// R != invalid
|
|
||||||
let r = {
|
|
||||||
let r = if zip216_enabled {
|
|
||||||
ExtendedPoint::from_bytes(&sig.rbar)
|
|
||||||
} else {
|
|
||||||
AffinePoint::from_bytes_pre_zip216_compatibility(sig.rbar).map(|p| p.to_extended())
|
|
||||||
};
|
|
||||||
if r.is_none().into() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
r.unwrap()
|
|
||||||
};
|
|
||||||
// S < order(G)
|
|
||||||
// (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 * c) + r - (p_g * s))
|
|
||||||
.mul_by_cofactor()
|
|
||||||
.is_identity()
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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, R: RngCore>(
|
|
||||||
mut rng: &mut R,
|
|
||||||
batch: &[BatchEntry<'a>],
|
|
||||||
p_g: SubgroupPoint,
|
|
||||||
) -> bool {
|
|
||||||
let mut acc = ExtendedPoint::identity();
|
|
||||||
|
|
||||||
for entry in batch {
|
|
||||||
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::<&[u8]>(&entry.sig.sbar[..]) {
|
|
||||||
Ok(s) => s,
|
|
||||||
Err(_) => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut c = h_star(&entry.sig.rbar[..], entry.msg);
|
|
||||||
|
|
||||||
let z = jubjub::Fr::random(&mut rng);
|
|
||||||
|
|
||||||
s.mul_assign(&z);
|
|
||||||
s = s.neg();
|
|
||||||
|
|
||||||
r *= z;
|
|
||||||
|
|
||||||
c.mul_assign(&z);
|
|
||||||
|
|
||||||
acc = acc + r + (entry.vk.0 * c) + (p_g * s);
|
|
||||||
}
|
|
||||||
|
|
||||||
acc.mul_by_cofactor().is_identity().into()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use group::Group;
|
|
||||||
use rand_core::SeedableRng;
|
|
||||||
use rand_xorshift::XorShiftRng;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use crate::sapling::constants::SPENDING_KEY_GENERATOR;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_batch_verify() {
|
|
||||||
let mut rng = XorShiftRng::from_seed([
|
|
||||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
|
||||||
0xbc, 0xe5,
|
|
||||||
]);
|
|
||||||
let p_g = SPENDING_KEY_GENERATOR;
|
|
||||||
|
|
||||||
let sk1 = PrivateKey(jubjub::Fr::random(&mut rng));
|
|
||||||
let vk1 = PublicKey::from_private(&sk1, p_g);
|
|
||||||
let msg1 = b"Foo bar";
|
|
||||||
let sig1 = sk1.sign(msg1, &mut rng, p_g);
|
|
||||||
assert!(vk1.verify(msg1, &sig1, p_g));
|
|
||||||
|
|
||||||
let sk2 = PrivateKey(jubjub::Fr::random(&mut rng));
|
|
||||||
let vk2 = PublicKey::from_private(&sk2, p_g);
|
|
||||||
let msg2 = b"Foo bar";
|
|
||||||
let sig2 = sk2.sign(msg2, &mut rng, p_g);
|
|
||||||
assert!(vk2.verify(msg2, &sig2, p_g));
|
|
||||||
|
|
||||||
let mut batch = vec![
|
|
||||||
BatchEntry {
|
|
||||||
vk: vk1,
|
|
||||||
msg: msg1,
|
|
||||||
sig: sig1,
|
|
||||||
},
|
|
||||||
BatchEntry {
|
|
||||||
vk: vk2,
|
|
||||||
msg: msg2,
|
|
||||||
sig: sig2,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
assert!(batch_verify(&mut rng, &batch, p_g));
|
|
||||||
|
|
||||||
batch[0].sig = sig2;
|
|
||||||
|
|
||||||
assert!(!batch_verify(&mut rng, &batch, p_g));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn cofactor_check() {
|
|
||||||
let mut rng = XorShiftRng::from_seed([
|
|
||||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
|
||||||
0xbc, 0xe5,
|
|
||||||
]);
|
|
||||||
let zero = jubjub::ExtendedPoint::identity();
|
|
||||||
let p_g = SPENDING_KEY_GENERATOR;
|
|
||||||
|
|
||||||
let jubjub_modulus_bytes = [
|
|
||||||
0xb7, 0x2c, 0xf7, 0xd6, 0x5e, 0x0e, 0x97, 0xd0, 0x82, 0x10, 0xc8, 0xcc, 0x93, 0x20,
|
|
||||||
0x68, 0xa6, 0x00, 0x3b, 0x34, 0x01, 0x01, 0x3b, 0x67, 0x06, 0xa9, 0xaf, 0x33, 0x65,
|
|
||||||
0xea, 0xb4, 0x7d, 0x0e,
|
|
||||||
];
|
|
||||||
|
|
||||||
// Get a point of order 8
|
|
||||||
let p8 = loop {
|
|
||||||
let r = jubjub::ExtendedPoint::random(&mut rng)
|
|
||||||
.to_niels()
|
|
||||||
.multiply_bits(&jubjub_modulus_bytes);
|
|
||||||
|
|
||||||
let r2 = r.double();
|
|
||||||
let r4 = r2.double();
|
|
||||||
let r8 = r4.double();
|
|
||||||
|
|
||||||
if r2 != zero && r4 != zero && r8 == zero {
|
|
||||||
break r;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let sk = PrivateKey(jubjub::Fr::random(&mut 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, &mut rng, p_g);
|
|
||||||
assert!(vk.verify(msg, &sig, p_g));
|
|
||||||
|
|
||||||
let vktorsion = PublicKey(vk.0 + p8);
|
|
||||||
assert!(vktorsion.verify(msg, &sig, p_g));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn round_trip_serialization() {
|
|
||||||
let mut rng = XorShiftRng::from_seed([
|
|
||||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
|
||||||
0xbc, 0xe5,
|
|
||||||
]);
|
|
||||||
let p_g = SPENDING_KEY_GENERATOR;
|
|
||||||
|
|
||||||
for _ in 0..1000 {
|
|
||||||
let sk = PrivateKey(jubjub::Fr::random(&mut rng));
|
|
||||||
let vk = PublicKey::from_private(&sk, p_g);
|
|
||||||
let msg = b"Foo bar";
|
|
||||||
let sig = sk.sign(msg, &mut rng, p_g);
|
|
||||||
|
|
||||||
let mut sk_bytes = [0u8; 32];
|
|
||||||
let mut vk_bytes = [0u8; 32];
|
|
||||||
let mut sig_bytes = [0u8; 64];
|
|
||||||
sk.write(&mut sk_bytes[..]).unwrap();
|
|
||||||
vk.write(&mut vk_bytes[..]).unwrap();
|
|
||||||
sig.write(&mut sig_bytes[..]).unwrap();
|
|
||||||
|
|
||||||
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::read(&vk_bytes[..]).unwrap();
|
|
||||||
let sig_2 = Signature::read(&sig_bytes[..]).unwrap();
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn random_signatures() {
|
|
||||||
let mut rng = XorShiftRng::from_seed([
|
|
||||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
|
|
||||||
0xbc, 0xe5,
|
|
||||||
]);
|
|
||||||
let p_g = SPENDING_KEY_GENERATOR;
|
|
||||||
|
|
||||||
for _ in 0..1000 {
|
|
||||||
let sk = PrivateKey(jubjub::Fr::random(&mut rng));
|
|
||||||
let vk = PublicKey::from_private(&sk, p_g);
|
|
||||||
|
|
||||||
let msg1 = b"Foo bar";
|
|
||||||
let msg2 = b"Spam eggs";
|
|
||||||
|
|
||||||
let sig1 = sk.sign(msg1, &mut rng, p_g);
|
|
||||||
let sig2 = sk.sign(msg2, &mut rng, p_g);
|
|
||||||
|
|
||||||
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 = jubjub::Fr::random(&mut rng);
|
|
||||||
let rsk = sk.randomize(alpha);
|
|
||||||
let rvk = vk.randomize(alpha, p_g);
|
|
||||||
|
|
||||||
let sig1 = rsk.sign(msg1, &mut rng, p_g);
|
|
||||||
let sig2 = rsk.sign(msg2, &mut rng, p_g);
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -228,9 +228,7 @@ mod tests {
|
||||||
use super::{
|
use super::{
|
||||||
testing::{arb_note_value_bounded, arb_trapdoor},
|
testing::{arb_note_value_bounded, arb_trapdoor},
|
||||||
CommitmentSum, OverflowError, TrapdoorSum, ValueCommitment, ValueSum,
|
CommitmentSum, OverflowError, TrapdoorSum, ValueCommitment, ValueSum,
|
||||||
VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
|
|
||||||
};
|
};
|
||||||
use crate::sapling::redjubjub;
|
|
||||||
|
|
||||||
proptest! {
|
proptest! {
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -260,8 +258,7 @@ mod tests {
|
||||||
.sum::<CommitmentSum>()
|
.sum::<CommitmentSum>()
|
||||||
.into_bvk(value_balance);
|
.into_bvk(value_balance);
|
||||||
|
|
||||||
assert_eq!(redjubjub::PublicKey::from_private(
|
assert_eq!(redjubjub::VerificationKey::from(&bsk), bvk);
|
||||||
&bsk, VALUE_COMMITMENT_RANDOMNESS_GENERATOR).0, bvk.0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,11 @@ use core::fmt::{self, Debug};
|
||||||
use core::iter::Sum;
|
use core::iter::Sum;
|
||||||
use core::ops::{Add, AddAssign, Sub, SubAssign};
|
use core::ops::{Add, AddAssign, Sub, SubAssign};
|
||||||
|
|
||||||
|
use group::GroupEncoding;
|
||||||
|
use redjubjub::Binding;
|
||||||
|
|
||||||
use super::{NoteValue, ValueCommitTrapdoor, ValueCommitment};
|
use super::{NoteValue, ValueCommitTrapdoor, ValueCommitment};
|
||||||
use crate::sapling::{constants::VALUE_COMMITMENT_VALUE_GENERATOR, redjubjub};
|
use crate::sapling::constants::VALUE_COMMITMENT_VALUE_GENERATOR;
|
||||||
|
|
||||||
/// A value operation overflowed.
|
/// A value operation overflowed.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -88,8 +91,9 @@ impl TrapdoorSum {
|
||||||
/// Transform this trapdoor sum into the corresponding RedJubjub private key.
|
/// Transform this trapdoor sum into the corresponding RedJubjub private key.
|
||||||
///
|
///
|
||||||
/// This is public for access by `zcash_proofs`.
|
/// This is public for access by `zcash_proofs`.
|
||||||
pub fn into_bsk(self) -> redjubjub::PrivateKey {
|
pub fn into_bsk(self) -> redjubjub::SigningKey<Binding> {
|
||||||
redjubjub::PrivateKey(self.0)
|
redjubjub::SigningKey::try_from(self.0.to_bytes())
|
||||||
|
.expect("valid scalars are valid signing keys")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +160,7 @@ impl CommitmentSum {
|
||||||
/// Transform this value commitment sum into the corresponding RedJubjub public key.
|
/// Transform this value commitment sum into the corresponding RedJubjub public key.
|
||||||
///
|
///
|
||||||
/// This is public for access by `zcash_proofs`.
|
/// This is public for access by `zcash_proofs`.
|
||||||
pub fn into_bvk<V: Into<i64>>(self, value_balance: V) -> redjubjub::PublicKey {
|
pub fn into_bvk<V: Into<i64>>(self, value_balance: V) -> redjubjub::VerificationKey<Binding> {
|
||||||
let value: i64 = value_balance.into();
|
let value: i64 = value_balance.into();
|
||||||
|
|
||||||
// Compute the absolute value.
|
// Compute the absolute value.
|
||||||
|
@ -175,7 +179,8 @@ impl CommitmentSum {
|
||||||
// Subtract `value_balance` from the sum to get the final bvk.
|
// Subtract `value_balance` from the sum to get the final bvk.
|
||||||
let bvk = self.0 - VALUE_COMMITMENT_VALUE_GENERATOR * value_balance;
|
let bvk = self.0 - VALUE_COMMITMENT_VALUE_GENERATOR * value_balance;
|
||||||
|
|
||||||
redjubjub::PublicKey(bvk)
|
redjubjub::VerificationKey::try_from(bvk.to_bytes())
|
||||||
|
.expect("valid points are valid verification keys")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use bellman::{gadgets::multipack, groth16::Proof};
|
use bellman::{gadgets::multipack, groth16::Proof};
|
||||||
use bls12_381::Bls12;
|
use bls12_381::Bls12;
|
||||||
use group::{ff::PrimeField, Curve, GroupEncoding};
|
use group::{ff::PrimeField, Curve};
|
||||||
|
use redjubjub::{Binding, SpendAuth};
|
||||||
|
|
||||||
use crate::sapling::{
|
use crate::sapling::{
|
||||||
note::ExtractedNoteCommitment,
|
note::ExtractedNoteCommitment,
|
||||||
redjubjub::{PublicKey, Signature},
|
|
||||||
value::{CommitmentSum, ValueCommitment},
|
value::{CommitmentSum, ValueCommitment},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -36,18 +36,17 @@ impl SaplingVerificationContextInner {
|
||||||
cv: &ValueCommitment,
|
cv: &ValueCommitment,
|
||||||
anchor: bls12_381::Scalar,
|
anchor: bls12_381::Scalar,
|
||||||
nullifier: &[u8; 32],
|
nullifier: &[u8; 32],
|
||||||
rk: &PublicKey,
|
rk: &redjubjub::VerificationKey<SpendAuth>,
|
||||||
sighash_value: &[u8; 32],
|
|
||||||
spend_auth_sig: &Signature,
|
|
||||||
zkproof: Proof<Bls12>,
|
zkproof: Proof<Bls12>,
|
||||||
verifier_ctx: &mut C,
|
verifier_ctx: &mut C,
|
||||||
spend_auth_sig_verifier: impl FnOnce(&mut C, &PublicKey, [u8; 64], &Signature) -> bool,
|
spend_auth_sig_verifier: impl FnOnce(&mut C, &redjubjub::VerificationKey<SpendAuth>) -> bool,
|
||||||
proof_verifier: impl FnOnce(&mut C, Proof<Bls12>, [bls12_381::Scalar; 7]) -> bool,
|
proof_verifier: impl FnOnce(&mut C, Proof<Bls12>, [bls12_381::Scalar; 7]) -> bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// The "cv is not small order" happens when a SpendDescription is deserialized.
|
// The "cv is not small order" happens when a SpendDescription is deserialized.
|
||||||
// This happens when transactions or blocks are received over the network, or when
|
// This happens when transactions or blocks are received over the network, or when
|
||||||
// mined blocks are introduced via the `submitblock` RPC method on full nodes.
|
// mined blocks are introduced via the `submitblock` RPC method on full nodes.
|
||||||
if rk.0.is_small_order().into() {
|
let rk_affine = jubjub::AffinePoint::from_bytes((*rk).into()).unwrap();
|
||||||
|
if rk_affine.is_small_order().into() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,14 +56,8 @@ impl SaplingVerificationContextInner {
|
||||||
// Grab the nullifier as a sequence of bytes
|
// Grab the nullifier as a sequence of bytes
|
||||||
let nullifier = &nullifier[..];
|
let nullifier = &nullifier[..];
|
||||||
|
|
||||||
// Compute the signature's message for rk/spend_auth_sig
|
|
||||||
let mut data_to_be_signed = [0u8; 64];
|
|
||||||
data_to_be_signed[0..32].copy_from_slice(&rk.0.to_bytes());
|
|
||||||
data_to_be_signed[32..64].copy_from_slice(&sighash_value[..]);
|
|
||||||
|
|
||||||
// Verify the spend_auth_sig
|
// Verify the spend_auth_sig
|
||||||
let rk_affine = rk.0.to_affine();
|
if !spend_auth_sig_verifier(verifier_ctx, rk) {
|
||||||
if !spend_auth_sig_verifier(verifier_ctx, rk, data_to_be_signed, spend_auth_sig) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,19 +138,12 @@ impl SaplingVerificationContextInner {
|
||||||
fn final_check<V: Into<i64>>(
|
fn final_check<V: Into<i64>>(
|
||||||
&self,
|
&self,
|
||||||
value_balance: V,
|
value_balance: V,
|
||||||
sighash_value: &[u8; 32],
|
binding_sig_verifier: impl FnOnce(redjubjub::VerificationKey<Binding>) -> bool,
|
||||||
binding_sig: Signature,
|
|
||||||
binding_sig_verifier: impl FnOnce(PublicKey, [u8; 64], Signature) -> bool,
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Compute the final bvk.
|
// Compute the final bvk.
|
||||||
let bvk = self.cv_sum.into_bvk(value_balance);
|
let bvk = self.cv_sum.into_bvk(value_balance);
|
||||||
|
|
||||||
// Compute the signature's message for bvk/binding_sig
|
|
||||||
let mut data_to_be_signed = [0u8; 64];
|
|
||||||
data_to_be_signed[0..32].copy_from_slice(&bvk.0.to_bytes());
|
|
||||||
data_to_be_signed[32..64].copy_from_slice(&sighash_value[..]);
|
|
||||||
|
|
||||||
// Verify the binding_sig
|
// Verify the binding_sig
|
||||||
binding_sig_verifier(bvk, data_to_be_signed, binding_sig)
|
binding_sig_verifier(bvk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,21 +68,11 @@ impl BatchValidator {
|
||||||
*spend.anchor(),
|
*spend.anchor(),
|
||||||
&spend.nullifier().0,
|
&spend.nullifier().0,
|
||||||
spend.rk(),
|
spend.rk(),
|
||||||
&sighash,
|
|
||||||
spend.spend_auth_sig(),
|
|
||||||
zkproof,
|
zkproof,
|
||||||
self,
|
self,
|
||||||
|this, rk, _, spend_auth_sig| {
|
|this, rk| {
|
||||||
let rk = redjubjub::VerificationKeyBytes::<redjubjub::SpendAuth>::from(
|
this.signatures
|
||||||
rk.0.to_bytes(),
|
.queue(((*rk).into(), *spend.spend_auth_sig(), &sighash));
|
||||||
);
|
|
||||||
let spend_auth_sig = {
|
|
||||||
let mut buf = [0; 64];
|
|
||||||
spend_auth_sig.write(&mut buf[..]).unwrap();
|
|
||||||
redjubjub::Signature::<redjubjub::SpendAuth>::from(buf)
|
|
||||||
};
|
|
||||||
|
|
||||||
this.signatures.queue((rk, spend_auth_sig, &sighash));
|
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
|this, proof, public_inputs| {
|
|this, proof, public_inputs| {
|
||||||
|
@ -125,23 +115,11 @@ impl BatchValidator {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the whole-bundle consensus rules, and batch the binding signature.
|
// Check the whole-bundle consensus rules, and batch the binding signature.
|
||||||
ctx.final_check(
|
ctx.final_check(*bundle.value_balance(), |bvk| {
|
||||||
*bundle.value_balance(),
|
self.signatures
|
||||||
&sighash,
|
.queue((bvk.into(), bundle.authorization().binding_sig, &sighash));
|
||||||
bundle.authorization().binding_sig,
|
true
|
||||||
|bvk, _, binding_sig| {
|
})
|
||||||
let bvk =
|
|
||||||
redjubjub::VerificationKeyBytes::<redjubjub::Binding>::from(bvk.0.to_bytes());
|
|
||||||
let binding_sig = {
|
|
||||||
let mut buf = [0; 64];
|
|
||||||
binding_sig.write(&mut buf[..]).unwrap();
|
|
||||||
redjubjub::Signature::<redjubjub::Binding>::from(buf)
|
|
||||||
};
|
|
||||||
|
|
||||||
self.signatures.queue((bvk, binding_sig, &sighash));
|
|
||||||
true
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Batch-validates the accumulated bundles.
|
/// Batch-validates the accumulated bundles.
|
||||||
|
|
|
@ -1,27 +1,25 @@
|
||||||
use bellman::groth16::{verify_proof, Proof};
|
use bellman::groth16::{verify_proof, Proof};
|
||||||
use bls12_381::Bls12;
|
use bls12_381::Bls12;
|
||||||
|
use redjubjub::{Binding, SpendAuth};
|
||||||
|
|
||||||
use super::SaplingVerificationContextInner;
|
use super::SaplingVerificationContextInner;
|
||||||
use crate::sapling::{
|
use crate::sapling::{
|
||||||
circuit::{PreparedOutputVerifyingKey, PreparedSpendVerifyingKey},
|
circuit::{PreparedOutputVerifyingKey, PreparedSpendVerifyingKey},
|
||||||
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
|
|
||||||
note::ExtractedNoteCommitment,
|
note::ExtractedNoteCommitment,
|
||||||
redjubjub::{PublicKey, Signature},
|
|
||||||
value::ValueCommitment,
|
value::ValueCommitment,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A context object for verifying the Sapling components of a single Zcash transaction.
|
/// A context object for verifying the Sapling components of a single Zcash transaction.
|
||||||
pub struct SaplingVerificationContext {
|
pub struct SaplingVerificationContext {
|
||||||
inner: SaplingVerificationContextInner,
|
inner: SaplingVerificationContextInner,
|
||||||
zip216_enabled: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SaplingVerificationContext {
|
impl SaplingVerificationContext {
|
||||||
/// Construct a new context to be used with a single transaction.
|
/// Construct a new context to be used with a single transaction.
|
||||||
pub fn new(zip216_enabled: bool) -> Self {
|
#[allow(clippy::new_without_default)]
|
||||||
|
pub fn new() -> Self {
|
||||||
SaplingVerificationContext {
|
SaplingVerificationContext {
|
||||||
inner: SaplingVerificationContextInner::new(),
|
inner: SaplingVerificationContextInner::new(),
|
||||||
zip216_enabled,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,25 +31,20 @@ impl SaplingVerificationContext {
|
||||||
cv: &ValueCommitment,
|
cv: &ValueCommitment,
|
||||||
anchor: bls12_381::Scalar,
|
anchor: bls12_381::Scalar,
|
||||||
nullifier: &[u8; 32],
|
nullifier: &[u8; 32],
|
||||||
rk: PublicKey,
|
rk: redjubjub::VerificationKey<SpendAuth>,
|
||||||
sighash_value: &[u8; 32],
|
sighash_value: &[u8; 32],
|
||||||
spend_auth_sig: Signature,
|
spend_auth_sig: redjubjub::Signature<SpendAuth>,
|
||||||
zkproof: Proof<Bls12>,
|
zkproof: Proof<Bls12>,
|
||||||
verifying_key: &PreparedSpendVerifyingKey,
|
verifying_key: &PreparedSpendVerifyingKey,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let zip216_enabled = self.zip216_enabled;
|
|
||||||
self.inner.check_spend(
|
self.inner.check_spend(
|
||||||
cv,
|
cv,
|
||||||
anchor,
|
anchor,
|
||||||
nullifier,
|
nullifier,
|
||||||
&rk,
|
&rk,
|
||||||
sighash_value,
|
|
||||||
&spend_auth_sig,
|
|
||||||
zkproof,
|
zkproof,
|
||||||
&mut (),
|
&mut (),
|
||||||
|_, rk, msg, spend_auth_sig| {
|
|_, rk| rk.verify(sighash_value, &spend_auth_sig).is_ok(),
|
||||||
rk.verify_with_zip216(&msg, spend_auth_sig, SPENDING_KEY_GENERATOR, zip216_enabled)
|
|
||||||
},
|
|
||||||
|_, proof, public_inputs| {
|
|_, proof, public_inputs| {
|
||||||
verify_proof(&verifying_key.0, &proof, &public_inputs[..]).is_ok()
|
verify_proof(&verifying_key.0, &proof, &public_inputs[..]).is_ok()
|
||||||
},
|
},
|
||||||
|
@ -81,20 +74,10 @@ impl SaplingVerificationContext {
|
||||||
&self,
|
&self,
|
||||||
value_balance: V,
|
value_balance: V,
|
||||||
sighash_value: &[u8; 32],
|
sighash_value: &[u8; 32],
|
||||||
binding_sig: Signature,
|
binding_sig: redjubjub::Signature<Binding>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.inner.final_check(
|
self.inner.final_check(value_balance, |bvk| {
|
||||||
value_balance,
|
bvk.verify(sighash_value, &binding_sig).is_ok()
|
||||||
sighash_value,
|
})
|
||||||
binding_sig,
|
|
||||||
|bvk, msg, binding_sig| {
|
|
||||||
bvk.verify_with_zip216(
|
|
||||||
&msg,
|
|
||||||
&binding_sig,
|
|
||||||
VALUE_COMMITMENT_RANDOMNESS_GENERATOR,
|
|
||||||
self.zip216_enabled,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ use std::fmt;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
use rand::{rngs::OsRng, CryptoRng, RngCore};
|
use rand::{rngs::OsRng, CryptoRng, RngCore};
|
||||||
|
use redjubjub::SpendAuth;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consensus::{self, BlockHeight, BranchId, NetworkUpgrade},
|
consensus::{self, BlockHeight, BranchId, NetworkUpgrade},
|
||||||
|
@ -15,7 +16,7 @@ use crate::{
|
||||||
self,
|
self,
|
||||||
builder::{self as sapling_builder, SaplingBuilder, SaplingMetadata},
|
builder::{self as sapling_builder, SaplingBuilder, SaplingMetadata},
|
||||||
prover::{OutputProver, SpendProver},
|
prover::{OutputProver, SpendProver},
|
||||||
redjubjub, Note, PaymentAddress,
|
Note, PaymentAddress,
|
||||||
},
|
},
|
||||||
transaction::{
|
transaction::{
|
||||||
components::{
|
components::{
|
||||||
|
@ -162,7 +163,7 @@ pub struct Builder<'a, P, R> {
|
||||||
// `add_sapling_spend` or `add_orchard_spend`, we will build an unauthorized, unproven
|
// `add_sapling_spend` or `add_orchard_spend`, we will build an unauthorized, unproven
|
||||||
// transaction, and then the caller will be responsible for using the spending keys or their
|
// transaction, and then the caller will be responsible for using the spending keys or their
|
||||||
// derivatives for proving and signing to complete transaction creation.
|
// derivatives for proving and signing to complete transaction creation.
|
||||||
sapling_asks: Vec<redjubjub::PrivateKey>,
|
sapling_asks: Vec<redjubjub::SigningKey<SpendAuth>>,
|
||||||
orchard_saks: Vec<orchard::keys::SpendAuthorizingKey>,
|
orchard_saks: Vec<orchard::keys::SpendAuthorizingKey>,
|
||||||
#[cfg(feature = "zfuture")]
|
#[cfg(feature = "zfuture")]
|
||||||
tze_builder: TzeBuilder<'a, TransactionData<Unauthorized>>,
|
tze_builder: TzeBuilder<'a, TransactionData<Unauthorized>>,
|
||||||
|
@ -334,8 +335,11 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
|
||||||
self.sapling_builder
|
self.sapling_builder
|
||||||
.add_spend(&mut self.rng, &extsk, note, merkle_path)?;
|
.add_spend(&mut self.rng, &extsk, note, merkle_path)?;
|
||||||
|
|
||||||
self.sapling_asks
|
// TODO: store a `redjubjub::SigningKey` inside `extsk`.
|
||||||
.push(redjubjub::PrivateKey(extsk.expsk.ask));
|
self.sapling_asks.push(
|
||||||
|
redjubjub::SigningKey::try_from(extsk.expsk.ask.to_bytes())
|
||||||
|
.expect("valid scalar is valid signing key"),
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use ff::PrimeField;
|
use ff::PrimeField;
|
||||||
|
use redjubjub::SpendAuth;
|
||||||
|
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
|
|
||||||
|
@ -12,7 +13,6 @@ use crate::{
|
||||||
SpendDescription, SpendDescriptionV5,
|
SpendDescription, SpendDescriptionV5,
|
||||||
},
|
},
|
||||||
note::ExtractedNoteCommitment,
|
note::ExtractedNoteCommitment,
|
||||||
redjubjub::{self, PublicKey, Signature},
|
|
||||||
value::ValueCommitment,
|
value::ValueCommitment,
|
||||||
Nullifier,
|
Nullifier,
|
||||||
},
|
},
|
||||||
|
@ -81,15 +81,20 @@ fn read_nullifier<R: Read>(mut reader: R) -> io::Result<Nullifier> {
|
||||||
/// Consensus rules (§4.4):
|
/// Consensus rules (§4.4):
|
||||||
/// - Canonical encoding is enforced here.
|
/// - Canonical encoding is enforced here.
|
||||||
/// - "Not small order" is enforced in SaplingVerificationContext::check_spend()
|
/// - "Not small order" is enforced in SaplingVerificationContext::check_spend()
|
||||||
fn read_rk<R: Read>(mut reader: R) -> io::Result<PublicKey> {
|
fn read_rk<R: Read>(mut reader: R) -> io::Result<redjubjub::VerificationKey<SpendAuth>> {
|
||||||
PublicKey::read(&mut reader)
|
let mut bytes = [0; 32];
|
||||||
|
reader.read_exact(&mut bytes)?;
|
||||||
|
redjubjub::VerificationKey::try_from(bytes)
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consensus rules (§4.4):
|
/// Consensus rules (§4.4):
|
||||||
/// - Canonical encoding is enforced here.
|
/// - Canonical encoding is enforced here.
|
||||||
/// - Signature validity is enforced in SaplingVerificationContext::check_spend()
|
/// - Signature validity is enforced in SaplingVerificationContext::check_spend()
|
||||||
fn read_spend_auth_sig<R: Read>(mut reader: R) -> io::Result<Signature> {
|
fn read_spend_auth_sig<R: Read>(mut reader: R) -> io::Result<redjubjub::Signature<SpendAuth>> {
|
||||||
Signature::read(&mut reader)
|
let mut sig = [0; 64];
|
||||||
|
reader.read_exact(&mut sig)?;
|
||||||
|
Ok(redjubjub::Signature::from(sig))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "temporary-zcashd")]
|
#[cfg(feature = "temporary-zcashd")]
|
||||||
|
@ -127,9 +132,9 @@ fn write_spend_v4<W: Write>(mut writer: W, spend: &SpendDescription<Authorized>)
|
||||||
writer.write_all(&spend.cv().to_bytes())?;
|
writer.write_all(&spend.cv().to_bytes())?;
|
||||||
writer.write_all(spend.anchor().to_repr().as_ref())?;
|
writer.write_all(spend.anchor().to_repr().as_ref())?;
|
||||||
writer.write_all(&spend.nullifier().0)?;
|
writer.write_all(&spend.nullifier().0)?;
|
||||||
spend.rk().write(&mut writer)?;
|
writer.write_all(&<[u8; 32]>::from(*spend.rk()))?;
|
||||||
writer.write_all(spend.zkproof())?;
|
writer.write_all(spend.zkproof())?;
|
||||||
spend.spend_auth_sig().write(&mut writer)
|
writer.write_all(&<[u8; 64]>::from(*spend.spend_auth_sig()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_spend_v5_without_witness_data<W: Write>(
|
fn write_spend_v5_without_witness_data<W: Write>(
|
||||||
|
@ -138,7 +143,7 @@ fn write_spend_v5_without_witness_data<W: Write>(
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
writer.write_all(&spend.cv().to_bytes())?;
|
writer.write_all(&spend.cv().to_bytes())?;
|
||||||
writer.write_all(&spend.nullifier().0)?;
|
writer.write_all(&spend.nullifier().0)?;
|
||||||
spend.rk().write(&mut writer)
|
writer.write_all(&<[u8; 32]>::from(*spend.rk()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_spend_v5<R: Read>(mut reader: &mut R) -> io::Result<SpendDescriptionV5> {
|
fn read_spend_v5<R: Read>(mut reader: &mut R) -> io::Result<SpendDescriptionV5> {
|
||||||
|
@ -350,7 +355,9 @@ pub(crate) fn read_v5_bundle<R: Read>(
|
||||||
let v_output_proofs = Array::read(&mut reader, n_outputs, |r| read_zkproof(r))?;
|
let v_output_proofs = Array::read(&mut reader, n_outputs, |r| read_zkproof(r))?;
|
||||||
|
|
||||||
let binding_sig = if n_spends > 0 || n_outputs > 0 {
|
let binding_sig = if n_spends > 0 || n_outputs > 0 {
|
||||||
Some(redjubjub::Signature::read(&mut reader)?)
|
let mut sig = [0; 64];
|
||||||
|
reader.read_exact(&mut sig)?;
|
||||||
|
Some(redjubjub::Signature::from(sig))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -413,7 +420,7 @@ pub(crate) fn write_v5_bundle<W: Write>(
|
||||||
Array::write(
|
Array::write(
|
||||||
&mut writer,
|
&mut writer,
|
||||||
bundle.shielded_spends().iter().map(|s| s.spend_auth_sig()),
|
bundle.shielded_spends().iter().map(|s| s.spend_auth_sig()),
|
||||||
|w, e| e.write(w),
|
|w, e| w.write_all(&<[u8; 64]>::from(**e)),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Array::write(
|
Array::write(
|
||||||
|
@ -423,7 +430,7 @@ pub(crate) fn write_v5_bundle<W: Write>(
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if !(bundle.shielded_spends().is_empty() && bundle.shielded_outputs().is_empty()) {
|
if !(bundle.shielded_spends().is_empty() && bundle.shielded_outputs().is_empty()) {
|
||||||
bundle.authorization().binding_sig.write(&mut writer)?;
|
writer.write_all(&<[u8; 64]>::from(bundle.authorization().binding_sig))?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
CompactSize::write(&mut writer, 0)?;
|
CompactSize::write(&mut writer, 0)?;
|
||||||
|
|
|
@ -23,7 +23,7 @@ use zcash_encoding::{CompactSize, Vector};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consensus::{BlockHeight, BranchId},
|
consensus::{BlockHeight, BranchId},
|
||||||
sapling::{self, builder as sapling_builder, redjubjub},
|
sapling::{self, builder as sapling_builder},
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
|
@ -624,7 +624,9 @@ impl Transaction {
|
||||||
let binding_sig = if version.has_sapling()
|
let binding_sig = if version.has_sapling()
|
||||||
&& !(shielded_spends.is_empty() && shielded_outputs.is_empty())
|
&& !(shielded_spends.is_empty() && shielded_outputs.is_empty())
|
||||||
{
|
{
|
||||||
Some(redjubjub::Signature::read(&mut reader)?)
|
let mut sig = [0; 64];
|
||||||
|
reader.read_exact(&mut sig)?;
|
||||||
|
Some(redjubjub::Signature::from(sig))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -784,7 +786,7 @@ impl Transaction {
|
||||||
|
|
||||||
if self.version.has_sapling() {
|
if self.version.has_sapling() {
|
||||||
if let Some(bundle) = self.sapling_bundle.as_ref() {
|
if let Some(bundle) = self.sapling_bundle.as_ref() {
|
||||||
bundle.authorization().binding_sig.write(&mut writer)?;
|
writer.write_all(&<[u8; 64]>::from(bundle.authorization().binding_sig))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ fn shielded_spends_hash<
|
||||||
data.extend_from_slice(&s_spend.cv().to_bytes());
|
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.anchor().to_repr().as_ref());
|
||||||
data.extend_from_slice(s_spend.nullifier().as_ref());
|
data.extend_from_slice(s_spend.nullifier().as_ref());
|
||||||
s_spend.rk().write(&mut data).unwrap();
|
data.extend_from_slice(&<[u8; 32]>::from(*s_spend.rk()));
|
||||||
data.extend_from_slice(s_spend.zkproof());
|
data.extend_from_slice(s_spend.zkproof());
|
||||||
}
|
}
|
||||||
Blake2bParams::new()
|
Blake2bParams::new()
|
||||||
|
|
|
@ -154,7 +154,7 @@ pub(crate) fn hash_sapling_spends<A: sapling::bundle::Authorization>(
|
||||||
|
|
||||||
nh.write_all(&s_spend.cv().to_bytes()).unwrap();
|
nh.write_all(&s_spend.cv().to_bytes()).unwrap();
|
||||||
nh.write_all(&s_spend.anchor().to_repr()).unwrap();
|
nh.write_all(&s_spend.anchor().to_repr()).unwrap();
|
||||||
s_spend.rk().write(&mut nh).unwrap();
|
nh.write_all(&<[u8; 32]>::from(*s_spend.rk())).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let compact_digest = ch.finalize();
|
let compact_digest = ch.finalize();
|
||||||
|
@ -476,14 +476,16 @@ impl TransactionDigest<Authorized> for BlockTxCommitmentDigester {
|
||||||
}
|
}
|
||||||
|
|
||||||
for spend in bundle.shielded_spends() {
|
for spend in bundle.shielded_spends() {
|
||||||
spend.spend_auth_sig().write(&mut h).unwrap();
|
h.write_all(&<[u8; 64]>::from(*spend.spend_auth_sig()))
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
for output in bundle.shielded_outputs() {
|
for output in bundle.shielded_outputs() {
|
||||||
h.write_all(output.zkproof()).unwrap();
|
h.write_all(output.zkproof()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
bundle.authorization().binding_sig.write(&mut h).unwrap();
|
h.write_all(&<[u8; 64]>::from(bundle.authorization().binding_sig))
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
h.finalize()
|
h.finalize()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue