@@ -2,6 +2,7 @@ use core::fmt;
22use  core:: ops:: RangeBounds ; 
33
44use  alloc:: sync:: Arc ; 
5+ use  alloc:: vec:: Vec ; 
56use  bitcoin:: { block:: Header ,  BlockHash } ; 
67
78use  crate :: BlockId ; 
@@ -284,16 +285,21 @@ where
284285/// all entries following it. If the existing checkpoint at height is a placeholder where 
285286/// `data: None` with the same hash, then the `data` is inserted to make a complete checkpoint. 
286287/// The returned chain will have a tip of the data passed in. If the data was already present 
287- /// then this just returns `self`. This method does not create new placeholders. 
288+ /// then this just returns `self`. 
289+ /// 
290+ /// When inserting data with a `prev_blockhash` that conflicts with existing checkpoints, 
291+ /// those checkpoints will be displaced and replaced with placeholders. When inserting data 
292+ /// whose block hash conflicts with the `prev_blockhash` of higher checkpoints, those higher 
293+ /// checkpoints will be purged. 
288294/// 
289295/// # Panics 
290296/// 
291297/// This panics if called with a genesis block that differs from that of `self`. 
292298#[ must_use]  
293299    pub  fn  insert ( self ,  height :  u32 ,  data :  D )  -> Self  { 
294300        let  mut  cp = self . clone ( ) ; 
295-         let  mut  tail = vec ! [ ] ; 
296-         let  base = loop  { 
301+         let  mut  tail:   Vec < ( u32 ,   D ) >  = vec ! [ ] ; 
302+         let  mut   base = loop  { 
297303            if  cp. height ( )  == height { 
298304                let  same_hash = cp. hash ( )  == data. to_blockhash ( ) ; 
299305                if  same_hash { 
@@ -322,47 +328,112 @@ where
322328            cp = cp. prev ( ) . expect ( "will break before genesis block" ) ; 
323329        } ; 
324330
325-         base. extend ( core:: iter:: once ( ( height,  data) ) . chain ( tail. into_iter ( ) . rev ( ) ) ) 
326-             . expect ( "tail is in order" ) 
327-     } 
331+         if  let  Some ( prev_hash)  = data. prev_blockhash ( )  { 
332+             // Check if the new data's `prev_blockhash` conflicts with the checkpoint at height - 1. 
333+             if  let  Some ( lower_cp)  = base. get ( height. saturating_sub ( 1 ) )  { 
334+                 // New data's `prev_blockhash` conflicts with existing checkpoint, so we displace 
335+                 // the existing checkpoint and create a placeholder. 
336+                 if  lower_cp. hash ( )  != prev_hash { 
337+                     // Find the base to link to at height - 2 or lower. 
338+                     let  link_base = if  height > 1  { 
339+                         base. floor_at ( height - 2 ) 
340+                     }  else  { 
341+                         None 
342+                     } ; 
328343
329-     /// Puts another checkpoint onto the linked list representing the blockchain. 
330- /// 
331- /// Returns an `Err(self)` if the block you are pushing on is not at a greater height that the 
332- /// one you are pushing on to. 
333- /// 
334- /// If `height` is non-contiguous and `data.prev_blockhash()` is available, a placeholder is 
335- /// created at height - 1. 
336- pub  fn  push ( self ,  height :  u32 ,  data :  D )  -> Result < Self ,  Self >  { 
337-         if  self . height ( )  < height { 
338-             let  mut  current_cp = self . 0 . clone ( ) ; 
339- 
340-             // If non-contiguous and `prev_blockhash` exists, insert a placeholder at height - 1. 
341-             if  height > self . height ( )  + 1  { 
342-                 if  let  Some ( prev_hash)  = data. prev_blockhash ( )  { 
343-                     let  empty = Arc :: new ( CPInner  { 
344+                     // Create a new placeholder at height - 1 with the required `prev_blockhash`. 
345+                     base = Self ( Arc :: new ( CPInner  { 
346+                         block_id :  BlockId  { 
347+                             height :  height - 1 , 
348+                             hash :  prev_hash, 
349+                         } , 
350+                         data :  None , 
351+                         prev :  link_base. map ( |cb| cb. 0 ) . or_else ( || { 
352+                             // Keep any existing checkpoints below height - 2. 
353+                             if  height > 1  { 
354+                                 base. prev ( ) . map ( |p| p. 0 ) 
355+                             }  else  { 
356+                                 None 
357+                             } 
358+                         } ) , 
359+                     } ) ) ; 
360+                 } 
361+             }  else  { 
362+                 // No checkpoint at height - 1, but we may need to create a placeholder. 
363+                 if  height > 0  { 
364+                     base = Self ( Arc :: new ( CPInner  { 
344365                        block_id :  BlockId  { 
345366                            height :  height - 1 , 
346367                            hash :  prev_hash, 
347368                        } , 
348369                        data :  None , 
349-                         prev :  Some ( current_cp) , 
350-                     } ) ; 
351-                     current_cp = empty; 
370+                         prev :  base. 0 . prev . clone ( ) , 
371+                     } ) ) ; 
352372                } 
353373            } 
374+         } 
354375
355-             Ok ( Self ( Arc :: new ( CPInner  { 
356-                 block_id :  BlockId  { 
357-                     height, 
358-                     hash :  data. to_blockhash ( ) , 
359-                 } , 
360-                 data :  Some ( data) , 
361-                 prev :  Some ( current_cp) , 
362-             } ) ) ) 
363-         }  else  { 
364-             Err ( self ) 
376+         // Check for conflicts with higher checkpoints and purge if necessary. 
377+         let  mut  filtered_tail = Vec :: new ( ) ; 
378+         for  ( tail_height,  tail_data)  in  tail. into_iter ( ) . rev ( )  { 
379+             // Check if this tail entry's `prev_blockhash` conflicts with our new data's blockhash. 
380+             if  let  Some ( tail_prev_hash)  = tail_data. prev_blockhash ( )  { 
381+                 // Conflict detected, so purge this and all tail entries. 
382+                 if  tail_prev_hash != data. to_blockhash ( )  { 
383+                     break ; 
384+                 } 
385+             } 
386+             filtered_tail. push ( ( tail_height,  tail_data) ) ; 
387+         } 
388+ 
389+         base. extend ( core:: iter:: once ( ( height,  data) ) . chain ( filtered_tail) ) 
390+             . expect ( "tail is in order" ) 
391+     } 
392+ 
393+     /// Extends the chain by pushing a new checkpoint. 
394+ /// 
395+ /// Returns `Err(self)` if the height is not greater than the current height, or if the data's 
396+ /// `prev_blockhash` conflicts with `self`. 
397+ /// 
398+ /// Creates a placeholder at height - 1 if the height is non-contiguous and 
399+ /// `data.prev_blockhash()` is available. 
400+ pub  fn  push ( mut  self ,  height :  u32 ,  data :  D )  -> Result < Self ,  Self >  { 
401+         // Reject if trying to push at or below current height - chain must grow forward 
402+         if  height <= self . height ( )  { 
403+             return  Err ( self ) ; 
404+         } 
405+ 
406+         if  let  Some ( prev_hash)  = data. prev_blockhash ( )  { 
407+             if  height == self . height ( )  + 1  { 
408+                 // For contiguous height, validate that prev_blockhash matches our hash 
409+                 // to ensure chain continuity 
410+                 if  self . hash ( )  != prev_hash { 
411+                     return  Err ( self ) ; 
412+                 } 
413+             }  else  { 
414+                 // For non-contiguous heights, create placeholder to maintain chain linkage 
415+                 // This allows sparse chains while preserving block relationships 
416+                 self  = CheckPoint ( Arc :: new ( CPInner  { 
417+                     block_id :  BlockId  { 
418+                         height :  height
419+                             . checked_sub ( 1 ) 
420+                             . expect ( "height has previous blocks so must be greater than 0" ) , 
421+                         hash :  prev_hash, 
422+                     } , 
423+                     data :  None , 
424+                     prev :  Some ( self . 0 ) , 
425+                 } ) ) ; 
426+             } 
365427        } 
428+ 
429+         Ok ( Self ( Arc :: new ( CPInner  { 
430+             block_id :  BlockId  { 
431+                 height, 
432+                 hash :  data. to_blockhash ( ) , 
433+             } , 
434+             data :  Some ( data) , 
435+             prev :  Some ( self . 0 ) , 
436+         } ) ) ) 
366437    } 
367438} 
368439
0 commit comments