Skip to content

Commit

Permalink
feat: add finish related error
Browse files Browse the repository at this point in the history
  • Loading branch information
reez committed Apr 25, 2024
1 parent 282fcfc commit 34f2b45
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 23 deletions.
28 changes: 27 additions & 1 deletion bdk-ffi/src/bdk.udl
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,32 @@ interface WalletCreationError {
LoadedNetworkDoesNotMatch(Network expected, Network? got);
};

[Error]
interface CreateTxError {
Descriptor(string e);
Persist(string e);
Policy(string e);
SpendingPolicyRequired(string kind);
Version0();
Version1Csv();
LockTime(string requested, string required);
RbfSequence();
RbfSequenceCsv(string rbf, string csv);
FeeTooLow(u64 required);
FeeRateTooLow(string required);
NoUtxosSelected();
OutputBelowDustLimit(u64 index);
ChangePolicyDescriptor();
CoinSelection(string e);
InsufficientFunds(u64 needed, u64 available);
NoRecipients();
Psbt(string e);
MissingKeyOrigin(string key);
UnknownUtxo(string outpoint);
MissingNonWitnessUtxo(string outpoint);
MiniscriptPsbt(string e);
};

[Error]
interface PersistenceError {
Write(string e);
Expand Down Expand Up @@ -302,7 +328,7 @@ interface TxBuilder {

TxBuilder enable_rbf_with_sequence(u32 nsequence);

[Throws=Alpha3Error]
[Throws=CreateTxError]
Psbt finish([ByRef] Wallet wallet);
};

Expand Down
151 changes: 137 additions & 14 deletions bdk-ffi/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use bdk::bitcoin::psbt::PsbtParseError as BdkPsbtParseError;
use bdk::bitcoin::Network;
use bdk::chain::tx_graph::CalculateFeeError as BdkCalculateFeeError;
use bdk::descriptor::DescriptorError as BdkDescriptorError;
use bdk::wallet::error::{BuildFeeBumpError, CreateTxError};
use bdk::wallet::error::BuildFeeBumpError;
use bdk::wallet::signer::SignerError as BdkSignerError;
use bdk::wallet::tx_builder::{AddUtxoError, AllowShrinkingError};
use bdk::wallet::{NewError, NewOrLoadError};
Expand All @@ -19,7 +19,7 @@ use bdk::keys::bip39::Error as BdkBip39Error;

use bdk::bitcoin::bip32;

use std::convert::Infallible;
use bdk::wallet::error::CreateTxError as BdkCreateTxError;
use std::convert::TryInto;

#[derive(Debug, thiserror::Error)]
Expand Down Expand Up @@ -64,6 +64,141 @@ pub enum Bip32Error {
UnknownError { e: String },
}

#[derive(Debug, thiserror::Error)]
pub enum CreateTxError {
#[error("Descriptor error: {e}")]
Descriptor { e: String },

#[error("Persistence failure: {e}")]
Persist { e: String },

#[error("Policy error: {e}")]
Policy { e: String },

#[error("Spending policy required for {kind}")]
SpendingPolicyRequired { kind: String },

#[error("Unsupported version 0")]
Version0,

#[error("Unsupported version 1 with CSV")]
Version1Csv,

#[error("Lock time conflict: requested {requested}, but required {required}")]
LockTime { requested: String, required: String },

#[error("Transaction requires RBF sequence number")]
RbfSequence,

#[error("RBF sequence: {rbf}, CSV sequence: {csv}")]
RbfSequenceCsv { rbf: String, csv: String },

#[error("Fee too low: {required} sat required")]
FeeTooLow { required: u64 },

#[error("Fee rate too low: {required}")]
FeeRateTooLow { required: String },

#[error("No UTXOs selected for the transaction")]
NoUtxosSelected,

#[error("Output value below dust limit at index {index}")]
OutputBelowDustLimit { index: u64 },

#[error("Change policy descriptor error")]
ChangePolicyDescriptor,

#[error("Coin selection failed: {e}")]
CoinSelection { e: String },

#[error("Insufficient funds: needed {needed} sat, available {available} sat")]
InsufficientFunds { needed: u64, available: u64 },

#[error("Transaction has no recipients")]
NoRecipients,

#[error("PSBT creation error: {e}")]
Psbt { e: String },

#[error("Missing key origin for: {key}")]
MissingKeyOrigin { key: String },

#[error("Reference to an unknown UTXO: {outpoint}")]
UnknownUtxo { outpoint: String },

#[error("Missing non-witness UTXO for outpoint: {outpoint}")]
MissingNonWitnessUtxo { outpoint: String },

#[error("Miniscript PSBT error: {e}")]
MiniscriptPsbt { e: String },
}

impl From<BdkCreateTxError<std::io::Error>> for CreateTxError {
fn from(error: BdkCreateTxError<std::io::Error>) -> Self {
match error {
BdkCreateTxError::Descriptor(e) => CreateTxError::Descriptor { e: e.to_string() },
BdkCreateTxError::Persist(e) => CreateTxError::Persist { e: e.to_string() },
BdkCreateTxError::Policy(e) => CreateTxError::Policy { e: e.to_string() },
BdkCreateTxError::SpendingPolicyRequired(kind) => {
CreateTxError::SpendingPolicyRequired {
kind: format!("{:?}", kind),
}
}
BdkCreateTxError::Version0 => CreateTxError::Version0,
BdkCreateTxError::Version1Csv => CreateTxError::Version1Csv,
BdkCreateTxError::LockTime {
requested,
required,
} => CreateTxError::LockTime {
requested: requested.to_string(),
required: required.to_string(),
},
BdkCreateTxError::RbfSequence => CreateTxError::RbfSequence,
BdkCreateTxError::RbfSequenceCsv { rbf, csv } => CreateTxError::RbfSequenceCsv {
rbf: rbf.to_string(),
csv: csv.to_string(),
},
BdkCreateTxError::FeeTooLow { required } => CreateTxError::FeeTooLow { required },
BdkCreateTxError::FeeRateTooLow { required } => CreateTxError::FeeRateTooLow {
required: required.to_string(),
},
BdkCreateTxError::NoUtxosSelected => CreateTxError::NoUtxosSelected,
BdkCreateTxError::OutputBelowDustLimit(index) => CreateTxError::OutputBelowDustLimit {
index: index as u64,
},
BdkCreateTxError::ChangePolicyDescriptor => CreateTxError::ChangePolicyDescriptor,
BdkCreateTxError::CoinSelection(e) => CreateTxError::CoinSelection { e: e.to_string() },
BdkCreateTxError::InsufficientFunds { needed, available } => {
CreateTxError::InsufficientFunds { needed, available }
}
BdkCreateTxError::NoRecipients => CreateTxError::NoRecipients,
BdkCreateTxError::Psbt(e) => CreateTxError::Psbt { e: e.to_string() },
BdkCreateTxError::MissingKeyOrigin(key) => CreateTxError::MissingKeyOrigin { key },
BdkCreateTxError::UnknownUtxo => CreateTxError::UnknownUtxo {
outpoint: "Unknown".to_string(),
},
BdkCreateTxError::MissingNonWitnessUtxo(outpoint) => {
CreateTxError::MissingNonWitnessUtxo {
outpoint: outpoint.to_string(),
}
}
BdkCreateTxError::MiniscriptPsbt(e) => {
CreateTxError::MiniscriptPsbt { e: e.to_string() }
}
}
}
}

impl From<AddUtxoError> for CreateTxError {
fn from(error: AddUtxoError) -> Self {
match error {
AddUtxoError::UnknownUtxo(outpoint) => CreateTxError::UnknownUtxo {
outpoint: outpoint.to_string(),
},
}
}
}

#[derive(Debug, thiserror::Error)]
pub enum CalculateFeeError {
#[error("missing transaction output: {out_points:?}")]
Expand Down Expand Up @@ -492,12 +627,6 @@ impl From<BuildFeeBumpError> for Alpha3Error {
}
}

impl From<CreateTxError<Infallible>> for Alpha3Error {
fn from(_: CreateTxError<Infallible>) -> Self {
Alpha3Error::Generic
}
}

impl From<AddUtxoError> for Alpha3Error {
fn from(_: AddUtxoError) -> Self {
Alpha3Error::Generic
Expand Down Expand Up @@ -544,12 +673,6 @@ impl From<NewError<std::io::Error>> for Alpha3Error {
}
}

impl From<CreateTxError<std::io::Error>> for Alpha3Error {
fn from(_: CreateTxError<std::io::Error>) -> Self {
Alpha3Error::Generic
}
}

impl From<BdkCalculateFeeError> for CalculateFeeError {
fn from(error: BdkCalculateFeeError) -> Self {
match error {
Expand Down
1 change: 1 addition & 0 deletions bdk-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::error::Bip32Error;
use crate::error::Bip39Error;
use crate::error::CalculateFeeError;
use crate::error::CannotConnectError;
use crate::error::CreateTxError;
use crate::error::DescriptorError;
use crate::error::EsploraError;
use crate::error::ExtractTxError;
Expand Down
40 changes: 32 additions & 8 deletions bdk-ffi/src/wallet.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::bitcoin::{FeeRate, OutPoint, Psbt, Script, Transaction};
use crate::descriptor::Descriptor;
use crate::error::{
Alpha3Error, CalculateFeeError, CannotConnectError, PersistenceError, SignerError,
TxidParseError, WalletCreationError,
Alpha3Error, CalculateFeeError, CannotConnectError, CreateTxError, PersistenceError,
SignerError, TxidParseError, WalletCreationError,
};

use crate::types::{AddressIndex, AddressInfo, Balance, CanonicalTx, LocalOutput, ScriptAmount};

use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf;
Expand Down Expand Up @@ -489,39 +490,60 @@ impl TxBuilder {
// })
// }

pub(crate) fn finish(&self, wallet: &Arc<Wallet>) -> Result<Arc<Psbt>, Alpha3Error> {
pub(crate) fn finish(&self, wallet: &Arc<Wallet>) -> Result<Arc<Psbt>, CreateTxError> {
// TODO: I had to change the wallet here to be mutable. Why is that now required with the 1.0 API?
let mut wallet = wallet.get_wallet();
let mut tx_builder = wallet.build_tx();
let mut wallet_lock = wallet.get_wallet();
let mut tx_builder = wallet_lock.build_tx();

// Add recipients
for (script, amount) in &self.recipients {
tx_builder.add_recipient(script.clone(), *amount);
}

// Set change policy
tx_builder.change_policy(self.change_policy);

// Add UTXOs if they exist
if !self.utxos.is_empty() {
let bdk_utxos: Vec<BdkOutPoint> = self.utxos.iter().map(BdkOutPoint::from).collect();
let utxos: &[BdkOutPoint] = &bdk_utxos;
tx_builder.add_utxos(utxos)?;
tx_builder
.add_utxos(&bdk_utxos)
.map_err(CreateTxError::from)?;
}

// Mark unspendable UTXOs
if !self.unspendable.is_empty() {
let bdk_unspendable: Vec<BdkOutPoint> =
self.unspendable.iter().map(BdkOutPoint::from).collect();
tx_builder.unspendable(bdk_unspendable);
}

// Manually select UTXOs
if self.manually_selected_only {
tx_builder.manually_selected_only();
}

// Set fee rate if specified
if let Some(fee_rate) = &self.fee_rate {
tx_builder.fee_rate(fee_rate.0);
}

// Set absolute fee if specified
if let Some(fee_amount) = self.fee_absolute {
tx_builder.fee_absolute(fee_amount);
}

// Configure transaction to drain wallet
if self.drain_wallet {
tx_builder.drain_wallet();
}

// Specify address to drain to
if let Some(script) = &self.drain_to {
tx_builder.drain_to(script.clone());
}

// Setup Replace-by-Fee (RBF) options
if let Some(rbf) = &self.rbf {
match *rbf {
RbfValue::Default => {
Expand All @@ -532,11 +554,13 @@ impl TxBuilder {
}
}
}

// if !&self.data.is_empty() {
// tx_builder.add_data(self.data.as_slice());
// }

let psbt = tx_builder.finish()?;
// Finalize the transaction
let psbt = tx_builder.finish().map_err(CreateTxError::from)?;

Ok(Arc::new(psbt.into()))
}
Expand Down

0 comments on commit 34f2b45

Please sign in to comment.