zebra_chain/primitives/
zcash_primitives.rs1use std::{io, ops::Deref};
5
6use zcash_primitives::transaction::{self as zp_tx, TxDigests};
7use zcash_protocol::value::BalanceError;
8
9use crate::{
10 amount::{Amount, NonNegative},
11 parameters::{Network, NetworkUpgrade},
12 serialization::ZcashSerialize,
13 transaction::{AuthDigest, HashType, SigHash, Transaction},
14 transparent::{self, Script},
15};
16
17#[derive(Clone, Debug)]
22struct TransparentAuth<'a> {
23 all_prev_outputs: &'a [transparent::Output],
24}
25
26impl zp_tx::components::transparent::Authorization for TransparentAuth<'_> {
27 type ScriptSig = zcash_primitives::legacy::Script;
28}
29
30impl zp_tx::sighash::TransparentAuthorizingContext for TransparentAuth<'_> {
33 fn input_amounts(&self) -> Vec<zp_tx::components::amount::NonNegativeAmount> {
34 self.all_prev_outputs
35 .iter()
36 .map(|prevout| {
37 prevout
38 .value
39 .try_into()
40 .expect("will not fail since it was previously validated")
41 })
42 .collect()
43 }
44
45 fn input_scriptpubkeys(&self) -> Vec<zcash_primitives::legacy::Script> {
46 self.all_prev_outputs
47 .iter()
48 .map(|prevout| {
49 zcash_primitives::legacy::Script(prevout.lock_script.as_raw_bytes().into())
50 })
51 .collect()
52 }
53}
54
55struct MapTransparent<'a> {
60 auth: TransparentAuth<'a>,
61}
62
63impl<'a>
64 zp_tx::components::transparent::MapAuth<
65 zp_tx::components::transparent::Authorized,
66 TransparentAuth<'a>,
67 > for MapTransparent<'a>
68{
69 fn map_script_sig(
70 &self,
71 s: <zp_tx::components::transparent::Authorized as zp_tx::components::transparent::Authorization>::ScriptSig,
72 ) -> <TransparentAuth as zp_tx::components::transparent::Authorization>::ScriptSig {
73 s
74 }
75
76 fn map_authorization(
77 &self,
78 _: zp_tx::components::transparent::Authorized,
79 ) -> TransparentAuth<'a> {
80 self.auth.clone()
82 }
83}
84
85struct IdentityMap;
86
87impl
88 zp_tx::components::sapling::MapAuth<
89 sapling_crypto::bundle::Authorized,
90 sapling_crypto::bundle::Authorized,
91 > for IdentityMap
92{
93 fn map_spend_proof(
94 &mut self,
95 p: <sapling_crypto::bundle::Authorized as sapling_crypto::bundle::Authorization>::SpendProof,
96 ) -> <sapling_crypto::bundle::Authorized as sapling_crypto::bundle::Authorization>::SpendProof
97 {
98 p
99 }
100
101 fn map_output_proof(
102 &mut self,
103 p: <sapling_crypto::bundle::Authorized as sapling_crypto::bundle::Authorization>::OutputProof,
104 ) -> <sapling_crypto::bundle::Authorized as sapling_crypto::bundle::Authorization>::OutputProof
105 {
106 p
107 }
108
109 fn map_auth_sig(
110 &mut self,
111 s: <sapling_crypto::bundle::Authorized as sapling_crypto::bundle::Authorization>::AuthSig,
112 ) -> <sapling_crypto::bundle::Authorized as sapling_crypto::bundle::Authorization>::AuthSig
113 {
114 s
115 }
116
117 fn map_authorization(
118 &mut self,
119 a: sapling_crypto::bundle::Authorized,
120 ) -> sapling_crypto::bundle::Authorized {
121 a
122 }
123}
124
125impl zp_tx::components::orchard::MapAuth<orchard::bundle::Authorized, orchard::bundle::Authorized>
126 for IdentityMap
127{
128 fn map_spend_auth(
129 &self,
130 s: <orchard::bundle::Authorized as orchard::bundle::Authorization>::SpendAuth,
131 ) -> <orchard::bundle::Authorized as orchard::bundle::Authorization>::SpendAuth {
132 s
133 }
134
135 fn map_authorization(&self, a: orchard::bundle::Authorized) -> orchard::bundle::Authorized {
136 a
137 }
138}
139
140#[derive(Debug)]
141struct PrecomputedAuth<'a> {
142 _phantom: std::marker::PhantomData<&'a ()>,
143}
144
145impl<'a> zp_tx::Authorization for PrecomputedAuth<'a> {
146 type TransparentAuth = TransparentAuth<'a>;
147 type SaplingAuth = sapling_crypto::bundle::Authorized;
148 type OrchardAuth = orchard::bundle::Authorized;
149}
150
151impl TryFrom<&transparent::Output> for zp_tx::components::TxOut {
155 type Error = io::Error;
156
157 #[allow(clippy::unwrap_in_result)]
158 fn try_from(output: &transparent::Output) -> Result<Self, Self::Error> {
159 let serialized_output_bytes = output
160 .zcash_serialize_to_vec()
161 .expect("zcash_primitives and Zebra transparent output formats must be compatible");
162
163 zp_tx::components::TxOut::read(&mut serialized_output_bytes.as_slice())
164 }
165}
166
167impl TryFrom<transparent::Output> for zp_tx::components::TxOut {
169 type Error = io::Error;
170
171 #[allow(clippy::needless_borrow)]
173 fn try_from(output: transparent::Output) -> Result<Self, Self::Error> {
174 (&output).try_into()
175 }
176}
177
178impl TryFrom<Amount<NonNegative>> for zp_tx::components::amount::NonNegativeAmount {
180 type Error = BalanceError;
181
182 fn try_from(amount: Amount<NonNegative>) -> Result<Self, Self::Error> {
183 zp_tx::components::amount::NonNegativeAmount::from_nonnegative_i64(amount.into())
184 }
185}
186
187impl From<&Script> for zcash_primitives::legacy::Script {
189 fn from(script: &Script) -> Self {
190 zcash_primitives::legacy::Script(script.as_raw_bytes().to_vec())
191 }
192}
193
194impl From<Script> for zcash_primitives::legacy::Script {
196 #[allow(clippy::needless_borrow)]
198 fn from(script: Script) -> Self {
199 (&script).into()
200 }
201}
202
203#[derive(Debug)]
205pub(crate) struct PrecomputedTxData<'a> {
206 tx_data: zp_tx::TransactionData<PrecomputedAuth<'a>>,
207 txid_parts: TxDigests<blake2b_simd::Hash>,
208 all_previous_outputs: &'a [transparent::Output],
209}
210
211impl<'a> PrecomputedTxData<'a> {
212 pub(crate) fn new(
246 tx: &'a Transaction,
247 nu: NetworkUpgrade,
248 all_previous_outputs: &'a [transparent::Output],
249 ) -> PrecomputedTxData<'a> {
250 let tx = tx
251 .to_librustzcash(nu)
252 .expect("`zcash_primitives` and Zebra tx formats must be compatible");
253
254 let txid_parts = tx.deref().digest(zp_tx::txid::TxIdDigester);
255
256 let f_transparent = MapTransparent {
257 auth: TransparentAuth {
258 all_prev_outputs: all_previous_outputs,
259 },
260 };
261
262 let tx_data: zp_tx::TransactionData<PrecomputedAuth> =
263 tx.into_data()
264 .map_authorization(f_transparent, IdentityMap, IdentityMap);
265
266 PrecomputedTxData {
267 tx_data,
268 txid_parts,
269 all_previous_outputs,
270 }
271 }
272}
273
274pub(crate) fn sighash(
285 precomputed_tx_data: &PrecomputedTxData,
286 hash_type: HashType,
287 input_index_script_code: Option<(usize, Vec<u8>)>,
288) -> SigHash {
289 let lock_script: zcash_primitives::legacy::Script;
290 let unlock_script: zcash_primitives::legacy::Script;
291 let signable_input = match input_index_script_code {
292 Some((input_index, script_code)) => {
293 let output = &precomputed_tx_data.all_previous_outputs[input_index];
294 lock_script = output.lock_script.clone().into();
295 unlock_script = zcash_primitives::legacy::Script(script_code);
296 zp_tx::sighash::SignableInput::Transparent {
297 hash_type: hash_type.bits() as _,
298 index: input_index,
299 script_code: &unlock_script,
300 script_pubkey: &lock_script,
301 value: output
302 .value
303 .try_into()
304 .expect("amount was previously validated"),
305 }
306 }
307 None => zp_tx::sighash::SignableInput::Shielded,
308 };
309
310 SigHash(
311 *zp_tx::sighash::signature_hash(
312 &precomputed_tx_data.tx_data,
313 &signable_input,
314 &precomputed_tx_data.txid_parts,
315 )
316 .as_ref(),
317 )
318}
319
320pub(crate) fn auth_digest(tx: &Transaction) -> AuthDigest {
328 let nu = tx.network_upgrade().expect("V5 tx has a network upgrade");
329
330 AuthDigest(
331 tx.to_librustzcash(nu)
332 .expect("V5 tx is convertible to its `zcash_params` equivalent")
333 .auth_commitment()
334 .as_ref()
335 .try_into()
336 .expect("digest has the correct size"),
337 )
338}
339
340pub(crate) fn transparent_output_address(
344 output: &transparent::Output,
345 network: &Network,
346) -> Option<transparent::Address> {
347 let tx_out = zp_tx::components::TxOut::try_from(output)
348 .expect("zcash_primitives and Zebra transparent output formats must be compatible");
349
350 let alt_addr = tx_out.recipient_address();
351
352 match alt_addr {
353 Some(zcash_primitives::legacy::TransparentAddress::PublicKeyHash(pub_key_hash)) => Some(
354 transparent::Address::from_pub_key_hash(network.kind(), pub_key_hash),
355 ),
356 Some(zcash_primitives::legacy::TransparentAddress::ScriptHash(script_hash)) => Some(
357 transparent::Address::from_script_hash(network.kind(), script_hash),
358 ),
359 None => None,
360 }
361}