use bitcoin::{TxOut, Txid};
use crate::Psbt;
#[derive(
Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, Error,
)]
#[display(doc_comments)]
pub enum MatchError {
NoInputTx(usize),
NoTxidMatch(usize, Txid),
UnmatchingInputNumber(usize),
WrongInputNo(usize),
}
pub trait InputPreviousTxo {
fn input_previous_txo(&self, index: usize) -> Result<&TxOut, MatchError>;
}
#[derive(
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Debug,
Display,
Error,
From,
)]
#[display(doc_comments)]
pub enum FeeError {
#[from]
MatchError(MatchError),
InputsLessThanOutputs,
}
pub trait Fee {
fn fee(&self) -> Result<u64, FeeError>;
}
impl InputPreviousTxo for Psbt {
fn input_previous_txo(&self, index: usize) -> Result<&TxOut, MatchError> {
if let (Some(input), Some(txin)) = (
self.inputs.get(index),
self.global.unsigned_tx.input.get(index),
) {
let txid = txin.previous_output.txid;
input
.witness_utxo
.as_ref()
.ok_or(MatchError::NoInputTx(index))
.or_else(|_| {
input
.non_witness_utxo
.as_ref()
.ok_or(MatchError::NoInputTx(index))
.and_then(|tx| {
if txid != tx.txid() {
Err(MatchError::NoTxidMatch(index, txid))
} else {
tx.output
.get(txin.previous_output.vout as usize)
.ok_or(MatchError::UnmatchingInputNumber(
index,
))
}
})
})
} else {
Err(MatchError::WrongInputNo(index))
}
}
}
impl Fee for Psbt {
fn fee(&self) -> Result<u64, FeeError> {
let mut input_sum = 0;
for index in 0..self.global.unsigned_tx.input.len() {
input_sum += self.input_previous_txo(index)?.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)
}
}
}