1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
// Wallet-level libraries for bitcoin protocol by LNP/BP Association
//
// Written in 2020-2022 by
//     Dr. Maxim Orlovsky <orlovsky@lnp-bp.org>
//
// This software is distributed without any warranty.
//
// You should have received a copy of the Apache-2.0 License
// along with this software.
// If not, see <https://opensource.org/licenses/Apache-2.0>.

use std::collections::BTreeMap;
use std::fmt::{Display, Formatter};
use std::str::FromStr;

use base64::Engine;
use bitcoin::util::bip32::{ExtendedPubKey, KeySource};
use bitcoin::{consensus, Transaction, Txid};
use bitcoin_blockchain::locks::LockTime;
#[cfg(feature = "serde")]
use serde_with::{hex::Hex, As, Same};

use crate::serialize::{Deserialize, Serialize};
use crate::v0::PsbtV0;
use crate::{raw, Error, FeeError, Input, Output, PsbtVersion, TxError};

// TODO: Do manual serde and strict encoding implementation to check the
//       deserialized values
#[derive(Clone, Eq, PartialEq, Debug, Default)]
#[derive(StrictEncode, StrictDecode)]
#[cfg_attr(
    feature = "serde",
    derive(Serialize, Deserialize),
    serde(crate = "serde_crate")
)]
pub struct Psbt {
    /// The version number of this PSBT. If omitted, the version number is 0.
    pub psbt_version: PsbtVersion,

    /// Transaction version.
    pub tx_version: u32,

    /// Fallback locktime (used if none of the inputs specifies their locktime).
    pub fallback_locktime: Option<LockTime>,

    /// The corresponding key-value map for each input.
    pub inputs: Vec<Input>,

    /// The corresponding key-value map for each output.
    pub outputs: Vec<Output>,

    /// A global map from extended public keys to the used key fingerprint and
    /// derivation path as defined by BIP 32
    pub xpub: BTreeMap<ExtendedPubKey, KeySource>,

    /// Global proprietary key-value pairs.
    #[cfg_attr(feature = "serde", serde(with = "As::<BTreeMap<Same, Hex>>"))]
    pub proprietary: BTreeMap<raw::ProprietaryKey, Vec<u8>>,

    /// Unknown global key-value pairs.
    #[cfg_attr(feature = "serde", serde(with = "As::<BTreeMap<Same, Hex>>"))]
    pub unknown: BTreeMap<raw::Key, Vec<u8>>,
}

impl Psbt {
    /// Checks that unsigned transaction does not have scriptSig's or witness
    /// data
    pub fn with(tx: Transaction, psbt_version: PsbtVersion) -> Result<Self, TxError> {
        let inputs = tx
            .input
            .into_iter()
            .enumerate()
            .map(|(index, txin)| Input::new(index, txin).map_err(TxError::from))
            .collect::<Result<_, TxError>>()?;
        let outputs = tx
            .output
            .into_iter()
            .enumerate()
            .map(|(index, txout)| Output::new(index, txout))
            .collect();

        let i32_version = tx.version;
        let tx_version = i32_version
            .try_into()
            .map_err(|_| TxError::InvalidTxVersion(i32_version))?;

        let fallback_locktime = match tx.lock_time.0 {
            0 => None,
            other => Some(other.into()),
        };

        Ok(Psbt {
            psbt_version,
            xpub: Default::default(),
            tx_version,
            fallback_locktime,
            inputs,
            outputs,
            proprietary: Default::default(),
            unknown: Default::default(),
        })
    }

    pub fn lock_time(&self) -> LockTime {
        let required_time_locktime = self
            .inputs
            .iter()
            .filter_map(|input| input.required_time_locktime)
            .max();
        let required_height_locktime = self
            .inputs
            .iter()
            .filter_map(|input| input.required_height_locktime)
            .max();

        match (
            required_time_locktime,
            required_height_locktime,
            self.fallback_locktime,
        ) {
            (None, None, fallback) => fallback.unwrap_or_default(),
            (Some(lock), None, _) => lock.into(),
            (None, Some(lock), _) => lock.into(),
            (Some(lock1), Some(_lock2), Some(fallback)) if fallback.is_time_based() => lock1.into(),
            (Some(_lock1), Some(lock2), Some(fallback)) if fallback.is_height_based() => {
                lock2.into()
            }
            (Some(lock1), Some(_lock2), _) => lock1.into(),
        }
    }

    pub(crate) fn tx_version(&self) -> i32 { i32::from_be_bytes(self.tx_version.to_be_bytes()) }

    /// Returns fee for a transaction, or returns error reporting resolver
    /// problem or wrong transaction structure
    pub fn fee(&self) -> Result<u64, FeeError> {
        let mut input_sum = 0;
        for inp in &self.inputs {
            input_sum += inp.input_prevout()?.value;
        }

        let output_sum = self.outputs.iter().map(|output| output.amount).sum();

        if input_sum < output_sum {
            Err(FeeError::InputsLessThanOutputs)
        } else {
            Ok(input_sum - output_sum)
        }
    }

    /// Returns transaction ID for an unsigned transaction. For SegWit
    /// transactions this is equal to the signed transaction id.
    #[inline]
    pub fn to_txid(&self) -> Txid { self.to_unsigned_tx().txid() }

    /// Constructs transaction with empty `scriptSig` and `witness`
    pub fn to_unsigned_tx(&self) -> Transaction {
        let version = self.tx_version();

        let lock_time = bitcoin::PackedLockTime(self.lock_time().into_consensus());

        let tx_inputs = self.inputs.iter().map(Input::to_unsigned_txin).collect();
        let tx_outputs = self.outputs.iter().map(Output::to_txout).collect();

        Transaction {
            version,
            lock_time,
            input: tx_inputs,
            output: tx_outputs,
        }
    }

