zcash_transparent/pczt/
parse.rs

1use alloc::collections::BTreeMap;
2use alloc::string::String;
3use alloc::vec::Vec;
4
5use bip32::ChildNumber;
6use zcash_protocol::{value::Zatoshis, TxId};
7
8use crate::{address::Script, sighash::SighashType};
9
10use super::{Bip32Derivation, Bundle, Input, Output};
11
12impl Bundle {
13    /// Parses a PCZT bundle from its component parts.
14    pub fn parse(inputs: Vec<Input>, outputs: Vec<Output>) -> Result<Self, ParseError> {
15        Ok(Self { inputs, outputs })
16    }
17}
18
19impl Input {
20    /// Parses a PCZT input from its component parts.
21    #[allow(clippy::too_many_arguments)]
22    pub fn parse(
23        prevout_txid: [u8; 32],
24        prevout_index: u32,
25        sequence: Option<u32>,
26        required_time_lock_time: Option<u32>,
27        required_height_lock_time: Option<u32>,
28        script_sig: Option<Vec<u8>>,
29        value: u64,
30        script_pubkey: Vec<u8>,
31        redeem_script: Option<Vec<u8>>,
32        partial_signatures: BTreeMap<[u8; 33], Vec<u8>>,
33        sighash_type: u8,
34        bip32_derivation: BTreeMap<[u8; 33], Bip32Derivation>,
35        ripemd160_preimages: BTreeMap<[u8; 20], Vec<u8>>,
36        sha256_preimages: BTreeMap<[u8; 32], Vec<u8>>,
37        hash160_preimages: BTreeMap<[u8; 20], Vec<u8>>,
38        hash256_preimages: BTreeMap<[u8; 32], Vec<u8>>,
39        proprietary: BTreeMap<String, Vec<u8>>,
40    ) -> Result<Self, ParseError> {
41        let prevout_txid = TxId::from_bytes(prevout_txid);
42
43        match required_time_lock_time {
44            None | Some(500000000..) => Ok(()),
45            Some(_) => Err(ParseError::InvalidRequiredTimeLocktime),
46        }?;
47
48        match required_height_lock_time {
49            None | Some(1..=499999999) => Ok(()),
50            Some(_) => Err(ParseError::InvalidRequiredHeightLocktime),
51        }?;
52
53        // TODO: Verify that the script is not nonsense.
54        let script_sig = script_sig.map(Script);
55
56        let value = Zatoshis::from_u64(value).map_err(|_| ParseError::InvalidValue)?;
57
58        // TODO: Verify that the script is not nonsense.
59        let script_pubkey = Script(script_pubkey);
60
61        // TODO: Verify that the script is not nonsense.
62        let redeem_script = redeem_script.map(Script);
63
64        let sighash_type =
65            SighashType::parse(sighash_type).ok_or(ParseError::InvalidSighashType)?;
66
67        Ok(Self {
68            prevout_txid,
69            prevout_index,
70            sequence,
71            required_time_lock_time,
72            required_height_lock_time,
73            script_sig,
74            value,
75            script_pubkey,
76            redeem_script,
77            partial_signatures,
78            sighash_type,
79            bip32_derivation,
80            ripemd160_preimages,
81            sha256_preimages,
82            hash160_preimages,
83            hash256_preimages,
84            proprietary,
85        })
86    }
87}
88
89impl Output {
90    /// Parses a PCZT output from its component parts.
91    pub fn parse(
92        value: u64,
93        script_pubkey: Vec<u8>,
94        redeem_script: Option<Vec<u8>>,
95        bip32_derivation: BTreeMap<[u8; 33], Bip32Derivation>,
96        user_address: Option<String>,
97        proprietary: BTreeMap<String, Vec<u8>>,
98    ) -> Result<Self, ParseError> {
99        let value = Zatoshis::from_u64(value).map_err(|_| ParseError::InvalidValue)?;
100
101        // TODO: Verify that the script is not nonsense.
102        let script_pubkey = Script(script_pubkey);
103
104        // TODO: Verify that the script is not nonsense.
105        let redeem_script = redeem_script.map(Script);
106
107        Ok(Self {
108            value,
109            script_pubkey,
110            redeem_script,
111            bip32_derivation,
112            user_address,
113            proprietary,
114        })
115    }
116}
117
118impl Bip32Derivation {
119    /// Parses a BIP 32 derivation path from its component parts.
120    pub fn parse(
121        seed_fingerprint: [u8; 32],
122        derivation_path: Vec<u32>,
123    ) -> Result<Self, ParseError> {
124        Ok(Self {
125            seed_fingerprint,
126            derivation_path: derivation_path.into_iter().map(ChildNumber).collect(),
127        })
128    }
129}
130
131/// Errors that can occur while parsing a PCZT bundle.
132#[derive(Debug)]
133pub enum ParseError {
134    InvalidRequiredHeightLocktime,
135    InvalidRequiredTimeLocktime,
136    /// An invalid `sighash_type` was provided.
137    InvalidSighashType,
138    /// An invalid `value` was provided.
139    InvalidValue,
140}