@@ -13,6 +13,7 @@ use bdk_core::bitcoin;
13
13
use bdk_core:: { BlockId , CheckPoint } ;
14
14
use bitcoin:: {
15
15
bip158:: { self , BlockFilter } ,
16
+ block:: Header ,
16
17
Block , BlockHash , ScriptBuf ,
17
18
} ;
18
19
use bitcoincore_rpc;
@@ -21,6 +22,9 @@ use bitcoincore_rpc::RpcApi;
21
22
/// Block height
22
23
type Height = u32 ;
23
24
25
+ /// Block header with associated block hash.
26
+ type HashedHeader = ( BlockHash , Header ) ;
27
+
24
28
/// Type that generates block [`Event`]s by matching a list of script pubkeys against a
25
29
/// [`BlockFilter`].
26
30
#[ derive( Debug ) ]
@@ -31,8 +35,8 @@ pub struct FilterIter<'c, C> {
31
35
spks : Vec < ScriptBuf > ,
32
36
// local cp
33
37
cp : Option < CheckPoint > ,
34
- // blocks map
35
- blocks : BTreeMap < Height , BlockHash > ,
38
+ // block headers
39
+ headers : BTreeMap < Height , HashedHeader > ,
36
40
// heights of matching blocks
37
41
matched : BTreeSet < Height > ,
38
42
// best height counter
@@ -53,7 +57,7 @@ impl<'c, C: RpcApi> FilterIter<'c, C> {
53
57
client,
54
58
spks : vec ! [ ] ,
55
59
cp : None ,
56
- blocks : BTreeMap :: new ( ) ,
60
+ headers : BTreeMap :: new ( ) ,
57
61
matched : BTreeSet :: new ( ) ,
58
62
height,
59
63
start : height,
@@ -102,6 +106,11 @@ impl<'c, C: RpcApi> FilterIter<'c, C> {
102
106
hash : tip_hash,
103
107
} ) )
104
108
}
109
+
110
+ /// Return all of the block headers that were collected during the scan.
111
+ pub fn block_headers ( & self ) -> & BTreeMap < Height , ( BlockHash , Header ) > {
112
+ & self . headers
113
+ }
105
114
}
106
115
107
116
/// Event inner type
@@ -151,27 +160,27 @@ impl<C: RpcApi> Iterator for FilterIter<'_, C> {
151
160
152
161
let mut reorg_depth = 0 ;
153
162
154
- loop {
163
+ let header = loop {
155
164
if reorg_depth >= Self :: MAX_REORG_DEPTH {
156
165
return Err ( Error :: ReorgDepthExceeded ) ;
157
166
}
158
167
159
168
let header = self . client . get_block_header ( & hash) ?;
160
169
161
170
let prev_height = height. saturating_sub ( 1 ) ;
162
- match self . blocks . get ( & prev_height) . copied ( ) {
171
+ match self . headers . get ( & prev_height) . copied ( ) {
163
172
// Not enough data.
164
- None => break ,
173
+ None => break header ,
165
174
// Ok, the chain is consistent.
166
- Some ( prev_hash) if prev_hash == header. prev_blockhash => break ,
175
+ Some ( ( prev_hash, _ ) ) if prev_hash == header. prev_blockhash => break header ,
167
176
_ => {
168
177
// Reorg detected, keep backtracking.
169
178
height = height. saturating_sub ( 1 ) ;
170
179
hash = self . client . get_block_hash ( height as u64 ) ?;
171
180
reorg_depth += 1 ;
172
181
}
173
182
}
174
- }
183
+ } ;
175
184
176
185
let filter_bytes = self . client . get_block_filter ( & hash) ?. filter ;
177
186
let filter = BlockFilter :: new ( & filter_bytes) ;
@@ -193,11 +202,11 @@ impl<C: RpcApi> Iterator for FilterIter<'_, C> {
193
202
194
203
// In case of a reorg, throw out any stale entries.
195
204
if reorg_depth > 0 {
196
- self . blocks . split_off ( & height) ;
205
+ self . headers . split_off ( & height) ;
197
206
self . matched . split_off ( & height) ;
198
207
}
199
208
// Record the scanned block
200
- self . blocks . insert ( height, hash) ;
209
+ self . headers . insert ( height, ( hash, header ) ) ;
201
210
// Record the matching block
202
211
if let Ok ( Some ( Event :: Block ( ..) ) ) = next_event {
203
212
self . matched . insert ( height) ;
@@ -216,17 +225,21 @@ impl<C: RpcApi> FilterIter<'_, C> {
216
225
fn find_base_with ( & mut self , mut cp : CheckPoint ) -> Result < BlockId , Error > {
217
226
loop {
218
227
let height = cp. height ( ) ;
219
- let fetched_hash = match self . blocks . get ( & height) {
220
- Some ( hash) => * hash,
221
- None => self . client . get_block_hash ( height as u64 ) ?,
228
+ let ( fetched_hash, header) = match self . headers . get ( & height) . copied ( ) {
229
+ Some ( value) => value,
230
+ None => {
231
+ let hash = self . client . get_block_hash ( height as u64 ) ?;
232
+ let header = self . client . get_block_header ( & hash) ?;
233
+ ( hash, header)
234
+ }
222
235
} ;
223
236
if cp. hash ( ) == fetched_hash {
224
- // ensure this block also exists in self
225
- self . blocks . insert ( height, cp . hash ( ) ) ;
237
+ // Ensure this block also exists in ` self`.
238
+ self . headers . insert ( height, ( fetched_hash , header ) ) ;
226
239
return Ok ( cp. block_id ( ) ) ;
227
240
}
228
- // remember conflicts
229
- self . blocks . insert ( height, fetched_hash) ;
241
+ // Remember conflicts.
242
+ self . headers . insert ( height, ( fetched_hash, header ) ) ;
230
243
cp = cp. prev ( ) . ok_or ( Error :: ReorgDepthExceeded ) ?;
231
244
}
232
245
}
@@ -236,15 +249,15 @@ impl<C: RpcApi> FilterIter<'_, C> {
236
249
/// Returns `None` if this [`FilterIter`] was not constructed using a [`CheckPoint`], or
237
250
/// if not all events have been emitted (by calling `next`).
238
251
pub fn chain_update ( & self ) -> Option < CheckPoint > {
239
- if self . cp . is_none ( ) || self . blocks . is_empty ( ) || self . height <= self . stop {
252
+ if self . cp . is_none ( ) || self . headers . is_empty ( ) || self . height <= self . stop {
240
253
return None ;
241
254
}
242
255
243
256
// We return blocks up to and including the initial height, all of the matching blocks,
244
257
// and blocks in the terminal range.
245
258
let tail_range = self . stop . saturating_sub ( 9 ) ..=self . stop ;
246
259
Some (
247
- CheckPoint :: from_block_ids ( self . blocks . iter ( ) . filter_map ( |( & height, & hash) | {
260
+ CheckPoint :: from_block_ids ( self . headers . iter ( ) . filter_map ( |( & height, & ( hash, _ ) ) | {
248
261
if height <= self . start
249
262
|| self . matched . contains ( & height)
250
263
|| tail_range. contains ( & height)
0 commit comments