Skip to main content

zebra_chain/transaction/
serialize.rs

1//! Contains impls of `ZcashSerialize`, `ZcashDeserialize` for all of the
2//! transaction types, so that all of the serialization logic is in one place.
3
4use std::{borrow::Borrow, io, sync::Arc};
5
6use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
7use halo2::pasta::group::ff::PrimeField;
8use hex::FromHex;
9use reddsa::{orchard::Binding, orchard::SpendAuth, Signature};
10
11use crate::{
12    amount,
13    block::MAX_BLOCK_BYTES,
14    parameters::{OVERWINTER_VERSION_GROUP_ID, SAPLING_VERSION_GROUP_ID, TX_V5_VERSION_GROUP_ID},
15    primitives::{Halo2Proof, ZkSnarkProof},
16    serialization::{
17        zcash_deserialize_external_count, zcash_serialize_empty_list,
18        zcash_serialize_external_count, AtLeastOne, CompactSizeMessage, ReadZcashExt,
19        SerializationError, TrustedPreallocate, ZcashDeserialize, ZcashDeserializeInto,
20        ZcashSerialize,
21    },
22};
23
24#[cfg(all(zcash_unstable = "nu7", feature = "tx_v6"))]
25use crate::parameters::TX_V6_VERSION_GROUP_ID;
26
27use super::*;
28use crate::sapling;
29
30impl ZcashDeserialize for jubjub::Fq {
31    fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
32        let possible_scalar = jubjub::Fq::from_bytes(&reader.read_32_bytes()?);
33
34        if possible_scalar.is_some().into() {
35            Ok(possible_scalar.unwrap())
36        } else {
37            Err(SerializationError::Parse(
38                "Invalid jubjub::Fq, input not canonical",
39            ))
40        }
41    }
42}
43
44impl ZcashDeserialize for pallas::Scalar {
45    fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
46        let possible_scalar = pallas::Scalar::from_repr(reader.read_32_bytes()?);
47
48        if possible_scalar.is_some().into() {
49            Ok(possible_scalar.unwrap())
50        } else {
51            Err(SerializationError::Parse(
52                "Invalid pallas::Scalar, input not canonical",
53            ))
54        }
55    }
56}
57
58impl ZcashDeserialize for pallas::Base {
59    fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
60        let possible_field_element = pallas::Base::from_repr(reader.read_32_bytes()?);
61
62        if possible_field_element.is_some().into() {
63            Ok(possible_field_element.unwrap())
64        } else {
65            Err(SerializationError::Parse(
66                "Invalid pallas::Base, input not canonical",
67            ))
68        }
69    }
70}
71
72impl<P: ZkSnarkProof> ZcashSerialize for JoinSplitData<P> {
73    fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
74        // Denoted as `nJoinSplit` and `vJoinSplit` in the spec.
75        let joinsplits: Vec<_> = self.joinsplits().cloned().collect();
76        joinsplits.zcash_serialize(&mut writer)?;
77
78        // Denoted as `joinSplitPubKey` in the spec.
79        writer.write_all(&<[u8; 32]>::from(self.pub_key)[..])?;
80
81        // Denoted as `joinSplitSig` in the spec.
82        writer.write_all(&<[u8; 64]>::from(self.sig)[..])?;
83        Ok(())
84    }
85}
86
87impl<P> ZcashDeserialize for Option<JoinSplitData<P>>
88where
89    P: ZkSnarkProof,
90    sprout::JoinSplit<P>: TrustedPreallocate,
91{
92    fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
93        // Denoted as `nJoinSplit` and `vJoinSplit` in the spec.
94        let joinsplits: Vec<sprout::JoinSplit<P>> = (&mut reader).zcash_deserialize_into()?;
95        match joinsplits.split_first() {
96            None => Ok(None),
97            Some((first, rest)) => {
98                // Denoted as `joinSplitPubKey` in the spec.
99                let pub_key = reader.read_32_bytes()?.into();
100                // Denoted as `joinSplitSig` in the spec.
101                let sig = reader.read_64_bytes()?.into();
102                Ok(Some(JoinSplitData {
103                    first: first.clone(),
104                    rest: rest.to_vec(),
105                    pub_key,
106                    sig,
107                }))
108            }
109        }
110    }
111}
112
113// Transaction::V5 serializes sapling ShieldedData in a single continuous byte
114// range, so we can implement its serialization and deserialization separately.
115// (Unlike V4, where it must be serialized as part of the transaction.)
116
117impl ZcashSerialize for Option<sapling::ShieldedData<sapling::SharedAnchor>> {
118    fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
119        match self {
120            None => {
121                // Denoted as `nSpendsSapling` in the spec.
122                zcash_serialize_empty_list(&mut writer)?;
123                // Denoted as `nOutputsSapling` in the spec.
124                zcash_serialize_empty_list(&mut writer)?;
125            }
126            Some(sapling_shielded_data) => {
127                sapling_shielded_data.zcash_serialize(&mut writer)?;
128            }
129        }
130        Ok(())
131    }
132}
133
134impl ZcashSerialize for sapling::ShieldedData<sapling::SharedAnchor> {
135    fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
136        // Collect arrays for Spends
137        // There's no unzip3, so we have to unzip twice.
138        let (spend_prefixes, spend_proofs_sigs): (Vec<_>, Vec<_>) = self
139            .spends()
140            .cloned()
141            .map(sapling::Spend::<sapling::SharedAnchor>::into_v5_parts)
142            .map(|(prefix, proof, sig)| (prefix, (proof, sig)))
143            .unzip();
144        let (spend_proofs, spend_sigs) = spend_proofs_sigs.into_iter().unzip();
145
146        // Collect arrays for Outputs
147        let (output_prefixes, output_proofs): (Vec<_>, _) = self
148            .outputs()
149            .cloned()
150            .map(sapling::Output::into_v5_parts)
151            .unzip();
152
153        // Denoted as `nSpendsSapling` and `vSpendsSapling` in the spec.
154        spend_prefixes.zcash_serialize(&mut writer)?;
155        // Denoted as `nOutputsSapling` and `vOutputsSapling` in the spec.
156        output_prefixes.zcash_serialize(&mut writer)?;
157
158        // Denoted as `valueBalanceSapling` in the spec.
159        self.value_balance.zcash_serialize(&mut writer)?;
160
161        // Denoted as `anchorSapling` in the spec.
162        // `TransferData` ensures this field is only present when there is at
163        // least one spend.
164        if let Some(shared_anchor) = self.shared_anchor() {
165            writer.write_all(&<[u8; 32]>::from(shared_anchor)[..])?;
166        }
167
168        // Denoted as `vSpendProofsSapling` in the spec.
169        zcash_serialize_external_count(&spend_proofs, &mut writer)?;
170        // Denoted as `vSpendAuthSigsSapling` in the spec.
171        zcash_serialize_external_count(&spend_sigs, &mut writer)?;
172
173        // Denoted as `vOutputProofsSapling` in the spec.
174        zcash_serialize_external_count(&output_proofs, &mut writer)?;
175
176        // Denoted as `bindingSigSapling` in the spec.
177        writer.write_all(&<[u8; 64]>::from(self.binding_sig)[..])?;
178
179        Ok(())
180    }
181}
182
183// we can't split ShieldedData out of Option<ShieldedData> deserialization,
184// because the counts are read along with the arrays.
185impl ZcashDeserialize for Option<sapling::ShieldedData<sapling::SharedAnchor>> {
186    #[allow(clippy::unwrap_in_result)]
187    fn zcash_deserialize<R: io::Read>(reader: R) -> Result<Self, SerializationError> {
188        deserialize_v5_sapling_shielded_data(reader, false)
189    }
190}
191
192/// Deserialize V5/V6 Sapling shielded data with an optional early coinbase
193/// rejection.
194///
195/// When `is_coinbase` is true, a non-zero `nSpendsSapling` count is rejected
196/// **before** allocating the spend vector, closing the late-validation gap
197/// described in GHSA-rgwx-8r98-p34c.
198#[allow(clippy::unwrap_in_result)]
199fn deserialize_v5_sapling_shielded_data<R: io::Read>(
200    mut reader: R,
201    is_coinbase: bool,
202) -> Result<Option<sapling::ShieldedData<sapling::SharedAnchor>>, SerializationError> {
203    // Denoted as `nSpendsSapling` in the spec — read count before allocating.
204    let spend_count: CompactSizeMessage = (&mut reader).zcash_deserialize_into()?;
205    let spend_count: usize = spend_count.into();
206
207    // # Consensus
208    //
209    // > A coinbase transaction MUST NOT have any Spend descriptions.
210    //
211    // <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
212    //
213    // Reject before allocating to prevent a peer from forcing thousands of
214    // spend-prefix allocations for a transaction that will always be invalid.
215    if is_coinbase && spend_count > 0 {
216        return Err(SerializationError::Parse(
217            "coinbase transaction must not have Sapling spends",
218        ));
219    }
220
221    // Denoted as `vSpendsSapling` in the spec.
222    let spend_prefixes: Vec<sapling::SpendPrefixInTransactionV5> =
223        zcash_deserialize_external_count(spend_count, &mut reader)?;
224
225    // Denoted as `nOutputsSapling` and `vOutputsSapling` in the spec.
226    let output_prefixes: Vec<_> = (&mut reader).zcash_deserialize_into()?;
227
228    // nSpendsSapling and nOutputsSapling as variables
229    let spends_count = spend_prefixes.len();
230    let outputs_count = output_prefixes.len();
231
232    // All the other fields depend on having spends or outputs
233    if spend_prefixes.is_empty() && output_prefixes.is_empty() {
234        return Ok(None);
235    }
236
237    // Denoted as `valueBalanceSapling` in the spec.
238    let value_balance = (&mut reader).zcash_deserialize_into()?;
239
240    // Denoted as `anchorSapling` in the spec.
241    //
242    // # Consensus
243    //
244    // > Elements of a Spend description MUST be valid encodings of the types given above.
245    //
246    // https://zips.z.cash/protocol/protocol.pdf#spenddesc
247    //
248    // Type is `B^{[ℓ_{Sapling}_{Merkle}]}`, i.e. 32 bytes
249    //
250    // > LEOS2IP_{256}(anchorSapling), if present, MUST be less than 𝑞_𝕁.
251    //
252    // https://zips.z.cash/protocol/protocol.pdf#spendencodingandconsensus
253    //
254    // Validated in [`crate::sapling::tree::Root::zcash_deserialize`].
255    let shared_anchor = if spends_count > 0 {
256        Some((&mut reader).zcash_deserialize_into()?)
257    } else {
258        None
259    };
260
261    // Denoted as `vSpendProofsSapling` in the spec.
262    //
263    // # Consensus
264    //
265    // > Elements of a Spend description MUST be valid encodings of the types given above.
266    //
267    // https://zips.z.cash/protocol/protocol.pdf#spenddesc
268    //
269    // Type is `ZKSpend.Proof`, described in
270    // https://zips.z.cash/protocol/protocol.pdf#grothencoding
271    // It is not enforced here; this just reads 192 bytes.
272    // The type is validated when validating the proof, see
273    // [`groth16::Item::try_from`]. In #3179 we plan to validate here instead.
274    let spend_proofs = zcash_deserialize_external_count(spends_count, &mut reader)?;
275
276    // Denoted as `vSpendAuthSigsSapling` in the spec.
277    //
278    // # Consensus
279    //
280    // > Elements of a Spend description MUST be valid encodings of the types given above.
281    //
282    // https://zips.z.cash/protocol/protocol.pdf#spenddesc
283    //
284    // Type is SpendAuthSig^{Sapling}.Signature, i.e.
285    // B^Y^{[ceiling(ℓ_G/8) + ceiling(bitlength(𝑟_G)/8)]} i.e. 64 bytes
286    // https://zips.z.cash/protocol/protocol.pdf#concretereddsa
287    // See [`redjubjub::Signature<SpendAuth>::zcash_deserialize`].
288    let spend_sigs = zcash_deserialize_external_count(spends_count, &mut reader)?;
289
290    // Denoted as `vOutputProofsSapling` in the spec.
291    //
292    // # Consensus
293    //
294    // > Elements of an Output description MUST be valid encodings of the types given above.
295    //
296    // https://zips.z.cash/protocol/protocol.pdf#outputdesc
297    //
298    // Type is `ZKOutput.Proof`, described in
299    // https://zips.z.cash/protocol/protocol.pdf#grothencoding
300    // It is not enforced here; this just reads 192 bytes.
301    // The type is validated when validating the proof, see
302    // [`groth16::Item::try_from`]. In #3179 we plan to validate here instead.
303    let output_proofs = zcash_deserialize_external_count(outputs_count, &mut reader)?;
304
305    // Denoted as `bindingSigSapling` in the spec.
306    let binding_sig = reader.read_64_bytes()?.into();
307
308    // Create shielded spends from deserialized parts
309    let spends: Vec<_> = spend_prefixes
310        .into_iter()
311        .zip(spend_proofs)
312        .zip(spend_sigs)
313        .map(|((prefix, proof), sig)| {
314            sapling::Spend::<sapling::SharedAnchor>::from_v5_parts(prefix, proof, sig)
315        })
316        .collect();
317
318    // Create shielded outputs from deserialized parts
319    let outputs = output_prefixes
320        .into_iter()
321        .zip(output_proofs)
322        .map(|(prefix, proof)| sapling::Output::from_v5_parts(prefix, proof))
323        .collect();
324
325    // Create transfers
326    //
327    // # Consensus
328    //
329    // > The anchor of each Spend description MUST refer to some earlier
330    // > block’s final Sapling treestate. The anchor is encoded separately
331    // > in each Spend description for v4 transactions, or encoded once and
332    // > shared between all Spend descriptions in a v5 transaction.
333    //
334    // <https://zips.z.cash/protocol/protocol.pdf#spendsandoutputs>
335    //
336    // This rule is also implemented in
337    // [`zebra_state::service::check::anchor`] and
338    // [`zebra_chain::sapling::spend`].
339    //
340    // The "anchor encoding for v5 transactions" is implemented here.
341    let transfers = match shared_anchor {
342        Some(shared_anchor) => sapling::TransferData::SpendsAndMaybeOutputs {
343            shared_anchor,
344            spends: spends
345                .try_into()
346                .expect("checked spends when parsing shared anchor"),
347            maybe_outputs: outputs,
348        },
349        None => sapling::TransferData::JustOutputs {
350            outputs: outputs
351                .try_into()
352                .expect("checked spends or outputs and returned early"),
353        },
354    };
355
356    Ok(Some(sapling::ShieldedData {
357        value_balance,
358        transfers,
359        binding_sig,
360    }))
361}
362
363impl ZcashSerialize for Option<orchard::ShieldedData> {
364    fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
365        match self {
366            None => {
367                // Denoted as `nActionsOrchard` in the spec.
368                zcash_serialize_empty_list(writer)?;
369
370                // We don't need to write anything else here.
371                // "The fields flagsOrchard, valueBalanceOrchard, anchorOrchard, sizeProofsOrchard,
372                // proofsOrchard , and bindingSigOrchard are present if and only if nActionsOrchard > 0."
373                // `§` note of the second table of https://zips.z.cash/protocol/protocol.pdf#txnencoding
374            }
375            Some(orchard_shielded_data) => {
376                orchard_shielded_data.zcash_serialize(&mut writer)?;
377            }
378        }
379        Ok(())
380    }
381}
382
383impl ZcashSerialize for orchard::ShieldedData {
384    fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
385        // Split the AuthorizedAction
386        let (actions, sigs): (Vec<orchard::Action>, Vec<Signature<SpendAuth>>) = self
387            .actions
388            .iter()
389            .cloned()
390            .map(orchard::AuthorizedAction::into_parts)
391            .unzip();
392
393        // Denoted as `nActionsOrchard` and `vActionsOrchard` in the spec.
394        actions.zcash_serialize(&mut writer)?;
395
396        // Denoted as `flagsOrchard` in the spec.
397        self.flags.zcash_serialize(&mut writer)?;
398
399        // Denoted as `valueBalanceOrchard` in the spec.
400        self.value_balance.zcash_serialize(&mut writer)?;
401
402        // Denoted as `anchorOrchard` in the spec.
403        self.shared_anchor.zcash_serialize(&mut writer)?;
404
405        // Denoted as `sizeProofsOrchard` and `proofsOrchard` in the spec.
406        self.proof.zcash_serialize(&mut writer)?;
407
408        // Denoted as `vSpendAuthSigsOrchard` in the spec.
409        zcash_serialize_external_count(&sigs, &mut writer)?;
410
411        // Denoted as `bindingSigOrchard` in the spec.
412        self.binding_sig.zcash_serialize(&mut writer)?;
413
414        Ok(())
415    }
416}
417
418// we can't split ShieldedData out of Option<ShieldedData> deserialization,
419// because the counts are read along with the arrays.
420impl ZcashDeserialize for Option<orchard::ShieldedData> {
421    fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
422        // Denoted as `nActionsOrchard` and `vActionsOrchard` in the spec.
423        let actions: Vec<orchard::Action> = (&mut reader).zcash_deserialize_into()?;
424
425        // "The fields flagsOrchard, valueBalanceOrchard, anchorOrchard, sizeProofsOrchard,
426        // proofsOrchard , and bindingSigOrchard are present if and only if nActionsOrchard > 0."
427        // `§` note of the second table of https://zips.z.cash/protocol/protocol.pdf#txnencoding
428        if actions.is_empty() {
429            return Ok(None);
430        }
431
432        // # Consensus
433        //
434        // > Elements of an Action description MUST be canonical encodings of the types given above.
435        //
436        // https://zips.z.cash/protocol/protocol.pdf#actiondesc
437        //
438        // Some Action elements are validated in this function; they are described below.
439
440        // Denoted as `flagsOrchard` in the spec.
441        // Consensus: type of each flag is 𝔹, i.e. a bit. This is enforced implicitly
442        // in [`Flags::zcash_deserialized`].
443        let flags: orchard::Flags = (&mut reader).zcash_deserialize_into()?;
444
445        // Denoted as `valueBalanceOrchard` in the spec.
446        let value_balance: amount::Amount = (&mut reader).zcash_deserialize_into()?;
447
448        // Denoted as `anchorOrchard` in the spec.
449        // Consensus: type is `{0 .. 𝑞_ℙ − 1}`. See [`orchard::tree::Root::zcash_deserialize`].
450        let shared_anchor: orchard::tree::Root = (&mut reader).zcash_deserialize_into()?;
451
452        // Denoted as `sizeProofsOrchard` and `proofsOrchard` in the spec.
453        // Consensus: type is `ZKAction.Proof`, i.e. a byte sequence.
454        // https://zips.z.cash/protocol/protocol.pdf#halo2encoding
455        let proof: Halo2Proof = (&mut reader).zcash_deserialize_into()?;
456
457        // Denoted as `vSpendAuthSigsOrchard` in the spec.
458        // Consensus: this validates the `spendAuthSig` elements, whose type is
459        // SpendAuthSig^{Orchard}.Signature, i.e.
460        // B^Y^{[ceiling(ℓ_G/8) + ceiling(bitlength(𝑟_G)/8)]} i.e. 64 bytes
461        // See [`Signature::zcash_deserialize`].
462        let sigs: Vec<Signature<SpendAuth>> =
463            zcash_deserialize_external_count(actions.len(), &mut reader)?;
464
465        // Denoted as `bindingSigOrchard` in the spec.
466        let binding_sig: Signature<Binding> = (&mut reader).zcash_deserialize_into()?;
467
468        // Create the AuthorizedAction from deserialized parts
469        let authorized_actions: Vec<orchard::AuthorizedAction> = actions
470            .into_iter()
471            .zip(sigs)
472            .map(|(action, spend_auth_sig)| {
473                orchard::AuthorizedAction::from_parts(action, spend_auth_sig)
474            })
475            .collect();
476
477        let actions: AtLeastOne<orchard::AuthorizedAction> = authorized_actions.try_into()?;
478
479        Ok(Some(orchard::ShieldedData {
480            flags,
481            value_balance,
482            shared_anchor,
483            proof,
484            actions,
485            binding_sig,
486        }))
487    }
488}
489
490impl<T: reddsa::SigType> ZcashSerialize for reddsa::Signature<T> {
491    fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
492        writer.write_all(&<[u8; 64]>::from(*self)[..])?;
493        Ok(())
494    }
495}
496
497impl<T: reddsa::SigType> ZcashDeserialize for reddsa::Signature<T> {
498    fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
499        Ok(reader.read_64_bytes()?.into())
500    }
501}
502
503impl ZcashSerialize for Transaction {
504    #[allow(clippy::unwrap_in_result)]
505    fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
506        // Post-Sapling, transaction size is limited to MAX_BLOCK_BYTES.
507        // (Strictly, the maximum transaction size is about 1.5 kB less,
508        // because blocks also include a block header.)
509        //
510        // Currently, all transaction structs are parsed as part of a
511        // block. So we don't need to check transaction size here, until
512        // we start parsing mempool transactions, or generating our own
513        // transactions (see #483).
514        //
515        // Since we checkpoint on Canopy activation, we won't ever need
516        // to check the smaller pre-Sapling transaction size limit.
517
518        // Denoted as `header` in the spec, contains the `fOverwintered` flag and the `version` field.
519        // Write `version` and set the `fOverwintered` bit if necessary
520        let overwintered_flag = if self.is_overwintered() { 1 << 31 } else { 0 };
521        let version = overwintered_flag | self.version();
522
523        writer.write_u32::<LittleEndian>(version)?;
524
525        match self {
526            Transaction::V1 {
527                inputs,
528                outputs,
529                lock_time,
530            } => {
531                // Denoted as `tx_in_count` and `tx_in` in the spec.
532                inputs.zcash_serialize(&mut writer)?;
533
534                // Denoted as `tx_out_count` and `tx_out` in the spec.
535                outputs.zcash_serialize(&mut writer)?;
536
537                // Denoted as `lock_time` in the spec.
538                lock_time.zcash_serialize(&mut writer)?;
539            }
540            Transaction::V2 {
541                inputs,
542                outputs,
543                lock_time,
544                joinsplit_data,
545            } => {
546                // Denoted as `tx_in_count` and `tx_in` in the spec.
547                inputs.zcash_serialize(&mut writer)?;
548
549                // Denoted as `tx_out_count` and `tx_out` in the spec.
550                outputs.zcash_serialize(&mut writer)?;
551
552                // Denoted as `lock_time` in the spec.
553                lock_time.zcash_serialize(&mut writer)?;
554
555                // A bundle of fields denoted in the spec as `nJoinSplit`, `vJoinSplit`,
556                // `joinSplitPubKey` and `joinSplitSig`.
557                match joinsplit_data {
558                    // Write 0 for nJoinSplits to signal no JoinSplitData.
559                    None => zcash_serialize_empty_list(writer)?,
560                    Some(jsd) => jsd.zcash_serialize(&mut writer)?,
561                }
562            }
563            Transaction::V3 {
564                inputs,
565                outputs,
566                lock_time,
567                expiry_height,
568                joinsplit_data,
569            } => {
570                // Denoted as `nVersionGroupId` in the spec.
571                writer.write_u32::<LittleEndian>(OVERWINTER_VERSION_GROUP_ID)?;
572
573                // Denoted as `tx_in_count` and `tx_in` in the spec.
574                inputs.zcash_serialize(&mut writer)?;
575
576                // Denoted as `tx_out_count` and `tx_out` in the spec.
577                outputs.zcash_serialize(&mut writer)?;
578
579                // Denoted as `lock_time` in the spec.
580                lock_time.zcash_serialize(&mut writer)?;
581
582                writer.write_u32::<LittleEndian>(expiry_height.0)?;
583
584                // A bundle of fields denoted in the spec as `nJoinSplit`, `vJoinSplit`,
585                // `joinSplitPubKey` and `joinSplitSig`.
586                match joinsplit_data {
587                    // Write 0 for nJoinSplits to signal no JoinSplitData.
588                    None => zcash_serialize_empty_list(writer)?,
589                    Some(jsd) => jsd.zcash_serialize(&mut writer)?,
590                }
591            }
592            Transaction::V4 {
593                inputs,
594                outputs,
595                lock_time,
596                expiry_height,
597                sapling_shielded_data,
598                joinsplit_data,
599            } => {
600                // Transaction V4 spec:
601                // https://zips.z.cash/protocol/protocol.pdf#txnencoding
602
603                // Denoted as `nVersionGroupId` in the spec.
604                writer.write_u32::<LittleEndian>(SAPLING_VERSION_GROUP_ID)?;
605
606                // Denoted as `tx_in_count` and `tx_in` in the spec.
607                inputs.zcash_serialize(&mut writer)?;
608
609                // Denoted as `tx_out_count` and `tx_out` in the spec.
610                outputs.zcash_serialize(&mut writer)?;
611
612                // Denoted as `lock_time` in the spec.
613                lock_time.zcash_serialize(&mut writer)?;
614
615                // Denoted as `nExpiryHeight` in the spec.
616                writer.write_u32::<LittleEndian>(expiry_height.0)?;
617
618                // The previous match arms serialize in one go, because the
619                // internal structure happens to nicely line up with the
620                // serialized structure. However, this is not possible for
621                // version 4 transactions, as the binding_sig for the
622                // ShieldedData is placed at the end of the transaction. So
623                // instead we have to interleave serialization of the
624                // ShieldedData and the JoinSplitData.
625
626                match sapling_shielded_data {
627                    None => {
628                        // Signal no value balance.
629                        writer.write_i64::<LittleEndian>(0)?;
630                        // Signal no shielded spends and no shielded outputs.
631                        zcash_serialize_empty_list(&mut writer)?;
632                        zcash_serialize_empty_list(&mut writer)?;
633                    }
634                    Some(sapling_shielded_data) => {
635                        // Denoted as `valueBalanceSapling` in the spec.
636                        sapling_shielded_data
637                            .value_balance
638                            .zcash_serialize(&mut writer)?;
639
640                        // Denoted as `nSpendsSapling` and `vSpendsSapling` in the spec.
641                        let spends: Vec<_> = sapling_shielded_data.spends().cloned().collect();
642                        spends.zcash_serialize(&mut writer)?;
643
644                        // Denoted as `nOutputsSapling` and `vOutputsSapling` in the spec.
645                        let outputs: Vec<_> = sapling_shielded_data
646                            .outputs()
647                            .cloned()
648                            .map(sapling::OutputInTransactionV4)
649                            .collect();
650                        outputs.zcash_serialize(&mut writer)?;
651                    }
652                }
653
654                // A bundle of fields denoted in the spec as `nJoinSplit`, `vJoinSplit`,
655                // `joinSplitPubKey` and `joinSplitSig`.
656                match joinsplit_data {
657                    None => zcash_serialize_empty_list(&mut writer)?,
658                    Some(jsd) => jsd.zcash_serialize(&mut writer)?,
659                }
660
661                // Denoted as `bindingSigSapling` in the spec.
662                if let Some(shielded_data) = sapling_shielded_data {
663                    writer.write_all(&<[u8; 64]>::from(shielded_data.binding_sig)[..])?;
664                }
665            }
666
667            Transaction::V5 {
668                network_upgrade,
669                lock_time,
670                expiry_height,
671                inputs,
672                outputs,
673                sapling_shielded_data,
674                orchard_shielded_data,
675            } => {
676                // Transaction V5 spec:
677                // https://zips.z.cash/protocol/protocol.pdf#txnencoding
678
679                // Denoted as `nVersionGroupId` in the spec.
680                writer.write_u32::<LittleEndian>(TX_V5_VERSION_GROUP_ID)?;
681
682                // Denoted as `nConsensusBranchId` in the spec.
683                writer.write_u32::<LittleEndian>(u32::from(
684                    network_upgrade
685                        .branch_id()
686                        .expect("valid transactions must have a network upgrade with a branch id"),
687                ))?;
688
689                // Denoted as `lock_time` in the spec.
690                lock_time.zcash_serialize(&mut writer)?;
691
692                // Denoted as `nExpiryHeight` in the spec.
693                writer.write_u32::<LittleEndian>(expiry_height.0)?;
694
695                // Denoted as `tx_in_count` and `tx_in` in the spec.
696                inputs.zcash_serialize(&mut writer)?;
697
698                // Denoted as `tx_out_count` and `tx_out` in the spec.
699                outputs.zcash_serialize(&mut writer)?;
700
701                // A bundle of fields denoted in the spec as `nSpendsSapling`, `vSpendsSapling`,
702                // `nOutputsSapling`,`vOutputsSapling`, `valueBalanceSapling`, `anchorSapling`,
703                // `vSpendProofsSapling`, `vSpendAuthSigsSapling`, `vOutputProofsSapling` and
704                // `bindingSigSapling`.
705                sapling_shielded_data.zcash_serialize(&mut writer)?;
706
707                // A bundle of fields denoted in the spec as `nActionsOrchard`, `vActionsOrchard`,
708                // `flagsOrchard`,`valueBalanceOrchard`, `anchorOrchard`, `sizeProofsOrchard`,
709                // `proofsOrchard`, `vSpendAuthSigsOrchard`, and `bindingSigOrchard`.
710                orchard_shielded_data.zcash_serialize(&mut writer)?;
711            }
712
713            #[cfg(all(zcash_unstable = "nu7", feature = "tx_v6"))]
714            Transaction::V6 {
715                network_upgrade,
716                lock_time,
717                expiry_height,
718                zip233_amount,
719                inputs,
720                outputs,
721                sapling_shielded_data,
722                orchard_shielded_data,
723            } => {
724                // Transaction V6 spec:
725                // https://zips.z.cash/zip-0230#specification
726
727                // Denoted as `nVersionGroupId` in the spec.
728                writer.write_u32::<LittleEndian>(TX_V6_VERSION_GROUP_ID)?;
729
730                // Denoted as `nConsensusBranchId` in the spec.
731                writer.write_u32::<LittleEndian>(u32::from(
732                    network_upgrade
733                        .branch_id()
734                        .expect("valid transactions must have a network upgrade with a branch id"),
735                ))?;
736
737                // Denoted as `lock_time` in the spec.
738                lock_time.zcash_serialize(&mut writer)?;
739
740                // Denoted as `nExpiryHeight` in the spec.
741                writer.write_u32::<LittleEndian>(expiry_height.0)?;
742
743                // Denoted as `zip233_amount` in the spec.
744                zip233_amount.zcash_serialize(&mut writer)?;
745
746                // Denoted as `tx_in_count` and `tx_in` in the spec.
747                inputs.zcash_serialize(&mut writer)?;
748
749                // Denoted as `tx_out_count` and `tx_out` in the spec.
750                outputs.zcash_serialize(&mut writer)?;
751
752                // A bundle of fields denoted in the spec as `nSpendsSapling`, `vSpendsSapling`,
753                // `nOutputsSapling`,`vOutputsSapling`, `valueBalanceSapling`, `anchorSapling`,
754                // `vSpendProofsSapling`, `vSpendAuthSigsSapling`, `vOutputProofsSapling` and
755                // `bindingSigSapling`.
756                sapling_shielded_data.zcash_serialize(&mut writer)?;
757
758                // A bundle of fields denoted in the spec as `nActionsOrchard`, `vActionsOrchard`,
759                // `flagsOrchard`,`valueBalanceOrchard`, `anchorOrchard`, `sizeProofsOrchard`,
760                // `proofsOrchard`, `vSpendAuthSigsOrchard`, and `bindingSigOrchard`.
761                orchard_shielded_data.zcash_serialize(&mut writer)?;
762            }
763        }
764        Ok(())
765    }
766}
767
768impl ZcashDeserialize for Transaction {
769    #[allow(clippy::unwrap_in_result)]
770    fn zcash_deserialize<R: io::Read>(reader: R) -> Result<Self, SerializationError> {
771        // # Consensus
772        //
773        // > [Pre-Sapling] The encoded size of the transaction MUST be less than or
774        // > equal to 100000 bytes.
775        //
776        // https://zips.z.cash/protocol/protocol.pdf#txnconsensus
777        //
778        // Zebra does not verify this rule because we checkpoint up to Canopy blocks, but:
779        // Since transactions must get mined into a block to be useful,
780        // we reject transactions that are larger than blocks.
781        //
782        // If the limit is reached, we'll get an UnexpectedEof error.
783        let mut limited_reader = reader.take(MAX_BLOCK_BYTES);
784
785        let (version, overwintered) = {
786            const LOW_31_BITS: u32 = (1 << 31) - 1;
787            // Denoted as `header` in the spec, contains the `fOverwintered` flag and the `version` field.
788            let header = limited_reader.read_u32::<LittleEndian>()?;
789            (header & LOW_31_BITS, header >> 31 != 0)
790        };
791
792        // # Consensus
793        //
794        // The next rules apply for different transaction versions as follows:
795        //
796        // [Pre-Overwinter]: Transactions version 1 and 2.
797        // [Overwinter onward]: Transactions version 3 and above.
798        // [Overwinter only, pre-Sapling]: Transactions version 3.
799        // [Sapling to Canopy inclusive, pre-NU5]: Transactions version 4.
800        // [NU5 onward]: Transactions version 4 and above.
801        //
802        // > The transaction version number MUST be greater than or equal to 1.
803        //
804        // > [Pre-Overwinter] The fOverwintered fag MUST NOT be set.
805        //
806        // > [Overwinter onward] The version group ID MUST be recognized.
807        //
808        // > [Overwinter onward] The fOverwintered flag MUST be set.
809        //
810        // > [Overwinter only, pre-Sapling] The transaction version number MUST be 3,
811        // > and the version group ID MUST be 0x03C48270.
812        //
813        // > [Sapling to Canopy inclusive, pre-NU5] The transaction version number MUST be 4,
814        // > and the version group ID MUST be 0x892F2085.
815        //
816        // > [NU5 onward] The transaction version number MUST be 4 or 5.
817        // > If the transaction version number is 4 then the version group ID MUST be 0x892F2085.
818        // > If the transaction version number is 5 then the version group ID MUST be 0x26A7270A.
819        //
820        // Note: Zebra checkpoints until Canopy blocks, this means only transactions versions
821        // 4 and 5 get fully verified. This satisfies "The transaction version number MUST be 4"
822        // and "The transaction version number MUST be 4 or 5" from the last two rules above.
823        // This is done in the zebra-consensus crate, in the transactions checks.
824        //
825        // https://zips.z.cash/protocol/protocol.pdf#txnconsensus
826        match (version, overwintered) {
827            (1, false) => Ok(Transaction::V1 {
828                // Denoted as `tx_in_count` and `tx_in` in the spec.
829                inputs: Vec::zcash_deserialize(&mut limited_reader)?,
830                // Denoted as `tx_out_count` and `tx_out` in the spec.
831                outputs: Vec::zcash_deserialize(&mut limited_reader)?,
832                // Denoted as `lock_time` in the spec.
833                lock_time: LockTime::zcash_deserialize(&mut limited_reader)?,
834            }),
835            (2, false) => {
836                // Version 2 transactions use Sprout-on-BCTV14.
837                type OptV2Jsd = Option<JoinSplitData<Bctv14Proof>>;
838                Ok(Transaction::V2 {
839                    // Denoted as `tx_in_count` and `tx_in` in the spec.
840                    inputs: Vec::zcash_deserialize(&mut limited_reader)?,
841                    // Denoted as `tx_out_count` and `tx_out` in the spec.
842                    outputs: Vec::zcash_deserialize(&mut limited_reader)?,
843                    // Denoted as `lock_time` in the spec.
844                    lock_time: LockTime::zcash_deserialize(&mut limited_reader)?,
845                    // A bundle of fields denoted in the spec as `nJoinSplit`, `vJoinSplit`,
846                    // `joinSplitPubKey` and `joinSplitSig`.
847                    joinsplit_data: OptV2Jsd::zcash_deserialize(&mut limited_reader)?,
848                })
849            }
850            (3, true) => {
851                // Denoted as `nVersionGroupId` in the spec.
852                let id = limited_reader.read_u32::<LittleEndian>()?;
853                if id != OVERWINTER_VERSION_GROUP_ID {
854                    return Err(SerializationError::Parse(
855                        "expected OVERWINTER_VERSION_GROUP_ID",
856                    ));
857                }
858                // Version 3 transactions use Sprout-on-BCTV14.
859                type OptV3Jsd = Option<JoinSplitData<Bctv14Proof>>;
860                Ok(Transaction::V3 {
861                    // Denoted as `tx_in_count` and `tx_in` in the spec.
862                    inputs: Vec::zcash_deserialize(&mut limited_reader)?,
863                    // Denoted as `tx_out_count` and `tx_out` in the spec.
864                    outputs: Vec::zcash_deserialize(&mut limited_reader)?,
865                    // Denoted as `lock_time` in the spec.
866                    lock_time: LockTime::zcash_deserialize(&mut limited_reader)?,
867                    // Denoted as `nExpiryHeight` in the spec.
868                    expiry_height: block::Height(limited_reader.read_u32::<LittleEndian>()?),
869                    // A bundle of fields denoted in the spec as `nJoinSplit`, `vJoinSplit`,
870                    // `joinSplitPubKey` and `joinSplitSig`.
871                    joinsplit_data: OptV3Jsd::zcash_deserialize(&mut limited_reader)?,
872                })
873            }
874            (4, true) => {
875                // Transaction V4 spec:
876                // https://zips.z.cash/protocol/protocol.pdf#txnencoding
877
878                // Denoted as `nVersionGroupId` in the spec.
879                let id = limited_reader.read_u32::<LittleEndian>()?;
880                if id != SAPLING_VERSION_GROUP_ID {
881                    return Err(SerializationError::Parse(
882                        "expected SAPLING_VERSION_GROUP_ID",
883                    ));
884                }
885                // Version 4 transactions use Sprout-on-Groth16.
886                type OptV4Jsd = Option<JoinSplitData<Groth16Proof>>;
887
888                // The previous match arms deserialize in one go, because the
889                // internal structure happens to nicely line up with the
890                // serialized structure. However, this is not possible for
891                // version 4 transactions, as the binding_sig for the
892                // ShieldedData is placed at the end of the transaction. So
893                // instead we have to pull the component parts out manually and
894                // then assemble them.
895
896                // Denoted as `tx_in_count` and `tx_in` in the spec.
897                let inputs: Vec<transparent::Input> = Vec::zcash_deserialize(&mut limited_reader)?;
898
899                // Denoted as `tx_out_count` and `tx_out` in the spec.
900                let outputs = Vec::zcash_deserialize(&mut limited_reader)?;
901
902                let is_coinbase = inputs.len() == 1
903                    && matches!(inputs.first(), Some(transparent::Input::Coinbase { .. }));
904
905                // Denoted as `lock_time` in the spec.
906                let lock_time = LockTime::zcash_deserialize(&mut limited_reader)?;
907
908                // Denoted as `nExpiryHeight` in the spec.
909                let expiry_height = block::Height(limited_reader.read_u32::<LittleEndian>()?);
910
911                // Denoted as `valueBalanceSapling` in the spec.
912                let value_balance = (&mut limited_reader).zcash_deserialize_into()?;
913
914                // Denoted as `nSpendsSapling` — read count before allocating.
915                let spend_count: CompactSizeMessage =
916                    (&mut limited_reader).zcash_deserialize_into()?;
917                let spend_count: usize = spend_count.into();
918
919                // # Consensus
920                //
921                // > A coinbase transaction MUST NOT have any Spend descriptions.
922                //
923                // <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
924                if is_coinbase && spend_count > 0 {
925                    return Err(SerializationError::Parse(
926                        "coinbase transaction must not have Sapling spends",
927                    ));
928                }
929
930                // Denoted as `vSpendsSapling` in the spec.
931                let shielded_spends: Vec<sapling::Spend<sapling::PerSpendAnchor>> =
932                    zcash_deserialize_external_count(spend_count, &mut limited_reader)?;
933
934                // Denoted as `nOutputsSapling` and `vOutputsSapling` in the spec.
935                let shielded_outputs =
936                    Vec::<sapling::OutputInTransactionV4>::zcash_deserialize(&mut limited_reader)?
937                        .into_iter()
938                        .map(sapling::Output::from_v4)
939                        .collect();
940
941                // A bundle of fields denoted in the spec as `nJoinSplit`, `vJoinSplit`,
942                // `joinSplitPubKey` and `joinSplitSig`.
943                let joinsplit_data = OptV4Jsd::zcash_deserialize(&mut limited_reader)?;
944
945                let sapling_transfers = if !shielded_spends.is_empty() {
946                    Some(sapling::TransferData::SpendsAndMaybeOutputs {
947                        shared_anchor: FieldNotPresent,
948                        spends: shielded_spends.try_into().expect("checked for spends"),
949                        maybe_outputs: shielded_outputs,
950                    })
951                } else if !shielded_outputs.is_empty() {
952                    Some(sapling::TransferData::JustOutputs {
953                        outputs: shielded_outputs.try_into().expect("checked for outputs"),
954                    })
955                } else {
956                    // # Consensus
957                    //
958                    // > [Sapling onward] If effectiveVersion = 4 and there are no Spend
959                    // > descriptions or Output descriptions, then valueBalanceSapling MUST be 0.
960                    //
961                    // https://zips.z.cash/protocol/protocol.pdf#txnconsensus
962                    if value_balance != 0 {
963                        return Err(SerializationError::BadTransactionBalance);
964                    }
965                    None
966                };
967
968                let sapling_shielded_data = match sapling_transfers {
969                    Some(transfers) => Some(sapling::ShieldedData {
970                        value_balance,
971                        transfers,
972                        // Denoted as `bindingSigSapling` in the spec.
973                        binding_sig: limited_reader.read_64_bytes()?.into(),
974                    }),
975                    None => None,
976                };
977
978                Ok(Transaction::V4 {
979                    inputs,
980                    outputs,
981                    lock_time,
982                    expiry_height,
983                    sapling_shielded_data,
984                    joinsplit_data,
985                })
986            }
987            (5, true) => {
988                // Transaction V5 spec:
989                // https://zips.z.cash/protocol/protocol.pdf#txnencoding
990
991                // Denoted as `nVersionGroupId` in the spec.
992                let id = limited_reader.read_u32::<LittleEndian>()?;
993                if id != TX_V5_VERSION_GROUP_ID {
994                    return Err(SerializationError::Parse("expected TX_V5_VERSION_GROUP_ID"));
995                }
996                // Denoted as `nConsensusBranchId` in the spec.
997                // Convert it to a NetworkUpgrade
998                let network_upgrade =
999                    NetworkUpgrade::try_from(limited_reader.read_u32::<LittleEndian>()?)?;
1000
1001                // # Consensus
1002                //
1003                // > [NU5 onward] The transaction version number MUST be 4 or 5.
1004                //
1005                // V5 transactions are only valid from NU5 onward, so reject
1006                // transactions with pre-NU5 consensus branch IDs.
1007                if network_upgrade < NetworkUpgrade::Nu5 {
1008                    return Err(SerializationError::Parse(
1009                        "v5 transaction must have NU5 or later consensus branch ID",
1010                    ));
1011                }
1012
1013                // Denoted as `lock_time` in the spec.
1014                let lock_time = LockTime::zcash_deserialize(&mut limited_reader)?;
1015
1016                // Denoted as `nExpiryHeight` in the spec.
1017                let expiry_height = block::Height(limited_reader.read_u32::<LittleEndian>()?);
1018
1019                // Denoted as `tx_in_count` and `tx_in` in the spec.
1020                let inputs: Vec<transparent::Input> = Vec::zcash_deserialize(&mut limited_reader)?;
1021
1022                // Denoted as `tx_out_count` and `tx_out` in the spec.
1023                let outputs = Vec::zcash_deserialize(&mut limited_reader)?;
1024
1025                let is_coinbase = inputs.len() == 1
1026                    && matches!(inputs.first(), Some(transparent::Input::Coinbase { .. }));
1027
1028                // A bundle of fields denoted in the spec as `nSpendsSapling`, `vSpendsSapling`,
1029                // `nOutputsSapling`,`vOutputsSapling`, `valueBalanceSapling`, `anchorSapling`,
1030                // `vSpendProofsSapling`, `vSpendAuthSigsSapling`, `vOutputProofsSapling` and
1031                // `bindingSigSapling`.
1032                let sapling_shielded_data =
1033                    deserialize_v5_sapling_shielded_data(&mut limited_reader, is_coinbase)?;
1034
1035                // A bundle of fields denoted in the spec as `nActionsOrchard`, `vActionsOrchard`,
1036                // `flagsOrchard`,`valueBalanceOrchard`, `anchorOrchard`, `sizeProofsOrchard`,
1037                // `proofsOrchard`, `vSpendAuthSigsOrchard`, and `bindingSigOrchard`.
1038                let orchard_shielded_data = (&mut limited_reader).zcash_deserialize_into()?;
1039
1040                let tx = Transaction::V5 {
1041                    network_upgrade,
1042                    lock_time,
1043                    expiry_height,
1044                    inputs,
1045                    outputs,
1046                    sapling_shielded_data,
1047                    orchard_shielded_data,
1048                };
1049
1050                tx.to_librustzcash(network_upgrade)?;
1051
1052                Ok(tx)
1053            }
1054            #[cfg(all(zcash_unstable = "nu7", feature = "tx_v6"))]
1055            (6, true) => {
1056                // Denoted as `nVersionGroupId` in the spec.
1057                let id = limited_reader.read_u32::<LittleEndian>()?;
1058                if id != TX_V6_VERSION_GROUP_ID {
1059                    return Err(SerializationError::Parse("expected TX_V6_VERSION_GROUP_ID"));
1060                }
1061                // Denoted as `nConsensusBranchId` in the spec.
1062                // Convert it to a NetworkUpgrade
1063                let network_upgrade =
1064                    NetworkUpgrade::try_from(limited_reader.read_u32::<LittleEndian>()?)?;
1065                // V6 transactions are only valid from NU5 onward, so reject
1066                // transactions with pre-NU5 consensus branch IDs.
1067                if network_upgrade < NetworkUpgrade::Nu5 {
1068                    return Err(SerializationError::Parse(
1069                        "v6 transaction must have NU5 or later consensus branch ID",
1070                    ));
1071                }
1072                // Denoted as `lock_time` in the spec.
1073                let lock_time = LockTime::zcash_deserialize(&mut limited_reader)?;
1074
1075                // Denoted as `nExpiryHeight` in the spec.
1076                let expiry_height = block::Height(limited_reader.read_u32::<LittleEndian>()?);
1077
1078                // Denoted as `zip233_amount` in the spec.
1079                let zip233_amount = (&mut limited_reader).zcash_deserialize_into()?;
1080
1081                // Denoted as `tx_in_count` and `tx_in` in the spec.
1082                let inputs: Vec<transparent::Input> = Vec::zcash_deserialize(&mut limited_reader)?;
1083
1084                // Denoted as `tx_out_count` and `tx_out` in the spec.
1085                let outputs = Vec::zcash_deserialize(&mut limited_reader)?;
1086
1087                let is_coinbase = inputs.len() == 1
1088                    && matches!(inputs.first(), Some(transparent::Input::Coinbase { .. }));
1089
1090                // A bundle of fields denoted in the spec as `nSpendsSapling`, `vSpendsSapling`,
1091                // `nOutputsSapling`,`vOutputsSapling`, `valueBalanceSapling`, `anchorSapling`,
1092                // `vSpendProofsSapling`, `vSpendAuthSigsSapling`, `vOutputProofsSapling` and
1093                // `bindingSigSapling`.
1094                let sapling_shielded_data =
1095                    deserialize_v5_sapling_shielded_data(&mut limited_reader, is_coinbase)?;
1096
1097                // A bundle of fields denoted in the spec as `nActionsOrchard`, `vActionsOrchard`,
1098                // `flagsOrchard`,`valueBalanceOrchard`, `anchorOrchard`, `sizeProofsOrchard`,
1099                // `proofsOrchard`, `vSpendAuthSigsOrchard`, and `bindingSigOrchard`.
1100                let orchard_shielded_data = (&mut limited_reader).zcash_deserialize_into()?;
1101
1102                Ok(Transaction::V6 {
1103                    network_upgrade,
1104                    lock_time,
1105                    expiry_height,
1106                    zip233_amount,
1107                    inputs,
1108                    outputs,
1109                    sapling_shielded_data,
1110                    orchard_shielded_data,
1111                })
1112            }
1113            (_, _) => Err(SerializationError::Parse("bad tx header")),
1114        }
1115    }
1116}
1117
1118impl<T> ZcashDeserialize for Arc<T>
1119where
1120    T: ZcashDeserialize,
1121{
1122    fn zcash_deserialize<R: io::Read>(reader: R) -> Result<Self, SerializationError> {
1123        Ok(Arc::new(T::zcash_deserialize(reader)?))
1124    }
1125}
1126
1127impl<T> ZcashSerialize for Arc<T>
1128where
1129    T: ZcashSerialize,
1130{
1131    fn zcash_serialize<W: io::Write>(&self, writer: W) -> Result<(), io::Error> {
1132        T::zcash_serialize(self, writer)
1133    }
1134}
1135
1136/// A Tx Input must have an Outpoint (32 byte hash + 4 byte index), a 4 byte sequence number,
1137/// and a signature script, which always takes a min of 1 byte (for a length 0 script).
1138pub(crate) const MIN_TRANSPARENT_INPUT_SIZE: u64 = 32 + 4 + 4 + 1;
1139
1140/// A Transparent output has an 8 byte value and script which takes a min of 1 byte.
1141pub(crate) const MIN_TRANSPARENT_OUTPUT_SIZE: u64 = 8 + 1;
1142
1143/// All txs must have at least one input, a 4 byte locktime, and at least one output.
1144///
1145/// Shielded transfers are much larger than transparent transfers,
1146/// so this is the minimum transaction size.
1147pub const MIN_TRANSPARENT_TX_SIZE: u64 =
1148    MIN_TRANSPARENT_INPUT_SIZE + 4 + MIN_TRANSPARENT_OUTPUT_SIZE;
1149
1150/// The minimum transaction size for v4 transactions.
1151///
1152/// v4 transactions also have an expiry height.
1153pub const MIN_TRANSPARENT_TX_V4_SIZE: u64 = MIN_TRANSPARENT_TX_SIZE + 4;
1154
1155/// The minimum transaction size for v5 transactions.
1156///
1157/// v5 transactions also have an expiry height and a consensus branch ID.
1158pub const MIN_TRANSPARENT_TX_V5_SIZE: u64 = MIN_TRANSPARENT_TX_SIZE + 4 + 4;
1159
1160/// No valid Zcash message contains more transactions than can fit in a single block
1161///
1162/// `tx` messages contain a single transaction, and `block` messages are limited to the maximum
1163/// block size.
1164impl TrustedPreallocate for Transaction {
1165    fn max_allocation() -> u64 {
1166        // A transparent transaction is the smallest transaction variant
1167        MAX_BLOCK_BYTES / MIN_TRANSPARENT_TX_SIZE
1168    }
1169}
1170
1171/// The maximum number of inputs in a valid Zcash on-chain transaction.
1172///
1173/// If a transaction contains more inputs than can fit in maximally large block, it might be
1174/// valid on the network and in the mempool, but it can never be mined into a block. So
1175/// rejecting these large edge-case transactions can never break consensus.
1176impl TrustedPreallocate for transparent::Input {
1177    fn max_allocation() -> u64 {
1178        MAX_BLOCK_BYTES / MIN_TRANSPARENT_INPUT_SIZE
1179    }
1180}
1181
1182/// The maximum number of outputs in a valid Zcash on-chain transaction.
1183///
1184/// If a transaction contains more outputs than can fit in maximally large block, it might be
1185/// valid on the network and in the mempool, but it can never be mined into a block. So
1186/// rejecting these large edge-case transactions can never break consensus.
1187impl TrustedPreallocate for transparent::Output {
1188    fn max_allocation() -> u64 {
1189        MAX_BLOCK_BYTES / MIN_TRANSPARENT_OUTPUT_SIZE
1190    }
1191}
1192
1193/// A serialized transaction.
1194///
1195/// Stores bytes that are guaranteed to be deserializable into a [`Transaction`].
1196///
1197/// Sorts in lexicographic order of the transaction's serialized data.
1198#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
1199pub struct SerializedTransaction {
1200    bytes: Vec<u8>,
1201}
1202
1203impl fmt::Display for SerializedTransaction {
1204    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1205        f.write_str(&hex::encode(&self.bytes))
1206    }
1207}
1208
1209impl fmt::Debug for SerializedTransaction {
1210    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1211        // A transaction with a lot of transfers can be extremely long in logs.
1212        let mut data_truncated = hex::encode(&self.bytes);
1213        if data_truncated.len() > 1003 {
1214            let end = data_truncated.len() - 500;
1215            // Replace the middle bytes with "...", but leave 500 bytes on either side.
1216            // The data is hex, so this replacement won't panic.
1217            data_truncated.replace_range(500..=end, "...");
1218        }
1219
1220        f.debug_tuple("SerializedTransaction")
1221            .field(&data_truncated)
1222            .finish()
1223    }
1224}
1225
1226/// Build a [`SerializedTransaction`] by serializing a block.
1227impl<B: Borrow<Transaction>> From<B> for SerializedTransaction {
1228    fn from(tx: B) -> Self {
1229        SerializedTransaction {
1230            bytes: tx
1231                .borrow()
1232                .zcash_serialize_to_vec()
1233                .expect("Writing to a `Vec` should never fail"),
1234        }
1235    }
1236}
1237
1238/// Access the serialized bytes of a [`SerializedTransaction`].
1239impl AsRef<[u8]> for SerializedTransaction {
1240    fn as_ref(&self) -> &[u8] {
1241        self.bytes.as_ref()
1242    }
1243}
1244
1245impl From<Vec<u8>> for SerializedTransaction {
1246    fn from(bytes: Vec<u8>) -> Self {
1247        Self { bytes }
1248    }
1249}
1250
1251impl FromHex for SerializedTransaction {
1252    type Error = <Vec<u8> as FromHex>::Error;
1253
1254    fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
1255        let bytes = <Vec<u8>>::from_hex(hex)?;
1256
1257        Ok(bytes.into())
1258    }
1259}