@@ -446,15 +446,52 @@ where
446446 self . database . borrow ( ) . iter_txs ( include_raw)
447447 }
448448
449- /// Return the balance, meaning the sum of this wallet's unspent outputs' values
449+ /// Return the balance, separated into available, trusted-pending, untrusted-pending and immature
450+ /// values.
450451 ///
451452 /// Note that this methods only operate on the internal database, which first needs to be
452453 /// [`Wallet::sync`] manually.
453- pub fn get_balance ( & self ) -> Result < u64 , Error > {
454- Ok ( self
455- . list_unspent ( ) ?
456- . iter ( )
457- . fold ( 0 , |sum, i| sum + i. txout . value ) )
454+ pub fn get_balance ( & self ) -> Result < Balance , Error > {
455+ let mut immature = 0 ;
456+ let mut trusted_pending = 0 ;
457+ let mut untrusted_pending = 0 ;
458+ let mut available = 0 ;
459+ let utxos = self . list_unspent ( ) ?;
460+ let database = self . database . borrow ( ) ;
461+ let last_sync_height = match database
462+ . get_sync_time ( ) ?
463+ . map ( |sync_time| sync_time. block_time . height )
464+ {
465+ Some ( height) => height,
466+ // None means database was never synced
467+ None => return Ok ( Balance :: default ( ) ) ,
468+ } ;
469+ for u in utxos {
470+ // Unwrap used since utxo set is created from database
471+ let tx = database
472+ . get_tx ( & u. outpoint . txid , true ) ?
473+ . expect ( "Transaction not found in database" ) ;
474+ if let Some ( tx_conf_time) = & tx. confirmation_time {
475+ if tx. transaction . expect ( "No transaction" ) . is_coin_base ( )
476+ && ( last_sync_height - tx_conf_time. height ) < COINBASE_MATURITY
477+ {
478+ immature += u. txout . value ;
479+ } else {
480+ available += u. txout . value ;
481+ }
482+ } else if u. keychain == KeychainKind :: Internal {
483+ trusted_pending += u. txout . value ;
484+ } else {
485+ untrusted_pending += u. txout . value ;
486+ }
487+ }
488+
489+ Ok ( Balance {
490+ immature,
491+ trusted_pending,
492+ untrusted_pending,
493+ confirmed : available,
494+ } )
458495 }
459496
460497 /// Add an external signer
@@ -4729,23 +4766,30 @@ pub(crate) mod test {
47294766 Some ( confirmation_time) ,
47304767 ( @coinbase true )
47314768 ) ;
4769+ let sync_time = SyncTime {
4770+ block_time : BlockTime {
4771+ height : confirmation_time,
4772+ timestamp : 0 ,
4773+ } ,
4774+ } ;
4775+ wallet
4776+ . database
4777+ . borrow_mut ( )
4778+ . set_sync_time ( sync_time)
4779+ . unwrap ( ) ;
47324780
47334781 let not_yet_mature_time = confirmation_time + COINBASE_MATURITY - 1 ;
47344782 let maturity_time = confirmation_time + COINBASE_MATURITY ;
47354783
4736- // The balance is nonzero, even if we can't spend anything
4737- // FIXME: we should differentiate the balance between immature,
4738- // trusted, untrusted_pending
4739- // See https://github.com/bitcoindevkit/bdk/issues/238
47404784 let balance = wallet. get_balance ( ) . unwrap ( ) ;
4741- assert ! ( balance ! = 0 ) ;
4785+ assert ! ( balance. immature != 0 && balance . confirmed = = 0 ) ;
47424786
47434787 // We try to create a transaction, only to notice that all
47444788 // our funds are unspendable
47454789 let addr = Address :: from_str ( "2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX" ) . unwrap ( ) ;
47464790 let mut builder = wallet. build_tx ( ) ;
47474791 builder
4748- . add_recipient ( addr. script_pubkey ( ) , balance / 2 )
4792+ . add_recipient ( addr. script_pubkey ( ) , balance. immature / 2 )
47494793 . set_current_height ( confirmation_time) ;
47504794 assert ! ( matches!(
47514795 builder. finish( ) . unwrap_err( ) ,
@@ -4758,7 +4802,7 @@ pub(crate) mod test {
47584802 // Still unspendable...
47594803 let mut builder = wallet. build_tx ( ) ;
47604804 builder
4761- . add_recipient ( addr. script_pubkey ( ) , balance / 2 )
4805+ . add_recipient ( addr. script_pubkey ( ) , balance. immature / 2 )
47624806 . set_current_height ( not_yet_mature_time) ;
47634807 assert ! ( matches!(
47644808 builder. finish( ) . unwrap_err( ) ,
@@ -4769,9 +4813,23 @@ pub(crate) mod test {
47694813 ) ) ;
47704814
47714815 // ...Now the coinbase is mature :)
4816+ let sync_time = SyncTime {
4817+ block_time : BlockTime {
4818+ height : maturity_time,
4819+ timestamp : 0 ,
4820+ } ,
4821+ } ;
4822+ wallet
4823+ . database
4824+ . borrow_mut ( )
4825+ . set_sync_time ( sync_time)
4826+ . unwrap ( ) ;
4827+
4828+ let balance = wallet. get_balance ( ) . unwrap ( ) ;
4829+ assert ! ( balance. immature == 0 && balance. confirmed != 0 ) ;
47724830 let mut builder = wallet. build_tx ( ) ;
47734831 builder
4774- . add_recipient ( addr. script_pubkey ( ) , balance / 2 )
4832+ . add_recipient ( addr. script_pubkey ( ) , balance. confirmed / 2 )
47754833 . set_current_height ( maturity_time) ;
47764834 builder. finish ( ) . unwrap ( ) ;
47774835 }
0 commit comments