// Bitcoin Dev Kit // Written in 2020 by Alekos Filini // // Copyright (c) 2020-2021 Bitcoin Dev Kit Developers // // This file is licensed under the Apache License, Version 2.0 or the MIT license // , at your option. // You may not use this file except in accordance with one or both of these // licenses. //! Additional functions on the `rust-bitcoin` `PartiallySignedTransaction` structure. use crate::FeeRate; use alloc::vec::Vec; use bitcoin::util::psbt::PartiallySignedTransaction as Psbt; use bitcoin::TxOut; // TODO upstream the functions here to `rust-bitcoin`? /// Trait to add functions to extract utxos and calculate fees. pub trait PsbtUtils { /// Get the `TxOut` for the specified input index, if it doesn't exist in the PSBT `None` is returned. fn get_utxo_for(&self, input_index: usize) -> Option; /// The total transaction fee amount, sum of input amounts minus sum of output amounts, in sats. /// If the PSBT is missing a TxOut for an input returns None. fn fee_amount(&self) -> Option; /// The transaction's fee rate. This value will only be accurate if calculated AFTER the /// `PartiallySignedTransaction` is finalized and all witness/signature data is added to the /// transaction. /// If the PSBT is missing a TxOut for an input returns None. fn fee_rate(&self) -> Option; } impl PsbtUtils for Psbt { #[allow(clippy::all)] // We want to allow `manual_map` but it is too new. fn get_utxo_for(&self, input_index: usize) -> Option { let tx = &self.unsigned_tx; if input_index >= tx.input.len() { return None; } if let Some(input) = self.inputs.get(input_index) { if let Some(wit_utxo) = &input.witness_utxo { Some(wit_utxo.clone()) } else if let Some(in_tx) = &input.non_witness_utxo { Some(in_tx.output[tx.input[input_index].previous_output.vout as usize].clone()) } else { None } } else { None } } fn fee_amount(&self) -> Option { let tx = &self.unsigned_tx; let utxos: Option> = (0..tx.input.len()).map(|i| self.get_utxo_for(i)).collect(); utxos.map(|inputs| { let input_amount: u64 = inputs.iter().map(|i| i.value).sum(); let output_amount: u64 = self.unsigned_tx.output.iter().map(|o| o.value).sum(); input_amount .checked_sub(output_amount) .expect("input amount must be greater than output amount") }) } fn fee_rate(&self) -> Option { let fee_amount = self.fee_amount(); fee_amount.map(|fee| { let weight = self.clone().extract_tx().weight(); FeeRate::from_wu(fee, weight) }) } }