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