2222// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2323// SOFTWARE.
2424
25+ //! Wallet
26+ //!
27+ //! This module defines the [`Wallet`] structure.
28+
2529use std:: cell:: RefCell ;
2630use std:: collections:: HashMap ;
2731use std:: collections:: { BTreeMap , HashSet } ;
@@ -65,8 +69,19 @@ use crate::types::*;
6569
6670const CACHE_ADDR_BATCH_SIZE : u32 = 100 ;
6771
72+ /// Type alias for a [`Wallet`] that uses [`OfflineBlockchain`]
6873pub type OfflineWallet < D > = Wallet < OfflineBlockchain , D > ;
6974
75+ /// A Bitcoin wallet
76+ ///
77+ /// A wallet takes descriptors, a [`database`](crate::database) and a
78+ /// [`blockchain`](crate::blockchain) and implements the basic functions that a Bitcoin wallets
79+ /// needs to operate, like [generating addresses](Wallet::get_new_address), [returning the balance](Wallet::get_balance),
80+ /// [creating transactions](Wallet::create_tx), etc.
81+ ///
82+ /// A wallet can be either "online" if the [`blockchain`](crate::blockchain) type provided
83+ /// implements [`OnlineBlockchain`], or "offline" if it doesn't. Offline wallets only expose
84+ /// methods that don't need any interaction with the blockchain to work.
7085pub struct Wallet < B : Blockchain , D : BatchDatabase > {
7186 descriptor : ExtendedDescriptor ,
7287 change_descriptor : Option < ExtendedDescriptor > ,
90105 B : Blockchain ,
91106 D : BatchDatabase ,
92107{
108+ /// Create a new "offline" wallet
93109 pub fn new_offline (
94110 descriptor : & str ,
95111 change_descriptor : Option < & str > ,
@@ -136,6 +152,7 @@ where
136152 } )
137153 }
138154
155+ /// Return a newly generated address using the external descriptor
139156 pub fn get_new_address ( & self ) -> Result < Address , Error > {
140157 let index = self . fetch_and_increment_index ( ScriptType :: External ) ?;
141158
@@ -145,25 +162,44 @@ where
145162 . ok_or ( Error :: ScriptDoesntHaveAddressForm )
146163 }
147164
165+ /// Return whether or not a `script` is part of this wallet (either internal or external)
148166 pub fn is_mine ( & self , script : & Script ) -> Result < bool , Error > {
149167 self . database . borrow ( ) . is_mine ( script)
150168 }
151169
170+ /// Return the list of unspent outputs of this wallet
171+ ///
172+ /// Note that this methods only operate on the internal database, which first needs to be
173+ /// [`Wallet::sync`] manually.
152174 pub fn list_unspent ( & self ) -> Result < Vec < UTXO > , Error > {
153175 self . database . borrow ( ) . iter_utxos ( )
154176 }
155177
178+ /// Return the list of transactions made and received by the wallet
179+ ///
180+ /// Optionally fill the [`TransactionDetails::transaction`] field with the raw transaction if
181+ /// `include_raw` is `true`.
182+ ///
183+ /// Note that this methods only operate on the internal database, which first needs to be
184+ /// [`Wallet::sync`] manually.
156185 pub fn list_transactions ( & self , include_raw : bool ) -> Result < Vec < TransactionDetails > , Error > {
157186 self . database . borrow ( ) . iter_txs ( include_raw)
158187 }
159188
189+ /// Return the balance, meaning the sum of this wallet's unspent outputs' values
190+ ///
191+ /// Note that this methods only operate on the internal database, which first needs to be
192+ /// [`Wallet::sync`] manually.
160193 pub fn get_balance ( & self ) -> Result < u64 , Error > {
161194 Ok ( self
162195 . list_unspent ( ) ?
163196 . iter ( )
164197 . fold ( 0 , |sum, i| sum + i. txout . value ) )
165198 }
166199
200+ /// Add an external signer
201+ ///
202+ /// See [the `signer` module](signer) for an example.
167203 pub fn add_signer (
168204 & mut self ,
169205 script_type : ScriptType ,
@@ -179,10 +215,31 @@ where
179215 signers. add_external ( id, ordering, signer) ;
180216 }
181217
218+ /// Add an address validator
219+ ///
220+ /// See [the `address_validator` module](address_validator) for an example.
182221 pub fn add_address_validator ( & mut self , validator : Arc < Box < dyn AddressValidator > > ) {
183222 self . address_validators . push ( validator) ;
184223 }
185224
225+ /// Create a new transaction following the options specified in the `builder`
226+ ///
227+ /// ## Example
228+ ///
229+ /// ```no_run
230+ /// # use std::str::FromStr;
231+ /// # use bitcoin::*;
232+ /// # use magical_bitcoin_wallet::*;
233+ /// # use magical_bitcoin_wallet::database::*;
234+ /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
235+ /// # let wallet: OfflineWallet<_> = Wallet::new_offline(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
236+ /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
237+ /// let (psbt, details) = wallet.create_tx(
238+ /// TxBuilder::with_recipients(vec![(to_address.script_pubkey(), 50_000)])
239+ /// )?;
240+ /// // sign and broadcast ...
241+ /// # Ok::<(), magical_bitcoin_wallet::Error>(())
242+ /// ```
186243 pub fn create_tx < Cs : coin_selection:: CoinSelectionAlgorithm > (
187244 & self ,
188245 builder : TxBuilder < Cs > ,
@@ -383,6 +440,31 @@ where
383440 Ok ( ( psbt, transaction_details) )
384441 }
385442
443+ /// Bump the fee of a transaction following the options specified in the `builder`
444+ ///
445+ /// Return an error if the transaction is already confirmed or doesn't explicitly signal RBF.
446+ ///
447+ /// **NOTE**: if the original transaction was made with [`TxBuilder::send_all`], the same
448+ /// option must be enabled when bumping its fees to correctly reduce the only output's value to
449+ /// increase the fees.
450+ ///
451+ /// ## Example
452+ ///
453+ /// ```no_run
454+ /// # use std::str::FromStr;
455+ /// # use bitcoin::*;
456+ /// # use magical_bitcoin_wallet::*;
457+ /// # use magical_bitcoin_wallet::database::*;
458+ /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
459+ /// # let wallet: OfflineWallet<_> = Wallet::new_offline(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
460+ /// let txid = Txid::from_str("faff0a466b70f5d5f92bd757a92c1371d4838bdd5bc53a06764e2488e51ce8f8").unwrap();
461+ /// let (psbt, details) = wallet.bump_fee(
462+ /// &txid,
463+ /// TxBuilder::new().fee_rate(FeeRate::from_sat_per_vb(5.0)),
464+ /// )?;
465+ /// // sign and broadcast ...
466+ /// # Ok::<(), magical_bitcoin_wallet::Error>(())
467+ /// ```
386468 // TODO: support for merging multiple transactions while bumping the fees
387469 // TODO: option to force addition of an extra output? seems bad for privacy to update the
388470 // change
@@ -601,6 +683,21 @@ where
601683 Ok ( ( psbt, details) )
602684 }
603685
686+ /// Sign a transaction with all the wallet's signers, in the order specified by every signer's
687+ /// [`SignerOrdering`]
688+ ///
689+ /// ## Example
690+ ///
691+ /// ```no_run
692+ /// # use std::str::FromStr;
693+ /// # use bitcoin::*;
694+ /// # use magical_bitcoin_wallet::*;
695+ /// # use magical_bitcoin_wallet::database::*;
696+ /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
697+ /// # let wallet: OfflineWallet<_> = Wallet::new_offline(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
698+ /// # let (psbt, _) = wallet.create_tx(TxBuilder::new())?;
699+ /// let (signed_psbt, finalized) = wallet.sign(psbt, None)?;
700+ /// # Ok::<(), magical_bitcoin_wallet::Error>(())
604701 pub fn sign ( & self , mut psbt : PSBT , assume_height : Option < u32 > ) -> Result < ( PSBT , bool ) , Error > {
605702 // this helps us doing our job later
606703 self . add_input_hd_keypaths ( & mut psbt) ?;
@@ -624,6 +721,7 @@ where
624721 self . finalize_psbt ( psbt, assume_height)
625722 }
626723
724+ /// Return the spending policies for the wallet's descriptor
627725 pub fn policies ( & self , script_type : ScriptType ) -> Result < Option < Policy > , Error > {
628726 match ( script_type, self . change_descriptor . as_ref ( ) ) {
629727 ( ScriptType :: External , _) => {
@@ -636,6 +734,10 @@ where
636734 }
637735 }
638736
737+ /// Return the "public" version of the wallet's descriptor, meaning a new descriptor that has
738+ /// the same structure but with every secret key removed
739+ ///
740+ /// This can be used to build a watch-only version of a wallet
639741 pub fn public_descriptor (
640742 & self ,
641743 script_type : ScriptType ,
@@ -647,6 +749,7 @@ where
647749 }
648750 }
649751
752+ /// Try to finalize a PSBT
650753 pub fn finalize_psbt (
651754 & self ,
652755 mut psbt : PSBT ,
@@ -976,6 +1079,7 @@ where
9761079 B : OnlineBlockchain ,
9771080 D : BatchDatabase ,
9781081{
1082+ /// Create a new "online" wallet
9791083 #[ maybe_async]
9801084 pub fn new (
9811085 descriptor : & str ,
@@ -992,6 +1096,7 @@ where
9921096 Ok ( wallet)
9931097 }
9941098
1099+ /// Sync the internal database with the blockchain
9951100 #[ maybe_async]
9961101 pub fn sync < P : ' static + Progress > (
9971102 & self ,
@@ -1025,7 +1130,10 @@ where
10251130 if self
10261131 . database
10271132 . borrow ( )
1028- . get_script_pubkey_from_path ( ScriptType :: Internal , max_address) ?
1133+ . get_script_pubkey_from_path (
1134+ ScriptType :: Internal ,
1135+ max_address. checked_sub ( 1 ) . unwrap_or ( 0 ) ,
1136+ ) ?
10291137 . is_none ( )
10301138 {
10311139 run_setup = true ;
@@ -1050,10 +1158,12 @@ where
10501158 }
10511159 }
10521160
1161+ /// Return a reference to the internal blockchain client
10531162 pub fn client ( & self ) -> & B {
10541163 & self . client
10551164 }
10561165
1166+ /// Broadcast a transaction to the network
10571167 #[ maybe_async]
10581168 pub fn broadcast ( & self , tx : Transaction ) -> Result < Txid , Error > {
10591169 maybe_await ! ( self . client. broadcast( & tx) ) ?;
0 commit comments