pczt/roles/prover/
orchard.rs1use orchard::circuit::ProvingKey;
2use rand_core::{CryptoRng, Rng};
3
4use crate::{Pczt, common::AnchorRequirement};
5
6impl super::Prover {
7 pub fn create_orchard_proof<R: Rng + CryptoRng>(
11 self,
12 rng: R,
13 pk: &ProvingKey,
14 ) -> Result<Self, OrchardError> {
15 let Pczt {
16 global,
17 transparent,
18 sapling,
19 orchard,
20 ironwood,
21 } = self.pczt;
22
23 let mut parsed = orchard
24 .into_parsed_with_version(
25 crate::orchard::orchard_bundle_version(&global)
26 .ok_or(OrchardError::UnsupportedConsensusBranchId)?,
27 AnchorRequirement::Required,
28 )
29 .map_err(OrchardError::Parser)?;
30
31 crate::orchard::verify_witnesses_root_to_anchor(&parsed.bundle, *parsed.bundle.anchor())
32 .map_err(OrchardError::InconsistentWitness)?;
33
34 parsed
35 .bundle
36 .create_proof(pk, rng)
37 .map_err(OrchardError::Prover)?;
38
39 Ok(Self {
40 pczt: Pczt {
41 global,
42 transparent,
43 sapling,
44 orchard: parsed.reserialize(),
45 ironwood,
46 },
47 })
48 }
49
50 pub fn create_ironwood_proof<R: Rng + CryptoRng>(
54 self,
55 rng: R,
56 pk: &ProvingKey,
57 ) -> Result<Self, IronwoodError> {
58 let Pczt {
59 global,
60 transparent,
61 sapling,
62 orchard,
63 ironwood,
64 } = self.pczt;
65
66 let mut parsed = ironwood
67 .into_ironwood_parsed(AnchorRequirement::Required)
68 .map_err(IronwoodError::Parser)?;
69
70 crate::orchard::verify_witnesses_root_to_anchor(&parsed.bundle, *parsed.bundle.anchor())
71 .map_err(IronwoodError::InconsistentWitness)?;
72
73 parsed
74 .bundle
75 .create_proof(pk, rng)
76 .map_err(IronwoodError::Prover)?;
77
78 Ok(Self {
79 pczt: Pczt {
80 global,
81 transparent,
82 sapling,
83 orchard,
84 ironwood: parsed.reserialize(),
85 },
86 })
87 }
88}
89
90#[derive(Debug)]
92pub enum OrchardError {
93 InconsistentWitness(crate::orchard::AnchorConsistencyError),
95 Parser(crate::orchard::ParseError),
96 Prover(orchard::pczt::ProverError),
97 UnsupportedConsensusBranchId,
100}
101
102#[derive(Debug)]
104pub enum IronwoodError {
105 InconsistentWitness(crate::orchard::AnchorConsistencyError),
107 Parser(crate::orchard::ParseError),
108 Prover(orchard::pczt::ProverError),
109}
110
111#[cfg(test)]
112mod tests {
113 use rand::{rand_core::UnwrapErr, rngs::SysRng};
114 extern crate std;
115
116 use std::sync::OnceLock;
117
118 use zcash_protocol::consensus::BranchId;
119
120 use super::{IronwoodError, OrchardError};
121 use crate::{orchard::testing::dummy_action, roles::creator::Creator, roles::prover::Prover};
122
123 static PROVING_KEY: OnceLock<orchard::circuit::ProvingKey> = OnceLock::new();
124
125 fn proving_key() -> &'static orchard::circuit::ProvingKey {
126 PROVING_KEY.get_or_init(|| {
127 orchard::circuit::ProvingKey::build(
128 orchard::circuit::OrchardCircuitVersion::FixedPostNu6_2,
129 )
130 })
131 }
132
133 #[test]
134 fn create_orchard_proof_fails_when_anchor_absent() {
135 let mut pczt = Creator::new(BranchId::Nu6_3.into(), 100, 133, None, None)
136 .unwrap()
137 .build()
138 .unwrap();
139 pczt.orchard.actions.push(dummy_action());
140
141 assert!(matches!(
142 Prover::new(pczt).create_orchard_proof(UnwrapErr(SysRng), proving_key()),
143 Err(OrchardError::Parser(
144 crate::orchard::ParseError::MissingAnchor
145 ))
146 ));
147 }
148
149 #[test]
150 fn create_ironwood_proof_fails_when_anchor_absent() {
151 let mut pczt = Creator::new(BranchId::Nu6_3.into(), 100, 133, None, None)
152 .unwrap()
153 .build()
154 .unwrap();
155 pczt.ironwood.actions.push(dummy_action());
156
157 assert!(matches!(
158 Prover::new(pczt).create_ironwood_proof(UnwrapErr(SysRng), proving_key()),
159 Err(IronwoodError::Parser(
160 crate::orchard::ParseError::MissingAnchor
161 ))
162 ));
163 }
164
165 #[test]
169 fn create_orchard_proof_preserves_absent_anchor_for_untouched_bundle() {
170 let pczt = Creator::new(BranchId::Nu6_3.into(), 100, 133, None, None)
171 .unwrap()
172 .build()
173 .unwrap();
174
175 let pczt = Prover::new(pczt)
176 .create_orchard_proof(UnwrapErr(SysRng), proving_key())
177 .unwrap()
178 .finish();
179
180 assert_eq!(pczt.orchard, crate::orchard::EMPTY_ORCHARD);
181
182 let bytes = pczt.clone().serialize().unwrap();
183 assert_eq!(
184 crate::parse(&bytes).unwrap().orchard,
185 crate::orchard::EMPTY_ORCHARD
186 );
187 }
188
189 #[test]
191 fn create_ironwood_proof_preserves_absent_anchor_for_untouched_bundle() {
192 let pczt = Creator::new(BranchId::Nu6_3.into(), 100, 133, None, None)
193 .unwrap()
194 .build()
195 .unwrap();
196
197 let pczt = Prover::new(pczt)
198 .create_ironwood_proof(UnwrapErr(SysRng), proving_key())
199 .unwrap()
200 .finish();
201
202 assert_eq!(pczt.ironwood, crate::orchard::EMPTY_IRONWOOD);
203
204 let bytes = pczt.clone().serialize().unwrap();
205 assert_eq!(
206 crate::parse(&bytes).unwrap().ironwood,
207 crate::orchard::EMPTY_IRONWOOD
208 );
209 }
210}