1
+ use std:: collections:: BTreeMap ;
2
+ use std:: process:: exit;
3
+ use std:: str:: FromStr ;
1
4
use bdk_bitcoind_rpc:: Emitter ;
2
- use bdk_chain:: { bdk_core, Balance } ;
5
+ use bdk_chain:: { bdk_core, Balance , KeychainIndexed } ;
6
+ use bdk_chain:: spk_client:: { FullScanRequestBuilder , FullScanResponse , SyncRequestBuilder , SyncResponse } ;
3
7
use bdk_testenv:: { bitcoincore_rpc:: RpcApi , TestEnv } ;
4
- use bdk_tx:: {
5
- create_psbt, create_selection, get_candidate_inputs, CreatePsbtParams , CreateSelectionParams ,
6
- InputGroup , Output , Signer ,
7
- } ;
8
- use bitcoin:: { key:: Secp256k1 , Address , Amount , BlockHash , FeeRate } ;
8
+ use bdk_wallet:: { AddressInfo , KeychainKind , LocalOutput , SignOptions } ;
9
+ use bdk_tx:: { create_psbt, create_selection, get_candidate_inputs, CreatePsbtParams , CreateSelectionParams , InputGroup , Output , Signer } ;
10
+ use bitcoin:: { key:: Secp256k1 , Address , Amount , BlockHash , FeeRate , Network , OutPoint } ;
9
11
use miniscript:: { plan:: Assets , Descriptor , DescriptorPublicKey } ;
12
+ use bdk_esplora:: esplora_client;
13
+ use bdk_esplora:: esplora_client:: Builder ;
14
+ use bdk_esplora:: EsploraExt ;
15
+ use bitcoin:: address:: NetworkChecked ;
16
+ use miniscript:: descriptor:: KeyMap ;
10
17
11
18
const EXTERNAL : & str = "external" ;
12
19
const INTERNAL : & str = "internal" ;
@@ -148,3 +155,112 @@ fn synopsis() -> anyhow::Result<()> {
148
155
149
156
Ok ( ( ) )
150
157
}
158
+
159
+ #[ test]
160
+ fn bdk_wallet_simple_tx ( ) -> anyhow:: Result < ( ) > {
161
+
162
+ const STOP_GAP : usize = 20 ;
163
+ const PARALLEL_REQUESTS : usize = 1 ;
164
+ let secp = bitcoin:: secp256k1:: Secp256k1 :: new ( ) ;
165
+
166
+ let descriptor_private: & str = "tr(tprv8ZgxMBicQKsPdNRGG6HuFapxQCFxsDDf7TDsV8tdUgZDdiiyA6dB2ssN4RSXyp52V3MRBm4KqAps3Txng59rNMUtUEtMPDphKkKDXmamd2T/86'/1'/0'/0/*)#usy7l3tt" ;
167
+ let change_descriptor_private: & str = "tr(tprv8ZgxMBicQKsPdNRGG6HuFapxQCFxsDDf7TDsV8tdUgZDdiiyA6dB2ssN4RSXyp52V3MRBm4KqAps3Txng59rNMUtUEtMPDphKkKDXmamd2T/86'/1'/0'/1/*)#dyplzymn" ;
168
+
169
+ let ( descriptor, _) : ( Descriptor < DescriptorPublicKey > , KeyMap ) = Descriptor :: parse_descriptor ( & secp, "tr(tprv8ZgxMBicQKsPdNRGG6HuFapxQCFxsDDf7TDsV8tdUgZDdiiyA6dB2ssN4RSXyp52V3MRBm4KqAps3Txng59rNMUtUEtMPDphKkKDXmamd2T/86'/1'/0'/0/*)#usy7l3tt" ) ?;
170
+ let ( change_descriptor, _) : ( Descriptor < DescriptorPublicKey > , KeyMap ) = Descriptor :: parse_descriptor ( & secp, "tr(tprv8ZgxMBicQKsPdNRGG6HuFapxQCFxsDDf7TDsV8tdUgZDdiiyA6dB2ssN4RSXyp52V3MRBm4KqAps3Txng59rNMUtUEtMPDphKkKDXmamd2T/86'/1'/0'/1/*)#dyplzymn" ) ?;
171
+
172
+ // Create the wallet
173
+ let mut wallet = bdk_wallet:: Wallet :: create ( descriptor_private, change_descriptor_private)
174
+ . network ( Network :: Regtest )
175
+ . create_wallet_no_persist ( ) ?;
176
+
177
+ let client: esplora_client:: BlockingClient = Builder :: new ( "http://127.0.0.1:3002" ) . build_blocking ( ) ;
178
+
179
+ println ! ( "Syncing wallet..." ) ;
180
+ let full_scan_request: FullScanRequestBuilder < KeychainKind > = wallet. start_full_scan ( ) ;
181
+ let update: FullScanResponse < KeychainKind > = client
182
+ . full_scan ( full_scan_request, STOP_GAP , PARALLEL_REQUESTS ) ?;
183
+
184
+ // Apply the update from the full scan to the wallet
185
+ wallet. apply_update ( update) ?;
186
+
187
+ let balance = wallet. balance ( ) ;
188
+ println ! ( "Wallet balance: {} sat" , balance. total( ) . to_sat( ) ) ;
189
+
190
+ if balance. total ( ) . to_sat ( ) < 300000 {
191
+ println ! ( "Your wallet does not have sufficient balance for the following steps!" ) ;
192
+ // Reveal a new address from your external keychain
193
+ let address: AddressInfo = wallet. reveal_next_address ( KeychainKind :: External ) ;
194
+ println ! (
195
+ "Send coins to {} (address generated at index {})" ,
196
+ address. address, address. index
197
+ ) ;
198
+ exit ( 0 )
199
+ }
200
+
201
+ let local_outputs: Vec < LocalOutput > = wallet. list_unspent ( ) . collect ( ) ;
202
+ dbg ! ( & local_outputs. len( ) ) ;
203
+ // dbg!(&local_outputs);
204
+ let outpoints: Vec < KeychainIndexed < KeychainKind , OutPoint > > = local_outputs
205
+ . into_iter ( )
206
+ . map ( |o| ( ( o. keychain , o. derivation_index ) , o. outpoint . clone ( ) ) )
207
+ . collect ( ) ;
208
+
209
+ let mut descriptors_map = BTreeMap :: new ( ) ;
210
+ descriptors_map. insert ( KeychainKind :: External , descriptor. clone ( ) ) ;
211
+ descriptors_map. insert ( KeychainKind :: Internal , change_descriptor. clone ( ) ) ;
212
+
213
+ let input_candidates_individual = get_candidate_inputs (
214
+ & wallet. tx_graph ( ) ,
215
+ & wallet. local_chain ( ) ,
216
+ outpoints,
217
+ descriptors_map,
218
+ Assets :: new ( ) ,
219
+ ) ?;
220
+ // dbg!(&input_candidates_0);
221
+
222
+ let recipient_address: Address < NetworkChecked > =
223
+ Address :: from_str ( "bcrt1qe908k9zu8m4jgzdddgg0lkj73yctfqueg7pea9" ) ?
224
+ . require_network ( Network :: Regtest ) ?;
225
+
226
+ let input_candidates_groups: Vec < InputGroup > = input_candidates_individual. into_iter ( ) . map ( |i| i. into ( ) ) . collect ( ) ;
227
+
228
+ let selection = create_selection ( CreateSelectionParams {
229
+ input_candidates : input_candidates_groups,
230
+ must_spend : Default :: default ( ) ,
231
+ change_descriptor : change_descriptor. at_derivation_index ( 0 ) ?,
232
+ target_feerate : FeeRate :: from_sat_per_vb ( 5 ) . unwrap ( ) ,
233
+ long_term_feerate : Some ( FeeRate :: from_sat_per_vb ( 1 ) . unwrap ( ) ) ,
234
+ target_outputs : vec ! [ Output :: with_script(
235
+ recipient_address. script_pubkey( ) ,
236
+ Amount :: from_sat( 200_000 ) ,
237
+ ) ] ,
238
+ max_rounds : 100_000 ,
239
+ } ) ?;
240
+
241
+ dbg ! ( & selection. inputs. len( ) ) ;
242
+
243
+ let ( mut psbt, _) = create_psbt ( CreatePsbtParams {
244
+ inputs : selection. inputs ,
245
+ outputs : selection. outputs ,
246
+ ..Default :: default ( )
247
+ } ) ?;
248
+ let signed = wallet. sign ( & mut psbt, SignOptions :: default ( ) ) ?;
249
+ assert ! ( signed) ;
250
+ let tx = psbt. extract_tx ( ) ?;
251
+
252
+ client. broadcast ( & tx) ?;
253
+ dbg ! ( "tx broadcast: {}" , tx. compute_txid( ) ) ;
254
+
255
+ println ! ( "Syncing wallet again..." ) ;
256
+ let sync_request = wallet. start_sync_with_revealed_spks ( ) ;
257
+ let update_2: SyncResponse = client
258
+ . sync ( sync_request, PARALLEL_REQUESTS ) ?;
259
+
260
+ wallet. apply_update ( update_2) ?;
261
+
262
+ let balance_2 = wallet. balance ( ) ;
263
+ println ! ( "Wallet balance: {} sat" , balance_2. total( ) . to_sat( ) ) ;
264
+
265
+ Ok ( ( ) )
266
+ }
0 commit comments