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}