@@ -6,6 +6,9 @@ use bitcoin::{block::Header, BlockHash};
66
77use crate :: BlockId ;
88
9+ /// Interval for skiplist pointers based on checkpoint index.
10+ const CHECKPOINT_SKIP_INTERVAL : u32 = 100 ;
11+
912/// A checkpoint is a node of a reference-counted linked list of [`BlockId`]s.
1013///
1114/// Checkpoints are cheaply cloneable and are useful to find the agreement point between two sparse
@@ -28,6 +31,10 @@ struct CPInner<D> {
2831 data : D ,
2932 /// Previous checkpoint (if any).
3033 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 ,
3138}
3239
3340/// 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> {
125132 self . 0 . prev . clone ( ) . map ( CheckPoint )
126133 }
127134
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+
128145 /// Iterate from this checkpoint in descending height.
129146 pub fn iter ( & self ) -> CheckPointIter < D > {
130147 self . clone ( ) . into_iter ( )
@@ -134,7 +151,47 @@ impl<D> CheckPoint<D> {
134151 ///
135152 /// Returns `None` if checkpoint at `height` does not exist`.
136153 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+ }
138195 }
139196
140197 /// Iterate checkpoints over a height range.
@@ -147,12 +204,38 @@ impl<D> CheckPoint<D> {
147204 {
148205 let start_bound = range. start_bound ( ) . cloned ( ) ;
149206 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 ( )
156239 . take_while ( move |cp| match start_bound {
157240 core:: ops:: Bound :: Included ( inc_bound) => cp. height ( ) >= inc_bound,
158241 core:: ops:: Bound :: Excluded ( exc_bound) => cp. height ( ) > exc_bound,
@@ -167,7 +250,38 @@ impl<D> CheckPoint<D> {
167250 ///
168251 /// Returns `None` if no checkpoint exists at or below the given height.
169252 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)
171285 }
172286
173287 /// Returns the checkpoint located a number of heights below this one.
@@ -205,6 +319,8 @@ where
205319 } ,
206320 data,
207321 prev : None ,
322+ skip : None ,
323+ index : 0 ,
208324 } ) )
209325 }
210326
@@ -269,8 +385,63 @@ where
269385 cp = cp. prev ( ) . expect ( "will break before genesis block" ) ;
270386 } ;
271387
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+ }
274445 }
275446
276447 /// Puts another checkpoint onto the linked list representing the blockchain.
@@ -279,13 +450,42 @@ where
279450 /// one you are pushing on to.
280451 pub fn push ( self , height : u32 , data : D ) -> Result < Self , Self > {
281452 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+
282480 Ok ( Self ( Arc :: new ( CPInner {
283481 block_id : BlockId {
284482 height,
285483 hash : data. to_blockhash ( ) ,
286484 } ,
287485 data,
288486 prev : Some ( self . 0 ) ,
487+ skip,
488+ index : new_index,
289489 } ) ) )
290490 } else {
291491 Err ( self )
0 commit comments