zcash_transparent/pczt.rs
1//! PCZT support for transparent Zcash.
2
3use alloc::collections::BTreeMap;
4use alloc::string::String;
5use alloc::vec::Vec;
6
7use bip32::ChildNumber;
8use getset::Getters;
9use zcash_protocol::{value::Zatoshis, TxId};
10
11use crate::{
12 address::Script,
13 keys::{NonHardenedChildIndex, TransparentKeyScope},
14 sighash::SighashType,
15};
16
17mod parse;
18pub use parse::ParseError;
19
20mod verify;
21pub use verify::VerifyError;
22
23mod updater;
24pub use updater::{InputUpdater, OutputUpdater, Updater, UpdaterError};
25
26#[cfg(feature = "transparent-inputs")]
27mod signer;
28#[cfg(feature = "transparent-inputs")]
29pub use signer::SignerError;
30
31mod spend_finalizer;
32pub use spend_finalizer::SpendFinalizerError;
33
34mod tx_extractor;
35pub use tx_extractor::{TxExtractorError, Unbound};
36
37/// PCZT fields that are specific to producing the transaction's transparent bundle (if
38/// any).
39///
40/// This struct is for representing Sapling in a partially-created transaction. If you
41/// have a fully-created transaction, use [the regular `Bundle` struct].
42///
43/// [the regular `Bundle` struct]: crate::bundle::Bundle
44#[derive(Debug, Getters)]
45#[getset(get = "pub")]
46pub struct Bundle {
47 /// The transparent inputs in this bundle.
48 ///
49 /// Entries are added by the Constructor, and modified by an Updater, IO Finalizer,
50 /// Signer, Combiner, or Spend Finalizer.
51 pub(crate) inputs: Vec<Input>,
52
53 /// The transparent outputs in this bundle.
54 ///
55 /// Entries are added by the Constructor, and modified by an Updater, IO Finalizer,
56 /// Signer, Combiner, or Spend Finalizer.
57 pub(crate) outputs: Vec<Output>,
58}
59
60impl Bundle {
61 /// Returns a mutable reference to the inputs in this bundle.
62 ///
63 /// This is used by Signers to apply signatures with [`Input::sign`].
64 pub fn inputs_mut(&mut self) -> &mut [Input] {
65 &mut self.inputs
66 }
67}
68
69/// Information about a transparent spend within a transaction.
70///
71/// This struct is for representing transparent spends in a partially-created transaction.
72/// If you have a fully-created transaction, use [the regular `TxIn` struct].
73///
74/// [the regular `TxIn` struct]: crate::bundle::TxIn
75#[derive(Debug, Getters)]
76#[getset(get = "pub")]
77pub struct Input {
78 /// The ID of the previous transaction containing the transparent coin being spent by
79 /// this input.
80 pub(crate) prevout_txid: TxId,
81
82 /// The index of the entry in the `vout` field of the previous transaction containing
83 /// the transparent coin being spent by this input.
84 pub(crate) prevout_index: u32,
85
86 /// The sequence number of this input.
87 ///
88 /// - This is set by the Constructor.
89 /// - If omitted, the sequence number is assumed to be the final sequence number
90 /// (`0xffffffff`).
91 pub(crate) sequence: Option<u32>,
92
93 /// The minimum Unix timstamp that this input requires to be set as the transaction's
94 /// lock time.
95 ///
96 /// - This is set by the Constructor.
97 /// - This must be greater than or equal to 500000000.
98 pub(crate) required_time_lock_time: Option<u32>,
99
100 /// The minimum block height that this input requires to be set as the transaction's
101 /// lock time.
102 ///
103 /// - This is set by the Constructor.
104 /// - This must be greater than 0 and less than 500000000.
105 pub(crate) required_height_lock_time: Option<u32>,
106
107 /// A satisfying witness for the `script_pubkey` of the input being spent.
108 ///
109 /// This is set by the Spend Finalizer.
110 pub(crate) script_sig: Option<Script>,
111
112 /// The value of the input being spent.
113 ///
114 /// - This is set by the Constructor.
115 /// - This is required by the IO Finalizer and Transaction Extractor, to derive the
116 /// shielded sighash needed for computing the binding signatures.
117 pub(crate) value: Zatoshis,
118
119 /// The `script_pubkey` of the input being spent.
120 ///
121 /// - This is set by the Constructor.
122 /// - This is required by the IO Finalizer and Transaction Extractor, to derive the
123 /// shielded sighash needed for computing the binding signatures.
124 pub(crate) script_pubkey: Script,
125
126 /// The script required to spend this output, if it is P2SH.
127 ///
128 /// Set to `None` if this is a P2PKH output.
129 pub(crate) redeem_script: Option<Script>,
130
131 /// A map from a pubkey to a signature created by it.
132 ///
133 /// - Each pubkey should appear in `script_pubkey` or `redeem_script`.
134 /// - Each entry is set by a Signer, and should contain an ECDSA signature that is
135 /// valid under the corresponding pubkey.
136 /// - These are required by the Spend Finalizer to assemble `script_sig`.
137 pub(crate) partial_signatures: BTreeMap<[u8; 33], Vec<u8>>,
138
139 /// The sighash type to be used for this input.
140 ///
141 /// - Signers must use this sighash type to produce their signatures. Signers that
142 /// cannot produce signatures for this sighash type must not provide a signature.
143 /// - Spend Finalizers must fail to finalize inputs which have signatures that do not
144 /// match this sighash type.
145 pub(crate) sighash_type: SighashType,
146
147 /// A map from a pubkey to the BIP 32 derivation path at which its corresponding
148 /// spending key can be found.
149 ///
150 /// - The pubkeys should appear in `script_pubkey` or `redeem_script`.
151 /// - Each entry is set by an Updater.
152 /// - Individual entries may be required by a Signer.
153 /// - It is not required that the map include entries for all of the used pubkeys.
154 /// In particular, it is not possible to include entries for non-BIP-32 pubkeys.
155 pub(crate) bip32_derivation: BTreeMap<[u8; 33], Bip32Derivation>,
156
157 /// Mappings of the form `key = RIPEMD160(value)`.
158 ///
159 /// - These may be used by the Signer to inspect parts of `script_pubkey` or
160 /// `redeem_script`.
161 pub(crate) ripemd160_preimages: BTreeMap<[u8; 20], Vec<u8>>,
162
163 /// Mappings of the form `key = SHA256(value)`.
164 ///
165 /// - These may be used by the Signer to inspect parts of `script_pubkey` or
166 /// `redeem_script`.
167 pub(crate) sha256_preimages: BTreeMap<[u8; 32], Vec<u8>>,
168
169 /// Mappings of the form `key = RIPEMD160(SHA256(value))`.
170 ///
171 /// - These may be used by the Signer to inspect parts of `script_pubkey` or
172 /// `redeem_script`.
173 pub(crate) hash160_preimages: BTreeMap<[u8; 20], Vec<u8>>,
174
175 /// Mappings of the form `key = SHA256(SHA256(value))`.
176 ///
177 /// - These may be used by the Signer to inspect parts of `script_pubkey` or
178 /// `redeem_script`.
179 pub(crate) hash256_preimages: BTreeMap<[u8; 32], Vec<u8>>,
180
181 /// Proprietary fields related to the transparent coin being spent.
182 pub(crate) proprietary: BTreeMap<String, Vec<u8>>,
183}
184
185/// Information about a transparent output within a transaction.
186///
187/// This struct is for representing transparent outputs in a partially-created
188/// transaction. If you have a fully-created transaction, use
189/// [the regular `TxOut` struct].
190///
191/// [the regular `TxOut` struct]: crate::bundle::TxOut
192#[derive(Debug, Getters)]
193#[getset(get = "pub")]
194pub struct Output {
195 /// The value of the output.
196 pub(crate) value: Zatoshis,
197
198 /// The script constraining how spending of this output must be authorized.
199 pub(crate) script_pubkey: Script,
200
201 /// The script required to spend this output, if it is P2SH.
202 ///
203 /// Set to `None` if this is a P2PKH output, or a P2SH with an unknown redeem script.
204 pub(crate) redeem_script: Option<Script>,
205
206 /// A map from a pubkey to the BIP 32 derivation path at which its corresponding
207 /// spending key can be found.
208 ///
209 /// - The pubkeys should appear in `script_pubkey` or `redeem_script`.
210 /// - Each entry is set by an Updater.
211 /// - Individual entries may be required by a Signer.
212 /// - It is not required that the map include entries for all of the used pubkeys.
213 /// In particular, it is not possible to include entries for non-BIP-32 pubkeys.
214 pub(crate) bip32_derivation: BTreeMap<[u8; 33], Bip32Derivation>,
215
216 /// The user-facing address to which this output is being sent, if any.
217 ///
218 /// - This is set by an Updater.
219 /// - Signers must parse this address (if present) and confirm that it contains
220 /// `recipient` (either directly, or e.g. as a receiver within a Unified Address).
221 pub(crate) user_address: Option<String>,
222
223 /// Proprietary fields related to the transparent coin being created.
224 pub(crate) proprietary: BTreeMap<String, Vec<u8>>,
225}
226
227/// The BIP 32 derivation path at which a key can be found.
228#[derive(Debug, Getters, PartialEq, Eq)]
229#[getset(get = "pub")]
230pub struct Bip32Derivation {
231 /// The [ZIP 32 seed fingerprint](https://zips.z.cash/zip-0032#seed-fingerprints).
232 seed_fingerprint: [u8; 32],
233
234 /// The sequence of indices corresponding to the HD path.
235 derivation_path: Vec<ChildNumber>,
236}
237
238impl Bip32Derivation {
239 /// Extracts the BIP 44 account index, scope, and address index from this derivation
240 /// path.
241 ///
242 /// Returns `None` if the seed fingerprints don't match, or if this is a non-standard
243 /// derivation path.
244 pub fn extract_bip_44_fields(
245 &self,
246 seed_fp: &zip32::fingerprint::SeedFingerprint,
247 expected_coin_type: ChildNumber,
248 ) -> Option<(zip32::AccountId, TransparentKeyScope, NonHardenedChildIndex)> {
249 if self.seed_fingerprint == seed_fp.to_bytes() {
250 match &self.derivation_path[..] {
251 [purpose, coin_type, account_index, scope, address_index]
252 if purpose == &ChildNumber(44 | ChildNumber::HARDENED_FLAG)
253 && coin_type.is_hardened()
254 && coin_type == &expected_coin_type
255 && account_index.is_hardened()
256 && !scope.is_hardened()
257 && !address_index.is_hardened() =>
258 {
259 let account_index = zip32::AccountId::try_from(account_index.index())
260 .expect("account_index is hardened");
261
262 let scope =
263 TransparentKeyScope::custom(scope.index()).expect("scope is not hardened");
264
265 let address_index = NonHardenedChildIndex::from_index(address_index.index())
266 .expect("address_index is not hardened");
267
268 Some((account_index, scope, address_index))
269 }
270 _ => None,
271 }
272 } else {
273 None
274 }
275 }
276}