Skip to main content

psbt_v2/v2/map/
output.rs

1// SPDX-License-Identifier: CC0-1.0
2
3use core::convert::TryFrom;
4use core::fmt;
5
6use bitcoin::bip32::KeySource;
7use bitcoin::io::Read;
8use bitcoin::key::{PublicKey, XOnlyPublicKey};
9use bitcoin::taproot::{TapLeafHash, TapTree};
10use bitcoin::{Amount, ScriptBuf, TxOut};
11
12use crate::consts::{
13    PSBT_OUT_AMOUNT, PSBT_OUT_BIP32_DERIVATION, PSBT_OUT_PROPRIETARY, PSBT_OUT_REDEEM_SCRIPT,
14    PSBT_OUT_SCRIPT, PSBT_OUT_TAP_BIP32_DERIVATION, PSBT_OUT_TAP_INTERNAL_KEY, PSBT_OUT_TAP_TREE,
15    PSBT_OUT_WITNESS_SCRIPT,
16};
17#[cfg(feature = "silent-payments")]
18use crate::consts::{PSBT_OUT_SP_V0_INFO, PSBT_OUT_SP_V0_LABEL};
19use crate::error::write_err;
20use crate::prelude::*;
21use crate::serialize::{Deserialize, Serialize};
22use crate::v2::map::Map;
23use crate::{raw, serialize};
24
25/// A key-value map for an output of the corresponding index in the unsigned
26/// transaction.
27#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
28#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
29pub struct Output {
30    /// The output's amount (serialized as satoshis).
31    pub amount: Amount,
32
33    /// The script for this output, also known as the scriptPubKey.
34    pub script_pubkey: ScriptBuf,
35
36    /// The redeem script for this output.
37    pub redeem_script: Option<ScriptBuf>,
38    /// The witness script for this output.
39    pub witness_script: Option<ScriptBuf>,
40    /// A map from public keys needed to spend this output to their
41    /// corresponding master key fingerprints and derivation paths.
42    #[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_as_seq"))]
43    pub bip32_derivations: BTreeMap<PublicKey, KeySource>,
44    /// The internal pubkey.
45    pub tap_internal_key: Option<XOnlyPublicKey>,
46    /// Taproot Output tree.
47    pub tap_tree: Option<TapTree>,
48    /// Map of tap root x only keys to origin info and leaf hashes contained in it.
49    #[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_as_seq"))]
50    pub tap_key_origins: BTreeMap<XOnlyPublicKey, (Vec<TapLeafHash>, KeySource)>,
51
52    /// BIP-375: Silent payment v0 address info (66 bytes: scan_key || spend_key).
53    #[cfg(feature = "silent-payments")]
54    pub sp_v0_info: Option<Vec<u8>>,
55
56    /// BIP-375: Silent payment v0 label (4-byte little-endian u32).
57    #[cfg(feature = "silent-payments")]
58    pub sp_v0_label: Option<u32>,
59
60    /// Proprietary key-value pairs for this output.
61    #[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_as_seq_byte_values"))]
62    pub proprietaries: BTreeMap<raw::ProprietaryKey, Vec<u8>>,
63    /// Unknown key-value pairs for this output.
64    #[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_as_seq_byte_values"))]
65    pub unknowns: BTreeMap<raw::Key, Vec<u8>>,
66}
67
68impl Output {
69    /// Creates a new [`Output`] using `utxo`.
70    pub fn new(utxo: TxOut) -> Self {
71        Output {
72            amount: utxo.value,
73            script_pubkey: utxo.script_pubkey,
74            redeem_script: None,
75            witness_script: None,
76            bip32_derivations: BTreeMap::new(),
77            tap_internal_key: None,
78            tap_tree: None,
79            tap_key_origins: BTreeMap::new(),
80            #[cfg(feature = "silent-payments")]
81            sp_v0_info: None,
82            #[cfg(feature = "silent-payments")]
83            sp_v0_label: None,
84            proprietaries: BTreeMap::new(),
85            unknowns: BTreeMap::new(),
86        }
87    }
88
89    // /// Converts this `Output` to a `v0::Output`.
90    // pub(crate) fn into_v0(self) -> v0::Output {
91    //     v0::Output {
92    //         redeem_script: self.redeem_script,
93    //         witness_script: self.witness_script,
94    //         bip32_derivation: self.bip32_derivations,
95    //         tap_internal_key: self.tap_internal_key,
96    //         tap_tree: self.tap_tree,
97    //         tap_key_origins: self.tap_key_origins,
98    //         proprietary: self.proprietaries,
99    //         unknown: self.unknowns,
100    //     }
101    // }
102
103    /// Creates the [`TxOut`] associated with this `Output`.
104    pub(crate) fn tx_out(&self) -> TxOut {
105        TxOut { value: self.amount, script_pubkey: self.script_pubkey.clone() }
106    }
107
108    pub(in crate::v2) fn decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, DecodeError> {
109        // These are placeholder values that never exist in a encode `Output`.
110        let invalid = TxOut { value: Amount::ZERO, script_pubkey: ScriptBuf::default() };
111        let mut rv = Self::new(invalid);
112
113        loop {
114            match raw::Pair::decode(r) {
115                Ok(pair) => rv.insert_pair(pair)?,
116                Err(serialize::Error::NoMorePairs) => break,
117                Err(e) => return Err(DecodeError::DeserPair(e)),
118            }
119        }
120
121        if rv.amount == Amount::ZERO {
122            return Err(DecodeError::MissingValue);
123        }
124        // BIP-375 allows outputs to be missing scriptPubkey
125        #[cfg(not(feature = "silent-payments"))]
126        if rv.script_pubkey == ScriptBuf::default() {
127            return Err(DecodeError::MissingScriptPubkey);
128        }
129
130        #[cfg(feature = "silent-payments")]
131        if rv.script_pubkey == ScriptBuf::default() && rv.sp_v0_info.is_none() {
132            return Err(DecodeError::MissingScriptPubkey);
133        }
134
135        #[cfg(feature = "silent-payments")]
136        if rv.sp_v0_label.is_some() && rv.sp_v0_info.is_none() {
137            return Err(DecodeError::LabelWithoutInfo);
138        }
139
140        Ok(rv)
141    }
142
143    fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), InsertPairError> {
144        let raw::Pair { key: raw_key, value: raw_value } = pair;
145
146        match raw_key.type_value {
147            PSBT_OUT_AMOUNT => {
148                if self.amount != Amount::ZERO {
149                    return Err(InsertPairError::DuplicateKey(raw_key));
150                }
151                let amount: Amount = Deserialize::deserialize(&raw_value)?;
152                self.amount = amount;
153            }
154            PSBT_OUT_SCRIPT => {
155                if self.script_pubkey != ScriptBuf::default() {
156                    return Err(InsertPairError::DuplicateKey(raw_key));
157                }
158                let script: ScriptBuf = Deserialize::deserialize(&raw_value)?;
159                self.script_pubkey = script;
160            }
161
162            PSBT_OUT_REDEEM_SCRIPT => {
163                v2_impl_psbt_insert_pair! {
164                    self.redeem_script <= <raw_key: _>|<raw_value: ScriptBuf>
165                }
166            }
167            PSBT_OUT_WITNESS_SCRIPT => {
168                v2_impl_psbt_insert_pair! {
169                    self.witness_script <= <raw_key: _>|<raw_value: ScriptBuf>
170                }
171            }
172            PSBT_OUT_BIP32_DERIVATION => {
173                v2_impl_psbt_insert_pair! {
174                    self.bip32_derivations <= <raw_key: PublicKey>|<raw_value: KeySource>
175                }
176            }
177            PSBT_OUT_PROPRIETARY => {
178                let key = raw::ProprietaryKey::try_from(raw_key.clone())?;
179                match self.proprietaries.entry(key) {
180                    btree_map::Entry::Vacant(empty_key) => {
181                        empty_key.insert(raw_value);
182                    }
183                    btree_map::Entry::Occupied(_) =>
184                        return Err(InsertPairError::DuplicateKey(raw_key)),
185                }
186            }
187            PSBT_OUT_TAP_INTERNAL_KEY => {
188                v2_impl_psbt_insert_pair! {
189                    self.tap_internal_key <= <raw_key: _>|<raw_value: XOnlyPublicKey>
190                }
191            }
192            PSBT_OUT_TAP_TREE => {
193                v2_impl_psbt_insert_pair! {
194                    self.tap_tree <= <raw_key: _>|<raw_value: TapTree>
195                }
196            }
197            PSBT_OUT_TAP_BIP32_DERIVATION => {
198                v2_impl_psbt_insert_pair! {
199                    self.tap_key_origins <= <raw_key: XOnlyPublicKey>|< raw_value: (Vec<TapLeafHash>, KeySource)>
200                }
201            }
202            #[cfg(feature = "silent-payments")]
203            PSBT_OUT_SP_V0_INFO => {
204                if self.sp_v0_info.is_some() {
205                    return Err(InsertPairError::DuplicateKey(raw_key));
206                }
207                if !raw_key.key.is_empty() {
208                    return Err(InsertPairError::InvalidKeyDataNotEmpty(raw_key));
209                }
210                if raw_value.len() != 66 {
211                    return Err(InsertPairError::ValueWrongLength(raw_value.len(), 66));
212                }
213                self.sp_v0_info = Some(raw_value);
214            }
215            #[cfg(feature = "silent-payments")]
216            PSBT_OUT_SP_V0_LABEL => {
217                if self.sp_v0_label.is_some() {
218                    return Err(InsertPairError::DuplicateKey(raw_key));
219                }
220                if !raw_key.key.is_empty() {
221                    return Err(InsertPairError::InvalidKeyDataNotEmpty(raw_key));
222                }
223                if raw_value.len() != 4 {
224                    return Err(InsertPairError::ValueWrongLength(raw_value.len(), 4));
225                }
226                let label =
227                    u32::from_le_bytes([raw_value[0], raw_value[1], raw_value[2], raw_value[3]]);
228                self.sp_v0_label = Some(label);
229            }
230            // Note, PSBT v2 does not exclude any keys from the input map.
231            _ => match self.unknowns.entry(raw_key) {
232                btree_map::Entry::Vacant(empty_key) => {
233                    empty_key.insert(raw_value);
234                }
235                btree_map::Entry::Occupied(k) =>
236                    return Err(InsertPairError::DuplicateKey(k.key().clone())),
237            },
238        }
239
240        Ok(())
241    }
242
243    /// Combines this [`Output`] with `other` `Output` (as described by BIP 174).
244    pub fn combine(&mut self, other: Self) -> Result<(), CombineError> {
245        if self.amount != other.amount {
246            return Err(CombineError::AmountMismatch { this: self.amount, that: other.amount });
247        }
248
249        if self.script_pubkey != other.script_pubkey {
250            return Err(CombineError::ScriptPubkeyMismatch {
251                this: self.script_pubkey.clone(),
252                that: other.script_pubkey,
253            });
254        }
255
256        v2_combine_option!(redeem_script, self, other);
257        v2_combine_option!(witness_script, self, other);
258        v2_combine_map!(bip32_derivations, self, other);
259        v2_combine_option!(tap_internal_key, self, other);
260        v2_combine_option!(tap_tree, self, other);
261        v2_combine_map!(tap_key_origins, self, other);
262        #[cfg(feature = "silent-payments")]
263        v2_combine_option!(sp_v0_info, self, other);
264        #[cfg(feature = "silent-payments")]
265        v2_combine_option!(sp_v0_label, self, other);
266        v2_combine_map!(proprietaries, self, other);
267        v2_combine_map!(unknowns, self, other);
268
269        Ok(())
270    }
271}
272
273impl Map for Output {
274    fn get_pairs(&self) -> Vec<raw::Pair> {
275        let mut rv: Vec<raw::Pair> = Default::default();
276
277        rv.push(raw::Pair {
278            key: raw::Key { type_value: PSBT_OUT_AMOUNT, key: vec![] },
279            value: self.amount.serialize(),
280        });
281
282        rv.push(raw::Pair {
283            key: raw::Key { type_value: PSBT_OUT_SCRIPT, key: vec![] },
284            value: self.script_pubkey.serialize(),
285        });
286
287        v2_impl_psbt_get_pair! {
288            rv.push(self.redeem_script, PSBT_OUT_REDEEM_SCRIPT)
289        }
290
291        v2_impl_psbt_get_pair! {
292            rv.push(self.witness_script, PSBT_OUT_WITNESS_SCRIPT)
293        }
294
295        v2_impl_psbt_get_pair! {
296            rv.push_map(self.bip32_derivations, PSBT_OUT_BIP32_DERIVATION)
297        }
298
299        v2_impl_psbt_get_pair! {
300            rv.push(self.tap_internal_key, PSBT_OUT_TAP_INTERNAL_KEY)
301        }
302
303        v2_impl_psbt_get_pair! {
304            rv.push(self.tap_tree, PSBT_OUT_TAP_TREE)
305        }
306
307        v2_impl_psbt_get_pair! {
308            rv.push_map(self.tap_key_origins, PSBT_OUT_TAP_BIP32_DERIVATION)
309        }
310
311        #[cfg(feature = "silent-payments")]
312        if let Some(sp_info) = &self.sp_v0_info {
313            rv.push(raw::Pair {
314                key: raw::Key { type_value: PSBT_OUT_SP_V0_INFO, key: vec![] },
315                value: sp_info.clone(),
316            });
317        }
318
319        #[cfg(feature = "silent-payments")]
320        if let Some(label) = self.sp_v0_label {
321            rv.push(raw::Pair {
322                key: raw::Key { type_value: PSBT_OUT_SP_V0_LABEL, key: vec![] },
323                value: label.to_le_bytes().to_vec(),
324            });
325        }
326
327        for (key, value) in self.proprietaries.iter() {
328            rv.push(raw::Pair { key: key.to_key(), value: value.clone() });
329        }
330
331        for (key, value) in self.unknowns.iter() {
332            rv.push(raw::Pair { key: key.clone(), value: value.clone() });
333        }
334
335        rv
336    }
337}
338
339/// Enables building an [`Output`] using the standard builder pattern.
340// This is only provided for uniformity with the `InputBuilder`.
341pub struct OutputBuilder(Output);
342
343impl OutputBuilder {
344    /// Creates a new builder that can be used to build an [`Output`] around `utxo`.
345    pub fn new(utxo: TxOut) -> Self { OutputBuilder(Output::new(utxo)) }
346
347    /// Build the [`Output`].
348    pub fn build(self) -> Output { self.0 }
349}
350
351/// An error while decoding.
352#[derive(Debug)]
353#[non_exhaustive]
354pub enum DecodeError {
355    /// Error inserting a key-value pair.
356    InsertPair(InsertPairError),
357    /// Error deserializing a pair.
358    DeserPair(serialize::Error),
359    /// Encoded output is missing a value.
360    MissingValue,
361    /// Encoded output is missing a script pubkey.
362    MissingScriptPubkey,
363    /// Encoded output is missing a sp_v0_info.
364    LabelWithoutInfo,
365}
366
367impl fmt::Display for DecodeError {
368    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
369        use DecodeError::*;
370
371        match *self {
372            InsertPair(ref e) => write_err!(f, "error inserting a pair"; e),
373            DeserPair(ref e) => write_err!(f, "error deserializing a pair"; e),
374            MissingValue => write!(f, "encoded output is missing a value"),
375            MissingScriptPubkey => write!(f, "encoded output is missing a script pubkey"),
376            LabelWithoutInfo => write!(f, "output has a sp_v0_label without a sp_v0_info"),
377        }
378    }
379}
380
381#[cfg(feature = "std")]
382impl std::error::Error for DecodeError {
383    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
384        use DecodeError::*;
385
386        match *self {
387            InsertPair(ref e) => Some(e),
388            DeserPair(ref e) => Some(e),
389            MissingValue | MissingScriptPubkey | LabelWithoutInfo => None,
390        }
391    }
392}
393
394impl From<InsertPairError> for DecodeError {
395    fn from(e: InsertPairError) -> Self { Self::InsertPair(e) }
396}
397
398/// Error inserting a key-value pair.
399#[derive(Debug)]
400pub enum InsertPairError {
401    /// Keys within key-value map should never be duplicated.
402    DuplicateKey(raw::Key),
403    /// Error deserializing raw value.
404    Deser(serialize::Error),
405    /// Key should contain data.
406    InvalidKeyDataEmpty(raw::Key),
407    /// Key should not contain data.
408    InvalidKeyDataNotEmpty(raw::Key),
409    /// Value was not the correct length (got, expected).
410    ValueWrongLength(usize, usize),
411}
412
413impl fmt::Display for InsertPairError {
414    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
415        use InsertPairError::*;
416
417        match *self {
418            DuplicateKey(ref key) => write!(f, "duplicate key: {}", key),
419            Deser(ref e) => write_err!(f, "error deserializing raw value"; e),
420            InvalidKeyDataEmpty(ref key) => write!(f, "key should contain data: {}", key),
421            InvalidKeyDataNotEmpty(ref key) => write!(f, "key should not contain data: {}", key),
422            ValueWrongLength(got, expected) => {
423                write!(f, "value wrong length (got: {}, expected: {})", got, expected)
424            }
425        }
426    }
427}
428
429#[cfg(feature = "std")]
430impl std::error::Error for InsertPairError {
431    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
432        use InsertPairError::*;
433
434        match *self {
435            Deser(ref e) => Some(e),
436            DuplicateKey(_)
437            | InvalidKeyDataEmpty(_)
438            | InvalidKeyDataNotEmpty(_)
439            | ValueWrongLength(..) => None,
440        }
441    }
442}
443
444impl From<serialize::Error> for InsertPairError {
445    fn from(e: serialize::Error) -> Self { Self::Deser(e) }
446}
447
448/// Error combining two output maps.
449#[derive(Debug, Clone, PartialEq, Eq)]
450#[non_exhaustive]
451pub enum CombineError {
452    /// The amounts are not the same.
453    AmountMismatch {
454        /// Attempted to combine a PBST with `this` previous txid.
455        this: Amount,
456        /// Into a PBST with `that` previous txid.
457        that: Amount,
458    },
459    /// The script_pubkeys are not the same.
460    ScriptPubkeyMismatch {
461        /// Attempted to combine a PBST with `this` script_pubkey.
462        this: ScriptBuf,
463        /// Into a PBST with `that` script_pubkey.
464        that: ScriptBuf,
465    },
466}
467
468impl fmt::Display for CombineError {
469    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
470        use CombineError::*;
471
472        match *self {
473            AmountMismatch { ref this, ref that } => {
474                write!(f, "combine two PSBTs with different amounts: {} {}", this, that)
475            }
476            ScriptPubkeyMismatch { ref this, ref that } => {
477                write!(f, "combine two PSBTs with different script_pubkeys: {:x} {:x}", this, that)
478            }
479        }
480    }
481}
482
483#[cfg(feature = "std")]
484impl std::error::Error for CombineError {
485    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
486        use CombineError::*;
487
488        match *self {
489            AmountMismatch { .. } | ScriptPubkeyMismatch { .. } => None,
490        }
491    }
492}
493
494#[cfg(test)]
495#[cfg(feature = "std")]
496mod tests {
497    use bitcoin::io::Cursor;
498
499    use super::*;
500
501    fn tx_out() -> TxOut {
502        // Arbitrary script, may not even be a valid scriptPubkey.
503        let script = ScriptBuf::from_hex("76a914162c5ea71c0b23f5b9022ef047c4a86470a5b07088ac")
504            .expect("failed to parse script form hex");
505        let value = Amount::from_sat(123_456_789);
506        TxOut { value, script_pubkey: script }
507    }
508
509    #[test]
510    fn serialize_roundtrip() {
511        let output = Output::new(tx_out());
512
513        let ser = output.serialize_map();
514        let mut d = Cursor::new(ser);
515
516        let decoded = Output::decode(&mut d).expect("failed to decode");
517
518        assert_eq!(decoded, output);
519    }
520}