1+ #![ allow( unused) ]
12use std:: { collections:: BTreeSet , io:: Write } ;
23
34use anyhow:: Ok ;
45use bdk_esplora:: { esplora_client, EsploraAsyncExt } ;
6+ use bdk_testenv:: bitcoincore_rpc:: RpcApi ;
57use bdk_wallet:: {
68 bitcoin:: { Amount , Network } ,
79 rusqlite:: Connection ,
@@ -13,14 +15,16 @@ const STOP_GAP: usize = 5;
1315const PARALLEL_REQUESTS : usize = 5 ;
1416
1517const DB_PATH : & str = "bdk-example-esplora-async.sqlite" ;
16- const NETWORK : Network = Network :: Signet ;
18+ // const NETWORK: Network = Network::Signet;
19+ const NETWORK : Network = Network :: Regtest ;
1720const EXTERNAL_DESC : & str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)" ;
1821const INTERNAL_DESC : & str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)" ;
1922const ESPLORA_URL : & str = "http://signet.bitcoindevkit.net" ;
2023
2124#[ tokio:: main]
2225async fn main ( ) -> Result < ( ) , anyhow:: Error > {
23- let mut conn = Connection :: open ( DB_PATH ) ?;
26+ // let mut conn = Connection::open(DB_PATH)?;
27+ let mut conn = Connection :: open_in_memory ( ) ?;
2428
2529 let wallet_opt = Wallet :: load ( )
2630 . descriptor ( KeychainKind :: External , Some ( EXTERNAL_DESC ) )
@@ -35,57 +39,176 @@ async fn main() -> Result<(), anyhow::Error> {
3539 . create_wallet ( & mut conn) ?,
3640 } ;
3741
38- let address = wallet. next_unused_address ( KeychainKind :: External ) ;
42+ // let address = wallet.next_unused_address(KeychainKind::External);
43+ let recv_addr = wallet. next_unused_address ( KeychainKind :: External ) ;
3944 wallet. persist ( & mut conn) ?;
40- println ! ( "Next unused address: ({}) {}" , address. index, address) ;
41-
42- let balance = wallet. balance ( ) ;
43- println ! ( "Wallet balance before syncing: {}" , balance. total( ) ) ;
44-
45- print ! ( "Syncing..." ) ;
46- let client = esplora_client:: Builder :: new ( ESPLORA_URL ) . build_async ( ) ?;
47-
48- let request = wallet. start_full_scan ( ) . inspect ( {
49- let mut stdout = std:: io:: stdout ( ) ;
50- let mut once = BTreeSet :: < KeychainKind > :: new ( ) ;
51- move |keychain, spk_i, _| {
52- if once. insert ( keychain) {
53- print ! ( "\n Scanning keychain [{:?}]" , keychain) ;
54- }
55- print ! ( " {:<3}" , spk_i) ;
56- stdout. flush ( ) . expect ( "must flush" )
57- }
58- } ) ;
59-
60- let update = client
61- . full_scan ( request, STOP_GAP , PARALLEL_REQUESTS )
62- . await ?;
63-
64- wallet. apply_update ( update) ?;
65- wallet. persist ( & mut conn) ?;
66- println ! ( ) ;
45+ // println!("Next unused address: ({}) {}", address.index, address);
46+
47+ // let balance = wallet.balance();
48+ // println!("Wallet balance before syncing: {}", balance.total());
49+
50+ // print!("Syncing...");
51+ // let client = esplora_client::Builder::new(ESPLORA_URL).build_async()?;
52+
53+ // let request = wallet.start_full_scan().inspect({
54+ // let mut stdout = std::io::stdout();
55+ // let mut once = BTreeSet::<KeychainKind>::new();
56+ // move |keychain, spk_i, _| {
57+ // if once.insert(keychain) {
58+ // print!("\nScanning keychain [{:?}]", keychain);
59+ // }
60+ // print!(" {:<3}", spk_i);
61+ // stdout.flush().expect("must flush")
62+ // }
63+ // });
64+
65+ // let update = client
66+ // .full_scan(request, STOP_GAP, PARALLEL_REQUESTS)
67+ // .await?;
68+
69+ // wallet.apply_update(update)?;
70+ // wallet.persist(&mut conn)?;
71+ // println!();
72+
73+ // let balance = wallet.balance();
74+ // println!("Wallet balance after syncing: {}", balance.total());
75+
76+ // if balance.total() < SEND_AMOUNT {
77+ // println!(
78+ // "Please send at least {} to the receiving address",
79+ // SEND_AMOUNT
80+ // );
81+ // std::process::exit(0);
82+ // }
83+
84+ // let mut tx_builder = wallet.build_tx();
85+ // tx_builder.add_recipient(address.script_pubkey(), SEND_AMOUNT);
86+
87+ // let mut psbt = tx_builder.finish()?;
88+ // let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
89+ // assert!(finalized);
90+
91+ // let tx = psbt.extract_tx()?;
92+ // client.broadcast(&tx).await?;
93+ // println!("Tx broadcasted! Txid: {}", tx.compute_txid());
94+
95+ use bdk_testenv:: bitcoincore_rpc:: bitcoincore_rpc_json:: CreateRawTransactionInput ;
96+ use bdk_testenv:: TestEnv ;
97+ let env = TestEnv :: new ( ) ?;
98+
99+ // premine
100+ let rpc = env. rpc_client ( ) ;
101+ let _ = env. mine_blocks ( 100 , None ) ;
102+ assert_eq ! ( rpc. get_block_count( ) ?, 101 ) ;
103+
104+ let utxo = rpc. list_unspent ( None , None , None , None , None ) ?[ 0 ] . clone ( ) ;
105+
106+ // Create tx1
107+ let utxos = vec ! [ CreateRawTransactionInput {
108+ txid: utxo. txid,
109+ vout: utxo. vout,
110+ sequence: None ,
111+ } ] ;
112+ let to_send = Amount :: ONE_BTC ;
113+ let fee = Amount :: from_sat ( 1_000 ) ;
114+ let change_addr = rpc. get_new_address ( None , None ) ?. assume_checked ( ) ;
115+ let out = [
116+ ( recv_addr. to_string ( ) , to_send) ,
117+ ( change_addr. to_string ( ) , utxo. amount - to_send - fee) ,
118+ ]
119+ . into ( ) ;
120+ let tx = rpc. create_raw_transaction ( & utxos, & out, None , None ) ?;
121+ let tx1 = rpc
122+ . sign_raw_transaction_with_wallet ( & tx, None , None ) ?
123+ . transaction ( ) ?;
124+
125+ // Create tx2 the double spend
126+ let new_addr = rpc. get_new_address ( None , None ) ?. assume_checked ( ) ;
127+ let out = [
128+ ( new_addr. to_string ( ) , to_send) ,
129+ ( change_addr. to_string ( ) , utxo. amount - to_send - ( fee * 2 ) ) ,
130+ ]
131+ . into ( ) ;
132+ let tx = rpc. create_raw_transaction ( & utxos, & out, None , None ) ?;
133+ let tx2 = rpc
134+ . sign_raw_transaction_with_wallet ( & tx, None , None ) ?
135+ . transaction ( ) ?;
136+
137+ // Sync after send tx 1
138+ let txid1 = rpc. send_raw_transaction ( & tx1) ?;
139+ println ! ( "Send tx1 {}" , txid1) ;
140+
141+ let base_url = format ! ( "http://{}" , & env. electrsd. esplora_url. clone( ) . unwrap( ) ) ;
142+ let client = esplora_client:: Builder :: new ( base_url. as_str ( ) ) . build_async ( ) ?;
143+
144+ while client. get_height ( ) . await ? < 101 {
145+ std:: thread:: sleep ( std:: time:: Duration :: from_millis ( 64 ) ) ;
146+ }
147+ env. wait_until_electrum_sees_txid ( txid1, std:: time:: Duration :: from_secs ( 10 ) ) ?;
67148
68- let balance = wallet. balance ( ) ;
69- println ! ( "Wallet balance after syncing: {}" , balance. total( ) ) ;
149+ let request = wallet. start_sync_with_revealed_spks ( ) ;
70150
71- if balance. total ( ) < SEND_AMOUNT {
72- println ! (
73- "Please send at least {} to the receiving address" ,
74- SEND_AMOUNT
75- ) ;
76- std:: process:: exit ( 0 ) ;
77- }
151+ let resp = client. sync ( request, PARALLEL_REQUESTS ) . await ?;
152+ assert_eq ! ( resp. tx_update. txs. first( ) . unwrap( ) . compute_txid( ) , txid1) ;
78153
79- let mut tx_builder = wallet. build_tx ( ) ;
80- tx_builder . add_recipient ( address . script_pubkey ( ) , SEND_AMOUNT ) ;
154+ wallet. apply_update ( resp ) ? ;
155+ wallet . persist ( & mut conn ) ? ;
81156
82- let mut psbt = tx_builder. finish ( ) ?;
83- let finalized = wallet. sign ( & mut psbt, SignOptions :: default ( ) ) ?;
84- assert ! ( finalized) ;
157+ assert_eq ! ( wallet. balance( ) . total( ) , Amount :: ONE_BTC ) ;
158+ println ! ( "Balance after send tx1: {}" , wallet. balance( ) . total( ) ) ;
159+ // We should expect tx1 to occur in a future sync
160+ let exp_spk_txids = wallet
161+ . tx_graph ( )
162+ . list_expected_spk_txids (
163+ wallet. local_chain ( ) ,
164+ wallet. local_chain ( ) . tip ( ) . block_id ( ) ,
165+ wallet. spk_index ( ) ,
166+ /*spk_index_range: */ ..,
167+ )
168+ . collect :: < Vec < _ > > ( ) ;
169+ assert_eq ! (
170+ exp_spk_txids. first( ) ,
171+ Some ( & ( recv_addr. script_pubkey( ) , txid1) )
172+ ) ;
173+
174+ // Now sync after send tx 2
175+ let txid2 = rpc. send_raw_transaction ( & tx2) ?;
176+ println ! ( "Send tx2 {}" , txid2) ;
177+ env. wait_until_electrum_sees_txid ( txid2, std:: time:: Duration :: from_secs ( 10 ) ) ?;
178+
179+ let request = wallet. start_sync_with_revealed_spks ( ) ;
180+
181+ let resp = client. sync ( request, PARALLEL_REQUESTS ) . await ?;
182+ assert ! ( resp. tx_update. txs. is_empty( ) ) ;
183+ assert ! ( resp
184+ . tx_update
185+ . evicted_ats
186+ . iter( )
187+ . any( |& ( txid, _) | txid == txid1) ) ;
188+
189+ wallet. apply_update ( resp) ?;
190+ wallet. persist ( & mut conn) ?;
85191
86- let tx = psbt. extract_tx ( ) ?;
87- client. broadcast ( & tx) . await ?;
88- println ! ( "Tx broadcasted! Txid: {}" , tx. compute_txid( ) ) ;
192+ println ! ( "Balance after send tx2: {}" , wallet. balance( ) . total( ) ) ;
193+ assert_eq ! ( wallet. balance( ) . total( ) , Amount :: ZERO ) ;
194+
195+ // Load the persisted wallet
196+ {
197+ wallet = Wallet :: load ( )
198+ . load_wallet ( & mut conn) ?
199+ . expect ( "wallet was persisted" ) ;
200+
201+ // tx1 is there, but is not canonical
202+ assert ! ( wallet. tx_graph( ) . full_txs( ) . any( |node| node. txid == txid1) ) ;
203+ assert ! ( wallet
204+ . tx_graph( )
205+ . list_canonical_txs( wallet. local_chain( ) , wallet. local_chain( ) . tip( ) . block_id( ) )
206+ . next( )
207+ . is_none( ) ) ;
208+ assert ! ( wallet. list_unspent( ) . next( ) . is_none( ) ) ;
209+ assert_eq ! ( wallet. balance( ) . total( ) , Amount :: ZERO ) ;
210+ println ! ( "Balance after load wallet: {}" , wallet. balance( ) . total( ) ) ;
211+ }
89212
90213 Ok ( ( ) )
91214}
0 commit comments