1use std::{borrow::Borrow, io, sync::Arc};
5
6use halo2::pasta::{group::ff::PrimeField, pallas};
7use hex::FromHex;
8
9use crate::{
10 block::MAX_BLOCK_BYTES,
11 primitives::ZkSnarkProof,
12 serialization::{
13 ReadZcashExt, SerializationError, TrustedPreallocate, ZcashDeserialize,
14 ZcashDeserializeInto, ZcashSerialize,
15 },
16};
17
18use super::*;
19use crate::sprout;
20
21#[cfg(any(test, feature = "proptest-impl"))]
23use crate::{
24 amount, ironwood, orchard,
25 primitives::Halo2Proof,
26 sapling,
27 serialization::{
28 zcash_deserialize_external_count, zcash_serialize_empty_list,
29 zcash_serialize_external_count, AtLeastOne, CompactSizeMessage,
30 },
31};
32#[cfg(any(test, feature = "proptest-impl"))]
33use reddsa::{orchard::Binding, orchard::SpendAuth, Signature};
34
35impl ZcashDeserialize for jubjub::Fq {
36 fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
37 let possible_scalar = jubjub::Fq::from_bytes(&reader.read_32_bytes()?);
38
39 if possible_scalar.is_some().into() {
40 Ok(possible_scalar.unwrap())
41 } else {
42 Err(SerializationError::Parse(
43 "Invalid jubjub::Fq, input not canonical",
44 ))
45 }
46 }
47}
48
49impl ZcashDeserialize for pallas::Scalar {
50 fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
51 let possible_scalar = pallas::Scalar::from_repr(reader.read_32_bytes()?);
52
53 if possible_scalar.is_some().into() {
54 Ok(possible_scalar.unwrap())
55 } else {
56 Err(SerializationError::Parse(
57 "Invalid pallas::Scalar, input not canonical",
58 ))
59 }
60 }
61}
62
63impl ZcashDeserialize for pallas::Base {
64 fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
65 let possible_field_element = pallas::Base::from_repr(reader.read_32_bytes()?);
66
67 if possible_field_element.is_some().into() {
68 Ok(possible_field_element.unwrap())
69 } else {
70 Err(SerializationError::Parse(
71 "Invalid pallas::Base, input not canonical",
72 ))
73 }
74 }
75}
76
77impl<P: ZkSnarkProof> ZcashSerialize for JoinSplitData<P> {
78 fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
79 let joinsplits: Vec<_> = self.joinsplits().cloned().collect();
81 joinsplits.zcash_serialize(&mut writer)?;
82
83 writer.write_all(&<[u8; 32]>::from(self.pub_key)[..])?;
85
86 writer.write_all(&<[u8; 64]>::from(self.sig)[..])?;
88 Ok(())
89 }
90}
91
92impl<P> ZcashDeserialize for Option<JoinSplitData<P>>
93where
94 P: ZkSnarkProof,
95 sprout::JoinSplit<P>: TrustedPreallocate,
96{
97 fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
98 let joinsplits: Vec<sprout::JoinSplit<P>> = (&mut reader).zcash_deserialize_into()?;
100 match joinsplits.split_first() {
101 None => Ok(None),
102 Some((first, rest)) => {
103 let pub_key = reader.read_32_bytes()?.into();
105 let sig = reader.read_64_bytes()?.into();
107 Ok(Some(JoinSplitData {
108 first: first.clone(),
109 rest: rest.to_vec(),
110 pub_key,
111 sig,
112 }))
113 }
114 }
115 }
116}
117
118#[cfg(any(test, feature = "proptest-impl"))]
128impl ZcashSerialize for Option<sapling::ShieldedData<sapling::PerSpendAnchor>> {
129 fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
130 match self {
131 None => {
132 zcash_serialize_empty_list(&mut writer)?;
133 zcash_serialize_empty_list(&mut writer)?;
134 }
135 Some(sd) => {
136 sd.zcash_serialize(&mut writer)?;
137 }
138 }
139 Ok(())
140 }
141}
142
143#[cfg(any(test, feature = "proptest-impl"))]
144impl ZcashSerialize for sapling::ShieldedData<sapling::PerSpendAnchor> {
145 fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
146 let spends: Vec<_> = self.spends().cloned().collect();
147 let outputs: Vec<_> = self
148 .outputs()
149 .cloned()
150 .map(sapling::Output::into_v4)
151 .collect();
152
153 spends.zcash_serialize(&mut writer)?;
154 outputs.zcash_serialize(&mut writer)?;
155 self.value_balance.zcash_serialize(&mut writer)?;
156 writer.write_all(&<[u8; 64]>::from(self.binding_sig)[..])?;
157 Ok(())
158 }
159}
160
161#[cfg(any(test, feature = "proptest-impl"))]
162impl ZcashDeserialize for Option<sapling::ShieldedData<sapling::PerSpendAnchor>> {
163 fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
164 let spends: Vec<sapling::Spend<sapling::PerSpendAnchor>> =
165 (&mut reader).zcash_deserialize_into()?;
166 let outputs: Vec<sapling::OutputInTransactionV4> =
167 (&mut reader).zcash_deserialize_into()?;
168
169 if spends.is_empty() && outputs.is_empty() {
170 return Ok(None);
171 }
172
173 let value_balance = (&mut reader).zcash_deserialize_into()?;
174 let binding_sig = reader.read_64_bytes()?.into();
175
176 let outputs: Vec<sapling::Output> = outputs
177 .into_iter()
178 .map(sapling::OutputInTransactionV4::into_output)
179 .collect();
180
181 let transfers = match spends.split_first() {
182 None => sapling::TransferData::JustOutputs {
183 outputs: outputs.try_into().map_err(|_| {
184 SerializationError::Parse(
185 "ShieldedData<PerSpendAnchor> with no spends or outputs",
186 )
187 })?,
188 },
189 Some((first, rest)) => sapling::TransferData::SpendsAndMaybeOutputs {
190 shared_anchor: sapling::FieldNotPresent,
191 spends: std::iter::once(first.clone())
192 .chain(rest.iter().cloned())
193 .collect::<Vec<_>>()
194 .try_into()
195 .map_err(|_| {
196 SerializationError::Parse("ShieldedData<PerSpendAnchor> spend list empty")
197 })?,
198 maybe_outputs: outputs,
199 },
200 };
201
202 Ok(Some(sapling::ShieldedData {
203 value_balance,
204 transfers,
205 binding_sig,
206 }))
207 }
208}
209
210#[cfg(any(test, feature = "proptest-impl"))]
215impl ZcashSerialize for Option<sapling::ShieldedData<sapling::SharedAnchor>> {
216 fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
217 match self {
218 None => {
219 zcash_serialize_empty_list(&mut writer)?;
221 zcash_serialize_empty_list(&mut writer)?;
223 }
224 Some(sapling_shielded_data) => {
225 sapling_shielded_data.zcash_serialize(&mut writer)?;
226 }
227 }
228 Ok(())
229 }
230}
231
232#[cfg(any(test, feature = "proptest-impl"))]
233impl ZcashSerialize for sapling::ShieldedData<sapling::SharedAnchor> {
234 fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
235 let (spend_prefixes, spend_proofs_sigs): (Vec<_>, Vec<_>) = self
238 .spends()
239 .cloned()
240 .map(sapling::Spend::<sapling::SharedAnchor>::into_v5_parts)
241 .map(|(prefix, proof, sig)| (prefix, (proof, sig)))
242 .unzip();
243 let (spend_proofs, spend_sigs) = spend_proofs_sigs.into_iter().unzip();
244
245 let (output_prefixes, output_proofs): (Vec<_>, _) = self
247 .outputs()
248 .cloned()
249 .map(sapling::Output::into_v5_parts)
250 .unzip();
251
252 spend_prefixes.zcash_serialize(&mut writer)?;
254 output_prefixes.zcash_serialize(&mut writer)?;
256
257 self.value_balance.zcash_serialize(&mut writer)?;
259
260 if let Some(shared_anchor) = self.shared_anchor() {
264 writer.write_all(&<[u8; 32]>::from(shared_anchor)[..])?;
265 }
266
267 zcash_serialize_external_count(&spend_proofs, &mut writer)?;
269 zcash_serialize_external_count(&spend_sigs, &mut writer)?;
271
272 zcash_serialize_external_count(&output_proofs, &mut writer)?;
274
275 writer.write_all(&<[u8; 64]>::from(self.binding_sig)[..])?;
277
278 Ok(())
279 }
280}
281
282#[cfg(any(test, feature = "proptest-impl"))]
285impl ZcashDeserialize for Option<sapling::ShieldedData<sapling::SharedAnchor>> {
286 #[allow(clippy::unwrap_in_result)]
287 fn zcash_deserialize<R: io::Read>(reader: R) -> Result<Self, SerializationError> {
288 deserialize_v5_sapling_shielded_data(reader, false)
289 }
290}
291
292#[allow(clippy::unwrap_in_result)]
299#[cfg(any(test, feature = "proptest-impl"))]
300fn deserialize_v5_sapling_shielded_data<R: io::Read>(
301 mut reader: R,
302 is_coinbase: bool,
303) -> Result<Option<sapling::ShieldedData<sapling::SharedAnchor>>, SerializationError> {
304 let spend_count: CompactSizeMessage = (&mut reader).zcash_deserialize_into()?;
306 let spend_count: usize = spend_count.into();
307
308 if is_coinbase && spend_count > 0 {
317 return Err(SerializationError::Parse(
318 "coinbase transaction must not have Sapling spends",
319 ));
320 }
321
322 let spend_prefixes: Vec<sapling::SpendPrefixInTransactionV5> =
324 zcash_deserialize_external_count(spend_count, &mut reader)?;
325
326 let output_prefixes: Vec<_> = (&mut reader).zcash_deserialize_into()?;
328
329 let spends_count = spend_prefixes.len();
331 let outputs_count = output_prefixes.len();
332
333 if spend_prefixes.is_empty() && output_prefixes.is_empty() {
335 return Ok(None);
336 }
337
338 let value_balance = (&mut reader).zcash_deserialize_into()?;
340
341 let shared_anchor = if spends_count > 0 {
357 Some((&mut reader).zcash_deserialize_into()?)
358 } else {
359 None
360 };
361
362 let spend_proofs = zcash_deserialize_external_count(spends_count, &mut reader)?;
376
377 let spend_sigs = zcash_deserialize_external_count(spends_count, &mut reader)?;
390
391 let output_proofs = zcash_deserialize_external_count(outputs_count, &mut reader)?;
405
406 let binding_sig = reader.read_64_bytes()?.into();
408
409 let spends: Vec<_> = spend_prefixes
411 .into_iter()
412 .zip(spend_proofs)
413 .zip(spend_sigs)
414 .map(|((prefix, proof), sig)| {
415 sapling::Spend::<sapling::SharedAnchor>::from_v5_parts(prefix, proof, sig)
416 })
417 .collect();
418
419 let outputs = output_prefixes
421 .into_iter()
422 .zip(output_proofs)
423 .map(|(prefix, proof)| sapling::Output::from_v5_parts(prefix, proof))
424 .collect();
425
426 let transfers = match shared_anchor {
443 Some(shared_anchor) => sapling::TransferData::SpendsAndMaybeOutputs {
444 shared_anchor,
445 spends: spends
446 .try_into()
447 .expect("checked spends when parsing shared anchor"),
448 maybe_outputs: outputs,
449 },
450 None => sapling::TransferData::JustOutputs {
451 outputs: outputs
452 .try_into()
453 .expect("checked spends or outputs and returned early"),
454 },
455 };
456
457 Ok(Some(sapling::ShieldedData {
458 value_balance,
459 transfers,
460 binding_sig,
461 }))
462}
463
464#[cfg(any(test, feature = "proptest-impl"))]
473fn zcash_serialize_optional_orchard_bundle<W: io::Write>(
474 shielded_data: Option<&orchard::ShieldedData>,
475 mut writer: W,
476) -> Result<(), io::Error> {
477 match shielded_data {
478 None => zcash_serialize_empty_list(&mut writer),
480 Some(shielded_data) => shielded_data.zcash_serialize(&mut writer),
481 }
482}
483
484#[cfg(any(test, feature = "proptest-impl"))]
485impl ZcashSerialize for Option<orchard::ShieldedData> {
486 fn zcash_serialize<W: io::Write>(&self, writer: W) -> Result<(), io::Error> {
487 zcash_serialize_optional_orchard_bundle(self.as_ref(), writer)
488 }
489}
490
491#[cfg(any(test, feature = "proptest-impl"))]
492impl ZcashSerialize for orchard::ShieldedData {
493 fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
494 let (actions, sigs): (Vec<orchard::Action>, Vec<Signature<SpendAuth>>) = self
496 .actions
497 .iter()
498 .cloned()
499 .map(orchard::AuthorizedAction::into_parts)
500 .unzip();
501
502 actions.zcash_serialize(&mut writer)?;
504
505 self.flags.zcash_serialize(&mut writer)?;
507
508 self.value_balance.zcash_serialize(&mut writer)?;
510
511 self.shared_anchor.zcash_serialize(&mut writer)?;
513
514 self.proof.zcash_serialize(&mut writer)?;
516
517 zcash_serialize_external_count(&sigs, &mut writer)?;
519
520 self.binding_sig.zcash_serialize(&mut writer)?;
522
523 Ok(())
524 }
525}
526
527#[cfg(any(test, feature = "proptest-impl"))]
536trait V6FlagCodec {
537 type Codec: ZcashDeserialize + Into<orchard::Flags>;
539}
540
541#[cfg(any(test, feature = "proptest-impl"))]
542impl V6FlagCodec for orchard::ShieldedDataV6 {
543 type Codec = orchard::Flags;
545}
546
547#[cfg(any(test, feature = "proptest-impl"))]
548impl V6FlagCodec for ironwood::ShieldedData {
549 type Codec = orchard::FlagsV6;
551}
552
553#[cfg(any(test, feature = "proptest-impl"))]
556fn deserialize_v6_orchard_shielded_data<R, T>(
557 reader: R,
558) -> Result<Option<orchard::ShieldedData>, SerializationError>
559where
560 R: io::Read,
561 T: V6FlagCodec,
562{
563 deserialize_orchard_shielded_data::<R, T::Codec>(reader)
564}
565
566#[cfg(any(test, feature = "proptest-impl"))]
567impl ZcashDeserialize for Option<orchard::ShieldedDataV6> {
568 fn zcash_deserialize<R: io::Read>(reader: R) -> Result<Self, SerializationError> {
569 Ok(
570 deserialize_v6_orchard_shielded_data::<R, orchard::ShieldedDataV6>(reader)?
571 .map(orchard::ShieldedDataV6::new),
572 )
573 }
574}
575
576#[cfg(any(test, feature = "proptest-impl"))]
577impl ZcashSerialize for Option<orchard::ShieldedDataV6> {
578 fn zcash_serialize<W: io::Write>(&self, writer: W) -> Result<(), io::Error> {
579 zcash_serialize_optional_orchard_bundle(self.as_ref().map(|data| data.data()), writer)
580 }
581}
582
583#[cfg(any(test, feature = "proptest-impl"))]
584impl ZcashDeserialize for Option<ironwood::ShieldedData> {
585 fn zcash_deserialize<R: io::Read>(reader: R) -> Result<Self, SerializationError> {
586 Ok(
587 deserialize_v6_orchard_shielded_data::<R, ironwood::ShieldedData>(reader)?
588 .map(orchard::ShieldedDataV6::new)
589 .map(ironwood::ShieldedData::new),
590 )
591 }
592}
593
594#[cfg(any(test, feature = "proptest-impl"))]
595impl ZcashSerialize for Option<ironwood::ShieldedData> {
596 fn zcash_serialize<W: io::Write>(&self, writer: W) -> Result<(), io::Error> {
597 zcash_serialize_optional_orchard_bundle(self.as_ref().map(|data| data.data()), writer)
598 }
599}
600
601#[cfg(any(test, feature = "proptest-impl"))]
604impl ZcashDeserialize for Option<orchard::ShieldedData> {
605 fn zcash_deserialize<R: io::Read>(reader: R) -> Result<Self, SerializationError> {
606 deserialize_orchard_shielded_data::<R, orchard::Flags>(reader)
610 }
611}
612
613#[cfg(any(test, feature = "proptest-impl"))]
618pub(crate) fn deserialize_orchard_shielded_data<R, F>(
619 mut reader: R,
620) -> Result<Option<orchard::ShieldedData>, SerializationError>
621where
622 R: io::Read,
623 F: ZcashDeserialize + Into<orchard::Flags>,
624{
625 let actions: Vec<orchard::Action> = (&mut reader).zcash_deserialize_into()?;
627
628 if actions.is_empty() {
632 return Ok(None);
633 }
634
635 let flags: orchard::Flags = F::zcash_deserialize(&mut reader)?.into();
646
647 let value_balance: amount::Amount = (&mut reader).zcash_deserialize_into()?;
649
650 let shared_anchor: orchard::tree::Root = (&mut reader).zcash_deserialize_into()?;
653
654 let proof: Halo2Proof = (&mut reader).zcash_deserialize_into()?;
658
659 let sigs: Vec<Signature<SpendAuth>> =
665 zcash_deserialize_external_count(actions.len(), &mut reader)?;
666
667 let binding_sig: Signature<Binding> = (&mut reader).zcash_deserialize_into()?;
669
670 let authorized_actions: Vec<orchard::AuthorizedAction> = actions
672 .into_iter()
673 .zip(sigs)
674 .map(|(action, spend_auth_sig)| {
675 orchard::AuthorizedAction::from_parts(action, spend_auth_sig)
676 })
677 .collect();
678
679 let actions: AtLeastOne<orchard::AuthorizedAction> = authorized_actions.try_into()?;
680
681 Ok(Some(orchard::ShieldedData {
682 flags,
683 value_balance,
684 shared_anchor,
685 proof,
686 actions,
687 binding_sig,
688 }))
689}
690
691impl<T: reddsa::SigType> ZcashSerialize for reddsa::Signature<T> {
692 fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
693 writer.write_all(&<[u8; 64]>::from(*self)[..])?;
694 Ok(())
695 }
696}
697
698impl<T: reddsa::SigType> ZcashDeserialize for reddsa::Signature<T> {
699 fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
700 Ok(reader.read_64_bytes()?.into())
701 }
702}
703
704impl<T> ZcashDeserialize for Arc<T>
705where
706 T: ZcashDeserialize,
707{
708 fn zcash_deserialize<R: io::Read>(reader: R) -> Result<Self, SerializationError> {
709 Ok(Arc::new(T::zcash_deserialize(reader)?))
710 }
711}
712
713impl<T> ZcashSerialize for Arc<T>
714where
715 T: ZcashSerialize,
716{
717 fn zcash_serialize<W: io::Write>(&self, writer: W) -> Result<(), io::Error> {
718 T::zcash_serialize(self, writer)
719 }
720}
721
722pub(crate) const MIN_TRANSPARENT_INPUT_SIZE: u64 = 32 + 4 + 4 + 1;
725
726pub(crate) const MIN_TRANSPARENT_OUTPUT_SIZE: u64 = 8 + 1;
728
729pub const MIN_TRANSPARENT_TX_SIZE: u64 =
734 MIN_TRANSPARENT_INPUT_SIZE + 4 + MIN_TRANSPARENT_OUTPUT_SIZE;
735
736pub const MIN_TRANSPARENT_TX_V4_SIZE: u64 = MIN_TRANSPARENT_TX_SIZE + 4;
740
741pub const MIN_TRANSPARENT_TX_V5_SIZE: u64 = MIN_TRANSPARENT_TX_SIZE + 4 + 4;
745
746impl TrustedPreallocate for Transaction {
751 fn max_allocation() -> u64 {
752 MAX_BLOCK_BYTES / MIN_TRANSPARENT_TX_SIZE
754 }
755}
756
757impl TrustedPreallocate for transparent::Input {
763 fn max_allocation() -> u64 {
764 MAX_BLOCK_BYTES / MIN_TRANSPARENT_INPUT_SIZE
765 }
766}
767
768impl TrustedPreallocate for transparent::Output {
774 fn max_allocation() -> u64 {
775 MAX_BLOCK_BYTES / MIN_TRANSPARENT_OUTPUT_SIZE
776 }
777}
778
779#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
785pub struct SerializedTransaction {
786 bytes: Vec<u8>,
787}
788
789impl fmt::Display for SerializedTransaction {
790 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
791 f.write_str(&hex::encode(&self.bytes))
792 }
793}
794
795impl fmt::Debug for SerializedTransaction {
796 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
797 let mut data_truncated = hex::encode(&self.bytes);
799 if data_truncated.len() > 1003 {
800 let end = data_truncated.len() - 500;
801 data_truncated.replace_range(500..=end, "...");
804 }
805
806 f.debug_tuple("SerializedTransaction")
807 .field(&data_truncated)
808 .finish()
809 }
810}
811
812impl<B: Borrow<Transaction>> From<B> for SerializedTransaction {
814 fn from(tx: B) -> Self {
815 SerializedTransaction {
816 bytes: tx
817 .borrow()
818 .zcash_serialize_to_vec()
819 .expect("Writing to a `Vec` should never fail"),
820 }
821 }
822}
823
824impl AsRef<[u8]> for SerializedTransaction {
826 fn as_ref(&self) -> &[u8] {
827 self.bytes.as_ref()
828 }
829}
830
831impl From<Vec<u8>> for SerializedTransaction {
832 fn from(bytes: Vec<u8>) -> Self {
833 Self { bytes }
834 }
835}
836
837impl FromHex for SerializedTransaction {
838 type Error = <Vec<u8> as FromHex>::Error;
839
840 fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
841 let bytes = <Vec<u8>>::from_hex(hex)?;
842
843 Ok(bytes.into())
844 }
845}