@@ -6,6 +6,9 @@ use bitcoin::{block::Header, BlockHash};
6
6
7
7
use crate :: BlockId ;
8
8
9
+ /// Interval for skiplist pointers based on checkpoint index.
10
+ const CHECKPOINT_SKIP_INTERVAL : u32 = 100 ;
11
+
9
12
/// A checkpoint is a node of a reference-counted linked list of [`BlockId`]s.
10
13
///
11
14
/// Checkpoints are cheaply cloneable and are useful to find the agreement point between two sparse
@@ -28,6 +31,10 @@ struct CPInner<D> {
28
31
data : D ,
29
32
/// Previous checkpoint (if any).
30
33
prev : Option < Arc < CPInner < D > > > ,
34
+ /// Skip pointer for fast traversals.
35
+ skip : Option < Arc < CPInner < D > > > ,
36
+ /// Index of this checkpoint (number of checkpoints from the first).
37
+ index : u32 ,
31
38
}
32
39
33
40
/// When a `CPInner` is dropped we need to go back down the chain and manually remove any
@@ -125,6 +132,16 @@ impl<D> CheckPoint<D> {
125
132
self . 0 . prev . clone ( ) . map ( CheckPoint )
126
133
}
127
134
135
+ /// Get the index of this checkpoint (number of checkpoints from the first).
136
+ pub fn index ( & self ) -> u32 {
137
+ self . 0 . index
138
+ }
139
+
140
+ /// Get the skip pointer checkpoint if it exists.
141
+ pub fn skip ( & self ) -> Option < CheckPoint < D > > {
142
+ self . 0 . skip . clone ( ) . map ( CheckPoint )
143
+ }
144
+
128
145
/// Iterate from this checkpoint in descending height.
129
146
pub fn iter ( & self ) -> CheckPointIter < D > {
130
147
self . clone ( ) . into_iter ( )
@@ -134,7 +151,47 @@ impl<D> CheckPoint<D> {
134
151
///
135
152
/// Returns `None` if checkpoint at `height` does not exist`.
136
153
pub fn get ( & self , height : u32 ) -> Option < Self > {
137
- self . range ( height..=height) . next ( )
154
+ // Quick path for current height
155
+ if self . height ( ) == height {
156
+ return Some ( self . clone ( ) ) ;
157
+ }
158
+
159
+ // Use skip pointers for efficient traversal
160
+ let mut current = self . clone ( ) ;
161
+
162
+ // First, use skip pointers to get close
163
+ while current. height ( ) > height {
164
+ // Try to use skip pointer if it won't overshoot
165
+ if let Some ( skip_cp) = current. skip ( ) {
166
+ if skip_cp. height ( ) >= height {
167
+ current = skip_cp;
168
+ continue ;
169
+ }
170
+ }
171
+
172
+ // Fall back to regular traversal
173
+ match current. prev ( ) {
174
+ Some ( prev) => {
175
+ if prev. height ( ) < height {
176
+ // Height doesn't exist in the chain
177
+ return None ;
178
+ }
179
+ current = prev;
180
+ }
181
+ None => return None ,
182
+ }
183
+
184
+ if current. height ( ) == height {
185
+ return Some ( current) ;
186
+ }
187
+ }
188
+
189
+ // Check if we found the height after the loop
190
+ if current. height ( ) == height {
191
+ Some ( current)
192
+ } else {
193
+ None
194
+ }
138
195
}
139
196
140
197
/// Iterate checkpoints over a height range.
@@ -147,12 +204,38 @@ impl<D> CheckPoint<D> {
147
204
{
148
205
let start_bound = range. start_bound ( ) . cloned ( ) ;
149
206
let end_bound = range. end_bound ( ) . cloned ( ) ;
150
- self . iter ( )
151
- . skip_while ( move |cp| match end_bound {
152
- core:: ops:: Bound :: Included ( inc_bound) => cp. height ( ) > inc_bound,
153
- core:: ops:: Bound :: Excluded ( exc_bound) => cp. height ( ) >= exc_bound,
154
- core:: ops:: Bound :: Unbounded => false ,
155
- } )
207
+
208
+ // Fast-path to find starting point using skip pointers
209
+ let mut current = self . clone ( ) ;
210
+
211
+ // Skip past checkpoints that are above the end bound
212
+ while match end_bound {
213
+ core:: ops:: Bound :: Included ( inc_bound) => current. height ( ) > inc_bound,
214
+ core:: ops:: Bound :: Excluded ( exc_bound) => current. height ( ) >= exc_bound,
215
+ core:: ops:: Bound :: Unbounded => false ,
216
+ } {
217
+ // Try to use skip pointer if it won't overshoot
218
+ if let Some ( skip_cp) = current. skip ( ) {
219
+ let use_skip = match end_bound {
220
+ core:: ops:: Bound :: Included ( inc_bound) => skip_cp. height ( ) > inc_bound,
221
+ core:: ops:: Bound :: Excluded ( exc_bound) => skip_cp. height ( ) >= exc_bound,
222
+ core:: ops:: Bound :: Unbounded => false ,
223
+ } ;
224
+ if use_skip {
225
+ current = skip_cp;
226
+ continue ;
227
+ }
228
+ }
229
+
230
+ // Fall back to regular traversal
231
+ match current. prev ( ) {
232
+ Some ( prev) => current = prev,
233
+ None => break ,
234
+ }
235
+ }
236
+
237
+ // Now iterate normally from the found starting point
238
+ current. into_iter ( )
156
239
. take_while ( move |cp| match start_bound {
157
240
core:: ops:: Bound :: Included ( inc_bound) => cp. height ( ) >= inc_bound,
158
241
core:: ops:: Bound :: Excluded ( exc_bound) => cp. height ( ) > exc_bound,
@@ -167,7 +250,38 @@ impl<D> CheckPoint<D> {
167
250
///
168
251
/// Returns `None` if no checkpoint exists at or below the given height.
169
252
pub fn floor_at ( & self , height : u32 ) -> Option < Self > {
170
- self . range ( ..=height) . next ( )
253
+ // Quick path for current height or higher
254
+ if self . height ( ) <= height {
255
+ return Some ( self . clone ( ) ) ;
256
+ }
257
+
258
+ // Use skip pointers for efficient traversal
259
+ let mut current = self . clone ( ) ;
260
+
261
+ while current. height ( ) > height {
262
+ // Try to use skip pointer if it won't undershoot
263
+ if let Some ( skip_cp) = current. skip ( ) {
264
+ if skip_cp. height ( ) > height {
265
+ current = skip_cp;
266
+ continue ;
267
+ }
268
+ }
269
+
270
+ // Fall back to regular traversal
271
+ match current. prev ( ) {
272
+ Some ( prev) => {
273
+ // If prev is at or below height, we've found our floor
274
+ if prev. height ( ) <= height {
275
+ return Some ( prev) ;
276
+ }
277
+ current = prev;
278
+ }
279
+ None => return None ,
280
+ }
281
+ }
282
+
283
+ // Current is at or below height
284
+ Some ( current)
171
285
}
172
286
173
287
/// Returns the checkpoint located a number of heights below this one.
@@ -205,6 +319,8 @@ where
205
319
} ,
206
320
data,
207
321
prev : None ,
322
+ skip : None ,
323
+ index : 0 ,
208
324
} ) )
209
325
}
210
326
@@ -269,8 +385,63 @@ where
269
385
cp = cp. prev ( ) . expect ( "will break before genesis block" ) ;
270
386
} ;
271
387
272
- base. extend ( core:: iter:: once ( ( height, data) ) . chain ( tail. into_iter ( ) . rev ( ) ) )
273
- . expect ( "tail is in order" )
388
+ // Rebuild the chain with proper indices
389
+ let mut result = base. clone ( ) ;
390
+ let base_index = result. index ( ) ;
391
+
392
+ // First insert the new block
393
+ result = result. push_with_index ( height, data, base_index + 1 ) . expect ( "height is valid" ) ;
394
+
395
+ // Then re-add all the tail blocks with updated indices
396
+ let mut current_index = base_index + 2 ;
397
+ for ( h, d) in tail. into_iter ( ) . rev ( ) {
398
+ result = result. push_with_index ( h, d, current_index) . expect ( "tail is in order" ) ;
399
+ current_index += 1 ;
400
+ }
401
+
402
+ result
403
+ }
404
+
405
+ // Helper method to push with a specific index (internal use)
406
+ fn push_with_index ( self , height : u32 , data : D , new_index : u32 ) -> Result < Self , Self > {
407
+ if self . height ( ) < height {
408
+ // Calculate skip pointer
409
+ let skip = if new_index >= CHECKPOINT_SKIP_INTERVAL && new_index % CHECKPOINT_SKIP_INTERVAL == 0 {
410
+ // Navigate back CHECKPOINT_SKIP_INTERVAL checkpoints
411
+ let target_index = new_index - CHECKPOINT_SKIP_INTERVAL ;
412
+ let mut current = Some ( self . 0 . clone ( ) ) ;
413
+ loop {
414
+ match current {
415
+ Some ( ref cp) if cp. index == target_index => break ,
416
+ Some ( ref cp) if cp. index < target_index => {
417
+ // We've gone too far back, skip pointer not available
418
+ current = None ;
419
+ break ;
420
+ }
421
+ Some ( ref cp) => {
422
+ current = cp. prev . clone ( ) ;
423
+ }
424
+ None => break ,
425
+ }
426
+ }
427
+ current
428
+ } else {
429
+ None
430
+ } ;
431
+
432
+ Ok ( Self ( Arc :: new ( CPInner {
433
+ block_id : BlockId {
434
+ height,
435
+ hash : data. to_blockhash ( ) ,
436
+ } ,
437
+ data,
438
+ prev : Some ( self . 0 ) ,
439
+ skip,
440
+ index : new_index,
441
+ } ) ) )
442
+ } else {
443
+ Err ( self )
444
+ }
274
445
}
275
446
276
447
/// Puts another checkpoint onto the linked list representing the blockchain.
@@ -279,13 +450,42 @@ where
279
450
/// one you are pushing on to.
280
451
pub fn push ( self , height : u32 , data : D ) -> Result < Self , Self > {
281
452
if self . height ( ) < height {
453
+ let new_index = self . 0 . index + 1 ;
454
+
455
+ // Calculate skip pointer
456
+ let skip = if new_index >= CHECKPOINT_SKIP_INTERVAL && new_index % CHECKPOINT_SKIP_INTERVAL == 0 {
457
+ // Navigate back CHECKPOINT_SKIP_INTERVAL checkpoints
458
+ let mut current = Some ( self . 0 . clone ( ) ) ;
459
+ let mut steps = 0 ;
460
+ loop {
461
+ match current {
462
+ Some ( ref cp) if cp. index == new_index - CHECKPOINT_SKIP_INTERVAL => break ,
463
+ Some ( ref cp) => {
464
+ current = cp. prev . clone ( ) ;
465
+ steps += 1 ;
466
+ // Safety check to avoid infinite loop
467
+ if steps > CHECKPOINT_SKIP_INTERVAL {
468
+ current = None ;
469
+ break ;
470
+ }
471
+ }
472
+ None => break ,
473
+ }
474
+ }
475
+ current
476
+ } else {
477
+ None
478
+ } ;
479
+
282
480
Ok ( Self ( Arc :: new ( CPInner {
283
481
block_id : BlockId {
284
482
height,
285
483
hash : data. to_blockhash ( ) ,
286
484
} ,
287
485
data,
288
486
prev : Some ( self . 0 ) ,
487
+ skip,
488
+ index : new_index,
289
489
} ) ) )
290
490
} else {
291
491
Err ( self )
0 commit comments