pczt/roles/tx_extractor/
mod.rs1use core::{cell::RefCell, marker::PhantomData};
6use rand_core::{CryptoRng, Rng};
7
8use zcash_primitives::transaction::{
9 Authorization, Transaction,
10 sighash::{PrecomputedSighashData, SignableInput},
11};
12
13use crate::Pczt;
14
15mod orchard;
16pub use self::orchard::OrchardError;
17pub type IronwoodError = OrchardError;
18
19mod sapling;
20pub use self::sapling::SaplingError;
21
22mod transparent;
23pub use self::transparent::TransparentError;
24
25pub struct TransactionExtractor<'a> {
26 pczt: Pczt,
27 sapling_vk: Option<(
28 &'a ::sapling::circuit::SpendVerifyingKey,
29 &'a ::sapling::circuit::OutputVerifyingKey,
30 )>,
31 orchard_vk: Option<&'a ::orchard::circuit::VerifyingKey>,
32 _unused: PhantomData<&'a ()>,
33}
34
35impl<'a> TransactionExtractor<'a> {
36 pub fn new(pczt: Pczt) -> Self {
38 Self {
39 pczt,
40 sapling_vk: None,
41 orchard_vk: None,
42 _unused: PhantomData,
43 }
44 }
45
46 pub fn with_sapling(
52 mut self,
53 spend_vk: &'a ::sapling::circuit::SpendVerifyingKey,
54 output_vk: &'a ::sapling::circuit::OutputVerifyingKey,
55 ) -> Self {
56 self.sapling_vk = Some((spend_vk, output_vk));
57 self
58 }
59
60 pub fn with_orchard(mut self, orchard_vk: &'a ::orchard::circuit::VerifyingKey) -> Self {
66 self.orchard_vk = Some(orchard_vk);
67 self
68 }
69
70 pub fn extract<R: Rng + CryptoRng>(self, mut rng: R) -> Result<Transaction, Error> {
75 let Self {
76 pczt,
77 sapling_vk,
78 orchard_vk,
79 _unused,
80 } = self;
81
82 let crate::ParsedPczt { tx_data, .. } = pczt.extract_tx_data::<Unbound, Error>(
83 crate::common::AnchorRequirement::Required,
84 |t| {
85 t.extract()
86 .map_err(|e| Error::Transparent(TransparentError::Extract(e)))
87 },
88 |s| {
89 s.extract()
90 .map_err(|e| Error::Sapling(SaplingError::Extract(e)))
91 },
92 |o| {
93 o.extract()
94 .map_err(|e| Error::Orchard(OrchardError::Extract(e)))
95 },
96 |i| {
97 i.extract()
98 .map_err(|e| Error::Ironwood(IronwoodError::Extract(e)))
99 },
100 )?;
101
102 let sighash_data = PrecomputedSighashData::new(tx_data);
104 let shielded_sighash = sighash_data.signature_hash(&SignableInput::Shielded);
105 let tx_data = sighash_data.into_tx();
106
107 let binding_rng = RefCell::new(&mut rng);
110 let tx_data = tx_data.try_map_bundles(
111 |t| Ok(t.map(|t| t.map_authorization(transparent::RemoveInputInfo))),
112 |s| {
113 s.map(|s| {
114 s.apply_binding_signature(
115 *shielded_sighash.as_ref(),
116 &mut **binding_rng.borrow_mut(),
117 )
118 .ok_or(Error::SighashMismatch)
119 })
120 .transpose()
121 },
122 |o| {
123 o.map(|o| {
124 o.apply_binding_signature(
125 *shielded_sighash.as_ref(),
126 &mut **binding_rng.borrow_mut(),
127 )
128 .ok_or(Error::SighashMismatch)
129 })
130 .transpose()
131 },
132 )?;
133
134 let tx = tx_data.freeze().expect("txid construction can't fail here");
135
136 if let Some(bundle) = tx.sapling_bundle() {
138 let (spend_vk, output_vk) = sapling_vk.ok_or(Error::SaplingRequired)?;
139
140 sapling::verify_bundle(
141 &mut rng,
142 bundle,
143 spend_vk,
144 output_vk,
145 *shielded_sighash.as_ref(),
146 )
147 .map_err(Error::Sapling)?;
148 }
149 if let Some(bundle) = tx.orchard_bundle() {
150 orchard::verify_bundle(&mut rng, bundle, orchard_vk, *shielded_sighash.as_ref())
151 .map_err(Error::Orchard)?;
152 }
153 if let Some(bundle) = tx.ironwood_bundle() {
154 orchard::verify_bundle(&mut rng, bundle, orchard_vk, *shielded_sighash.as_ref())
155 .map_err(Error::Ironwood)?;
156 }
157
158 Ok(tx)
159 }
160}
161
162struct Unbound;
163
164impl Authorization for Unbound {
165 type TransparentAuth = ::transparent::pczt::Unbound;
166 type SaplingAuth = ::sapling::pczt::Unbound;
167 type OrchardAuth = ::orchard::pczt::Unbound;
168}
169
170#[derive(Debug)]
172pub enum Error {
173 Extract(crate::ExtractError),
174 Ironwood(IronwoodError),
175 Orchard(OrchardError),
176 Sapling(SaplingError),
177 SaplingRequired,
178 SighashMismatch,
179 Transparent(TransparentError),
180}
181
182impl From<crate::ExtractError> for Error {
183 fn from(e: crate::ExtractError) -> Self {
184 Error::Extract(e)
185 }
186}