zcash_transparent/pczt/
updater.rs

1use alloc::string::String;
2use alloc::vec::Vec;
3use ripemd::Ripemd160;
4use sha2::{Digest, Sha256};
5
6use crate::address::{Script, TransparentAddress};
7
8use super::{Bip32Derivation, Bundle, Input, Output};
9
10impl Bundle {
11    /// Updates the bundle with information provided in the given closure.
12    pub fn update_with<F>(&mut self, f: F) -> Result<(), UpdaterError>
13    where
14        F: FnOnce(Updater<'_>) -> Result<(), UpdaterError>,
15    {
16        f(Updater(self))
17    }
18}
19
20/// An updater for a transparent PCZT bundle.
21pub struct Updater<'a>(&'a mut Bundle);
22
23impl Updater<'_> {
24    /// Provides read access to the bundle being updated.
25    pub fn bundle(&self) -> &Bundle {
26        self.0
27    }
28
29    /// Updates the input at the given index with information provided in the given
30    /// closure.
31    pub fn update_input_with<F>(&mut self, index: usize, f: F) -> Result<(), UpdaterError>
32    where
33        F: FnOnce(InputUpdater<'_>) -> Result<(), UpdaterError>,
34    {
35        f(InputUpdater(
36            self.0
37                .inputs
38                .get_mut(index)
39                .ok_or(UpdaterError::InvalidIndex)?,
40        ))
41    }
42
43    /// Updates the input at the given index with information provided in the given
44    /// closure.
45    pub fn update_output_with<F>(&mut self, index: usize, f: F) -> Result<(), UpdaterError>
46    where
47        F: FnOnce(OutputUpdater<'_>) -> Result<(), UpdaterError>,
48    {
49        f(OutputUpdater(
50            self.0
51                .outputs
52                .get_mut(index)
53                .ok_or(UpdaterError::InvalidIndex)?,
54        ))
55    }
56}
57
58/// An updater for a transparent PCZT input.
59pub struct InputUpdater<'a>(&'a mut Input);
60
61impl InputUpdater<'_> {
62    /// Sets the redeem script for this input.
63    ///
64    /// Returns an error if the input is not P2SH, or the given `redeem_script` does not
65    /// match the input's `script_pubkey`.
66    pub fn set_redeem_script(&mut self, redeem_script: Script) -> Result<(), UpdaterError> {
67        if let Some(TransparentAddress::ScriptHash(hash)) = self.0.script_pubkey.address() {
68            if hash[..] == Ripemd160::digest(Sha256::digest(&redeem_script.0))[..] {
69                self.0.redeem_script = Some(redeem_script);
70                Ok(())
71            } else {
72                Err(UpdaterError::WrongRedeemScript)
73            }
74        } else {
75            Err(UpdaterError::NotP2sh)
76        }
77    }
78
79    /// Sets the BIP 32 derivation path for the given pubkey.
80    pub fn set_bip32_derivation(&mut self, pubkey: [u8; 33], derivation: Bip32Derivation) {
81        self.0.bip32_derivation.insert(pubkey, derivation);
82    }
83
84    /// Stores the given value along with `key = RIPEMD160(value)`.
85    pub fn set_ripemd160_preimage(&mut self, value: Vec<u8>) {
86        let hash = Ripemd160::digest(&value);
87        self.0.ripemd160_preimages.insert(hash.into(), value);
88    }
89
90    /// Stores the given value along with `key = SHA256(value)`.
91    pub fn set_sha256_preimage(&mut self, value: Vec<u8>) {
92        let hash = Sha256::digest(&value);
93        self.0.sha256_preimages.insert(hash.into(), value);
94    }
95
96    /// Stores the given value along with `key = RIPEMD160(SHA256(value))`.
97    pub fn set_hash160_preimage(&mut self, value: Vec<u8>) {
98        let hash = Ripemd160::digest(Sha256::digest(&value));
99        self.0.hash160_preimages.insert(hash.into(), value);
100    }
101
102    /// Stores the given value along with `key = SHA256(SHA256(value))`.
103    pub fn set_hash256_preimage(&mut self, value: Vec<u8>) {
104        let hash = Sha256::digest(Sha256::digest(&value));
105        self.0.hash256_preimages.insert(hash.into(), value);
106    }
107
108    /// Stores the given proprietary value at the given key.
109    pub fn set_proprietary(&mut self, key: String, value: Vec<u8>) {
110        self.0.proprietary.insert(key, value);
111    }
112}
113
114/// An updater for a transparent PCZT output.
115pub struct OutputUpdater<'a>(&'a mut Output);
116
117impl OutputUpdater<'_> {
118    /// Sets the redeem script for this output.
119    ///
120    /// Returns an error if the output is not P2SH, or the given `redeem_script` does not
121    /// match the output's `script_pubkey`.
122    pub fn set_redeem_script(&mut self, redeem_script: Script) -> Result<(), UpdaterError> {
123        if let Some(TransparentAddress::ScriptHash(hash)) = self.0.script_pubkey.address() {
124            if hash[..] == Ripemd160::digest(Sha256::digest(&redeem_script.0))[..] {
125                self.0.redeem_script = Some(redeem_script);
126                Ok(())
127            } else {
128                Err(UpdaterError::WrongRedeemScript)
129            }
130        } else {
131            Err(UpdaterError::NotP2sh)
132        }
133    }
134
135    /// Sets the BIP 32 derivation path for the given pubkey.
136    pub fn set_bip32_derivation(&mut self, pubkey: [u8; 33], derivation: Bip32Derivation) {
137        self.0.bip32_derivation.insert(pubkey, derivation);
138    }
139
140    /// Sets the user-facing address that the new note is being sent to.
141    pub fn set_user_address(&mut self, user_address: String) {
142        self.0.user_address = Some(user_address);
143    }
144
145    /// Stores the given proprietary value at the given key.
146    pub fn set_proprietary(&mut self, key: String, value: Vec<u8>) {
147        self.0.proprietary.insert(key, value);
148    }
149}
150
151/// Errors that can occur while signing a transparent input in a PCZT.
152#[derive(Debug)]
153pub enum UpdaterError {
154    /// An out-of-bounds index was provided when looking up an input or output.
155    InvalidIndex,
156    /// A `redeem_script` can only be set on a P2SH coin.
157    NotP2sh,
158    /// The provided `redeem_script` does not match the input's `script_pubkey`.
159    WrongRedeemScript,
160}