sapling_crypto/
prover.rs

1//! Abstractions over the proving system and parameters.
2
3use bellman::groth16::{create_random_proof, Proof};
4use bls12_381::Bls12;
5use rand_core::RngCore;
6
7use crate::{
8    bundle::GrothProofBytes,
9    circuit,
10    constants::GROTH_PROOF_SIZE,
11    keys::EphemeralSecretKey,
12    value::{NoteValue, ValueCommitTrapdoor},
13    MerklePath,
14};
15
16use super::{
17    circuit::{Output, OutputParameters, Spend, SpendParameters, ValueCommitmentOpening},
18    Diversifier, Note, PaymentAddress, ProofGenerationKey, Rseed,
19};
20
21/// Interface for creating Sapling Spend proofs.
22pub trait SpendProver {
23    /// The proof type created by this prover.
24    type Proof;
25
26    /// Prepares an instance of the Sapling Spend circuit for the given inputs.
27    ///
28    /// Returns `None` if `diversifier` is not a valid Sapling diversifier.
29    #[allow(clippy::too_many_arguments)]
30    fn prepare_circuit(
31        proof_generation_key: ProofGenerationKey,
32        diversifier: Diversifier,
33        rseed: Rseed,
34        value: NoteValue,
35        alpha: jubjub::Fr,
36        rcv: ValueCommitTrapdoor,
37        anchor: bls12_381::Scalar,
38        merkle_path: MerklePath,
39    ) -> Option<circuit::Spend>;
40
41    /// Create the proof for a Sapling [`SpendDescription`].
42    ///
43    /// [`SpendDescription`]: crate::bundle::SpendDescription
44    fn create_proof<R: RngCore>(&self, circuit: circuit::Spend, rng: &mut R) -> Self::Proof;
45
46    /// Encodes the given Sapling [`SpendDescription`] proof, erasing its type.
47    ///
48    /// [`SpendDescription`]: crate::bundle::SpendDescription
49    fn encode_proof(proof: Self::Proof) -> GrothProofBytes;
50}
51
52/// Interface for creating Sapling Output proofs.
53pub trait OutputProver {
54    /// The proof type created by this prover.
55    type Proof;
56
57    /// Prepares an instance of the Sapling Output circuit for the given inputs.
58    ///
59    /// Returns `None` if `diversifier` is not a valid Sapling diversifier.
60    fn prepare_circuit(
61        esk: &EphemeralSecretKey,
62        payment_address: PaymentAddress,
63        rcm: jubjub::Fr,
64        value: NoteValue,
65        rcv: ValueCommitTrapdoor,
66    ) -> circuit::Output;
67
68    /// Create the proof for a Sapling [`OutputDescription`].
69    ///
70    /// [`OutputDescription`]: crate::bundle::OutputDescription
71    fn create_proof<R: RngCore>(&self, circuit: circuit::Output, rng: &mut R) -> Self::Proof;
72
73    /// Encodes the given Sapling [`OutputDescription`] proof, erasing its type.
74    ///
75    /// [`OutputDescription`]: crate::bundle::OutputDescription
76    fn encode_proof(proof: Self::Proof) -> GrothProofBytes;
77}
78
79impl SpendProver for SpendParameters {
80    type Proof = Proof<Bls12>;
81
82    fn prepare_circuit(
83        proof_generation_key: ProofGenerationKey,
84        diversifier: Diversifier,
85        rseed: Rseed,
86        value: NoteValue,
87        alpha: jubjub::Fr,
88        rcv: ValueCommitTrapdoor,
89        anchor: bls12_381::Scalar,
90        merkle_path: MerklePath,
91    ) -> Option<Spend> {
92        // Construct the value commitment
93        let value_commitment_opening = ValueCommitmentOpening {
94            value,
95            randomness: rcv.inner(),
96        };
97
98        // Construct the viewing key
99        let viewing_key = proof_generation_key.to_viewing_key();
100
101        // Construct the payment address with the viewing key / diversifier
102        let payment_address = viewing_key.to_payment_address(diversifier)?;
103
104        let note = Note::from_parts(payment_address, value, rseed);
105
106        // We now have the full witness for our circuit
107        let pos: u64 = merkle_path.position().into();
108        Some(Spend {
109            value_commitment_opening: Some(value_commitment_opening),
110            proof_generation_key: Some(proof_generation_key),
111            payment_address: Some(payment_address),
112            commitment_randomness: Some(note.rcm()),
113            ar: Some(alpha),
114            auth_path: merkle_path
115                .path_elems()
116                .iter()
117                .enumerate()
118                .map(|(i, node)| Some(((*node).into(), (pos >> i) & 0x1 == 1)))
119                .collect(),
120            anchor: Some(anchor),
121        })
122    }
123
124    fn create_proof<R: RngCore>(&self, circuit: Spend, rng: &mut R) -> Self::Proof {
125        create_random_proof(circuit, &self.0, rng).expect("proving should not fail")
126    }
127
128    fn encode_proof(proof: Self::Proof) -> GrothProofBytes {
129        let mut zkproof = [0u8; GROTH_PROOF_SIZE];
130        proof
131            .write(&mut zkproof[..])
132            .expect("should be able to serialize a proof");
133        zkproof
134    }
135}
136
137impl OutputProver for OutputParameters {
138    type Proof = Proof<Bls12>;
139
140    fn prepare_circuit(
141        esk: &EphemeralSecretKey,
142        payment_address: PaymentAddress,
143        rcm: jubjub::Fr,
144        value: NoteValue,
145        rcv: ValueCommitTrapdoor,
146    ) -> Output {
147        // Construct the value commitment for the proof instance
148        let value_commitment_opening = ValueCommitmentOpening {
149            value,
150            randomness: rcv.inner(),
151        };
152
153        // We now have a full witness for the output proof.
154        Output {
155            value_commitment_opening: Some(value_commitment_opening),
156            payment_address: Some(payment_address),
157            commitment_randomness: Some(rcm),
158            esk: Some(esk.0),
159        }
160    }
161
162    fn create_proof<R: RngCore>(&self, circuit: Output, rng: &mut R) -> Self::Proof {
163        create_random_proof(circuit, &self.0, rng).expect("proving should not fail")
164    }
165
166    fn encode_proof(proof: Self::Proof) -> GrothProofBytes {
167        let mut zkproof = [0u8; GROTH_PROOF_SIZE];
168        proof
169            .write(&mut zkproof[..])
170            .expect("should be able to serialize a proof");
171        zkproof
172    }
173}
174
175#[cfg(any(test, feature = "test-dependencies"))]
176#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
177pub mod mock {
178    use ff::Field;
179
180    use super::{OutputProver, SpendProver};
181    use crate::{
182        bundle::GrothProofBytes,
183        circuit::{self, ValueCommitmentOpening},
184        constants::GROTH_PROOF_SIZE,
185        keys::EphemeralSecretKey,
186        value::{NoteValue, ValueCommitTrapdoor},
187        Diversifier, MerklePath, PaymentAddress, ProofGenerationKey, Rseed,
188    };
189
190    pub struct MockSpendProver;
191
192    impl SpendProver for MockSpendProver {
193        type Proof = GrothProofBytes;
194
195        fn prepare_circuit(
196            proof_generation_key: ProofGenerationKey,
197            diversifier: Diversifier,
198            _rseed: Rseed,
199            value: NoteValue,
200            alpha: jubjub::Fr,
201            rcv: ValueCommitTrapdoor,
202            anchor: bls12_381::Scalar,
203            _merkle_path: MerklePath,
204        ) -> Option<circuit::Spend> {
205            let payment_address = proof_generation_key
206                .to_viewing_key()
207                .ivk()
208                .to_payment_address(diversifier);
209            Some(circuit::Spend {
210                value_commitment_opening: Some(ValueCommitmentOpening {
211                    value,
212                    randomness: rcv.inner(),
213                }),
214                proof_generation_key: Some(proof_generation_key),
215                payment_address,
216                commitment_randomness: Some(jubjub::Scalar::ZERO),
217                ar: Some(alpha),
218                auth_path: vec![],
219                anchor: Some(anchor),
220            })
221        }
222
223        fn create_proof<R: rand_core::RngCore>(
224            &self,
225            _circuit: circuit::Spend,
226            _rng: &mut R,
227        ) -> Self::Proof {
228            [0u8; GROTH_PROOF_SIZE]
229        }
230
231        fn encode_proof(proof: Self::Proof) -> GrothProofBytes {
232            proof
233        }
234    }
235
236    pub struct MockOutputProver;
237
238    impl OutputProver for MockOutputProver {
239        type Proof = GrothProofBytes;
240
241        fn prepare_circuit(
242            esk: &EphemeralSecretKey,
243            payment_address: PaymentAddress,
244            rcm: jubjub::Fr,
245            value: NoteValue,
246            rcv: ValueCommitTrapdoor,
247        ) -> circuit::Output {
248            circuit::Output {
249                value_commitment_opening: Some(ValueCommitmentOpening {
250                    value,
251                    randomness: rcv.inner(),
252                }),
253                payment_address: Some(payment_address),
254                commitment_randomness: Some(rcm),
255                esk: Some(esk.0),
256            }
257        }
258
259        fn create_proof<R: rand_core::RngCore>(
260            &self,
261            _circuit: circuit::Output,
262            _rng: &mut R,
263        ) -> Self::Proof {
264            [0u8; GROTH_PROOF_SIZE]
265        }
266
267        fn encode_proof(proof: Self::Proof) -> GrothProofBytes {
268            proof
269        }
270    }
271}