diff --git a/zcash_primitives/CHANGELOG.md b/zcash_primitives/CHANGELOG.md index da7ba6f92..756d67ef3 100644 --- a/zcash_primitives/CHANGELOG.md +++ b/zcash_primitives/CHANGELOG.md @@ -11,6 +11,7 @@ and this library adheres to Rust's notion of - `zcash_primitives::sapling`: - `circuit` module (moved from `zcash_proofs::circuit::sapling`). - `constants` module. + - `prover::SpendProver` ### Removed - `zcash_primitives::constants`: diff --git a/zcash_primitives/src/sapling/prover.rs b/zcash_primitives/src/sapling/prover.rs index 3650d5458..dcf0d6505 100644 --- a/zcash_primitives/src/sapling/prover.rs +++ b/zcash_primitives/src/sapling/prover.rs @@ -1,16 +1,49 @@ //! Abstractions over the proving system and parameters. +use rand_core::RngCore; + use crate::{ sapling::{ self, redjubjub::{PublicKey, Signature}, - value::ValueCommitment, + value::{NoteValue, ValueCommitTrapdoor, ValueCommitment}, + MerklePath, }, transaction::components::{Amount, GROTH_PROOF_SIZE}, }; use super::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed}; +/// Interface for creating Sapling Spend proofs. +pub trait SpendProver { + /// The proof type created by this prover. + type Proof; + + /// Prepares an instance of the Sapling Spend circuit for the given inputs. + /// + /// Returns `None` if `diversifier` is not a valid Sapling diversifier. + #[allow(clippy::too_many_arguments)] + fn prepare_circuit( + proof_generation_key: ProofGenerationKey, + diversifier: Diversifier, + rseed: Rseed, + value: NoteValue, + alpha: jubjub::Fr, + rcv: ValueCommitTrapdoor, + anchor: bls12_381::Scalar, + merkle_path: MerklePath, + ) -> Option; + + /// Create the proof for a Sapling [`SpendDescription`]. + /// + /// [`SpendDescription`]: crate::transaction::components::SpendDescription + fn create_proof( + &self, + circuit: sapling::circuit::Spend, + rng: &mut R, + ) -> Self::Proof; +} + /// Interface for creating zero-knowledge proofs for shielded transactions. pub trait TxProver { /// Type for persisting any necessary context across multiple Sapling proofs. diff --git a/zcash_proofs/src/sapling/prover.rs b/zcash_proofs/src/sapling/prover.rs index 435290108..6398f6119 100644 --- a/zcash_proofs/src/sapling/prover.rs +++ b/zcash_proofs/src/sapling/prover.rs @@ -1,11 +1,12 @@ use bellman::groth16::{create_random_proof, Proof}; use bls12_381::Bls12; use group::GroupEncoding; -use rand_core::OsRng; +use rand_core::{OsRng, RngCore}; use zcash_primitives::{ sapling::{ circuit::{Output, Spend, ValueCommitmentOpening}, constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR}, + prover::SpendProver, redjubjub::{PublicKey, Signature}, value::{CommitmentSum, NoteValue, TrapdoorSum, ValueCommitTrapdoor, ValueCommitment}, Diversifier, MerklePath, Note, PaymentAddress, ProofGenerationKey, Rseed, @@ -15,6 +16,56 @@ use zcash_primitives::{ use crate::{OutputParameters, SpendParameters}; +impl SpendProver for SpendParameters { + type Proof = Proof; + + fn prepare_circuit( + proof_generation_key: ProofGenerationKey, + diversifier: Diversifier, + rseed: Rseed, + value: NoteValue, + alpha: jubjub::Fr, + rcv: ValueCommitTrapdoor, + anchor: bls12_381::Scalar, + merkle_path: MerklePath, + ) -> Option { + // Construct the value commitment + let value_commitment_opening = ValueCommitmentOpening { + value: value.inner(), + randomness: rcv.inner(), + }; + + // Construct the viewing key + 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)?; + + let note = Note::from_parts(payment_address, value, rseed); + + // We now have the full witness for our circuit + let pos: u64 = merkle_path.position().into(); + Some(Spend { + value_commitment_opening: Some(value_commitment_opening), + proof_generation_key: Some(proof_generation_key), + payment_address: Some(payment_address), + commitment_randomness: Some(note.rcm()), + ar: Some(alpha), + auth_path: merkle_path + .path_elems() + .iter() + .enumerate() + .map(|(i, node)| Some(((*node).into(), pos >> i & 0x1 == 1))) + .collect(), + anchor: Some(anchor), + }) + } + + fn create_proof(&self, circuit: Spend, rng: &mut R) -> Self::Proof { + create_random_proof(circuit, &self.0, rng).expect("proving should not fail") + } +} + /// A context object for creating the Sapling components of a Zcash transaction. pub struct SaplingProvingContext { bsk: TrapdoorSum, @@ -62,44 +113,26 @@ impl SaplingProvingContext { self.bsk += &rcv; // Construct the value commitment - let value_commitment_opening = ValueCommitmentOpening { - value, - randomness: rcv.inner(), - }; - let value_commitment = ValueCommitment::derive(NoteValue::from_raw(value), rcv); - - // Construct the viewing key - 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).ok_or(())?; + let value = NoteValue::from_raw(value); + let value_commitment = ValueCommitment::derive(value, rcv.clone()); // This is the result of the re-randomization, we compute it for the caller let rk = PublicKey(proof_generation_key.ak.into()).randomize(ar, SPENDING_KEY_GENERATOR); - // Let's compute the nullifier while we have the position - let note = Note::from_parts(payment_address, NoteValue::from_raw(value), rseed); - - // We now have the full witness for our circuit - let pos: u64 = merkle_path.position().into(); - let instance = Spend { - value_commitment_opening: Some(value_commitment_opening), - proof_generation_key: Some(proof_generation_key), - payment_address: Some(payment_address), - commitment_randomness: Some(note.rcm()), - ar: Some(ar), - auth_path: merkle_path - .path_elems() - .iter() - .enumerate() - .map(|(i, node)| Some(((*node).into(), pos >> i & 0x1 == 1))) - .collect(), - anchor: Some(anchor), - }; + let instance = SpendParameters::prepare_circuit( + proof_generation_key, + diversifier, + rseed, + value, + ar, + rcv, + anchor, + merkle_path, + ) + .ok_or(())?; // Create proof - let proof = create_random_proof(instance, &proving_key.0, &mut rng) - .expect("proving should not fail"); + let proof = proving_key.create_proof(instance, &mut rng); // Accumulate the value commitment in the context self.cv_sum += &value_commitment;