// You may not use this file except in accordance with one or both of these
// licenses.
-//! Errors
-//!
-//! This module defines the errors that can be thrown by [`crate`] functions.
-
use crate::bitcoin::Network;
use crate::{descriptor, wallet};
use alloc::{string::String, vec::Vec};
Psbt(bitcoin::psbt::Error),
}
-/// Errors returned by `Wallet::calculate_fee`.
-#[derive(Debug)]
-pub enum CalculateFeeError {
- /// Missing `TxOut` for one of the inputs of the tx
- MissingTxOut,
- /// When the transaction is invalid according to the graph it has a negative fee
- NegativeFee(i64),
-}
-
/// Errors returned by miniscript when updating inconsistent PSBTs
-#[allow(missing_docs)] // TODO add docs
#[derive(Debug, Clone)]
pub enum MiniscriptPsbtError {
Conversion(miniscript::descriptor::ConversionError),
#[allow(unused_imports)]
#[macro_use]
-pub mod error;
+pub(crate) mod error;
pub mod descriptor;
pub mod keys;
pub mod psbt;
use core::ops::Deref;
use miniscript::psbt::{PsbtExt, PsbtInputExt, PsbtInputSatisfier};
+use bdk_chain::tx_graph::CalculateFeeError;
#[allow(unused_imports)]
use log::{debug, error, info, trace};
calc_checksum, into_wallet_descriptor_checked, DerivedDescriptor, DescriptorMeta,
ExtendedDescriptor, ExtractPolicy, IntoWalletDescriptor, Policy, XKeyUtils,
};
-use crate::error::{CalculateFeeError, Error, MiniscriptPsbtError};
+use crate::error::{Error, MiniscriptPsbtError};
use crate::psbt::PsbtUtils;
use crate::signer::SignerError;
use crate::types::*;
///
/// Note `tx` does not have to be in the graph for this to work.
pub fn calculate_fee(&self, tx: &Transaction) -> Result<u64, CalculateFeeError> {
- match self.indexed_graph.graph().calculate_fee(tx) {
- None => Err(CalculateFeeError::MissingTxOut),
- Some(fee) if fee < 0 => Err(CalculateFeeError::NegativeFee(fee)),
- Some(fee) => Ok(u64::try_from(fee).unwrap()),
- }
+ self.indexed_graph.graph().calculate_fee(tx)
}
/// Calculate the `FeeRate` for a given transaction.
return Err(Error::IrreplaceableTransaction);
}
- let fee = graph.calculate_fee(&tx).ok_or(Error::FeeRateUnavailable)?;
- if fee < 0 {
- // It's available but it's wrong so let's say it's unavailable
- return Err(Error::FeeRateUnavailable)?;
- }
- let fee = fee as u64;
- let feerate = FeeRate::from_wu(fee, tx.weight());
+ let fee = self
+ .calculate_fee(&tx)
+ .map_err(|_| Error::FeeRateUnavailable)?;
+ let fee_rate = self
+ .calculate_fee_rate(&tx)
+ .map_err(|_| Error::FeeRateUnavailable)?;
// remove the inputs from the tx and process them
let original_txin = tx.input.drain(..).collect::<Vec<_>>();
utxos: original_utxos,
bumping_fee: Some(tx_builder::PreviousFee {
absolute: fee,
- rate: feerate.as_sat_per_vb(),
+ rate: fee_rate.as_sat_per_vb(),
}),
..Default::default()
};
use bdk::wallet::AddressIndex::*;
use bdk::wallet::{AddressIndex, AddressInfo, Balance, Wallet};
use bdk::{Error, FeeRate, KeychainKind};
+use bdk_chain::tx_graph::CalculateFeeError;
use bdk_chain::COINBASE_MATURITY;
use bdk_chain::{BlockId, ConfirmationTime};
use bitcoin::hashes::Hash;
fn test_get_funded_wallet_tx_fees() {
let (wallet, _) = get_funded_wallet(get_test_wpkh());
assert_eq!(wallet.get_balance().confirmed, 50000);
- let mut tx_fee_amounts: Vec<(Txid, Result<u64, bdk::error::CalculateFeeError>)> = wallet
+ let mut tx_fee_amounts: Vec<(Txid, Result<u64, CalculateFeeError>)> = wallet
.transactions()
.map(|ct| {
let fee = wallet.calculate_fee(ct.node.tx);
assert_eq!(tx_fee_amounts.len(), 2);
assert_matches!(
tx_fee_amounts.get(1),
- Some((_, Err(bdk::error::CalculateFeeError::MissingTxOut)))
+ Some((_, Err(CalculateFeeError::MissingTxOut(_))))
);
assert_matches!(tx_fee_amounts.get(0), Some((_, Ok(1000))))
}
fn test_get_funded_wallet_tx_fee_rate() {
let (wallet, _) = get_funded_wallet(get_test_wpkh());
assert_eq!(wallet.get_balance().confirmed, 50000);
- let mut tx_fee_rates: Vec<(Txid, Result<FeeRate, bdk::error::CalculateFeeError>)> = wallet
+ let mut tx_fee_rates: Vec<(Txid, Result<FeeRate, CalculateFeeError>)> = wallet
.transactions()
.map(|ct| {
let fee_rate = wallet.calculate_fee_rate(ct.node.tx);
assert_eq!(tx_fee_rates.len(), 2);
assert_matches!(
tx_fee_rates.get(1),
- Some((_, Err(bdk::error::CalculateFeeError::MissingTxOut)))
+ Some((_, Err(CalculateFeeError::MissingTxOut(_))))
);
assert_matches!(tx_fee_rates.get(0), Some((_, Ok(_))))
}
pub tx_node: TxNode<'a, T, A>,
}
+/// Errors returned by `TxGraph::calculate_fee`.
+#[derive(Debug, PartialEq, Eq)]
+pub enum CalculateFeeError {
+ /// Missing `TxOut` for one or more of the inputs of the tx
+ MissingTxOut(Vec<OutPoint>),
+ /// When the transaction is invalid according to the graph it has a negative fee
+ NegativeFee(i64),
+}
+
impl<A> TxGraph<A> {
/// Iterate over all tx outputs known by [`TxGraph`].
///
}
/// Calculates the fee of a given transaction. Returns 0 if `tx` is a coinbase transaction.
- /// Returns `Some(_)` if we have all the `TxOut`s being spent by `tx` in the graph (either as
- /// the full transactions or individual txouts). If the returned value is negative, then the
- /// transaction is invalid according to the graph.
- ///
- /// Returns `None` if we're missing an input for the tx in the graph.
+ /// Returns `OK(_)` if we have all the `TxOut`s being spent by `tx` in the graph (either as
+ /// the full transactions or individual txouts).
///
/// Note `tx` does not have to be in the graph for this to work.
- pub fn calculate_fee(&self, tx: &Transaction) -> Option<i64> {
+ pub fn calculate_fee(&self, tx: &Transaction) -> Result<u64, CalculateFeeError> {
if tx.is_coin_base() {
- return Some(0);
+ return Ok(0);
}
- let inputs_sum = tx
- .input
- .iter()
- .map(|txin| {
- self.get_txout(txin.previous_output)
- .map(|txout| txout.value as i64)
- })
- .sum::<Option<i64>>()?;
+ let inputs_sum = tx.input.iter().fold(
+ (0_u64, Vec::new()),
+ |(mut sum, mut missing_outpoints), txin| match self.get_txout(txin.previous_output) {
+ None => {
+ missing_outpoints.push(txin.previous_output);
+ (sum, missing_outpoints)
+ }
+ Some(txout) => {
+ sum += txout.value;
+ (sum, missing_outpoints)
+ }
+ },
+ );
+
+ let inputs_sum = if inputs_sum.1.is_empty() {
+ Ok(inputs_sum.0 as i64)
+ } else {
+ Err(CalculateFeeError::MissingTxOut(inputs_sum.1))
+ }?;
let outputs_sum = tx
.output
.map(|txout| txout.value as i64)
.sum::<i64>();
- Some(inputs_sum - outputs_sum)
+ let fee = inputs_sum - outputs_sum;
+ if fee < 0 {
+ Err(CalculateFeeError::NegativeFee(fee))
+ } else {
+ Ok(fee as u64)
+ }
}
/// The transactions spending from this output.
#[macro_use]
mod common;
+use bdk_chain::tx_graph::CalculateFeeError;
use bdk_chain::{
collections::*,
local_chain::LocalChain,
}],
};
- assert_eq!(graph.calculate_fee(&tx), Some(100));
+ assert_eq!(graph.calculate_fee(&tx), Ok(100));
tx.input.remove(2);
- // fee would be negative
- assert_eq!(graph.calculate_fee(&tx), Some(-200));
+ // fee would be negative, should return CalculateFeeError::NegativeFee
+ assert_eq!(
+ graph.calculate_fee(&tx),
+ Err(CalculateFeeError::NegativeFee(-200))
+ );
- // If we have an unknown outpoint, fee should return None.
+ // If we have an unknown outpoint, fee should return CalculateFeeError::MissingTxOut.
+ let outpoint = OutPoint {
+ txid: h!("unknown_txid"),
+ vout: 0,
+ };
tx.input.push(TxIn {
- previous_output: OutPoint {
- txid: h!("unknown_txid"),
- vout: 0,
- },
+ previous_output: outpoint,
..Default::default()
});
- assert_eq!(graph.calculate_fee(&tx), None);
+ assert_eq!(
+ graph.calculate_fee(&tx),
+ Err(CalculateFeeError::MissingTxOut(vec!(outpoint)))
+ );
}
#[test]
let graph = TxGraph::<()>::default();
- assert_eq!(graph.calculate_fee(&tx), Some(0));
+ assert_eq!(graph.calculate_fee(&tx), Ok(0));
}
#[test]