@@ -24,8 +24,8 @@ impl<D> Clone for CheckPoint<D> {
2424struct CPInner < D > {
2525 /// Block id
2626 block_id : BlockId ,
27- /// Data.
28- data : D ,
27+ /// Data (if any) .
28+ data : Option < D > ,
2929 /// Previous checkpoint (if any).
3030 prev : Option < Arc < CPInner < D > > > ,
3131}
@@ -68,6 +68,11 @@ impl<D> Drop for CPInner<D> {
6868pub trait ToBlockHash {
6969 /// Returns the [`BlockHash`] for the associated [`CheckPoint`] `data` type.
7070 fn to_blockhash ( & self ) -> BlockHash ;
71+
72+ /// Returns `None` if the type has no knowledge of the previous [`BlockHash`].
73+ fn prev_blockhash ( & self ) -> Option < BlockHash > {
74+ None
75+ }
7176}
7277
7378impl ToBlockHash for BlockHash {
@@ -80,6 +85,10 @@ impl ToBlockHash for Header {
8085 fn to_blockhash ( & self ) -> BlockHash {
8186 self . block_hash ( )
8287 }
88+
89+ fn prev_blockhash ( & self ) -> Option < BlockHash > {
90+ Some ( self . prev_blockhash )
91+ }
8392}
8493
8594impl < D > PartialEq for CheckPoint < D > {
@@ -92,13 +101,13 @@ impl<D> PartialEq for CheckPoint<D> {
92101
93102// Methods for any `D`
94103impl < D > CheckPoint < D > {
95- /// Get a reference of the `data` of the checkpoint.
96- pub fn data_ref ( & self ) -> & D {
97- & self . 0 . data
104+ /// Get a reference of the `data` of the checkpoint if it exists .
105+ pub fn data_ref ( & self ) -> Option < & D > {
106+ self . 0 . data . as_ref ( )
98107 }
99108
100- /// Get the `data` of a the checkpoint.
101- pub fn data ( & self ) -> D
109+ /// Get the `data` of the checkpoint if it exists .
110+ pub fn data ( & self ) -> Option < D >
102111 where
103112 D : Clone ,
104113 {
@@ -170,6 +179,17 @@ impl<D> CheckPoint<D> {
170179 self . range ( ..=height) . next ( )
171180 }
172181
182+ /// Finds the checkpoint with `data` at `height` if one exists, otherwise the neareast
183+ /// checkpoint with `data` at a lower height.
184+ ///
185+ /// This is equivalent to taking the “floor” of "height" over this checkpoint chain, filtering
186+ /// out any placeholder entries that do not contain any `data`.
187+ ///
188+ /// Returns `None` if no checkpoint with `data` exists at or below the given height.
189+ pub fn find_data ( & self , height : u32 ) -> Option < Self > {
190+ self . range ( ..=height) . find ( |cp| cp. data_ref ( ) . is_some ( ) )
191+ }
192+
173193 /// Returns the checkpoint located a number of heights below this one.
174194 ///
175195 /// This is a convenience wrapper for [`CheckPoint::floor_at`], subtracting `to_subtract` from
@@ -198,13 +218,30 @@ where
198218 /// Construct a new base [`CheckPoint`] from given `height` and `data` at the front of a linked
199219 /// list.
200220 pub fn new ( height : u32 , data : D ) -> Self {
221+ // If `data` has a `prev_blockhash`, create a placeholder checkpoint one height below.
222+ let prev = if height > 0 {
223+ match data. prev_blockhash ( ) {
224+ Some ( prev_blockhash) => Some ( Arc :: new ( CPInner {
225+ block_id : BlockId {
226+ height : height - 1 ,
227+ hash : prev_blockhash,
228+ } ,
229+ data : None ,
230+ prev : None ,
231+ } ) ) ,
232+ None => None ,
233+ }
234+ } else {
235+ None
236+ } ;
237+
201238 Self ( Arc :: new ( CPInner {
202239 block_id : BlockId {
203240 height,
204241 hash : data. to_blockhash ( ) ,
205242 } ,
206- data,
207- prev : None ,
243+ data : Some ( data ) ,
244+ prev,
208245 } ) )
209246 }
210247
@@ -251,21 +288,30 @@ where
251288 let mut tail = vec ! [ ] ;
252289 let base = loop {
253290 if cp. height ( ) == height {
254- if cp. hash ( ) == data. to_blockhash ( ) {
255- return self ;
291+ let same_hash = cp. hash ( ) == data. to_blockhash ( ) ;
292+ if same_hash {
293+ if cp. data ( ) . is_some ( ) {
294+ return self ;
295+ } else {
296+ // If `CheckPoint` is a placeholder, return previous `CheckPoint`.
297+ break cp. prev ( ) . expect ( "can't be called on genesis block" ) ;
298+ }
299+ } else {
300+ assert_ne ! ( cp. height( ) , 0 , "cannot replace genesis block" ) ;
301+ // If we have a conflict we just return the inserted data because the tail is by
302+ // implication invalid.
303+ tail = vec ! [ ] ;
304+ break cp. prev ( ) . expect ( "can't be called on genesis block" ) ;
256305 }
257- assert_ne ! ( cp. height( ) , 0 , "cannot replace genesis block" ) ;
258- // If we have a conflict we just return the inserted data because the tail is by
259- // implication invalid.
260- tail = vec ! [ ] ;
261- break cp. prev ( ) . expect ( "can't be called on genesis block" ) ;
262306 }
263307
264308 if cp. height ( ) < height {
265309 break cp;
266310 }
267311
268- tail. push ( ( cp. height ( ) , cp. data ( ) ) ) ;
312+ if let Some ( d) = cp. data ( ) {
313+ tail. push ( ( cp. height ( ) , d) ) ;
314+ }
269315 cp = cp. prev ( ) . expect ( "will break before genesis block" ) ;
270316 } ;
271317
@@ -277,15 +323,35 @@ where
277323 ///
278324 /// Returns an `Err(self)` if the block you are pushing on is not at a greater height that the
279325 /// one you are pushing on to.
326+ ///
327+ /// If `height` is non-contiguous and `data.prev_blockhash()` is available, a placeholder is
328+ /// created at height - 1.
280329 pub fn push ( self , height : u32 , data : D ) -> Result < Self , Self > {
281330 if self . height ( ) < height {
331+ let mut current_cp = self . 0 . clone ( ) ;
332+
333+ // If non-contiguous and `prev_blockhash` exists, insert a placeholder at height - 1.
334+ if height > self . height ( ) + 1 {
335+ if let Some ( prev_hash) = data. prev_blockhash ( ) {
336+ let empty = Arc :: new ( CPInner {
337+ block_id : BlockId {
338+ height : height - 1 ,
339+ hash : prev_hash,
340+ } ,
341+ data : None ,
342+ prev : Some ( current_cp) ,
343+ } ) ;
344+ current_cp = empty;
345+ }
346+ }
347+
282348 Ok ( Self ( Arc :: new ( CPInner {
283349 block_id : BlockId {
284350 height,
285351 hash : data. to_blockhash ( ) ,
286352 } ,
287- data,
288- prev : Some ( self . 0 ) ,
353+ data : Some ( data ) ,
354+ prev : Some ( current_cp ) ,
289355 } ) ) )
290356 } else {
291357 Err ( self )
0 commit comments