1+ use std:: collections:: BTreeMap ;
2+ use std:: process:: exit;
3+ use std:: str:: FromStr ;
14use 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 } ;
37use 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 } ;
911use 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 ;
1017
1118const EXTERNAL : & str = "external" ;
1219const INTERNAL : & str = "internal" ;
@@ -148,3 +155,112 @@ fn synopsis() -> anyhow::Result<()> {
148155
149156 Ok ( ( ) )
150157}
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