Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate JoinSplit proofs #3128

Merged
merged 8 commits into from
Dec 10, 2021
22 changes: 0 additions & 22 deletions zebra-chain/src/sapling/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,28 +105,6 @@ impl Output {

(prefix, self.zkproof)
}

/// Encodes the primary inputs for the proof statement as 5 Bls12_381 base
/// field elements, to match bellman::groth16::verify_proof.
///
/// NB: jubjub::Fq is a type alias for bls12_381::Scalar.
///
/// https://zips.z.cash/protocol/protocol.pdf#cctsaplingoutput
pub fn primary_inputs(&self) -> Vec<jubjub::Fq> {
let mut inputs = vec![];

let cv_affine = jubjub::AffinePoint::from_bytes(self.cv.into()).unwrap();
inputs.push(cv_affine.get_u());
inputs.push(cv_affine.get_v());

let epk_affine = jubjub::AffinePoint::from_bytes(self.ephemeral_key.into()).unwrap();
inputs.push(epk_affine.get_u());
inputs.push(epk_affine.get_v());

inputs.push(self.cm_u);

inputs
}
}

impl OutputInTransactionV4 {
Expand Down
30 changes: 0 additions & 30 deletions zebra-chain/src/sapling/spend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,36 +110,6 @@ impl From<(Spend<PerSpendAnchor>, FieldNotPresent)> for Spend<PerSpendAnchor> {
}
}

impl Spend<PerSpendAnchor> {
/// Encodes the primary inputs for the proof statement as 7 Bls12_381 base
/// field elements, to match bellman::groth16::verify_proof.
///
/// NB: jubjub::Fq is a type alias for bls12_381::Scalar.
///
/// https://zips.z.cash/protocol/protocol.pdf#cctsaplingspend
pub fn primary_inputs(&self) -> Vec<jubjub::Fq> {
let mut inputs = vec![];

let rk_affine = jubjub::AffinePoint::from_bytes(self.rk.into()).unwrap();
inputs.push(rk_affine.get_u());
inputs.push(rk_affine.get_v());

let cv_affine = jubjub::AffinePoint::from_bytes(self.cv.into()).unwrap();
inputs.push(cv_affine.get_u());
inputs.push(cv_affine.get_v());

// TODO: V4 only
inputs.push(jubjub::Fq::from_bytes(&self.per_spend_anchor.into()).unwrap());

let nullifier_limbs: [jubjub::Fq; 2] = self.nullifier.into();

inputs.push(nullifier_limbs[0]);
inputs.push(nullifier_limbs[1]);

inputs
}
}

impl Spend<SharedAnchor> {
/// Combine the prefix and non-prefix fields from V5 transaction
/// deserialization.
Expand Down
1 change: 1 addition & 0 deletions zebra-chain/src/sprout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ pub mod note;
pub mod tree;

pub use joinsplit::JoinSplit;
pub use joinsplit::RandomSeed;
pub use note::{EncryptedNote, Note, Nullifier};
4 changes: 2 additions & 2 deletions zebra-chain/src/sprout/arbitrary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
primitives::ZkSnarkProof,
};

use super::{commitment, note, tree, JoinSplit};
use super::{commitment, joinsplit, note, tree, JoinSplit};

impl<P: ZkSnarkProof + Arbitrary + 'static> Arbitrary for JoinSplit<P> {
type Parameters = ();
Expand All @@ -18,7 +18,7 @@ impl<P: ZkSnarkProof + Arbitrary + 'static> Arbitrary for JoinSplit<P> {
array::uniform2(any::<note::Nullifier>()),
array::uniform2(any::<commitment::NoteCommitment>()),
array::uniform32(any::<u8>()),
array::uniform32(any::<u8>()),
any::<joinsplit::RandomSeed>(),
array::uniform2(any::<note::Mac>()),
any::<P>(),
array::uniform2(any::<note::EncryptedNote>()),
Expand Down
6 changes: 6 additions & 0 deletions zebra-chain/src/sprout/commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,9 @@ impl From<NoteCommitment> for [u8; 32] {
cm.0
}
}

