@@ -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}
@@ -64,6 +64,11 @@ impl<D> Drop for CPInner<D> {
6464pub trait ToBlockHash {
6565 /// Returns the [`BlockHash`] for the associated [`CheckPoint`] `data` type.
6666 fn to_blockhash ( & self ) -> BlockHash ;
67+
68+ /// Returns `None` if the type has no knowledge of the previous [`BlockHash`].
69+ fn prev_blockhash ( & self ) -> Option < BlockHash > {
70+ None
71+ }
6772}
6873
6974impl ToBlockHash for BlockHash {
@@ -76,6 +81,10 @@ impl ToBlockHash for Header {
7681 fn to_blockhash ( & self ) -> BlockHash {
7782 self . block_hash ( )
7883 }
84+
85+ fn prev_blockhash ( & self ) -> Option < BlockHash > {
86+ Some ( self . prev_blockhash )
87+ }
7988}
8089
8190impl < D > PartialEq for CheckPoint < D > {
@@ -88,13 +97,13 @@ impl<D> PartialEq for CheckPoint<D> {
8897
8998// Methods for any `D`
9099impl < D > CheckPoint < D > {
91- /// Get a reference of the `data` of the checkpoint.
92- pub fn data_ref ( & self ) -> & D {
93- & self . 0 . data
100+ /// Get a reference of the `data` of the checkpoint if it exists .
101+ pub fn data_ref ( & self ) -> Option < & D > {
102+ self . 0 . data . as_ref ( )
94103 }
95104
96- /// Get the `data` of a the checkpoint.
97- pub fn data ( & self ) -> D
105+ /// Get the `data` of the checkpoint if it exists .
106+ pub fn data ( & self ) -> Option < D >
98107 where
99108 D : Clone ,
100109 {
@@ -166,6 +175,17 @@ impl<D> CheckPoint<D> {
166175 self . range ( ..=height) . next ( )
167176 }
168177
178+ /// Finds the checkpoint with `data` at `height` if one exists, otherwise the neareast
179+ /// checkpoint with `data` at a lower height.
180+ ///
181+ /// This is equivalent to taking the “floor” of "height" over this checkpoint chain, filtering
182+ /// out any placeholder entries that do not contain any `data`.
183+ ///
184+ /// Returns `None` if no checkpoint with `data` exists at or below the given height.
185+ pub fn find_data ( & self , height : u32 ) -> Option < Self > {
186+ self . range ( ..=height) . find ( |cp| cp. data_ref ( ) . is_some ( ) )
187+ }
188+
169189 /// Returns the checkpoint located a number of heights below this one.
170190 ///
171191 /// This is a convenience wrapper for [`CheckPoint::floor_at`], subtracting `to_subtract` from
@@ -194,13 +214,30 @@ where
194214 /// Construct a new base [`CheckPoint`] from given `height` and `data` at the front of a linked
195215 /// list.
196216 pub fn new ( height : u32 , data : D ) -> Self {
217+ // If `data` has a `prev_blockhash`, create a placeholder checkpoint one height below.
218+ let prev = if height > 0 {
219+ match data. prev_blockhash ( ) {
220+ Some ( prev_blockhash) => Some ( Arc :: new ( CPInner {
221+ block_id : BlockId {
222+ height : height - 1 ,
223+ hash : prev_blockhash,
224+ } ,
225+ data : None ,
226+ prev : None ,
227+ } ) ) ,
228+ None => None ,
229+ }
230+ } else {
231+ None
232+ } ;
233+
197234 Self ( Arc :: new ( CPInner {
198235 block_id : BlockId {
199236 height,
200237 hash : data. to_blockhash ( ) ,
201238 } ,
202- data,
203- prev : None ,
239+ data : Some ( data ) ,
240+ prev,
204241 } ) )
205242 }
206243
@@ -247,21 +284,30 @@ where
247284 let mut tail = vec ! [ ] ;
248285 let base = loop {
249286 if cp. height ( ) == height {
250- if cp. hash ( ) == data. to_blockhash ( ) {
251- return self ;
287+ let same_hash = cp. hash ( ) == data. to_blockhash ( ) ;
288+ if same_hash {
289+ if cp. data ( ) . is_some ( ) {
290+ return self ;
291+ } else {
292+ // If `CheckPoint` is a placeholder, return previous `CheckPoint`.
293+ break cp. prev ( ) . expect ( "can't be called on genesis block" ) ;
294+ }
295+ } else {
296+ assert_ne ! ( cp. height( ) , 0 , "cannot replace genesis block" ) ;
297+ // If we have a conflict we just return the inserted data because the tail is by
298+ // implication invalid.
299+ tail = vec ! [ ] ;
300+ break cp. prev ( ) . expect ( "can't be called on genesis block" ) ;
252301 }
253- assert_ne ! ( cp. height( ) , 0 , "cannot replace genesis block" ) ;
254- // If we have a conflict we just return the inserted data because the tail is by
255- // implication invalid.
256- tail = vec ! [ ] ;
257- break cp. prev ( ) . expect ( "can't be called on genesis block" ) ;
258302 }
259303
260304 if cp. height ( ) < height {
261305 break cp;
262306 }
263307
264- tail. push ( ( cp. height ( ) , cp. data ( ) ) ) ;
308+ if let Some ( d) = cp. data ( ) {
309+ tail. push ( ( cp. height ( ) , d) ) ;
310+ }
265311 cp = cp. prev ( ) . expect ( "will break before genesis block" ) ;
266312 } ;
267313
@@ -273,15 +319,35 @@ where
273319 ///
274320 /// Returns an `Err(self)` if the block you are pushing on is not at a greater height that the
275321 /// one you are pushing on to.
322+ ///
323+ /// If `height` is non-contiguous and `data.prev_blockhash()` is available, a placeholder is
324+ /// created at height - 1.
276325 pub fn push ( self , height : u32 , data : D ) -> Result < Self , Self > {
277326 if self . height ( ) < height {
327+ let mut current_cp = self . 0 . clone ( ) ;
328+
329+ // If non-contiguous and `prev_blockhash` exists, insert a placeholder at height - 1.
330+ if height > self . height ( ) + 1 {
331+ if let Some ( prev_hash) = data. prev_blockhash ( ) {
332+ let empty = Arc :: new ( CPInner {
333+ block_id : BlockId {
334+ height : height - 1 ,
335+ hash : prev_hash,
336+ } ,
337+ data : None ,
338+ prev : Some ( current_cp) ,
339+ } ) ;
340+ current_cp = empty;
341+ }
342+ }
343+
278344 Ok ( Self ( Arc :: new ( CPInner {
279345 block_id : BlockId {
280346 height,
281347 hash : data. to_blockhash ( ) ,
282348 } ,
283- data,
284- prev : Some ( self . 0 ) ,
349+ data : Some ( data ) ,
350+ prev : Some ( current_cp ) ,
285351 } ) ) )
286352 } else {
287353 Err ( self )
0 commit comments