zcash_transparent/pczt/
spend_finalizer.rs

1use ripemd::Ripemd160;
2use sha2::{Digest, Sha256};
3
4use crate::address::{Script, TransparentAddress};
5
6impl super::Bundle {
7    /// Finalizes the spends for this bundle.
8    pub fn finalize_spends(&mut self) -> Result<(), SpendFinalizerError> {
9        // For each input, the Spend Finalizer determines if the input has enough data to
10        // pass validation. If it does, it must construct the `script_sig` and place it
11        // into the PCZT input. If `script_sig` is empty for an input, the field should
12        // remain unset rather than assigned an empty array.
13        for input in &mut self.inputs {
14            match input.script_pubkey.address() {
15                Some(TransparentAddress::PublicKeyHash(hash)) => {
16                    let (pubkey, sig_bytes) = {
17                        let mut iter = input.partial_signatures.iter();
18                        match (iter.next(), iter.next()) {
19                            (Some(entry), None) => Ok(entry),
20                            (None, _) => Err(SpendFinalizerError::MissingSignature),
21                            (Some(_), Some(_)) => Err(SpendFinalizerError::UnexpectedSignatures),
22                        }
23                    }?;
24
25                    // Check that the signature is for this input.
26                    if hash[..] != Ripemd160::digest(Sha256::digest(pubkey))[..] {
27                        return Err(SpendFinalizerError::UnexpectedSignatures);
28                    }
29
30                    // P2PKH scriptSig
31                    input.script_sig = Some(Script::default() << &sig_bytes[..] << &pubkey[..]);
32                }
33                Some(TransparentAddress::ScriptHash(_)) => {
34                    return Err(SpendFinalizerError::UnsupportedScriptPubkey)
35                }
36                None => return Err(SpendFinalizerError::UnsupportedScriptPubkey),
37            }
38        }
39
40        // All other data except the UTXO and proprietary fields in the input should be
41        // cleared from the PSBT. The UTXO should be kept to allow Transaction Extractors
42        // to verify the final network serialized transaction.
43        for input in &mut self.inputs {
44            input.required_time_lock_time = None;
45            input.required_height_lock_time = None;
46            input.redeem_script = None;
47            input.partial_signatures.clear();
48            input.bip32_derivation.clear();
49            input.ripemd160_preimages.clear();
50            input.sha256_preimages.clear();
51            input.hash160_preimages.clear();
52            input.hash256_preimages.clear();
53        }
54
55        Ok(())
56    }
57}
58
59/// Errors that can occur while finalizing the transparent inputs of a PCZT bundle.
60#[derive(Debug)]
61pub enum SpendFinalizerError {
62    /// `partial_signatures` contained no signatures.
63    MissingSignature,
64    /// `partial_signatures` contained unexpected signatures.
65    UnexpectedSignatures,
66    /// The `script_pubkey` kind is unsupported.
67    UnsupportedScriptPubkey,
68}