use bitcoin::{Transaction, TxIn, TxOut, Txid};
use crate::{Input, Psbt};
#[derive(
Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, Error
)]
#[display(doc_comments)]
pub enum InputMatchError {
NoInputTx,
NoTxidMatch(Txid),
UnmatchedInputNumber(u32),
}
pub trait InputPrevout {
fn input_prevout(&self, txin: &TxIn) -> Result<&TxOut, InputMatchError>;
}
#[derive(
Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, Error, From
)]
#[display(doc_comments)]
pub enum FeeError {
#[from]
MatchError(InputMatchError),
InputsLessThanOutputs,
}
pub trait Fee {
fn fee(&self) -> Result<u64, FeeError>;
}
impl InputPrevout for Input {
fn input_prevout(&self, txin: &TxIn) -> Result<&TxOut, InputMatchError> {
let txid = txin.previous_output.txid;
if let Some(txout) = &self.witness_utxo {
Ok(txout)
} else if let Some(tx) = &self.non_witness_utxo {
if tx.txid() != txid {
return Err(InputMatchError::NoTxidMatch(txid));
}
let prev_index = txin.previous_output.vout;
tx.output
.get(prev_index as usize)
.ok_or(InputMatchError::UnmatchedInputNumber(prev_index))
} else {
Err(InputMatchError::NoInputTx)
}
}
}
impl Fee for Psbt {
fn fee(&self) -> Result<u64, FeeError> {
let mut input_sum = 0;
for (inp, txin) in self.inputs.iter().zip(self.global.unsigned_tx.input.iter()) {
input_sum += inp.input_prevout(txin)?.value;
}
let output_sum = self
.global
.unsigned_tx
.output
.iter()
.map(|txout| txout.value)
.sum();
if input_sum < output_sum {
Err(FeeError::InputsLessThanOutputs)
} else {
Ok(input_sum - output_sum)
}
}
}
pub trait Tx {
#[inline]
fn to_txid(&self) -> Txid { self.to_transaction().txid() }
fn to_transaction(&self) -> Transaction;
}
impl Tx for Psbt {
#[inline]
fn to_transaction(&self) -> Transaction {
let mut tx = self.global.unsigned_tx.clone();
for txin in &mut tx.input {
txin.witness = Default::default();
txin.script_sig = Default::default();
}
tx
}
}