1
1
use anyhow:: Ok ;
2
2
use bdk_esplora:: { esplora_client, EsploraAsyncExt } ;
3
3
use bdk_wallet:: {
4
- bitcoin:: { Amount , Network , Txid } ,
4
+ bitcoin:: { Amount , FeeRate , Network } ,
5
+ psbt:: PsbtUtils ,
5
6
rusqlite:: Connection ,
6
7
KeychainKind , SignOptions , Wallet ,
7
8
} ;
8
- use std:: {
9
- collections:: { BTreeSet , HashSet } ,
10
- io:: Write ,
11
- } ;
9
+ use std:: { collections:: BTreeSet , io:: Write } ;
12
10
13
11
const SEND_AMOUNT : Amount = Amount :: from_sat ( 5000 ) ;
14
12
const STOP_GAP : usize = 5 ;
@@ -23,7 +21,6 @@ const ESPLORA_URL: &str = "http://signet.bitcoindevkit.net";
23
21
#[ tokio:: main]
24
22
async fn main ( ) -> Result < ( ) , anyhow:: Error > {
25
23
let mut conn = Connection :: open ( DB_PATH ) ?;
26
-
27
24
let wallet_opt = Wallet :: load ( )
28
25
. descriptor ( KeychainKind :: External , Some ( EXTERNAL_DESC ) )
29
26
. descriptor ( KeychainKind :: Internal , Some ( INTERNAL_DESC ) )
@@ -39,12 +36,12 @@ async fn main() -> Result<(), anyhow::Error> {
39
36
40
37
let address = wallet. next_unused_address ( KeychainKind :: External ) ;
41
38
wallet. persist ( & mut conn) ?;
42
- println ! ( "Next unused address: ({}) {}" , address. index, address ) ;
39
+ println ! ( "Next unused address: ({}) {address }" , address. index) ;
43
40
44
41
let balance = wallet. balance ( ) ;
45
42
println ! ( "Wallet balance before syncing: {}" , balance. total( ) ) ;
46
43
47
- println ! ( "=== Performing Full Sync === " ) ;
44
+ println ! ( "Full Sync... " ) ;
48
45
let client = esplora_client:: Builder :: new ( ESPLORA_URL ) . build_async ( ) ?;
49
46
50
47
let request = wallet. start_full_scan ( ) . inspect ( {
@@ -54,7 +51,9 @@ async fn main() -> Result<(), anyhow::Error> {
54
51
if once. insert ( keychain) {
55
52
print ! ( "\n Scanning keychain [{keychain:?}]" ) ;
56
53
}
57
- print ! ( " {spk_i:<3}" ) ;
54
+ if spk_i. is_multiple_of ( 5 ) {
55
+ print ! ( " {spk_i:<3}" ) ;
56
+ }
58
57
stdout. flush ( ) . expect ( "must flush" )
59
58
}
60
59
} ) ;
@@ -79,27 +78,22 @@ async fn main() -> Result<(), anyhow::Error> {
79
78
println ! ( "Please send at least {SEND_AMOUNT} to the receiving address" ) ;
80
79
std:: process:: exit ( 0 ) ;
81
80
}
82
-
83
81
let mut tx_builder = wallet. build_tx ( ) ;
84
82
tx_builder. add_recipient ( address. script_pubkey ( ) , SEND_AMOUNT ) ;
85
83
86
84
let mut psbt = tx_builder. finish ( ) ?;
87
85
let finalized = wallet. sign ( & mut psbt, SignOptions :: default ( ) ) ?;
88
86
assert ! ( finalized) ;
89
-
87
+ let original_fee = psbt. fee_amount ( ) . unwrap ( ) ;
88
+ let tx_feerate = psbt. fee_rate ( ) . unwrap ( ) ;
90
89
let tx = psbt. extract_tx ( ) ?;
91
90
client. broadcast ( & tx) . await ?;
92
- println ! ( "Tx broadcasted! Txid: {}" , tx. compute_txid( ) ) ;
93
-
94
- let unconfirmed_txids: HashSet < Txid > = wallet
95
- . transactions ( )
96
- . filter ( |tx| tx. chain_position . is_unconfirmed ( ) )
97
- . map ( |tx| tx. tx_node . txid )
98
- . collect ( ) ;
91
+ let txid = tx. compute_txid ( ) ;
92
+ println ! ( "Tx broadcasted! Txid: {txid}" ) ;
99
93
100
- println ! ( "\n === Performing Partial Sync === \n " ) ;
94
+ println ! ( "Partial Sync... " ) ;
101
95
print ! ( "SCANNING: " ) ;
102
- let mut printed = 0 ;
96
+ let mut printed: u32 = 0 ;
103
97
let sync_request = wallet
104
98
. start_sync_with_revealed_spks ( )
105
99
. inspect ( move |_, sync_progress| {
@@ -114,30 +108,63 @@ async fn main() -> Result<(), anyhow::Error> {
114
108
} ) ;
115
109
let sync_update = client. sync ( sync_request, PARALLEL_REQUESTS ) . await ?;
116
110
println ! ( ) ;
111
+ wallet. apply_update ( sync_update) ?;
112
+ wallet. persist ( & mut conn) ?;
117
113
118
- let mut evicted_txs = Vec :: new ( ) ;
119
- for txid in unconfirmed_txids {
120
- let tx_node = wallet
121
- . tx_graph ( )
122
- . full_txs ( )
123
- . find ( |full_tx| full_tx. txid == txid) ;
124
- let wallet_tx = wallet. get_tx ( txid) ;
125
-
126
- let is_evicted = match wallet_tx {
127
- Some ( wallet_tx) => {
128
- !wallet_tx. chain_position . is_unconfirmed ( )
129
- && !wallet_tx. chain_position . is_confirmed ( )
130
- }
131
- None => true ,
132
- } ;
133
-
134
- if is_evicted {
135
- if let Some ( full_tx) = tx_node {
136
- evicted_txs. push ( ( full_tx. txid , full_tx. last_seen . unwrap_or ( 0 ) ) ) ;
137
- } else {
138
- evicted_txs. push ( ( txid, 0 ) ) ;
114
+ let feerate = FeeRate :: from_sat_per_kwu ( tx_feerate. to_sat_per_kwu ( ) + 250 ) ;
115
+ let mut builder = wallet. build_fee_bump ( txid) . expect ( "failed to bump tx" ) ;
116
+ builder. fee_rate ( feerate) ;
117
+ let mut bumped_psbt = builder. finish ( ) . unwrap ( ) ;
118
+ let finalize_btx = wallet. sign ( & mut bumped_psbt, SignOptions :: default ( ) ) ?;
119
+ assert ! ( finalize_btx) ;
120
+ let new_fee = bumped_psbt. fee_amount ( ) . unwrap ( ) ;
121
+ let bumped_tx = bumped_psbt. extract_tx ( ) ?;
122
+ assert_eq ! (
123
+ bumped_tx
124
+ . output
125
+ . iter( )
126
+ . find( |txout| txout. script_pubkey == address. script_pubkey( ) )
127
+ . unwrap( )
128
+ . value,
129
+ SEND_AMOUNT ,
130
+ "Outputs should be the same"
131
+ ) ;
132
+ assert ! (
133
+ new_fee > original_fee,
134
+ "New fee ({new_fee}) should be higher than original ({original_fee})" ,
135
+ ) ;
136
+ client. broadcast ( & bumped_tx) . await ?;
137
+ println ! ( "Broadcasted bumped tx. Txid: {}" , bumped_tx. compute_txid( ) ) ;
138
+
139
+ println ! ( "syncing after broadcasting bumped tx..." ) ;
140
+ print ! ( "SCANNING: " ) ;
141
+ let sync_request = wallet
142
+ . start_sync_with_revealed_spks ( )
143
+ . inspect ( move |_, sync_progress| {
144
+ let progress_percent =
145
+ ( 100 * sync_progress. consumed ( ) ) as f32 / sync_progress. total ( ) as f32 ;
146
+ let progress_percent = progress_percent. round ( ) as u32 ;
147
+ if progress_percent. is_multiple_of ( 10 ) && progress_percent > printed {
148
+ print ! ( "{progress_percent}% " ) ;
149
+ std:: io:: stdout ( ) . flush ( ) . expect ( "must flush" ) ;
150
+ printed = progress_percent;
139
151
}
140
- }
152
+ } ) ;
153
+ let sync_update = client. sync ( sync_request, PARALLEL_REQUESTS ) . await ?;
154
+ println ! ( ) ;
155
+
156
+ let mut evicted_txs = Vec :: new ( ) ;
157
+
158
+ let last_seen = wallet
159
+ . tx_graph ( )
160
+ . full_txs ( )
161
+ . find ( |full_tx| full_tx. txid == txid)
162
+ . map_or ( 0 , |full_tx| full_tx. last_seen . unwrap_or ( 0 ) ) ;
163
+ if !evicted_txs
164
+ . iter ( )
165
+ . any ( |( evicted_txid, _) | evicted_txid == & txid)
166
+ {
167
+ evicted_txs. push ( ( txid, last_seen) ) ;
141
168
}
142
169
143
170
if !evicted_txs. is_empty ( ) {
0 commit comments