1use std::sync::Arc;
4
5use crate::methods::arrayhex;
6use chrono::{DateTime, Utc};
7use derive_getters::Getters;
8use derive_new::new;
9use hex::ToHex;
10
11use zcash_script::script::Asm;
12use zebra_chain::{
13 amount::{self, Amount, NegativeOrZero, NonNegative},
14 block::{self, merkle::AUTH_DIGEST_PLACEHOLDER, Height},
15 orchard,
16 parameters::Network,
17 primitives::ed25519,
18 sapling::ValueCommitment,
19 serialization::ZcashSerialize,
20 transaction::{self, SerializedTransaction, Transaction, UnminedTx, VerifiedUnminedTx},
21 transparent::Script,
22};
23use zebra_script::Sigops;
24use zebra_state::IntoDisk;
25
26use super::super::opthex;
27use super::zec::Zec;
28
29#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
31#[serde(bound = "FeeConstraint: amount::Constraint + Clone")]
32pub struct TransactionTemplate<FeeConstraint>
33where
34 FeeConstraint: amount::Constraint + Clone + Copy,
35{
36 #[serde(with = "hex")]
38 pub(crate) data: SerializedTransaction,
39
40 #[serde(with = "hex")]
42 #[getter(copy)]
43 pub(crate) hash: transaction::Hash,
44
45 #[serde(rename = "authdigest")]
47 #[serde(with = "hex")]
48 #[getter(copy)]
49 pub(crate) auth_digest: transaction::AuthDigest,
50
51 pub(crate) depends: Vec<u16>,
58
59 #[getter(copy)]
65 pub(crate) fee: Amount<FeeConstraint>,
66
67 pub(crate) sigops: u32,
69
70 pub(crate) required: bool,
74}
75
76impl From<&VerifiedUnminedTx> for TransactionTemplate<NonNegative> {
78 fn from(tx: &VerifiedUnminedTx) -> Self {
79 assert!(
80 !tx.transaction.transaction.is_coinbase(),
81 "unexpected coinbase transaction in mempool"
82 );
83
84 Self {
85 data: tx.transaction.transaction.as_ref().into(),
86 hash: tx.transaction.id.mined_id(),
87 auth_digest: tx
88 .transaction
89 .id
90 .auth_digest()
91 .unwrap_or(AUTH_DIGEST_PLACEHOLDER),
92
93 depends: Vec::new(),
95
96 fee: tx.miner_fee,
97
98 sigops: tx.sigops,
99
100 required: false,
102 }
103 }
104}
105
106impl From<VerifiedUnminedTx> for TransactionTemplate<NonNegative> {
107 fn from(tx: VerifiedUnminedTx) -> Self {
108 Self::from(&tx)
109 }
110}
111
112impl TransactionTemplate<NegativeOrZero> {
113 pub fn from_coinbase(tx: &UnminedTx, miner_fee: Amount<NonNegative>) -> Self {
119 assert!(
120 tx.transaction.is_coinbase(),
121 "invalid generated coinbase transaction: \
122 must have exactly one input, which must be a coinbase input",
123 );
124
125 let miner_fee = (-miner_fee)
126 .constrain()
127 .expect("negating a NonNegative amount always results in a valid NegativeOrZero");
128
129 Self {
130 data: tx.transaction.as_ref().into(),
131 hash: tx.id.mined_id(),
132 auth_digest: tx.id.auth_digest().unwrap_or(AUTH_DIGEST_PLACEHOLDER),
133
134 depends: Vec::new(),
136
137 fee: miner_fee,
138
139 sigops: tx.sigops().expect("sigops count should be valid"),
140
141 required: true,
143 }
144 }
145}
146
147#[allow(clippy::too_many_arguments)]
150#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
151pub struct TransactionObject {
152 #[serde(skip_serializing_if = "Option::is_none")]
155 #[getter(copy)]
156 pub(crate) in_active_chain: Option<bool>,
157 #[serde(with = "hex")]
159 pub(crate) hex: SerializedTransaction,
160 #[serde(skip_serializing_if = "Option::is_none")]
163 #[getter(copy)]
164 pub(crate) height: Option<i32>,
165 #[serde(skip_serializing_if = "Option::is_none")]
169 #[getter(copy)]
170 pub(crate) confirmations: Option<u32>,
171
172 #[serde(rename = "vin")]
174 pub(crate) inputs: Vec<Input>,
175
176 #[serde(rename = "vout")]
178 pub(crate) outputs: Vec<Output>,
179
180 #[serde(rename = "vShieldedSpend")]
182 pub(crate) shielded_spends: Vec<ShieldedSpend>,
183
184 #[serde(rename = "vShieldedOutput")]
186 pub(crate) shielded_outputs: Vec<ShieldedOutput>,
187
188 #[serde(rename = "vjoinsplit")]
190 pub(crate) joinsplits: Vec<JoinSplit>,
191
192 #[serde(
194 skip_serializing_if = "Option::is_none",
195 with = "opthex",
196 default,
197 rename = "bindingSig"
198 )]
199 #[getter(copy)]
200 pub(crate) binding_sig: Option<[u8; 64]>,
201
202 #[serde(
204 skip_serializing_if = "Option::is_none",
205 with = "opthex",
206 default,
207 rename = "joinSplitPubKey"
208 )]
209 #[getter(copy)]
210 pub(crate) joinsplit_pub_key: Option<[u8; 32]>,
211
212 #[serde(
214 skip_serializing_if = "Option::is_none",
215 with = "opthex",
216 default,
217 rename = "joinSplitSig"
218 )]
219 #[getter(copy)]
220 pub(crate) joinsplit_sig: Option<[u8; ed25519::Signature::BYTE_SIZE]>,
221
222 #[serde(rename = "orchard", skip_serializing_if = "Option::is_none")]
224 pub(crate) orchard: Option<Orchard>,
225
226 #[serde(rename = "valueBalance", skip_serializing_if = "Option::is_none")]
228 #[getter(copy)]
229 pub(crate) value_balance: Option<f64>,
230
231 #[serde(rename = "valueBalanceZat", skip_serializing_if = "Option::is_none")]
233 #[getter(copy)]
234 pub(crate) value_balance_zat: Option<i64>,
235
236 #[serde(skip_serializing_if = "Option::is_none")]
238 #[getter(copy)]
239 pub(crate) size: Option<i64>,
240
241 #[serde(skip_serializing_if = "Option::is_none")]
243 #[getter(copy)]
244 pub(crate) time: Option<i64>,
245
246 #[serde(with = "hex")]
248 #[getter(copy)]
249 pub txid: transaction::Hash,
250
251 #[serde(
254 rename = "authdigest",
255 with = "opthex",
256 skip_serializing_if = "Option::is_none",
257 default
258 )]
259 #[getter(copy)]
260 pub(crate) auth_digest: Option<transaction::AuthDigest>,
261
262 pub(crate) overwintered: bool,
264
265 pub(crate) version: u32,
267
268 #[serde(
270 rename = "versiongroupid",
271 with = "opthex",
272 skip_serializing_if = "Option::is_none",
273 default
274 )]
275 pub(crate) version_group_id: Option<Vec<u8>>,
276
277 #[serde(rename = "locktime")]
279 pub(crate) lock_time: u32,
280
281 #[serde(rename = "expiryheight", skip_serializing_if = "Option::is_none")]
283 #[getter(copy)]
284 pub(crate) expiry_height: Option<Height>,
285
286 #[serde(
288 rename = "blockhash",
289 with = "opthex",
290 skip_serializing_if = "Option::is_none",
291 default
292 )]
293 #[getter(copy)]
294 pub(crate) block_hash: Option<block::Hash>,
295
296 #[serde(rename = "blocktime", skip_serializing_if = "Option::is_none")]
298 #[getter(copy)]
299 pub(crate) block_time: Option<i64>,
300}
301
302#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
304#[serde(untagged)]
305pub enum Input {
306 Coinbase {
308 #[serde(with = "hex")]
310 coinbase: Vec<u8>,
311 sequence: u32,
313 },
314 NonCoinbase {
316 txid: String,
318 vout: u32,
320 #[serde(rename = "scriptSig")]
322 script_sig: ScriptSig,
323 sequence: u32,
325 #[serde(skip_serializing_if = "Option::is_none")]
327 value: Option<f64>,
328 #[serde(rename = "valueSat", skip_serializing_if = "Option::is_none")]
330 value_zat: Option<i64>,
331 #[serde(skip_serializing_if = "Option::is_none")]
333 address: Option<String>,
334 },
335}
336
337#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
339pub struct Output {
340 value: f64,
342 #[serde(rename = "valueZat")]
344 value_zat: i64,
345 n: u32,
347 #[serde(rename = "scriptPubKey")]
349 script_pub_key: ScriptPubKey,
350}
351
352#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
354pub struct ScriptPubKey {
355 asm: String,
357 #[serde(with = "hex")]
359 hex: Script,
360 #[serde(rename = "reqSigs")]
362 #[serde(default)]
363 #[serde(skip_serializing_if = "Option::is_none")]
364 #[getter(copy)]
365 req_sigs: Option<u32>,
366 r#type: String,
368 #[serde(default)]
370 #[serde(skip_serializing_if = "Option::is_none")]
371 addresses: Option<Vec<String>>,
372}
373
374#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
376pub struct ScriptSig {
377 asm: String,
379 hex: Script,
381}
382
383#[allow(clippy::too_many_arguments)]
385#[serde_with::serde_as]
386#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
387pub struct JoinSplit {
388 #[serde(rename = "vpub_old")]
390 old_public_value: f64,
391 #[serde(rename = "vpub_oldZat")]
393 old_public_value_zat: i64,
394 #[serde(rename = "vpub_new")]
396 new_public_value: f64,
397 #[serde(rename = "vpub_newZat")]
399 new_public_value_zat: i64,
400 #[serde(with = "hex")]
402 #[getter(copy)]
403 anchor: [u8; 32],
404 #[serde_as(as = "Vec<serde_with::hex::Hex>")]
406 nullifiers: Vec<[u8; 32]>,
407 #[serde_as(as = "Vec<serde_with::hex::Hex>")]
409 commitments: Vec<[u8; 32]>,
410 #[serde(rename = "onetimePubKey")]
412 #[serde(with = "hex")]
413 #[getter(copy)]
414 one_time_pubkey: [u8; 32],
415 #[serde(rename = "randomSeed")]
417 #[serde(with = "hex")]
418 #[getter(copy)]
419 random_seed: [u8; 32],
420 #[serde_as(as = "Vec<serde_with::hex::Hex>")]
422 macs: Vec<[u8; 32]>,
423 #[serde(with = "hex")]
425 proof: Vec<u8>,
426 #[serde_as(as = "Vec<serde_with::hex::Hex>")]
428 ciphertexts: Vec<Vec<u8>>,
429}
430
431#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
433pub struct ShieldedSpend {
434 #[serde(with = "hex")]
436 #[getter(skip)]
437 cv: ValueCommitment,
438 #[serde(with = "hex")]
440 #[getter(copy)]
441 anchor: [u8; 32],
442 #[serde(with = "hex")]
444 #[getter(copy)]
445 nullifier: [u8; 32],
446 #[serde(with = "hex")]
448 #[getter(copy)]
449 rk: [u8; 32],
450 #[serde(with = "hex")]
452 #[getter(copy)]
453 proof: [u8; 192],
454 #[serde(rename = "spendAuthSig", with = "hex")]
456 #[getter(copy)]
457 spend_auth_sig: [u8; 64],
458}
459
460impl ShieldedSpend {
462 pub fn cv(&self) -> ValueCommitment {
464 self.cv.clone()
465 }
466}
467
468#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
470pub struct ShieldedOutput {
471 #[serde(with = "hex")]
473 #[getter(skip)]
474 cv: ValueCommitment,
475 #[serde(rename = "cmu", with = "hex")]
477 cm_u: [u8; 32],
478 #[serde(rename = "ephemeralKey", with = "hex")]
480 ephemeral_key: [u8; 32],
481 #[serde(rename = "encCiphertext", with = "arrayhex")]
483 enc_ciphertext: [u8; 580],
484 #[serde(rename = "outCiphertext", with = "hex")]
486 out_ciphertext: [u8; 80],
487 #[serde(with = "hex")]
489 proof: [u8; 192],
490}
491
492impl ShieldedOutput {
494 pub fn cv(&self) -> ValueCommitment {
496 self.cv.clone()
497 }
498}
499
500#[serde_with::serde_as]
502#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
503pub struct Orchard {
504 actions: Vec<OrchardAction>,
506 #[serde(rename = "valueBalance")]
508 value_balance: f64,
509 #[serde(rename = "valueBalanceZat")]
511 value_balance_zat: i64,
512 #[serde(skip_serializing_if = "Option::is_none")]
514 flags: Option<OrchardFlags>,
515 #[serde_as(as = "Option<serde_with::hex::Hex>")]
517 #[serde(skip_serializing_if = "Option::is_none")]
518 #[getter(copy)]
519 anchor: Option<[u8; 32]>,
520 #[serde_as(as = "Option<serde_with::hex::Hex>")]
522 #[serde(skip_serializing_if = "Option::is_none")]
523 proof: Option<Vec<u8>>,
524 #[serde(rename = "bindingSig")]
526 #[serde(skip_serializing_if = "Option::is_none")]
527 #[serde_as(as = "Option<serde_with::hex::Hex>")]
528 #[getter(copy)]
529 binding_sig: Option<[u8; 64]>,
530}
531
532#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
534pub struct OrchardFlags {
535 #[serde(rename = "enableOutputs")]
537 enable_outputs: bool,
538 #[serde(rename = "enableSpends")]
540 enable_spends: bool,
541}
542
543#[allow(clippy::too_many_arguments)]
545#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
546pub struct OrchardAction {
547 #[serde(with = "hex")]
549 cv: [u8; 32],
550 #[serde(with = "hex")]
552 nullifier: [u8; 32],
553 #[serde(with = "hex")]
555 rk: [u8; 32],
556 #[serde(rename = "cmx", with = "hex")]
558 cm_x: [u8; 32],
559 #[serde(rename = "ephemeralKey", with = "hex")]
561 ephemeral_key: [u8; 32],
562 #[serde(rename = "encCiphertext", with = "arrayhex")]
564 enc_ciphertext: [u8; 580],
565 #[serde(rename = "spendAuthSig", with = "hex")]
567 spend_auth_sig: [u8; 64],
568 #[serde(rename = "outCiphertext", with = "hex")]
570 out_ciphertext: [u8; 80],
571}
572
573impl Default for TransactionObject {
574 fn default() -> Self {
575 Self {
576 hex: SerializedTransaction::from(
577 [0u8; zebra_chain::transaction::MIN_TRANSPARENT_TX_SIZE as usize].to_vec(),
578 ),
579 height: Option::default(),
580 confirmations: Option::default(),
581 inputs: Vec::new(),
582 outputs: Vec::new(),
583 shielded_spends: Vec::new(),
584 shielded_outputs: Vec::new(),
585 joinsplits: Vec::new(),
586 orchard: None,
587 binding_sig: None,
588 joinsplit_pub_key: None,
589 joinsplit_sig: None,
590 value_balance: None,
591 value_balance_zat: None,
592 size: None,
593 time: None,
594 txid: transaction::Hash::from([0u8; 32]),
595 in_active_chain: None,
596 auth_digest: None,
597 overwintered: false,
598 version: 4,
599 version_group_id: None,
600 lock_time: 0,
601 expiry_height: None,
602 block_hash: None,
603 block_time: None,
604 }
605 }
606}
607
608impl TransactionObject {
609 #[allow(clippy::unwrap_in_result)]
611 #[allow(clippy::too_many_arguments)]
612 pub fn from_transaction(
613 tx: Arc<Transaction>,
614 height: Option<block::Height>,
615 confirmations: Option<u32>,
616 network: &Network,
617 block_time: Option<DateTime<Utc>>,
618 block_hash: Option<block::Hash>,
619 in_active_chain: Option<bool>,
620 txid: transaction::Hash,
621 ) -> Self {
622 let block_time = block_time.map(|bt| bt.timestamp());
623 Self {
624 hex: tx.clone().into(),
625 height: if in_active_chain.unwrap_or_default() {
626 height.map(|height| height.0 as i32)
627 } else if block_hash.is_some() {
628 Some(-1)
630 } else {
631 None
633 },
634 confirmations: if in_active_chain.unwrap_or_default() {
635 confirmations
636 } else if block_hash.is_some() {
637 Some(0)
639 } else {
640 None
642 },
643 inputs: tx
644 .inputs()
645 .iter()
646 .map(|input| match input {
647 zebra_chain::transparent::Input::Coinbase { sequence, .. } => Input::Coinbase {
648 coinbase: input
649 .coinbase_script()
650 .expect("we know it is a valid coinbase script"),
651 sequence: *sequence,
652 },
653 zebra_chain::transparent::Input::PrevOut {
654 sequence,
655 unlock_script,
656 outpoint,
657 } => Input::NonCoinbase {
658 txid: outpoint.hash.encode_hex(),
659 vout: outpoint.index,
660 script_sig: ScriptSig {
661 asm: zcash_script::script::Code(unlock_script.as_raw_bytes().to_vec())
662 .to_asm(false),
663 hex: unlock_script.clone(),
664 },
665 sequence: *sequence,
666 value: None,
667 value_zat: None,
668 address: None,
669 },
670 })
671 .collect(),
672 outputs: tx
673 .outputs()
674 .iter()
675 .enumerate()
676 .map(|output| {
677 let (addresses, req_sigs) = output
679 .1
680 .address(network)
681 .map(|address| (vec![address.to_string()], 1))
682 .unzip();
683
684 Output {
685 value: Zec::from(output.1.value).lossy_zec(),
686 value_zat: output.1.value.zatoshis(),
687 n: output.0 as u32,
688 script_pub_key: ScriptPubKey {
689 asm: zcash_script::script::Code(
690 output.1.lock_script.as_raw_bytes().to_vec(),
691 )
692 .to_asm(false),
693 hex: output.1.lock_script.clone(),
694 req_sigs,
695 r#type: zcash_script::script::Code(
696 output.1.lock_script.as_raw_bytes().to_vec(),
697 )
698 .to_component()
699 .ok()
700 .and_then(|c| c.refine().ok())
701 .and_then(|component| zcash_script::solver::standard(&component))
702 .map(|kind| match kind {
703 zcash_script::solver::ScriptKind::PubKeyHash { .. } => "pubkeyhash",
704 zcash_script::solver::ScriptKind::ScriptHash { .. } => "scripthash",
705 zcash_script::solver::ScriptKind::MultiSig { .. } => "multisig",
706 zcash_script::solver::ScriptKind::NullData { .. } => "nulldata",
707 zcash_script::solver::ScriptKind::PubKey { .. } => "pubkey",
708 })
709 .unwrap_or("nonstandard")
710 .to_string(),
711 addresses,
712 },
713 }
714 })
715 .collect(),
716 shielded_spends: tx
717 .sapling_spends_per_anchor()
718 .map(|spend| {
719 let mut anchor = spend.per_spend_anchor.as_bytes();
720 anchor.reverse();
721
722 let mut nullifier = spend.nullifier.as_bytes();
723 nullifier.reverse();
724
725 let mut rk: [u8; 32] = spend.clone().rk.into();
726 rk.reverse();
727
728 let spend_auth_sig: [u8; 64] = spend.spend_auth_sig.into();
729
730 ShieldedSpend {
731 cv: spend.cv.clone(),
732 anchor,
733 nullifier,
734 rk,
735 proof: spend.zkproof.0,
736 spend_auth_sig,
737 }
738 })
739 .collect(),
740 shielded_outputs: tx
741 .sapling_outputs()
742 .map(|output| {
743 let mut cm_u: [u8; 32] = output.cm_u.to_bytes();
744 cm_u.reverse();
745 let mut ephemeral_key: [u8; 32] = output.ephemeral_key.into();
746 ephemeral_key.reverse();
747 let enc_ciphertext: [u8; 580] = output.enc_ciphertext.into();
748 let out_ciphertext: [u8; 80] = output.out_ciphertext.into();
749
750 ShieldedOutput {
751 cv: output.cv.clone(),
752 cm_u,
753 ephemeral_key,
754 enc_ciphertext,
755 out_ciphertext,
756 proof: output.zkproof.0,
757 }
758 })
759 .collect(),
760 joinsplits: tx
761 .sprout_joinsplits()
762 .map(|joinsplit| {
763 let mut ephemeral_key_bytes: [u8; 32] = joinsplit.ephemeral_key.to_bytes();
764 ephemeral_key_bytes.reverse();
765
766 JoinSplit {
767 old_public_value: Zec::from(joinsplit.vpub_old).lossy_zec(),
768 old_public_value_zat: joinsplit.vpub_old.zatoshis(),
769 new_public_value: Zec::from(joinsplit.vpub_new).lossy_zec(),
770 new_public_value_zat: joinsplit.vpub_new.zatoshis(),
771 anchor: joinsplit.anchor.bytes_in_display_order(),
772 nullifiers: joinsplit
773 .nullifiers
774 .iter()
775 .map(|n| n.bytes_in_display_order())
776 .collect(),
777 commitments: joinsplit
778 .commitments
779 .iter()
780 .map(|c| c.bytes_in_display_order())
781 .collect(),
782 one_time_pubkey: ephemeral_key_bytes,
783 random_seed: joinsplit.random_seed.bytes_in_display_order(),
784 macs: joinsplit
785 .vmacs
786 .iter()
787 .map(|m| m.bytes_in_display_order())
788 .collect(),
789 proof: joinsplit.zkproof.unwrap_or_default(),
790 ciphertexts: joinsplit
791 .enc_ciphertexts
792 .iter()
793 .map(|c| c.zcash_serialize_to_vec().unwrap_or_default())
794 .collect(),
795 }
796 })
797 .collect(),
798 value_balance: Some(Zec::from(tx.sapling_value_balance().sapling_amount()).lossy_zec()),
799 value_balance_zat: Some(tx.sapling_value_balance().sapling_amount().zatoshis()),
800 orchard: Some(Orchard {
801 actions: tx
802 .orchard_actions()
803 .collect::<Vec<_>>()
804 .iter()
805 .map(|action| {
806 let spend_auth_sig: [u8; 64] = tx
807 .orchard_shielded_data()
808 .and_then(|shielded_data| {
809 shielded_data
810 .actions
811 .iter()
812 .find(|authorized_action| authorized_action.action == **action)
813 .map(|authorized_action| {
814 authorized_action.spend_auth_sig.into()
815 })
816 })
817 .unwrap_or([0; 64]);
818
819 let cv: [u8; 32] = action.cv.into();
820 let nullifier: [u8; 32] = action.nullifier.into();
821 let rk: [u8; 32] = action.rk.into();
822 let cm_x: [u8; 32] = action.cm_x.into();
823 let ephemeral_key: [u8; 32] = action.ephemeral_key.into();
824 let enc_ciphertext: [u8; 580] = action.enc_ciphertext.into();
825 let out_ciphertext: [u8; 80] = action.out_ciphertext.into();
826
827 OrchardAction {
828 cv,
829 nullifier,
830 rk,
831 cm_x,
832 ephemeral_key,
833 enc_ciphertext,
834 spend_auth_sig,
835 out_ciphertext,
836 }
837 })
838 .collect(),
839 value_balance: Zec::from(tx.orchard_value_balance().orchard_amount()).lossy_zec(),
840 value_balance_zat: tx.orchard_value_balance().orchard_amount().zatoshis(),
841 flags: tx.orchard_shielded_data().map(|data| {
842 OrchardFlags::new(
843 data.flags.contains(orchard::Flags::ENABLE_OUTPUTS),
844 data.flags.contains(orchard::Flags::ENABLE_SPENDS),
845 )
846 }),
847 anchor: tx
848 .orchard_shielded_data()
849 .map(|data| data.shared_anchor.bytes_in_display_order()),
850 proof: tx
851 .orchard_shielded_data()
852 .map(|data| data.proof.bytes_in_display_order()),
853 binding_sig: tx
854 .orchard_shielded_data()
855 .map(|data| data.binding_sig.into()),
856 }),
857 binding_sig: tx.sapling_binding_sig().map(|raw_sig| raw_sig.into()),
858 joinsplit_pub_key: tx.joinsplit_pub_key().map(|raw_key| {
859 let mut key: [u8; 32] = raw_key.into();
861 key.reverse();
862 key
863 }),
864 joinsplit_sig: tx.joinsplit_sig().map(|raw_sig| raw_sig.into()),
865 size: tx.as_bytes().len().try_into().ok(),
866 time: block_time,
867 txid,
868 in_active_chain,
869 auth_digest: tx.auth_digest(),
870 overwintered: tx.is_overwintered(),
871 version: tx.version(),
872 version_group_id: tx.version_group_id().map(|id| id.to_be_bytes().to_vec()),
873 lock_time: tx.raw_lock_time(),
874 expiry_height: tx.expiry_height(),
875 block_hash,
876 block_time,
877 }
878 }
879}