diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index 42473bc3..c5966f25 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -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); @@ -302,7 +328,7 @@ interface TxBuilder { TxBuilder enable_rbf_with_sequence(u32 nsequence); - [Throws=Alpha3Error] + [Throws=CreateTxError] Psbt finish([ByRef] Wallet wallet); }; diff --git a/bdk-ffi/src/error.rs b/bdk-ffi/src/error.rs index 2ce25a05..8d3bfe16 100644 --- a/bdk-ffi/src/error.rs +++ b/bdk-ffi/src/error.rs @@ -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}; @@ -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)] @@ -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> for CreateTxError { + fn from(error: BdkCreateTxError) -> 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 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:?}")] @@ -492,12 +627,6 @@ impl From for Alpha3Error { } } -impl From> for Alpha3Error { - fn from(_: CreateTxError) -> Self { - Alpha3Error::Generic - } -} - impl From for Alpha3Error { fn from(_: AddUtxoError) -> Self { Alpha3Error::Generic @@ -544,12 +673,6 @@ impl From> for Alpha3Error { } } -impl From> for Alpha3Error { - fn from(_: CreateTxError) -> Self { - Alpha3Error::Generic - } -} - impl From for CalculateFeeError { fn from(error: BdkCalculateFeeError) -> Self { match error { diff --git a/bdk-ffi/src/lib.rs b/bdk-ffi/src/lib.rs index a54863ae..4dd7e12a 100644 --- a/bdk-ffi/src/lib.rs +++ b/bdk-ffi/src/lib.rs @@ -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; diff --git a/bdk-ffi/src/wallet.rs b/bdk-ffi/src/wallet.rs index 36f7dea6..4a66718f 100644 --- a/bdk-ffi/src/wallet.rs +++ b/bdk-ffi/src/wallet.rs @@ -1,8 +1,8 @@ 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}; @@ -489,7 +489,7 @@ impl TxBuilder { // }) // } - pub(crate) fn finish(&self, wallet: &Arc) -> Result, Alpha3Error> { + pub(crate) fn finish(&self, wallet: &Arc) -> Result, 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(); @@ -499,8 +499,9 @@ impl TxBuilder { tx_builder.change_policy(self.change_policy); if !self.utxos.is_empty() { let bdk_utxos: Vec = 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)?; } if !self.unspendable.is_empty() { let bdk_unspendable: Vec = @@ -536,7 +537,7 @@ impl TxBuilder { // tx_builder.add_data(self.data.as_slice()); // } - let psbt = tx_builder.finish()?; + let psbt = tx_builder.finish().map_err(CreateTxError::from)?; Ok(Arc::new(psbt.into())) }