@@ -23,16 +23,18 @@ pub use bdk_chain::keychain::Balance;
2323use bdk_chain:: {
2424 indexed_tx_graph,
2525 keychain:: { self , KeychainTxOutIndex } ,
26- local_chain:: { self , CannotConnectError , CheckPoint , CheckPointIter , LocalChain } ,
26+ local_chain:: {
27+ self , ApplyHeaderError , CannotConnectError , CheckPoint , CheckPointIter , LocalChain ,
28+ } ,
2729 tx_graph:: { CanonicalTx , TxGraph } ,
2830 Append , BlockId , ChainPosition , ConfirmationTime , ConfirmationTimeHeightAnchor , FullTxOut ,
2931 IndexedTxGraph , Persist , PersistBackend ,
3032} ;
3133use bitcoin:: secp256k1:: { All , Secp256k1 } ;
3234use bitcoin:: sighash:: { EcdsaSighashType , TapSighashType } ;
3335use bitcoin:: {
34- absolute, Address , Network , OutPoint , Script , ScriptBuf , Sequence , Transaction , TxOut , Txid ,
35- Weight , Witness ,
36+ absolute, Address , Block , Network , OutPoint , Script , ScriptBuf , Sequence , Transaction , TxOut ,
37+ Txid , Weight , Witness ,
3638} ;
3739use bitcoin:: { consensus:: encode:: serialize, BlockHash } ;
3840use bitcoin:: { constants:: genesis_block, psbt} ;
@@ -428,6 +430,55 @@ pub enum InsertTxError {
428430 } ,
429431}
430432
433+ impl fmt:: Display for InsertTxError {
434+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
435+ match self {
436+ InsertTxError :: ConfirmationHeightCannotBeGreaterThanTip {
437+ tip_height,
438+ tx_height,
439+ } => {
440+ write ! ( f, "cannot insert tx with confirmation height ({}) higher than internal tip height ({})" , tx_height, tip_height)
441+ }
442+ }
443+ }
444+ }
445+
446+ #[ cfg( feature = "std" ) ]
447+ impl std:: error:: Error for InsertTxError { }
448+
449+ /// An error that may occur when applying a block to [`Wallet`].
450+ #[ derive( Debug ) ]
451+ pub enum ApplyBlockError {
452+ /// Occurs when the update chain cannot connect with original chain.
453+ CannotConnect ( CannotConnectError ) ,
454+ /// Occurs when the `connected_to` hash does not match the hash derived from `block`.
455+ UnexpectedConnectedToHash {
456+ /// Block hash of `connected_to`.
457+ connected_to_hash : BlockHash ,
458+ /// Expected block hash of `connected_to`, as derived from `block`.
459+ expected_hash : BlockHash ,
460+ } ,
461+ }
462+
463+ impl fmt:: Display for ApplyBlockError {
464+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
465+ match self {
466+ ApplyBlockError :: CannotConnect ( err) => err. fmt ( f) ,
467+ ApplyBlockError :: UnexpectedConnectedToHash {
468+ expected_hash : block_hash,
469+ connected_to_hash : checkpoint_hash,
470+ } => write ! (
471+ f,
472+ "`connected_to` hash {} differs from the expected hash {} (which is derived from `block`)" ,
473+ checkpoint_hash, block_hash
474+ ) ,
475+ }
476+ }
477+ }
478+
479+ #[ cfg( feature = "std" ) ]
480+ impl std:: error:: Error for ApplyBlockError { }
481+
431482impl < D > Wallet < D > {
432483 /// Initialize an empty [`Wallet`].
433484 pub fn new < E : IntoWalletDescriptor > (
@@ -2302,7 +2353,7 @@ impl<D> Wallet<D> {
23022353 self . persist . commit ( ) . map ( |c| c. is_some ( ) )
23032354 }
23042355
2305- /// Returns the changes that will be staged with the next call to [`commit`].
2356+ /// Returns the changes that will be committed with the next call to [`commit`].
23062357 ///
23072358 /// [`commit`]: Self::commit
23082359 pub fn staged ( & self ) -> & ChangeSet
@@ -2326,6 +2377,86 @@ impl<D> Wallet<D> {
23262377 pub fn local_chain ( & self ) -> & LocalChain {
23272378 & self . chain
23282379 }
2380+
2381+ /// Introduces a `block` of `height` to the wallet, and tries to connect it to the
2382+ /// `prev_blockhash` of the block's header.
2383+ ///
2384+ /// This is a convenience method that is equivalent to calling [`apply_block_connected_to`]
2385+ /// with `prev_blockhash` and `height-1` as the `connected_to` parameter.
2386+ ///
2387+ /// [`apply_block_connected_to`]: Self::apply_block_connected_to
2388+ pub fn apply_block ( & mut self , block : Block , height : u32 ) -> Result < ( ) , CannotConnectError >
2389+ where
2390+ D : PersistBackend < ChangeSet > ,
2391+ {
2392+ let connected_to = match height. checked_sub ( 1 ) {
2393+ Some ( prev_height) => BlockId {
2394+ height : prev_height,
2395+ hash : block. header . prev_blockhash ,
2396+ } ,
2397+ None => BlockId {
2398+ height,
2399+ hash : block. block_hash ( ) ,
2400+ } ,
2401+ } ;
2402+ self . apply_block_connected_to ( block, height, connected_to)
2403+ . map_err ( |err| match err {
2404+ ApplyHeaderError :: InconsistentBlocks => {
2405+ unreachable ! ( "connected_to is derived from the block so must be consistent" )
2406+ }
2407+ ApplyHeaderError :: CannotConnect ( err) => err,
2408+ } )
2409+ }
2410+
2411+ /// Applies relevant transactions from `block` of `height` to the wallet, and connects the
2412+ /// block to the internal chain.
2413+ ///
2414+ /// The `connected_to` parameter informs the wallet how this block connects to the internal
2415+ /// [`LocalChain`]. Relevant transactions are filtered from the `block` and inserted into the
2416+ /// internal [`TxGraph`].
2417+ pub fn apply_block_connected_to (
2418+ & mut self ,
2419+ block : Block ,
2420+ height : u32 ,
2421+ connected_to : BlockId ,
2422+ ) -> Result < ( ) , ApplyHeaderError >
2423+ where
2424+ D : PersistBackend < ChangeSet > ,
2425+ {
2426+ let mut changeset = ChangeSet :: default ( ) ;
2427+ changeset. append (
2428+ self . chain
2429+ . apply_header_connected_to ( & block. header , height, connected_to) ?
2430+ . into ( ) ,
2431+ ) ;
2432+ changeset. append (
2433+ self . indexed_graph
2434+ . apply_block_relevant ( block, height)
2435+ . into ( ) ,
2436+ ) ;
2437+ self . persist . stage ( changeset) ;
2438+ Ok ( ( ) )
2439+ }
2440+
2441+ /// Apply relevant unconfirmed transactions to the wallet.
2442+ ///
2443+ /// Transactions that are not relevant are filtered out.
2444+ ///
2445+ /// This method takes in an iterator of `(tx, last_seen)` where `last_seen` is the timestamp of
2446+ /// when the transaction was last seen in the mempool. This is used for conflict resolution
2447+ /// when there is conflicting unconfirmed transactions. The transaction with the later
2448+ /// `last_seen` is prioritied.
2449+ pub fn apply_unconfirmed_txs < ' t > (
2450+ & mut self ,
2451+ unconfirmed_txs : impl IntoIterator < Item = ( & ' t Transaction , u64 ) > ,
2452+ ) where
2453+ D : PersistBackend < ChangeSet > ,
2454+ {
2455+ let indexed_graph_changeset = self
2456+ . indexed_graph
2457+ . batch_insert_relevant_unconfirmed ( unconfirmed_txs) ;
2458+ self . persist . stage ( ChangeSet :: from ( indexed_graph_changeset) ) ;
2459+ }
23292460}
23302461
23312462impl < D > AsRef < bdk_chain:: tx_graph:: TxGraph < ConfirmationTimeHeightAnchor > > for Wallet < D > {
0 commit comments