1
+ #![ allow( unused) ]
1
2
use std:: { collections:: BTreeSet , io:: Write } ;
2
3
3
4
use anyhow:: Ok ;
4
5
use bdk_esplora:: { esplora_client, EsploraAsyncExt } ;
6
+ use bdk_testenv:: bitcoincore_rpc:: RpcApi ;
5
7
use bdk_wallet:: {
6
8
bitcoin:: { Amount , Network } ,
7
9
rusqlite:: Connection ,
@@ -13,14 +15,16 @@ const STOP_GAP: usize = 5;
13
15
const PARALLEL_REQUESTS : usize = 5 ;
14
16
15
17
const 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 ;
17
20
const EXTERNAL_DESC : & str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)" ;
18
21
const INTERNAL_DESC : & str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)" ;
19
22
const ESPLORA_URL : & str = "http://signet.bitcoindevkit.net" ;
20
23
21
24
#[ tokio:: main]
22
25
async 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 ( ) ?;
24
28
25
29
let wallet_opt = Wallet :: load ( )
26
30
. descriptor ( KeychainKind :: External , Some ( EXTERNAL_DESC ) )
@@ -35,57 +39,176 @@ async fn main() -> Result<(), anyhow::Error> {
35
39
. create_wallet ( & mut conn) ?,
36
40
} ;
37
41
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 ) ;
39
44
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 ) ) ?;
67
148
68
- let balance = wallet. balance ( ) ;
69
- println ! ( "Wallet balance after syncing: {}" , balance. total( ) ) ;
149
+ let request = wallet. start_sync_with_revealed_spks ( ) ;
70
150
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) ;
78
153
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 ) ? ;
81
156
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) ?;
85
191
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
+ }
89
212
90
213
Ok ( ( ) )
91
214
}
0 commit comments