zebra_chain/primitives/
zcash_primitives.rs
1use std::{io, ops::Deref, sync::Arc};
5
6use zcash_primitives::transaction::{self as zp_tx, TxDigests};
7use zcash_protocol::value::BalanceError;
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<zcash_protocol::value::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
144impl TryFrom<&transparent::Output> for zcash_transparent::bundle::TxOut {
148 type Error = io::Error;
149
150 #[allow(clippy::unwrap_in_result)]
151 fn try_from(output: &transparent::Output) -> Result<Self, Self::Error> {
152 let serialized_output_bytes = output
153 .zcash_serialize_to_vec()
154 .expect("zcash_primitives and Zebra transparent output formats must be compatible");
155
156 zcash_transparent::bundle::TxOut::read(&mut serialized_output_bytes.as_slice())
157 }
158}
159
160impl TryFrom<transparent::Output> for zcash_transparent::bundle::TxOut {
162 type Error = io::Error;
163
164 #[allow(clippy::needless_borrow)]
166 fn try_from(output: transparent::Output) -> Result<Self, Self::Error> {
167 (&output).try_into()
168 }
169}
170
171impl TryFrom<Amount<NonNegative>> for zcash_protocol::value::Zatoshis {
173 type Error = BalanceError;
174
175 fn try_from(amount: Amount<NonNegative>) -> Result<Self, Self::Error> {
176 zcash_protocol::value::Zatoshis::from_nonnegative_i64(amount.into())
177 }
178}
179
180impl From<&Script> for zcash_primitives::legacy::Script {
182 fn from(script: &Script) -> Self {
183 zcash_primitives::legacy::Script(script.as_raw_bytes().to_vec())
184 }
185}
186
187impl From<Script> for zcash_primitives::legacy::Script {
189 #[allow(clippy::needless_borrow)]
191 fn from(script: Script) -> Self {
192 (&script).into()
193 }
194}
195
196#[derive(Debug)]
198pub(crate) struct PrecomputedTxData {
199 tx_data: zp_tx::TransactionData<PrecomputedAuth>,
200 txid_parts: TxDigests<blake2b_simd::Hash>,
201 all_previous_outputs: Arc<Vec<transparent::Output>>,
202}
203
204impl PrecomputedTxData {
205 pub(crate) fn new(
239 tx: &Transaction,
240 nu: NetworkUpgrade,
241 all_previous_outputs: Arc<Vec<transparent::Output>>,
242 ) -> Result<PrecomputedTxData, Error> {
243 let tx = tx.to_librustzcash(nu)?;
244
245 let txid_parts = tx.deref().digest(zp_tx::txid::TxIdDigester);
246
247 let f_transparent = MapTransparent {
248 auth: TransparentAuth {
249 all_prev_outputs: all_previous_outputs.clone(),
250 },
251 };
252
253 let tx_data: zp_tx::TransactionData<PrecomputedAuth> =
254 tx.into_data()
255 .map_authorization(f_transparent, IdentityMap, IdentityMap);
256
257 Ok(PrecomputedTxData {
258 tx_data,
259 txid_parts,
260 all_previous_outputs,
261 })
262 }
263}
264
265pub(crate) fn sighash(
276 precomputed_tx_data: &PrecomputedTxData,
277 hash_type: HashType,
278 input_index_script_code: Option<(usize, Vec<u8>)>,
279) -> SigHash {
280 let lock_script: zcash_primitives::legacy::Script;
281 let unlock_script: zcash_primitives::legacy::Script;
282 let signable_input = match input_index_script_code {
283 Some((input_index, script_code)) => {
284 let output = &precomputed_tx_data.all_previous_outputs[input_index];
285 lock_script = output.lock_script.clone().into();
286 unlock_script = zcash_primitives::legacy::Script(script_code);
287 zp_tx::sighash::SignableInput::Transparent(
288 zcash_transparent::sighash::SignableInput::from_parts(
289 hash_type.try_into().expect("hash type should be ALL"),
290 input_index,
291 &unlock_script,
292 &lock_script,
293 output
294 .value
295 .try_into()
296 .expect("amount was previously validated"),
297 ),
298 )
299 }
300 None => zp_tx::sighash::SignableInput::Shielded,
301 };
302
303 SigHash(
304 *zp_tx::sighash::signature_hash(
305 &precomputed_tx_data.tx_data,
306 &signable_input,
307 &precomputed_tx_data.txid_parts,
308 )
309 .as_ref(),
310 )
311}
312
313pub(crate) fn auth_digest(tx: &Transaction) -> AuthDigest {
321 let nu = tx.network_upgrade().expect("V5 tx has a network upgrade");
322
323 AuthDigest(
324 tx.to_librustzcash(nu)
325 .expect("V5 tx is convertible to its `zcash_params` equivalent")
326 .auth_commitment()
327 .as_ref()
328 .try_into()
329 .expect("digest has the correct size"),
330 )
331}