testutils_macros/
lib.rs

1// Bitcoin Dev Kit
2// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
3//
4// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
5//
6// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
7// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
8// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
9// You may not use this file except in accordance with one or both of these
10// licenses.
11
12#[macro_use]
13extern crate quote;
14
15use proc_macro::TokenStream;
16
17use syn::spanned::Spanned;
18use syn::{parse, parse2, Ident, ReturnType};
19
20#[proc_macro_attribute]
21pub fn bdk_blockchain_tests(attr: TokenStream, item: TokenStream) -> TokenStream {
22    let root_ident = if !attr.is_empty() {
23        match parse::<syn::ExprPath>(attr) {
24            Ok(parsed) => parsed,
25            Err(e) => {
26                let error_string = e.to_string();
27                return (quote! {
28                    compile_error!("Invalid crate path: {:?}", #error_string)
29                })
30                .into();
31            }
32        }
33    } else {
34        parse2::<syn::ExprPath>(quote! { bdk }).unwrap()
35    };
36
37    match parse::<syn::ItemFn>(item) {
38        Err(_) => (quote! {
39            compile_error!("#[bdk_blockchain_tests] can only be used on `fn`s")
40        })
41        .into(),
42        Ok(parsed) => {
43            let parsed_sig_ident = parsed.sig.ident.clone();
44            let mod_name = Ident::new(
45                &format!("generated_tests_{}", parsed_sig_ident.to_string()),
46                parsed.span(),
47            );
48
49            let return_type = match parsed.sig.output {
50                ReturnType::Type(_, ref t) => t.clone(),
51                ReturnType::Default => {
52                    return (quote! {
53                        compile_error!("The tagged function must return a type that impl `Blockchain`")
54                    }).into();
55                }
56            };
57
58            let output = quote! {
59
60            #parsed
61
62            mod #mod_name {
63                use bitcoin::Network;
64
65                use miniscript::Descriptor;
66
67                use testutils::{TestClient, serial};
68
69                use #root_ident::blockchain::{Blockchain, noop_progress};
70                use #root_ident::descriptor::ExtendedDescriptor;
71                use #root_ident::database::MemoryDatabase;
72                use #root_ident::types::KeychainKind;
73                use #root_ident::{Wallet, TxBuilder, FeeRate};
74                use #root_ident::wallet::AddressIndex::New;
75
76                use super::*;
77
78                fn get_blockchain() -> #return_type {
79                    #parsed_sig_ident()
80                }
81
82                fn get_wallet_from_descriptors(descriptors: &(String, Option<String>)) -> Wallet<#return_type, MemoryDatabase> {
83                    Wallet::new(&descriptors.0.to_string(), descriptors.1.as_ref(), Network::Regtest, MemoryDatabase::new(), get_blockchain()).unwrap()
84                }
85
86                fn init_single_sig() -> (Wallet<#return_type, MemoryDatabase>, (String, Option<String>), TestClient) {
87                    let descriptors = testutils! {
88                        @descriptors ( "wpkh(Alice)" ) ( "wpkh(Alice)" ) ( @keys ( "Alice" => (@generate_xprv "/44'/0'/0'/0/*", "/44'/0'/0'/1/*") ) )
89                    };
90
91                    let test_client = TestClient::new();
92                    let wallet = get_wallet_from_descriptors(&descriptors);
93
94                    (wallet, descriptors, test_client)
95                }
96
97                #[test]
98                #[serial]
99                fn test_sync_simple() {
100                    let (wallet, descriptors, mut test_client) = init_single_sig();
101
102                    let tx = testutils! {
103                        @tx ( (@external descriptors, 0) => 50_000 )
104                    };
105                    println!("{:?}", tx);
106                    let txid = test_client.receive(tx);
107
108                    wallet.sync(noop_progress(), None).unwrap();
109
110                    assert_eq!(wallet.get_balance().unwrap(), 50_000);
111                    assert_eq!(wallet.list_unspent().unwrap()[0].keychain, KeychainKind::External);
112
113                    let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
114                    assert_eq!(list_tx_item.txid, txid);
115                    assert_eq!(list_tx_item.received, 50_000);
116                    assert_eq!(list_tx_item.sent, 0);
117                    assert_eq!(list_tx_item.height, None);
118                }
119
120                #[test]
121                #[serial]
122                fn test_sync_stop_gap_20() {
123                    let (wallet, descriptors, mut test_client) = init_single_sig();
124
125                    test_client.receive(testutils! {
126                        @tx ( (@external descriptors, 5) => 50_000 )
127                    });
128                    test_client.receive(testutils! {
129                        @tx ( (@external descriptors, 25) => 50_000 )
130                    });
131
132                    wallet.sync(noop_progress(), None).unwrap();
133
134                    assert_eq!(wallet.get_balance().unwrap(), 100_000);
135                    assert_eq!(wallet.list_transactions(false).unwrap().len(), 2);
136                }
137
138                #[test]
139                #[serial]
140                fn test_sync_before_and_after_receive() {
141                    let (wallet, descriptors, mut test_client) = init_single_sig();
142
143                    wallet.sync(noop_progress(), None).unwrap();
144                    assert_eq!(wallet.get_balance().unwrap(), 0);
145
146                    test_client.receive(testutils! {
147                        @tx ( (@external descriptors, 0) => 50_000 )
148                    });
149
150                    wallet.sync(noop_progress(), None).unwrap();
151
152                    assert_eq!(wallet.get_balance().unwrap(), 50_000);
153                    assert_eq!(wallet.list_transactions(false).unwrap().len(), 1);
154                }
155
156                #[test]
157                #[serial]
158                fn test_sync_multiple_outputs_same_tx() {
159                    let (wallet, descriptors, mut test_client) = init_single_sig();
160
161                    let txid = test_client.receive(testutils! {
162                        @tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000, (@external descriptors, 5) => 30_000 )
163                    });
164
165                    wallet.sync(noop_progress(), None).unwrap();
166
167                    assert_eq!(wallet.get_balance().unwrap(), 105_000);
168                    assert_eq!(wallet.list_transactions(false).unwrap().len(), 1);
169                    assert_eq!(wallet.list_unspent().unwrap().len(), 3);
170
171                    let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
172                    assert_eq!(list_tx_item.txid, txid);
173                    assert_eq!(list_tx_item.received, 105_000);
174                    assert_eq!(list_tx_item.sent, 0);
175                    assert_eq!(list_tx_item.height, None);
176                }
177
178                #[test]
179                #[serial]
180                fn test_sync_receive_multi() {
181                    let (wallet, descriptors, mut test_client) = init_single_sig();
182
183                    test_client.receive(testutils! {
184                        @tx ( (@external descriptors, 0) => 50_000 )
185                    });
186                    test_client.receive(testutils! {
187                        @tx ( (@external descriptors, 5) => 25_000 )
188                    });
189
190                    wallet.sync(noop_progress(), None).unwrap();
191
192                    assert_eq!(wallet.get_balance().unwrap(), 75_000);
193                    assert_eq!(wallet.list_transactions(false).unwrap().len(), 2);
194                    assert_eq!(wallet.list_unspent().unwrap().len(), 2);
195                }
196
197                #[test]
198                #[serial]
199                fn test_sync_address_reuse() {
200                    let (wallet, descriptors, mut test_client) = init_single_sig();
201
202                    test_client.receive(testutils! {
203                        @tx ( (@external descriptors, 0) => 50_000 )
204                    });
205
206                    wallet.sync(noop_progress(), None).unwrap();
207                    assert_eq!(wallet.get_balance().unwrap(), 50_000);
208
209                    test_client.receive(testutils! {
210                        @tx ( (@external descriptors, 0) => 25_000 )
211                    });
212
213                    wallet.sync(noop_progress(), None).unwrap();
214                    assert_eq!(wallet.get_balance().unwrap(), 75_000);
215                }
216
217                #[test]
218                #[serial]
219                fn test_sync_receive_rbf_replaced() {
220                    let (wallet, descriptors, mut test_client) = init_single_sig();
221
222                    let txid = test_client.receive(testutils! {
223                        @tx ( (@external descriptors, 0) => 50_000 ) ( @replaceable true )
224                    });
225
226                    wallet.sync(noop_progress(), None).unwrap();
227
228                    assert_eq!(wallet.get_balance().unwrap(), 50_000);
229                    assert_eq!(wallet.list_transactions(false).unwrap().len(), 1);
230                    assert_eq!(wallet.list_unspent().unwrap().len(), 1);
231
232                    let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
233                    assert_eq!(list_tx_item.txid, txid);
234                    assert_eq!(list_tx_item.received, 50_000);
235                    assert_eq!(list_tx_item.sent, 0);
236                    assert_eq!(list_tx_item.height, None);
237
238                    let new_txid = test_client.bump_fee(&txid);
239
240                    wallet.sync(noop_progress(), None).unwrap();
241
242                    assert_eq!(wallet.get_balance().unwrap(), 50_000);
243                    assert_eq!(wallet.list_transactions(false).unwrap().len(), 1);
244                    assert_eq!(wallet.list_unspent().unwrap().len(), 1);
245
246                    let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
247                    assert_eq!(list_tx_item.txid, new_txid);
248                    assert_eq!(list_tx_item.received, 50_000);
249                    assert_eq!(list_tx_item.sent, 0);
250                    assert_eq!(list_tx_item.height, None);
251                }
252
253                #[test]
254                #[serial]
255                fn test_sync_reorg_block() {
256                    let (wallet, descriptors, mut test_client) = init_single_sig();
257
258                    let txid = test_client.receive(testutils! {
259                        @tx ( (@external descriptors, 0) => 50_000 ) ( @confirmations 1 ) ( @replaceable true )
260                    });
261
262                    wallet.sync(noop_progress(), None).unwrap();
263
264                    assert_eq!(wallet.get_balance().unwrap(), 50_000);
265                    assert_eq!(wallet.list_transactions(false).unwrap().len(), 1);
266                    assert_eq!(wallet.list_unspent().unwrap().len(), 1);
267
268                    let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
269                    assert_eq!(list_tx_item.txid, txid);
270                    assert!(list_tx_item.height.is_some());
271
272                    // Invalidate 1 block
273                    test_client.invalidate(1);
274
275                    wallet.sync(noop_progress(), None).unwrap();
276
277                    assert_eq!(wallet.get_balance().unwrap(), 50_000);
278
279                    let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
280                    assert_eq!(list_tx_item.txid, txid);
281                    assert_eq!(list_tx_item.height, None);
282                }
283
284                #[test]
285                #[serial]
286                fn test_sync_after_send() {
287                    let (wallet, descriptors, mut test_client) = init_single_sig();
288                    println!("{}", descriptors.0);
289                    let node_addr = test_client.get_node_address(None);
290
291                    test_client.receive(testutils! {
292                        @tx ( (@external descriptors, 0) => 50_000 )
293                    });
294
295                    wallet.sync(noop_progress(), None).unwrap();
296                    assert_eq!(wallet.get_balance().unwrap(), 50_000);
297
298                    let mut builder = wallet.build_tx();
299                    builder.add_recipient(node_addr.script_pubkey(), 25_000);
300                    let (mut psbt, details) = builder.finish().unwrap();
301                    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
302                    assert!(finalized, "Cannot finalize transaction");
303                    let tx = psbt.extract_tx();
304                    println!("{}", bitcoin::consensus::encode::serialize_hex(&tx));
305                    wallet.broadcast(tx).unwrap();
306
307                    wallet.sync(noop_progress(), None).unwrap();
308                    assert_eq!(wallet.get_balance().unwrap(), details.received);
309
310                    assert_eq!(wallet.list_transactions(false).unwrap().len(), 2);
311                    assert_eq!(wallet.list_unspent().unwrap().len(), 1);
312                }
313
314                #[test]
315                #[serial]
316                fn test_sync_outgoing_from_scratch() {
317                    let (wallet, descriptors, mut test_client) = init_single_sig();
318                    let node_addr = test_client.get_node_address(None);
319
320                    let received_txid = test_client.receive(testutils! {
321                        @tx ( (@external descriptors, 0) => 50_000 )
322                    });
323
324                    wallet.sync(noop_progress(), None).unwrap();
325                    assert_eq!(wallet.get_balance().unwrap(), 50_000);
326
327                    let mut builder = wallet.build_tx();
328                    builder.add_recipient(node_addr.script_pubkey(), 25_000);
329                    let (mut psbt, details) = builder.finish().unwrap();
330                    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
331                    assert!(finalized, "Cannot finalize transaction");
332                    let sent_txid = wallet.broadcast(psbt.extract_tx()).unwrap();
333
334                    wallet.sync(noop_progress(), None).unwrap();
335                    assert_eq!(wallet.get_balance().unwrap(), details.received);
336
337                    // empty wallet
338                    let wallet = get_wallet_from_descriptors(&descriptors);
339                    wallet.sync(noop_progress(), None).unwrap();
340
341                    let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::<std::collections::HashMap<_, _>>();
342
343                    let received = tx_map.get(&received_txid).unwrap();
344                    assert_eq!(received.received, 50_000);
345                    assert_eq!(received.sent, 0);
346
347                    let sent = tx_map.get(&sent_txid).unwrap();
348                    assert_eq!(sent.received, details.received);
349                    assert_eq!(sent.sent, details.sent);
350                    assert_eq!(sent.fees, details.fees);
351                }
352
353                #[test]
354                #[serial]
355                fn test_sync_long_change_chain() {
356                    let (wallet, descriptors, mut test_client) = init_single_sig();
357                    let node_addr = test_client.get_node_address(None);
358
359                    test_client.receive(testutils! {
360                        @tx ( (@external descriptors, 0) => 50_000 )
361                    });
362
363                    wallet.sync(noop_progress(), None).unwrap();
364                    assert_eq!(wallet.get_balance().unwrap(), 50_000);
365
366                    let mut total_sent = 0;
367                    for _ in 0..5 {
368                        let mut builder = wallet.build_tx();
369                        builder.add_recipient(node_addr.script_pubkey(), 5_000);
370                        let (mut psbt, details) = builder.finish().unwrap();
371                        let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
372                        assert!(finalized, "Cannot finalize transaction");
373                        wallet.broadcast(psbt.extract_tx()).unwrap();
374
375                        wallet.sync(noop_progress(), None).unwrap();
376
377                        total_sent += 5_000 + details.fees;
378                    }
379
380                    wallet.sync(noop_progress(), None).unwrap();
381                    assert_eq!(wallet.get_balance().unwrap(), 50_000 - total_sent);
382
383                    // empty wallet
384                    let wallet = get_wallet_from_descriptors(&descriptors);
385                    wallet.sync(noop_progress(), None).unwrap();
386                    assert_eq!(wallet.get_balance().unwrap(), 50_000 - total_sent);
387                }
388
389                #[test]
390                #[serial]
391                fn test_sync_bump_fee() {
392                    let (wallet, descriptors, mut test_client) = init_single_sig();
393                    let node_addr = test_client.get_node_address(None);
394
395                    test_client.receive(testutils! {
396                        @tx ( (@external descriptors, 0) => 50_000 ) (@confirmations 1)
397                    });
398
399                    wallet.sync(noop_progress(), None).unwrap();
400                    assert_eq!(wallet.get_balance().unwrap(), 50_000);
401
402                    let mut builder = wallet.build_tx();
403                    builder.add_recipient(node_addr.script_pubkey().clone(), 5_000).enable_rbf();
404                    let (mut psbt, details) = builder.finish().unwrap();
405                    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
406                    assert!(finalized, "Cannot finalize transaction");
407                    wallet.broadcast(psbt.extract_tx()).unwrap();
408                    wallet.sync(noop_progress(), None).unwrap();
409                    assert_eq!(wallet.get_balance().unwrap(), 50_000 - details.fees - 5_000);
410                    assert_eq!(wallet.get_balance().unwrap(), details.received);
411
412                    let mut builder = wallet.build_fee_bump(details.txid).unwrap();
413                    builder.fee_rate(FeeRate::from_sat_per_vb(2.1));
414                    let (mut new_psbt, new_details) = builder.finish().unwrap();
415                    let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
416                    assert!(finalized, "Cannot finalize transaction");
417                    wallet.broadcast(new_psbt.extract_tx()).unwrap();
418                    wallet.sync(noop_progress(), None).unwrap();
419                    assert_eq!(wallet.get_balance().unwrap(), 50_000 - new_details.fees - 5_000);
420                    assert_eq!(wallet.get_balance().unwrap(), new_details.received);
421
422                    assert!(new_details.fees > details.fees);
423                }
424
425                #[test]
426                #[serial]
427                fn test_sync_bump_fee_remove_change() {
428                    let (wallet, descriptors, mut test_client) = init_single_sig();
429                    let node_addr = test_client.get_node_address(None);
430
431                    test_client.receive(testutils! {
432                        @tx ( (@external descriptors, 0) => 50_000 ) (@confirmations 1)
433                    });
434
435                    wallet.sync(noop_progress(), None).unwrap();
436                    assert_eq!(wallet.get_balance().unwrap(), 50_000);
437
438                    let mut builder = wallet.build_tx();
439                    builder.add_recipient(node_addr.script_pubkey().clone(), 49_000).enable_rbf();
440                    let (mut psbt, details) = builder.finish().unwrap();
441                    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
442                    assert!(finalized, "Cannot finalize transaction");
443                    wallet.broadcast(psbt.extract_tx()).unwrap();
444                    wallet.sync(noop_progress(), None).unwrap();
445                    assert_eq!(wallet.get_balance().unwrap(), 1_000 - details.fees);
446                    assert_eq!(wallet.get_balance().unwrap(), details.received);
447
448                    let mut builder = wallet.build_fee_bump(details.txid).unwrap();
449                    builder.fee_rate(FeeRate::from_sat_per_vb(5.0));
450                    let (mut new_psbt, new_details) = builder.finish().unwrap();
451                    let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
452                    assert!(finalized, "Cannot finalize transaction");
453                    wallet.broadcast(new_psbt.extract_tx()).unwrap();
454                    wallet.sync(noop_progress(), None).unwrap();
455                    assert_eq!(wallet.get_balance().unwrap(), 0);
456                    assert_eq!(new_details.received, 0);
457
458                    assert!(new_details.fees > details.fees);
459                }
460
461                #[test]
462                #[serial]
463                fn test_sync_bump_fee_add_input() {
464                    let (wallet, descriptors, mut test_client) = init_single_sig();
465                    let node_addr = test_client.get_node_address(None);
466
467                    test_client.receive(testutils! {
468                        @tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000 ) (@confirmations 1)
469                    });
470
471                    wallet.sync(noop_progress(), None).unwrap();
472                    assert_eq!(wallet.get_balance().unwrap(), 75_000);
473
474                    let mut builder = wallet.build_tx();
475                    builder.add_recipient(node_addr.script_pubkey().clone(), 49_000).enable_rbf();
476                    let (mut psbt, details) = builder.finish().unwrap();
477                    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
478                    assert!(finalized, "Cannot finalize transaction");
479                    wallet.broadcast(psbt.extract_tx()).unwrap();
480                    wallet.sync(noop_progress(), None).unwrap();
481                    assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fees);
482                    assert_eq!(details.received, 1_000 - details.fees);
483
484                    let mut builder = wallet.build_fee_bump(details.txid).unwrap();
485                    builder.fee_rate(FeeRate::from_sat_per_vb(10.0));
486                    let (mut new_psbt, new_details) = builder.finish().unwrap();
487                    let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
488                    assert!(finalized, "Cannot finalize transaction");
489                    wallet.broadcast(new_psbt.extract_tx()).unwrap();
490                    wallet.sync(noop_progress(), None).unwrap();
491                    assert_eq!(new_details.sent, 75_000);
492                    assert_eq!(wallet.get_balance().unwrap(), new_details.received);
493                }
494
495                #[test]
496                #[serial]
497                fn test_sync_bump_fee_add_input_no_change() {
498                    let (wallet, descriptors, mut test_client) = init_single_sig();
499                    let node_addr = test_client.get_node_address(None);
500
501                    test_client.receive(testutils! {
502                        @tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000 ) (@confirmations 1)
503                    });
504
505                    wallet.sync(noop_progress(), None).unwrap();
506                    assert_eq!(wallet.get_balance().unwrap(), 75_000);
507
508                    let mut builder = wallet.build_tx();
509                    builder.add_recipient(node_addr.script_pubkey().clone(), 49_000).enable_rbf();
510                    let (mut psbt, details) = builder.finish().unwrap();
511                    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
512                    assert!(finalized, "Cannot finalize transaction");
513                    wallet.broadcast(psbt.extract_tx()).unwrap();
514                    wallet.sync(noop_progress(), None).unwrap();
515                    assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fees);
516                    assert_eq!(details.received, 1_000 - details.fees);
517
518                    let mut builder = wallet.build_fee_bump(details.txid).unwrap();
519                    builder.fee_rate(FeeRate::from_sat_per_vb(123.0));
520                    let (mut new_psbt, new_details) = builder.finish().unwrap();
521                    println!("{:#?}", new_details);
522
523                    let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
524                    assert!(finalized, "Cannot finalize transaction");
525                    wallet.broadcast(new_psbt.extract_tx()).unwrap();
526                    wallet.sync(noop_progress(), None).unwrap();
527                    assert_eq!(new_details.sent, 75_000);
528                    assert_eq!(wallet.get_balance().unwrap(), 0);
529                    assert_eq!(new_details.received, 0);
530                }
531
532                #[test]
533                #[serial]
534                fn test_sync_receive_coinbase() {
535                    let (wallet, descriptors, mut test_client) = init_single_sig();
536                    let wallet_addr = wallet.get_address(New).unwrap();
537
538                    wallet.sync(noop_progress(), None).unwrap();
539                    assert_eq!(wallet.get_balance().unwrap(), 0);
540
541                    test_client.generate(1, Some(wallet_addr));
542
543                    wallet.sync(noop_progress(), None).unwrap();
544                    assert!(wallet.get_balance().unwrap() > 0);
545                }
546            }
547
548                        };
549
550            output.into()
551        }
552    }
553}