pczt/roles/io_finalizer/
mod.rs1use rand_core::{CryptoRng, Rng};
7use zcash_primitives::transaction::sighash::SignableInput;
8
9use crate::{
10 ExtractError, ParsedPczt, Pczt,
11 common::{
12 FLAG_SHIELDED_MODIFIABLE, FLAG_TRANSPARENT_INPUTS_MODIFIABLE,
13 FLAG_TRANSPARENT_OUTPUTS_MODIFIABLE,
14 },
15 precompute_sighash_data, sighash,
16};
17
18pub struct IoFinalizer {
19 pczt: Pczt,
20}
21
22impl IoFinalizer {
23 pub fn new(pczt: Pczt) -> Self {
25 Self { pczt }
26 }
27
28 pub fn finalize_io<R: Rng + CryptoRng>(self, mut rng: R) -> Result<Pczt, Error> {
32 let Self { pczt } = self;
33
34 let has_orchard_actions = !pczt.orchard.actions.is_empty();
35 let has_ironwood_actions = !pczt.ironwood.actions.is_empty();
36 let has_sapling_spends = !pczt.sapling.spends.is_empty();
37 let has_shielded_spends = has_sapling_spends || has_orchard_actions || has_ironwood_actions;
38 let has_shielded_outputs =
39 !(pczt.sapling.outputs.is_empty() && !has_orchard_actions && !has_ironwood_actions);
40
41 if pczt.transparent.inputs.is_empty() && !has_shielded_spends {
44 return Err(Error::NoSpends);
45 }
46 if pczt.transparent.outputs.is_empty() && !has_shielded_outputs {
47 return Err(Error::NoOutputs);
48 }
49
50 let anchor_requirement =
51 crate::common::AnchorRequirement::for_pre_authorization(pczt.global.tx_version);
52
53 let ParsedPczt {
54 mut global,
55 transparent,
56 mut sapling,
57 mut orchard,
58 mut ironwood,
59 tx_data,
60 } = pczt.extract_tx_data(
61 anchor_requirement,
62 |t| {
63 t.extract_effects()
64 .map_err(ExtractError::TransparentExtract)
65 },
66 |s| s.extract_effects().map_err(ExtractError::SaplingExtract),
67 |o| o.extract_effects().map_err(ExtractError::OrchardExtract),
68 |i| i.extract_effects().map_err(ExtractError::IronwoodExtract),
69 )?;
70
71 if has_shielded_spends || has_shielded_outputs {
74 global.tx_modifiable &= !(FLAG_TRANSPARENT_INPUTS_MODIFIABLE
75 | FLAG_TRANSPARENT_OUTPUTS_MODIFIABLE
76 | FLAG_SHIELDED_MODIFIABLE);
77 }
78 let sighash_data = precompute_sighash_data(&global, tx_data)?;
79 let shielded_sighash = sighash(&sighash_data, &SignableInput::Shielded);
80
81 sapling
85 .bundle
86 .finalize_io(shielded_sighash, &mut rng)
87 .map_err(Error::SaplingFinalize)?;
88 if has_orchard_actions {
93 orchard
94 .bundle
95 .finalize_io(shielded_sighash, &mut rng)
96 .map_err(Error::OrchardFinalize)?;
97 }
98 if has_ironwood_actions {
99 ironwood
100 .bundle
101 .finalize_io(shielded_sighash, &mut rng)
102 .map_err(Error::IronwoodFinalize)?;
103 }
104
105 Ok(Pczt {
106 global,
107 transparent: crate::transparent::Bundle::serialize_from(transparent),
108 sapling: sapling.reserialize(),
109 orchard: orchard.reserialize(),
110 ironwood: ironwood.reserialize(),
111 })
112 }
113}
114
115#[derive(Debug)]
117pub enum Error {
118 Extract(crate::ExtractError),
119 NoOutputs,
120 NoSpends,
121 IronwoodFinalize(orchard::pczt::IoFinalizerError),
122 OrchardFinalize(orchard::pczt::IoFinalizerError),
123 SaplingFinalize(sapling::pczt::IoFinalizerError),
124}
125
126impl From<crate::ExtractError> for Error {
127 fn from(e: crate::ExtractError) -> Self {
128 Error::Extract(e)
129 }
130}