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 Ok(())
677 }
678}
679
680impl ZcashDeserialize for Transaction {
681 #[allow(clippy::unwrap_in_result)]
682 fn zcash_deserialize<R: io::Read>(reader: R) -> Result<Self, SerializationError> {
683 // # Consensus
684 //
685 // > [Pre-Sapling] The encoded size of the transaction MUST be less than or
686 // > equal to 100000 bytes.
687 //
688 // https://zips.z.cash/protocol/protocol.pdf#txnconsensus
689 //
690 // Zebra does not verify this rule because we checkpoint up to Canopy blocks, but:
691 // Since transactions must get mined into a block to be useful,
692 // we reject transactions that are larger than blocks.
693 //
694 // If the limit is reached, we'll get an UnexpectedEof error.
695 let mut limited_reader = reader.take(MAX_BLOCK_BYTES);
696
697 let (version, overwintered) = {
698 const LOW_31_BITS: u32 = (1 << 31) - 1;
699 // Denoted as `header` in the spec, contains the `fOverwintered` flag and the `version` field.
700 let header = limited_reader.read_u32::<LittleEndian>()?;
701 (header & LOW_31_BITS, header >> 31 != 0)
702 };
703
704 // # Consensus
705 //
706 // The next rules apply for different transaction versions as follows:
707 //
708 // [Pre-Overwinter]: Transactions version 1 and 2.
709 // [Overwinter onward]: Transactions version 3 and above.
710 // [Overwinter only, pre-Sapling]: Transactions version 3.
711 // [Sapling to Canopy inclusive, pre-NU5]: Transactions version 4.
712 // [NU5 onward]: Transactions version 4 and above.
713 //
714 // > The transaction version number MUST be greater than or equal to 1.
715 //
716 // > [Pre-Overwinter] The fOverwintered fag MUST NOT be set.
717 //
718 // > [Overwinter onward] The version group ID MUST be recognized.
719 //
720 // > [Overwinter onward] The fOverwintered flag MUST be set.
721 //
722 // > [Overwinter only, pre-Sapling] The transaction version number MUST be 3,
723 // > and the version group ID MUST be 0x03C48270.
724 //
725 // > [Sapling to Canopy inclusive, pre-NU5] The transaction version number MUST be 4,
726 // > and the version group ID MUST be 0x892F2085.
727 //
728 // > [NU5 onward] The transaction version number MUST be 4 or 5.
729 // > If the transaction version number is 4 then the version group ID MUST be 0x892F2085.
730 // > If the transaction version number is 5 then the version group ID MUST be 0x26A7270A.
731 //
732 // Note: Zebra checkpoints until Canopy blocks, this means only transactions versions
733 // 4 and 5 get fully verified. This satisfies "The transaction version number MUST be 4"
734 // and "The transaction version number MUST be 4 or 5" from the last two rules above.
735 // This is done in the zebra-consensus crate, in the transactions checks.
736 //
737 // https://zips.z.cash/protocol/protocol.pdf#txnconsensus
738 match (version, overwintered) {
739 (1, false) => Ok(Transaction::V1 {
740 // Denoted as `tx_in_count` and `tx_in` in the spec.
741 inputs: Vec::zcash_deserialize(&mut limited_reader)?,
742 // Denoted as `tx_out_count` and `tx_out` in the spec.
743 outputs: Vec::zcash_deserialize(&mut limited_reader)?,
744 // Denoted as `lock_time` in the spec.
745 lock_time: LockTime::zcash_deserialize(&mut limited_reader)?,
746 }),
747 (2, false) => {
748 // Version 2 transactions use Sprout-on-BCTV14.
749 type OptV2Jsd = Option<JoinSplitData<Bctv14Proof>>;
750 Ok(Transaction::V2 {
751 // Denoted as `tx_in_count` and `tx_in` in the spec.
752 inputs: Vec::zcash_deserialize(&mut limited_reader)?,
753 // Denoted as `tx_out_count` and `tx_out` in the spec.
754 outputs: Vec::zcash_deserialize(&mut limited_reader)?,
755 // Denoted as `lock_time` in the spec.
756 lock_time: LockTime::zcash_deserialize(&mut limited_reader)?,
757 // A bundle of fields denoted in the spec as `nJoinSplit`, `vJoinSplit`,
758 // `joinSplitPubKey` and `joinSplitSig`.
759 joinsplit_data: OptV2Jsd::zcash_deserialize(&mut limited_reader)?,
760 })
761 }
762 (3, true) => {
763 // Denoted as `nVersionGroupId` in the spec.
764 let id = limited_reader.read_u32::<LittleEndian>()?;
765 if id != OVERWINTER_VERSION_GROUP_ID {
766 return Err(SerializationError::Parse(
767 "expected OVERWINTER_VERSION_GROUP_ID",
768 ));
769 }
770 // Version 3 transactions use Sprout-on-BCTV14.
771 type OptV3Jsd = Option<JoinSplitData<Bctv14Proof>>;
772 Ok(Transaction::V3 {
773 // Denoted as `tx_in_count` and `tx_in` in the spec.
774 inputs: Vec::zcash_deserialize(&mut limited_reader)?,
775 // Denoted as `tx_out_count` and `tx_out` in the spec.
776 outputs: Vec::zcash_deserialize(&mut limited_reader)?,
777 // Denoted as `lock_time` in the spec.
778 lock_time: LockTime::zcash_deserialize(&mut limited_reader)?,
779 // Denoted as `nExpiryHeight` in the spec.
780 expiry_height: block::Height(limited_reader.read_u32::<LittleEndian>()?),
781 // A bundle of fields denoted in the spec as `nJoinSplit`, `vJoinSplit`,
782 // `joinSplitPubKey` and `joinSplitSig`.
783 joinsplit_data: OptV3Jsd::zcash_deserialize(&mut limited_reader)?,
784 })
785 }
786 (4, true) => {
787 // Transaction V4 spec:
788 // https://zips.z.cash/protocol/protocol.pdf#txnencoding
789
790 // Denoted as `nVersionGroupId` in the spec.
791 let id = limited_reader.read_u32::<LittleEndian>()?;
792 if id != SAPLING_VERSION_GROUP_ID {
793 return Err(SerializationError::Parse(
794 "expected SAPLING_VERSION_GROUP_ID",
795 ));
796 }
797 // Version 4 transactions use Sprout-on-Groth16.
798 type OptV4Jsd = Option<JoinSplitData<Groth16Proof>>;
799
800 // The previous match arms deserialize in one go, because the
801 // internal structure happens to nicely line up with the
802 // serialized structure. However, this is not possible for
803 // version 4 transactions, as the binding_sig for the
804 // ShieldedData is placed at the end of the transaction. So
805 // instead we have to pull the component parts out manually and
806 // then assemble them.
807
808 // Denoted as `tx_in_count` and `tx_in` in the spec.
809 let inputs = Vec::zcash_deserialize(&mut limited_reader)?;
810
811 // Denoted as `tx_out_count` and `tx_out` in the spec.
812 let outputs = Vec::zcash_deserialize(&mut limited_reader)?;
813
814 // Denoted as `lock_time` in the spec.
815 let lock_time = LockTime::zcash_deserialize(&mut limited_reader)?;
816
817 // Denoted as `nExpiryHeight` in the spec.
818 let expiry_height = block::Height(limited_reader.read_u32::<LittleEndian>()?);
819
820 // Denoted as `valueBalanceSapling` in the spec.
821 let value_balance = (&mut limited_reader).zcash_deserialize_into()?;
822
823 // Denoted as `nSpendsSapling` and `vSpendsSapling` in the spec.
824 let shielded_spends = Vec::zcash_deserialize(&mut limited_reader)?;
825
826 // Denoted as `nOutputsSapling` and `vOutputsSapling` in the spec.
827 let shielded_outputs =
828 Vec::<sapling::OutputInTransactionV4>::zcash_deserialize(&mut limited_reader)?
829 .into_iter()
830 .map(sapling::Output::from_v4)
831 .collect();
832
833 // A bundle of fields denoted in the spec as `nJoinSplit`, `vJoinSplit`,
834 // `joinSplitPubKey` and `joinSplitSig`.
835 let joinsplit_data = OptV4Jsd::zcash_deserialize(&mut limited_reader)?;
836
837 let sapling_transfers = if !shielded_spends.is_empty() {
838 Some(sapling::TransferData::SpendsAndMaybeOutputs {
839 shared_anchor: FieldNotPresent,
840 spends: shielded_spends.try_into().expect("checked for spends"),
841 maybe_outputs: shielded_outputs,
842 })
843 } else if !shielded_outputs.is_empty() {
844 Some(sapling::TransferData::JustOutputs {
845 outputs: shielded_outputs.try_into().expect("checked for outputs"),
846 })
847 } else {
848 // # Consensus
849 //
850 // > [Sapling onward] If effectiveVersion = 4 and there are no Spend
851 // > descriptions or Output descriptions, then valueBalanceSapling MUST be 0.
852 //
853 // https://zips.z.cash/protocol/protocol.pdf#txnconsensus
854 if value_balance != 0 {
855 return Err(SerializationError::BadTransactionBalance);
856 }
857 None
858 };
859
860 let sapling_shielded_data = match sapling_transfers {
861 Some(transfers) => Some(sapling::ShieldedData {
862 value_balance,
863 transfers,
864 // Denoted as `bindingSigSapling` in the spec.
865 binding_sig: limited_reader.read_64_bytes()?.into(),
866 }),
867 None => None,
868 };
869
870 Ok(Transaction::V4 {
871 inputs,
872 outputs,
873 lock_time,
874 expiry_height,
875 sapling_shielded_data,
876 joinsplit_data,
877 })
878 }
879 (5, true) => {
880 // Transaction V5 spec:
881 // https://zips.z.cash/protocol/protocol.pdf#txnencoding
882
883 // Denoted as `nVersionGroupId` in the spec.
884 let id = limited_reader.read_u32::<LittleEndian>()?;
885 if id != TX_V5_VERSION_GROUP_ID {
886 return Err(SerializationError::Parse("expected TX_V5_VERSION_GROUP_ID"));
887 }
888 // Denoted as `nConsensusBranchId` in the spec.
889 // Convert it to a NetworkUpgrade
890 let network_upgrade =
891 NetworkUpgrade::try_from(limited_reader.read_u32::<LittleEndian>()?)?;
892
893 // Denoted as `lock_time` in the spec.
894 let lock_time = LockTime::zcash_deserialize(&mut limited_reader)?;
895
896 // Denoted as `nExpiryHeight` in the spec.
897 let expiry_height = block::Height(limited_reader.read_u32::<LittleEndian>()?);
898
899 // Denoted as `tx_in_count` and `tx_in` in the spec.
900 let inputs = Vec::zcash_deserialize(&mut limited_reader)?;
901
902 // Denoted as `tx_out_count` and `tx_out` in the spec.
903 let outputs = Vec::zcash_deserialize(&mut limited_reader)?;
904
905 // A bundle of fields denoted in the spec as `nSpendsSapling`, `vSpendsSapling`,
906 // `nOutputsSapling`,`vOutputsSapling`, `valueBalanceSapling`, `anchorSapling`,
907 // `vSpendProofsSapling`, `vSpendAuthSigsSapling`, `vOutputProofsSapling` and
908 // `bindingSigSapling`.
909 let sapling_shielded_data = (&mut limited_reader).zcash_deserialize_into()?;
910
911 // A bundle of fields denoted in the spec as `nActionsOrchard`, `vActionsOrchard`,
912 // `flagsOrchard`,`valueBalanceOrchard`, `anchorOrchard`, `sizeProofsOrchard`,
913 // `proofsOrchard`, `vSpendAuthSigsOrchard`, and `bindingSigOrchard`.
914 let orchard_shielded_data = (&mut limited_reader).zcash_deserialize_into()?;
915
916 Ok(Transaction::V5 {
917 network_upgrade,
918 lock_time,
919 expiry_height,
920 inputs,
921 outputs,
922 sapling_shielded_data,
923 orchard_shielded_data,
924 })
925 }
926 (_, _) => Err(SerializationError::Parse("bad tx header")),
927 }
928 }
929}
930
931impl<T> ZcashDeserialize for Arc<T>
932where
933 T: ZcashDeserialize,
934{
935 fn zcash_deserialize<R: io::Read>(reader: R) -> Result<Self, SerializationError> {
936 Ok(Arc::new(T::zcash_deserialize(reader)?))
937 }
938}
939
940impl<T> ZcashSerialize for Arc<T>
941where
942 T: ZcashSerialize,
943{
944 fn zcash_serialize<W: io::Write>(&self, writer: W) -> Result<(), io::Error> {
945 T::zcash_serialize(self, writer)
946 }
947}
948
949/// A Tx Input must have an Outpoint (32 byte hash + 4 byte index), a 4 byte sequence number,
950/// and a signature script, which always takes a min of 1 byte (for a length 0 script).
951pub(crate) const MIN_TRANSPARENT_INPUT_SIZE: u64 = 32 + 4 + 4 + 1;
952
953/// A Transparent output has an 8 byte value and script which takes a min of 1 byte.
954pub(crate) const MIN_TRANSPARENT_OUTPUT_SIZE: u64 = 8 + 1;
955
956/// All txs must have at least one input, a 4 byte locktime, and at least one output.
957///
958/// Shielded transfers are much larger than transparent transfers,
959/// so this is the minimum transaction size.
960pub const MIN_TRANSPARENT_TX_SIZE: u64 =
961 MIN_TRANSPARENT_INPUT_SIZE + 4 + MIN_TRANSPARENT_OUTPUT_SIZE;
962
963/// The minimum transaction size for v4 transactions.
964///
965/// v4 transactions also have an expiry height.
966pub const MIN_TRANSPARENT_TX_V4_SIZE: u64 = MIN_TRANSPARENT_TX_SIZE + 4;
967
968/// The minimum transaction size for v5 transactions.
969///
970/// v5 transactions also have an expiry height and a consensus branch ID.
971pub const MIN_TRANSPARENT_TX_V5_SIZE: u64 = MIN_TRANSPARENT_TX_SIZE + 4 + 4;
972
973/// No valid Zcash message contains more transactions than can fit in a single block
974///
975/// `tx` messages contain a single transaction, and `block` messages are limited to the maximum
976/// block size.
977impl TrustedPreallocate for Transaction {
978 fn max_allocation() -> u64 {
979 // A transparent transaction is the smallest transaction variant
980 MAX_BLOCK_BYTES / MIN_TRANSPARENT_TX_SIZE
981 }
982}
983
984/// The maximum number of inputs in a valid Zcash on-chain transaction.
985///
986/// If a transaction contains more inputs than can fit in maximally large block, it might be
987/// valid on the network and in the mempool, but it can never be mined into a block. So
988/// rejecting these large edge-case transactions can never break consensus.
989impl TrustedPreallocate for transparent::Input {
990 fn max_allocation() -> u64 {
991 MAX_BLOCK_BYTES / MIN_TRANSPARENT_INPUT_SIZE
992 }
993}
994
995/// The maximum number of outputs in a valid Zcash on-chain transaction.
996///
997/// If a transaction contains more outputs than can fit in maximally large block, it might be
998/// valid on the network and in the mempool, but it can never be mined into a block. So
999/// rejecting these large edge-case transactions can never break consensus.
1000impl TrustedPreallocate for transparent::Output {
1001 fn max_allocation() -> u64 {
1002 MAX_BLOCK_BYTES / MIN_TRANSPARENT_OUTPUT_SIZE
1003 }
1004}
1005
1006/// A serialized transaction.
1007///
1008/// Stores bytes that are guaranteed to be deserializable into a [`Transaction`].
1009///
1010/// Sorts in lexicographic order of the transaction's serialized data.
1011#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
1012pub struct SerializedTransaction {
1013 bytes: Vec<u8>,
1014}
1015
1016impl fmt::Display for SerializedTransaction {
1017 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1018 f.write_str(&hex::encode(&self.bytes))
1019 }
1020}
1021
1022impl fmt::Debug for SerializedTransaction {
1023 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1024 // A transaction with a lot of transfers can be extremely long in logs.
1025 let mut data_truncated = hex::encode(&self.bytes);
1026 if data_truncated.len() > 1003 {
1027 let end = data_truncated.len() - 500;
1028 // Replace the middle bytes with "...", but leave 500 bytes on either side.
1029 // The data is hex, so this replacement won't panic.
1030 data_truncated.replace_range(500..=end, "...");
1031 }
1032
1033 f.debug_tuple("SerializedTransaction")
1034 .field(&data_truncated)
1035 .finish()
1036 }
1037}
1038
1039/// Build a [`SerializedTransaction`] by serializing a block.
1040impl<B: Borrow<Transaction>> From<B> for SerializedTransaction {
1041 fn from(tx: B) -> Self {
1042 SerializedTransaction {
1043 bytes: tx
1044 .borrow()
1045 .zcash_serialize_to_vec()
1046 .expect("Writing to a `Vec` should never fail"),
1047 }
1048 }
1049}
1050
1051/// Access the serialized bytes of a [`SerializedTransaction`].
1052impl AsRef<[u8]> for SerializedTransaction {
1053 fn as_ref(&self) -> &[u8] {
1054 self.bytes.as_ref()
1055 }
1056}
1057
1058impl From<Vec<u8>> for SerializedTransaction {
1059 fn from(bytes: Vec<u8>) -> Self {
1060 Self { bytes }
1061 }
1062}
1063
1064impl FromHex for SerializedTransaction {
1065 type Error = <Vec<u8> as FromHex>::Error;
1066
1067 fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
1068 let bytes = <Vec<u8>>::from_hex(hex)?;
1069
1070 Ok(bytes.into())
1071 }
1072}