@@ -56,16 +56,17 @@ pub mod hardwaresigner;
5656
5757pub use utils:: IsDust ;
5858
59+ use crate :: descriptor;
5960#[ allow( deprecated) ]
6061use coin_selection:: DefaultCoinSelectionAlgorithm ;
6162use signer:: { SignOptions , SignerOrdering , SignersContainer , TransactionSigner } ;
6263use tx_builder:: { BumpFee , CreateTx , FeePolicy , TxBuilder , TxParams } ;
6364use utils:: { check_nsequence_rbf, After , Older , SecpCtx } ;
6465
65- use crate :: descriptor:: policy:: BuildSatisfaction ;
66+ use crate :: descriptor:: policy:: { BuildSatisfaction , PolicyError } ;
6667use crate :: descriptor:: {
67- calc_checksum, into_wallet_descriptor_checked, DerivedDescriptor , DescriptorMeta ,
68- ExtendedDescriptor , ExtractPolicy , IntoWalletDescriptor , Policy , XKeyUtils ,
68+ calc_checksum, into_wallet_descriptor_checked, DerivedDescriptor , DescriptorError ,
69+ DescriptorMeta , ExtendedDescriptor , ExtractPolicy , IntoWalletDescriptor , Policy , XKeyUtils ,
6970} ;
7071use crate :: error:: { Error , MiniscriptPsbtError } ;
7172use crate :: psbt:: PsbtUtils ;
@@ -401,6 +402,64 @@ pub enum InsertTxError {
401402 } ,
402403}
403404
405+ #[ cfg( feature = "std" ) ]
406+ impl < P : core:: fmt:: Display + core:: fmt:: Debug > std:: error:: Error for NewError < P > { }
407+
408+ #[ derive( Debug ) ]
409+ /// Error returned from [`TxBuilder::finish`]
410+ pub enum CreateTxError < P > {
411+ /// There was a problem with the descriptors passed in
412+ Descriptor ( DescriptorError ) ,
413+ /// We were unable to write wallet data to the persistence backend
414+ Persist ( P ) ,
415+ /// There was a problem while extracting and manipulating policies
416+ Policy ( PolicyError ) ,
417+ /// TODO: replace this with specific error types
418+ Bdk ( Error ) ,
419+ }
420+
421+ #[ cfg( feature = "std" ) ]
422+ impl < P > fmt:: Display for CreateTxError < P >
423+ where
424+ P : fmt:: Display ,
425+ {
426+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
427+ match self {
428+ Self :: Descriptor ( e) => e. fmt ( f) ,
429+ Self :: Persist ( e) => {
430+ write ! (
431+ f,
432+ "failed to write wallet data to persistence backend: {}" ,
433+ e
434+ )
435+ }
436+ Self :: Bdk ( e) => e. fmt ( f) ,
437+ Self :: Policy ( e) => e. fmt ( f) ,
438+ }
439+ }
440+ }
441+
442+ impl < P > From < descriptor:: error:: Error > for CreateTxError < P > {
443+ fn from ( err : descriptor:: error:: Error ) -> Self {
444+ CreateTxError :: Descriptor ( err)
445+ }
446+ }
447+
448+ impl < P > From < PolicyError > for CreateTxError < P > {
449+ fn from ( err : PolicyError ) -> Self {
450+ CreateTxError :: Policy ( err)
451+ }
452+ }
453+
454+ impl < P > From < Error > for CreateTxError < P > {
455+ fn from ( err : Error ) -> Self {
456+ CreateTxError :: Bdk ( err)
457+ }
458+ }
459+
460+ #[ cfg( feature = "std" ) ]
461+ impl < P : core:: fmt:: Display + core:: fmt:: Debug > std:: error:: Error for CreateTxError < P > { }
462+
404463impl < D > Wallet < D > {
405464 /// Initialize an empty [`Wallet`].
406465 pub fn new < E : IntoWalletDescriptor > (
@@ -1092,6 +1151,8 @@ impl<D> Wallet<D> {
10921151 /// # use std::str::FromStr;
10931152 /// # use bitcoin::*;
10941153 /// # use bdk::*;
1154+ /// # use bdk::wallet::{ChangeSet,CreateTxError};
1155+ /// # use bdk_chain::PersistBackend;
10951156 /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
10961157 /// # let mut wallet = doctest_wallet!();
10971158 /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
@@ -1103,7 +1164,7 @@ impl<D> Wallet<D> {
11031164 /// };
11041165 ///
11051166 /// // sign and broadcast ...
1106- /// # Ok::<(), bdk::Error >(())
1167+ /// # Ok::<(), CreateTxError<<() as PersistBackend<ChangeSet>>::WriteError> >(())
11071168 /// ```
11081169 ///
11091170 /// [`TxBuilder`]: crate::TxBuilder
@@ -1155,15 +1216,15 @@ impl<D> Wallet<D> {
11551216 && external_policy. requires_path ( )
11561217 && params. external_policy_path . is_none ( )
11571218 {
1158- return Err ( Error :: SpendingPolicyRequired ( KeychainKind :: External ) ) ;
1219+ return Err ( Error :: SpendingPolicyRequired ( KeychainKind :: External ) . into ( ) ) ;
11591220 } ;
11601221 // Same for the internal_policy path, if present
11611222 if let Some ( internal_policy) = & internal_policy {
11621223 if params. change_policy != tx_builder:: ChangeSpendPolicy :: ChangeForbidden
11631224 && internal_policy. requires_path ( )
11641225 && params. internal_policy_path . is_none ( )
11651226 {
1166- return Err ( Error :: SpendingPolicyRequired ( KeychainKind :: Internal ) ) ;
1227+ return Err ( Error :: SpendingPolicyRequired ( KeychainKind :: Internal ) . into ( ) ) ;
11671228 } ;
11681229 }
11691230
@@ -1192,13 +1253,14 @@ impl<D> Wallet<D> {
11921253
11931254 let version = match params. version {
11941255 Some ( tx_builder:: Version ( 0 ) ) => {
1195- return Err ( Error :: Generic ( "Invalid version `0`" . into ( ) ) )
1256+ return Err ( Error :: Generic ( "Invalid version `0`" . into ( ) ) . into ( ) )
11961257 }
11971258 Some ( tx_builder:: Version ( 1 ) ) if requirements. csv . is_some ( ) => {
11981259 return Err ( Error :: Generic (
11991260 "TxBuilder requested version `1`, but at least `2` is needed to use OP_CSV"
12001261 . into ( ) ,
1201- ) )
1262+ )
1263+ . into ( ) )
12021264 }
12031265 Some ( tx_builder:: Version ( x) ) => x,
12041266 None if requirements. csv . is_some ( ) => 2 ,
@@ -1240,7 +1302,7 @@ impl<D> Wallet<D> {
12401302 // Specific nLockTime required and it's compatible with the constraints
12411303 Some ( x) if requirements. timelock . unwrap ( ) . is_same_unit ( x) && x >= requirements. timelock . unwrap ( ) => x,
12421304 // Invalid nLockTime required
1243- Some ( x) => return Err ( Error :: Generic ( format ! ( "TxBuilder requested timelock of `{:?}`, but at least `{:?}` is required to spend from this script" , x, requirements. timelock. unwrap( ) ) ) )
1305+ Some ( x) => return Err ( Error :: Generic ( format ! ( "TxBuilder requested timelock of `{:?}`, but at least `{:?}` is required to spend from this script" , x, requirements. timelock. unwrap( ) ) ) . into ( ) )
12441306 } ;
12451307
12461308 let n_sequence = match ( params. rbf , requirements. csv ) {
@@ -1260,7 +1322,8 @@ impl<D> Wallet<D> {
12601322 ( Some ( tx_builder:: RbfValue :: Value ( rbf) ) , _) if !rbf. is_rbf ( ) => {
12611323 return Err ( Error :: Generic (
12621324 "Cannot enable RBF with a nSequence >= 0xFFFFFFFE" . into ( ) ,
1263- ) )
1325+ )
1326+ . into ( ) )
12641327 }
12651328 // RBF with a specific value requested, but the value is incompatible with CSV
12661329 ( Some ( tx_builder:: RbfValue :: Value ( rbf) ) , Some ( csv) )
@@ -1269,7 +1332,8 @@ impl<D> Wallet<D> {
12691332 return Err ( Error :: Generic ( format ! (
12701333 "Cannot enable RBF with nSequence `{:?}` given a required OP_CSV of `{:?}`" ,
12711334 rbf, csv
1272- ) ) )
1335+ ) )
1336+ . into ( ) )
12731337 }
12741338
12751339 // RBF enabled with the default value with CSV also enabled. CSV takes precedence
@@ -1290,7 +1354,8 @@ impl<D> Wallet<D> {
12901354 if * fee < previous_fee. absolute {
12911355 return Err ( Error :: FeeTooLow {
12921356 required : previous_fee. absolute ,
1293- } ) ;
1357+ }
1358+ . into ( ) ) ;
12941359 }
12951360 }
12961361 ( FeeRate :: from_sat_per_vb ( 0.0 ) , * fee)
@@ -1301,7 +1366,8 @@ impl<D> Wallet<D> {
13011366 if * rate < required_feerate {
13021367 return Err ( Error :: FeeRateTooLow {
13031368 required : required_feerate,
1304- } ) ;
1369+ }
1370+ . into ( ) ) ;
13051371 }
13061372 }
13071373 ( * rate, 0 )
@@ -1316,7 +1382,7 @@ impl<D> Wallet<D> {
13161382 } ;
13171383
13181384 if params. manually_selected_only && params. utxos . is_empty ( ) {
1319- return Err ( Error :: NoUtxosSelected ) ;
1385+ return Err ( Error :: NoUtxosSelected . into ( ) ) ;
13201386 }
13211387
13221388 // we keep it as a float while we accumulate it, and only round it at the end
@@ -1330,7 +1396,7 @@ impl<D> Wallet<D> {
13301396 && value. is_dust ( script_pubkey)
13311397 && !script_pubkey. is_provably_unspendable ( )
13321398 {
1333- return Err ( Error :: OutputBelowDustLimit ( index) ) ;
1399+ return Err ( Error :: OutputBelowDustLimit ( index) . into ( ) ) ;
13341400 }
13351401
13361402 if self . is_mine ( script_pubkey) {
@@ -1365,7 +1431,8 @@ impl<D> Wallet<D> {
13651431 {
13661432 return Err ( Error :: Generic (
13671433 "The `change_policy` can be set only if the wallet has a change_descriptor" . into ( ) ,
1368- ) ) ;
1434+ )
1435+ . into ( ) ) ;
13691436 }
13701437
13711438 let ( required_utxos, optional_utxos) = self . preselect_utxos (
@@ -1391,7 +1458,7 @@ impl<D> Wallet<D> {
13911458 . stage ( ChangeSet :: from ( indexed_tx_graph:: ChangeSet :: from (
13921459 index_changeset,
13931460 ) ) ) ;
1394- self . persist . commit ( ) . expect ( "TODO" ) ;
1461+ self . persist . commit ( ) . map_err ( CreateTxError :: Persist ) ? ;
13951462 spk
13961463 }
13971464 } ;
@@ -1435,10 +1502,11 @@ impl<D> Wallet<D> {
14351502 return Err ( Error :: InsufficientFunds {
14361503 needed : * dust_threshold,
14371504 available : remaining_amount. saturating_sub ( * change_fee) ,
1438- } ) ;
1505+ }
1506+ . into ( ) ) ;
14391507 }
14401508 } else {
1441- return Err ( Error :: NoRecipients ) ;
1509+ return Err ( Error :: NoRecipients . into ( ) ) ;
14421510 }
14431511 }
14441512
@@ -1485,6 +1553,8 @@ impl<D> Wallet<D> {
14851553 /// # use std::str::FromStr;
14861554 /// # use bitcoin::*;
14871555 /// # use bdk::*;
1556+ /// # use bdk::wallet::{ChangeSet, CreateTxError};
1557+ /// # use bdk_chain::PersistBackend;
14881558 /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
14891559 /// # let mut wallet = doctest_wallet!();
14901560 /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
@@ -1508,7 +1578,7 @@ impl<D> Wallet<D> {
15081578 /// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
15091579 /// let fee_bumped_tx = psbt.extract_tx();
15101580 /// // broadcast fee_bumped_tx to replace original
1511- /// # Ok::<(), bdk::Error >(())
1581+ /// # Ok::<(), CreateTxError<<() as PersistBackend<ChangeSet>>::WriteError> >(())
15121582 /// ```
15131583 // TODO: support for merging multiple transactions while bumping the fees
15141584 pub fn build_fee_bump (
@@ -1655,6 +1725,8 @@ impl<D> Wallet<D> {
16551725 /// # use std::str::FromStr;
16561726 /// # use bitcoin::*;
16571727 /// # use bdk::*;
1728+ /// # use bdk::wallet::{ChangeSet, CreateTxError};
1729+ /// # use bdk_chain::PersistBackend;
16581730 /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
16591731 /// # let mut wallet = doctest_wallet!();
16601732 /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
@@ -1665,7 +1737,7 @@ impl<D> Wallet<D> {
16651737 /// };
16661738 /// let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
16671739 /// assert!(finalized, "we should have signed all the inputs");
1668- /// # Ok::<(), bdk::Error >(())
1740+ /// # Ok::<(), CreateTxError<<() as PersistBackend<ChangeSet>>::WriteError> >(())
16691741 pub fn sign (
16701742 & self ,
16711743 psbt : & mut psbt:: PartiallySignedTransaction ,
0 commit comments