    /// Returns transaction with empty `scriptSig` and `witness`
    pub fn into_unsigned_tx(self) -> Transaction {
        let version = self.tx_version();

        let lock_time = bitcoin::PackedLockTime(self.lock_time().into_consensus());

        let tx_inputs = self.inputs.iter().map(Input::to_unsigned_txin).collect();
        let tx_outputs = self.outputs.into_iter().map(Output::into_txout).collect();

        Transaction {
            version,
            lock_time,
            input: tx_inputs,
            output: tx_outputs,
        }
    }

    /// Extract the (partially) signed transaction from this PSBT by filling in
    /// the available signature information in place.
    #[inline]
    pub fn extract_signed_tx(&self) -> Transaction {
        let mut tx: Transaction = self.to_unsigned_tx();

        for (vin, psbtin) in tx.input.iter_mut().zip(self.inputs.iter()) {
            vin.script_sig = psbtin.final_script_sig.clone().unwrap_or_default().into();
            vin.witness = psbtin.final_script_witness.clone().unwrap_or_default();
        }

        tx
    }

    /// Combines this [`Psbt`] with `other` PSBT as described by BIP 174.
    ///
    /// In accordance with BIP 174 this function is commutative i.e.,
    /// `A.combine(B) == B.combine(A)`
    #[inline]
    pub fn combine(self, other: Self) -> Result<Self, Error> {
        let mut first = PsbtV0::from(self);
        first.combine(other.into())?;
        Ok(first.into())
    }
}

impl From<PsbtV0> for Psbt {
    fn from(v0: PsbtV0) -> Self {
        let tx = v0.unsigned_tx;

        let inputs = v0
            .inputs
            .into_iter()
            .zip(tx.input)
            .enumerate()
            .map(|(index, (input, txin))| Input::with(index, input, txin))
            .collect();

        let outputs = v0
            .outputs
            .into_iter()
            .zip(tx.output)
            .enumerate()
            .map(|(index, (output, txout))| Output::with(index, output, txout))
            .collect();

        let tx_version = u32::from_be_bytes(tx.version.to_be_bytes());

        let fallback_locktime = match tx.lock_time.0 {
            0 => None,
            other => Some(other.into()),
        };

        Psbt {
            // We need to serialize back in the same version we deserialzied from
            psbt_version: PsbtVersion::V0,
            xpub: v0.xpub,
            tx_version,
            fallback_locktime,
            inputs,
            outputs,
            proprietary: v0.proprietary,
            unknown: v0.unknown,
        }
    }
}

impl From<Psbt> for PsbtV0 {
    fn from(psbt: Psbt) -> Self {
        let version = psbt.tx_version();
        let lock_time = bitcoin::PackedLockTime(psbt.lock_time().into_consensus());

        let (v0_inputs, tx_inputs) = psbt.inputs.into_iter().map(Input::split).unzip();
        let (v0_outputs, tx_outputs) = psbt.outputs.into_iter().map(Output::split).unzip();

        let unsigned_tx = Transaction {
            version,
            lock_time,
            input: tx_inputs,
            output: tx_outputs,
        };

        PsbtV0 {
            unsigned_tx,
            version: PsbtVersion::V0 as u32,
            xpub: psbt.xpub,
            proprietary: psbt.proprietary,
            unknown: psbt.unknown,
            inputs: v0_inputs,
            outputs: v0_outputs,
        }
    }
}

// TODO: Implement own PSBT BIP174 serialization trait and its own custom error
//       type handling different PSBT versions.
impl Serialize for Psbt {
    fn serialize(&self) -> Vec<u8> { consensus::encode::serialize::<PsbtV0>(&self.clone().into()) }
}

impl Deserialize for Psbt {
    fn deserialize(bytes: &[u8]) -> Result<Self, consensus::encode::Error> {
        consensus::deserialize::<PsbtV0>(bytes).map(Psbt::from)
    }
}

impl Display for Psbt {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let engine = base64::engine::GeneralPurpose::new(
            &base64::alphabet::STANDARD,
            base64::engine::GeneralPurposeConfig::new(),
        );
        f.write_str(&engine.encode(self.serialize()))
    }
}

#[derive(Debug, Display, Error, From)]
#[display(inner)]
pub enum PsbtParseError {
    #[from]
    Data(consensus::encode::Error),

    #[from]
    Base64(base64::DecodeError),
}

impl FromStr for Psbt {
    type Err = PsbtParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let engine = base64::engine::GeneralPurpose::new(
            &base64::alphabet::STANDARD,
            base64::engine::GeneralPurposeConfig::new(),
        );
        let bytes = engine.decode(s)?;
        Psbt::deserialize(&bytes).map_err(PsbtParseError::from)
    }
}

#[cfg(test)]
mod test {
    use amplify::hex::FromHex;

    use super::*;

    #[test]
    #[ignore]
    fn psbt_bip174_serialization() {
        let hex = "\
            70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566\
            cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a91\
            4d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a914\
            3545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda50101000\
            00000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f\
            9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985fffff\
            fff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b4\
            0100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff020\
            0c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac\
            72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587024\
            7304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a\
            5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02\
            db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e\
            7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0\
            c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20\
            167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4\
            ea169393380734464f84f2ab300000000000000";

        let psbt = Psbt::from_str(hex).unwrap();
        let hex_prime = psbt.to_string();
        let psbt_prime = Psbt::deserialize(&Vec::from_hex(&hex_prime).unwrap()).unwrap();
        assert_eq!(psbt, psbt_prime);
        assert_eq!(hex, hex_prime);
    }
}