@@ -6,7 +6,7 @@ use bdk_chain::{BlockId, CanonicalizationParams, ConfirmationBlockTime};
6
6
use bdk_wallet:: coin_selection;
7
7
use bdk_wallet:: descriptor:: { calc_checksum, DescriptorError } ;
8
8
use bdk_wallet:: error:: CreateTxError ;
9
- use bdk_wallet:: psbt:: PsbtUtils ;
9
+ use bdk_wallet:: psbt:: { self , PsbtUtils } ;
10
10
use bdk_wallet:: signer:: { SignOptions , SignerError } ;
11
11
use bdk_wallet:: test_utils:: * ;
12
12
use bdk_wallet:: KeychainKind ;
@@ -25,6 +25,87 @@ use rand::SeedableRng;
25
25
26
26
mod common;
27
27
28
+ // Test we can select and spend an indexed but not-yet-canonical utxo
29
+ #[ test]
30
+ fn test_spend_non_canonical_txout ( ) -> anyhow:: Result < ( ) > {
31
+ let ( desc, change_desc) = get_test_wpkh_and_change_desc ( ) ;
32
+ let mut wallet = Wallet :: create ( desc, change_desc)
33
+ . network ( Network :: Regtest )
34
+ . create_wallet_no_persist ( )
35
+ . unwrap ( ) ;
36
+
37
+ let recip = ScriptBuf :: from_hex ( "0014446906a6560d8ad760db3156706e72e171f3a2aa" ) . unwrap ( ) ;
38
+
39
+ // Receive tx0 (coinbase)
40
+ let tx = Transaction {
41
+ input : vec ! [ TxIn :: default ( ) ] ,
42
+ output : vec ! [ TxOut {
43
+ value: Amount :: ONE_BTC ,
44
+ script_pubkey: wallet
45
+ . reveal_next_address( KeychainKind :: External )
46
+ . script_pubkey( ) ,
47
+ } ] ,
48
+ ..new_tx ( 1 )
49
+ } ;
50
+ let block = BlockId {
51
+ height : 100 ,
52
+ hash : Hash :: hash ( b"100" ) ,
53
+ } ;
54
+ insert_tx_anchor ( & mut wallet, tx, block) ;
55
+ let block = BlockId {
56
+ height : 1000 ,
57
+ hash : Hash :: hash ( b"1000" ) ,
58
+ } ;
59
+ insert_checkpoint ( & mut wallet, block) ;
60
+
61
+ // Create tx1
62
+ let mut params = psbt:: PsbtParams :: default ( ) ;
63
+ params. add_recipients ( [ ( recip. clone ( ) , Amount :: from_btc ( 0.01 ) ?) ] ) ;
64
+ let psbt = wallet. create_psbt ( params) ?. 0 ;
65
+ let txid = psbt. unsigned_tx . compute_txid ( ) ;
66
+ let ( vout, _) = psbt
67
+ . unsigned_tx
68
+ . output
69
+ . iter ( )
70
+ . enumerate ( )
71
+ . find ( |( _, txo) | wallet. is_mine ( txo. script_pubkey . clone ( ) ) )
72
+ . unwrap ( ) ;
73
+ let to_select_op = OutPoint :: new ( txid, vout as u32 ) ;
74
+
75
+ let txid1 = psbt. unsigned_tx . compute_txid ( ) ;
76
+ wallet. insert_tx ( psbt. unsigned_tx ) ;
77
+
78
+ // Create tx2, spending the change of tx1
79
+ let mut params = psbt:: PsbtParams :: default ( ) ;
80
+ params
81
+ . add_utxos ( & [ to_select_op] )
82
+ . feerate ( FeeRate :: from_sat_per_vb_unchecked ( 10 ) )
83
+ . add_recipients ( [ ( recip, Amount :: from_btc ( 0.01 ) ?) ] ) ;
84
+
85
+ let psbt = wallet. create_psbt ( params) ?. 0 ;
86
+
87
+ assert_eq ! ( psbt. unsigned_tx. input. len( ) , 1 ) ;
88
+ assert_eq ! ( psbt. unsigned_tx. input[ 0 ] . previous_output, to_select_op) ;
89
+
90
+ let txid2 = psbt. unsigned_tx . compute_txid ( ) ;
91
+ wallet. insert_tx ( psbt. unsigned_tx ) ;
92
+
93
+ // Query the set of unbroadcasted txs
94
+ let txs = wallet
95
+ . transactions_with_params ( CanonicalizationParams {
96
+ assume_canonical : vec ! [ txid2] ,
97
+ } )
98
+ . filter ( |c| c. chain_position . is_unconfirmed ( ) )
99
+ . collect :: < Vec < _ > > ( ) ;
100
+
101
+ assert_eq ! ( txs. len( ) , 2 ) ;
102
+
103
+ assert ! ( txs. iter( ) . any( |c| c. tx_node. txid == txid1) ) ;
104
+ assert ! ( txs. iter( ) . any( |c| c. tx_node. txid == txid2) ) ;
105
+
106
+ Ok ( ( ) )
107
+ }
108
+
28
109
#[ test]
29
110
fn test_error_external_and_internal_are_the_same ( ) {
30
111
// identical descriptors should fail to create wallet
0 commit comments