impl From<&NoteCommitment> for [u8; 32] {
fn from(cm: &NoteCommitment) -> [u8; 32] {
cm.0
}
}
35 changes: 32 additions & 3 deletions zebra-chain/src/sprout/joinsplit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,35 @@ use crate::{

use super::{commitment, note, tree};

/// A 256-bit seed that must be chosen independently at
/// random for each [JoinSplit description].
///
/// [JoinSplit description]: https://zips.z.cash/protocol/protocol.pdf#joinsplitencodingandconsensus
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[cfg_attr(
any(test, feature = "proptest-impl"),
derive(proptest_derive::Arbitrary)
)]
pub struct RandomSeed([u8; 32]);

impl From<[u8; 32]> for RandomSeed {
fn from(bytes: [u8; 32]) -> Self {
Self(bytes)
}
}

impl From<RandomSeed> for [u8; 32] {
fn from(rt: RandomSeed) -> [u8; 32] {
rt.0
}
}

impl From<&RandomSeed> for [u8; 32] {
fn from(random_seed: &RandomSeed) -> Self {
random_seed.clone().into()
}
}
conradoplg marked this conversation as resolved.
Show resolved Hide resolved

/// A _JoinSplit Description_, as described in [protocol specification §7.2][ps].
///
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#joinsplitencoding
Expand All @@ -37,7 +66,7 @@ pub struct JoinSplit<P: ZkSnarkProof> {
pub ephemeral_key: x25519::PublicKey,
/// A 256-bit seed that must be chosen independently at random for each
/// JoinSplit description.
pub random_seed: [u8; 32],
pub random_seed: RandomSeed,
/// A message authentication tag.
pub vmacs: [note::Mac; 2],
/// A ZK JoinSplit proof, either a
Expand All @@ -59,7 +88,7 @@ impl<P: ZkSnarkProof> ZcashSerialize for JoinSplit<P> {
writer.write_32_bytes(&self.commitments[0].into())?;
writer.write_32_bytes(&self.commitments[1].into())?;
writer.write_all(&self.ephemeral_key.as_bytes()[..])?;
writer.write_all(&self.random_seed[..])?;
writer.write_32_bytes(&(&self.random_seed).into())?;
self.vmacs[0].zcash_serialize(&mut writer)?;
self.vmacs[1].zcash_serialize(&mut writer)?;
self.zkproof.zcash_serialize(&mut writer)?;
Expand Down Expand Up @@ -105,7 +134,7 @@ impl<P: ZkSnarkProof> ZcashDeserialize for JoinSplit<P> {
commitment::NoteCommitment::from(reader.read_32_bytes()?),
],
ephemeral_key: x25519_dalek::PublicKey::from(reader.read_32_bytes()?),
random_seed: reader.read_32_bytes()?,
random_seed: RandomSeed::from(reader.read_32_bytes()?),
vmacs: [
note::Mac::zcash_deserialize(&mut reader)?,
note::Mac::zcash_deserialize(&mut reader)?,
Expand Down
18 changes: 18 additions & 0 deletions zebra-chain/src/sprout/note/mac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@ use std::io::{self, Read};
)]
pub struct Mac([u8; 32]);

impl From<[u8; 32]> for Mac {
fn from(bytes: [u8; 32]) -> Self {
Self(bytes)
}
}

impl From<Mac> for [u8; 32] {
fn from(rt: Mac) -> [u8; 32] {
rt.0
}
}

impl From<&Mac> for [u8; 32] {
fn from(mac: &Mac) -> Self {
mac.clone().into()
}
}
conradoplg marked this conversation as resolved.
Show resolved Hide resolved

impl ZcashDeserialize for Mac {
fn zcash_deserialize<R: Read>(mut reader: R) -> Result<Self, SerializationError> {
let bytes = reader.read_32_bytes()?;
Expand Down
6 changes: 6 additions & 0 deletions zebra-chain/src/sprout/note/nullifiers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,9 @@ impl From<Nullifier> for [u8; 32] {
n.0
}
}

impl From<&Nullifier> for [u8; 32] {
fn from(n: &Nullifier) -> Self {
n.0
}
conradoplg marked this conversation as resolved.
Show resolved Hide resolved
}
38 changes: 37 additions & 1 deletion zebra-chain/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use crate::{
amount::{Amount, Error as AmountError, NegativeAllowed, NonNegative},
block, orchard,
parameters::NetworkUpgrade,
primitives::{Bctv14Proof, Groth16Proof},
primitives::{ed25519, Bctv14Proof, Groth16Proof},
sapling, sprout,
transparent::{
self, outputs_from_utxos,
Expand Down Expand Up @@ -612,6 +612,42 @@ impl Transaction {
}
}

/// Access the JoinSplit public validating key in this transaction,
/// regardless of version, if any.
pub fn sprout_joinsplit_pub_key(&self) -> Option<ed25519::VerificationKeyBytes> {
conradoplg marked this conversation as resolved.
Show resolved Hide resolved
match self {
// JoinSplits with Bctv14 Proofs
Transaction::V2 {
joinsplit_data: Some(joinsplit_data),
..
}
| Transaction::V3 {
joinsplit_data: Some(joinsplit_data),
..
} => Some(joinsplit_data.pub_key),
// JoinSplits with Groth Proofs
Transaction::V4 {
joinsplit_data: Some(joinsplit_data),
..
} => Some(joinsplit_data.pub_key),
// No JoinSplits
Transaction::V1 { .. }
| Transaction::V2 {
joinsplit_data: None,
..
}
| Transaction::V3 {
joinsplit_data: None,
..
}
| Transaction::V4 {
joinsplit_data: None,
..
}
| Transaction::V5 { .. } => None,
}
}

/// Return if the transaction has any Sprout JoinSplit data.
pub fn has_sprout_joinsplit_data(&self) -> bool {
match self {
Expand Down
Loading