zebra_chain/primitives/
zcash_primitives.rs1use std::{io, ops::Deref, sync::Arc};
5
6use zcash_primitives::transaction::{self as zp_tx, TxDigests};
7use zcash_protocol::value::{BalanceError, ZatBalance, Zatoshis};
8
9use crate::{
10 amount::{Amount, NonNegative},
11 parameters::NetworkUpgrade,
12 serialization::ZcashSerialize,
13 transaction::{AuthDigest, HashType, SigHash, Transaction},
14 transparent::{self, Script},
15 Error,
16};
17
18#[derive(Clone, Debug)]
23struct TransparentAuth {
24 all_prev_outputs: Arc<Vec<transparent::Output>>,
25}
26
27impl zcash_transparent::bundle::Authorization for TransparentAuth {
28 type ScriptSig = zcash_primitives::legacy::Script;
29}
30
31impl zcash_transparent::sighash::TransparentAuthorizingContext for TransparentAuth {
34 fn input_amounts(&self) -> Vec<Zatoshis> {
35 self.all_prev_outputs
36 .iter()
37 .map(|prevout| {
38 prevout
39 .value
40 .try_into()
41 .expect("will not fail since it was previously validated")
42 })
43 .collect()
44 }
45
46 fn input_scriptpubkeys(&self) -> Vec<zcash_primitives::legacy::Script> {
47 self.all_prev_outputs
48 .iter()
49 .map(|prevout| {
50 zcash_primitives::legacy::Script(prevout.lock_script.as_raw_bytes().into())
51 })
52 .collect()
53 }
54}
55
56struct MapTransparent {
61 auth: TransparentAuth,
62}
63
64impl zcash_transparent::bundle::MapAuth<zcash_transparent::bundle::Authorized, TransparentAuth>
65 for MapTransparent
66{
67 fn map_script_sig(
68 &self,
69 s: <zcash_transparent::bundle::Authorized as zcash_transparent::bundle::Authorization>::ScriptSig,
70 ) -> <TransparentAuth as zcash_transparent::bundle::Authorization>::ScriptSig {
71 s
72 }
73
74 fn map_authorization(&self, _: zcash_transparent::bundle::Authorized) -> TransparentAuth {
75 self.auth.clone()
77 }
78}
79
80struct IdentityMap;
81
82impl
83 zp_tx::components::sapling::MapAuth<
84 sapling_crypto::bundle::Authorized,
85 sapling_crypto::bundle::Authorized,
86 > for IdentityMap
87{
88 fn map_spend_proof(
89 &mut self,
90 p: <sapling_crypto::bundle::Authorized as sapling_crypto::bundle::Authorization>::SpendProof,
91 ) -> <sapling_crypto::bundle::Authorized as sapling_crypto::bundle::Authorization>::SpendProof
92 {
93 p
94 }
95
96 fn map_output_proof(
97 &mut self,
98 p: <sapling_crypto::bundle::Authorized as sapling_crypto::bundle::Authorization>::OutputProof,
99 ) -> <sapling_crypto::bundle::Authorized as sapling_crypto::bundle::Authorization>::OutputProof
100 {
101 p
102 }
103
104 fn map_auth_sig(
105 &mut self,
106 s: <sapling_crypto::bundle::Authorized as sapling_crypto::bundle::Authorization>::AuthSig,
107 ) -> <sapling_crypto::bundle::Authorized as sapling_crypto::bundle::Authorization>::AuthSig
108 {
109 s
110 }
111
112 fn map_authorization(
113 &mut self,
114 a: sapling_crypto::bundle::Authorized,
115 ) -> sapling_crypto::bundle::Authorized {
116 a
117 }
118}
119
120impl zp_tx::components::orchard::MapAuth<orchard::bundle::Authorized, orchard::bundle::Authorized>
121 for IdentityMap
122{
123 fn map_spend_auth(
124 &self,
125 s: <orchard::bundle::Authorized as orchard::bundle::Authorization>::SpendAuth,
126 ) -> <orchard::bundle::Authorized as orchard::bundle::Authorization>::SpendAuth {
127 s
128 }
129
130 fn map_authorization(&self, a: orchard::bundle::Authorized) -> orchard::bundle::Authorized {
131 a
132 }
133}
134
135#[derive(Debug)]
136struct PrecomputedAuth {}
137
138impl zp_tx::Authorization for PrecomputedAuth {
139 type TransparentAuth = TransparentAuth;
140 type SaplingAuth = sapling_crypto::bundle::Authorized;
141 type OrchardAuth = orchard::bundle::Authorized;
142
143 #[cfg(zcash_unstable = "zfuture")]
144 type TzeAuth = zp_tx::components::tze::Authorized;
145}
146
147impl TryFrom<&transparent::Output> for zcash_transparent::bundle::TxOut {
151 type Error = io::Error;
152
153 #[allow(clippy::unwrap_in_result)]
154 fn try_from(output: &transparent::Output) -> Result<Self, Self::Error> {
155 let serialized_output_bytes = output
156 .zcash_serialize_to_vec()
157 .expect("zcash_primitives and Zebra transparent output formats must be compatible");
158
159 zcash_transparent::bundle::TxOut::read(&mut serialized_output_bytes.as_slice())
160 }
161}
162
163impl TryFrom<transparent::Output> for zcash_transparent::bundle::TxOut {
165 type Error = io::Error;
166
167 #[allow(clippy::needless_borrow)]
169 fn try_from(output: transparent::Output) -> Result<Self, Self::Error> {
170 (&output).try_into()
171 }
172}
173
174impl TryFrom<Amount<NonNegative>> for zcash_protocol::value::Zatoshis {
176 type Error = BalanceError;
177
178 fn try_from(amount: Amount<NonNegative>) -> Result<Self, Self::Error> {
179 zcash_protocol::value::Zatoshis::from_nonnegative_i64(amount.into())
180 }
181}
182
183impl TryFrom<Amount> for ZatBalance {
184 type Error = BalanceError;
185
186 fn try_from(amount: Amount) -> Result<Self, Self::Error> {
187 ZatBalance::from_i64(amount.into())
188 }
189}
190
191impl From<&Script> for zcash_primitives::legacy::Script {
193 fn from(script: &Script) -> Self {
194 zcash_primitives::legacy::Script(script.as_raw_bytes().to_vec())
195 }
196}
197
198impl From<Script> for zcash_primitives::legacy::Script {
200 #[allow(clippy::needless_borrow)]
202 fn from(script: Script) -> Self {
203 (&script).into()
204 }
205}
206
207#[derive(Debug)]
209pub(crate) struct PrecomputedTxData {
210 tx_data: zp_tx::TransactionData<PrecomputedAuth>,
211 txid_parts: TxDigests<blake2b_simd::Hash>,
212 all_previous_outputs: Arc<Vec<transparent::Output>>,
213}
214
215impl PrecomputedTxData {
216 pub(crate) fn new(
250 tx: &Transaction,
251 nu: NetworkUpgrade,
252 all_previous_outputs: Arc<Vec<transparent::Output>>,
253 ) -> Result<PrecomputedTxData, Error> {
254 let tx = tx.to_librustzcash(nu)?;
255
256 let txid_parts = tx.deref().digest(zp_tx::txid::TxIdDigester);
257
258 let f_transparent = MapTransparent {
259 auth: TransparentAuth {
260 all_prev_outputs: all_previous_outputs.clone(),
261 },
262 };
263
264 let tx_data: zp_tx::TransactionData<PrecomputedAuth> = tx.into_data().map_authorization(
265 f_transparent,
266 IdentityMap,
267 IdentityMap,
268 #[cfg(zcash_unstable = "zfuture")]
269 (),
270 );
271
272 Ok(PrecomputedTxData {
273 tx_data,
274 txid_parts,
275 all_previous_outputs,
276 })
277 }
278
279 pub fn orchard_bundle(
281 &self,
282 ) -> Option<orchard::bundle::Bundle<orchard::bundle::Authorized, ZatBalance>> {
283 self.tx_data.orchard_bundle().cloned()
284 }
285
286 pub fn sapling_bundle(
288 &self,
289 ) -> Option<sapling_crypto::Bundle<sapling_crypto::bundle::Authorized, ZatBalance>> {
290 self.tx_data.sapling_bundle().cloned()
291 }
292}
293
294pub(crate) fn sighash(
305 precomputed_tx_data: &PrecomputedTxData,
306 hash_type: HashType,
307 input_index_script_code: Option<(usize, Vec<u8>)>,
308) -> SigHash {
309 let lock_script: zcash_primitives::legacy::Script;
310 let unlock_script: zcash_primitives::legacy::Script;
311 let signable_input = match input_index_script_code {
312 Some((input_index, script_code)) => {
313 let output = &precomputed_tx_data.all_previous_outputs[input_index];
314 lock_script = output.lock_script.clone().into();
315 unlock_script = zcash_primitives::legacy::Script(script_code);
316 zp_tx::sighash::SignableInput::Transparent(
317 zcash_transparent::sighash::SignableInput::from_parts(
318 hash_type.try_into().expect("hash type should be ALL"),
319 input_index,
320 &unlock_script,
321 &lock_script,
322 output
323 .value
324 .try_into()
325 .expect("amount was previously validated"),
326 ),
327 )
328 }
329 None => zp_tx::sighash::SignableInput::Shielded,
330 };
331
332 SigHash(
333 *zp_tx::sighash::signature_hash(
334 &precomputed_tx_data.tx_data,
335 &signable_input,
336 &precomputed_tx_data.txid_parts,
337 )
338 .as_ref(),
339 )
340}
341
342pub(crate) fn auth_digest(tx: &Transaction) -> AuthDigest {
350 let nu = tx.network_upgrade().expect("V5 tx has a network upgrade");
351
352 AuthDigest(
353 tx.to_librustzcash(nu)
354 .expect("V5 tx is convertible to its `zcash_params` equivalent")
355 .auth_commitment()
356 .as_ref()
357 .try_into()
358 .expect("digest has the correct size"),
359 )
360}