@@ -24,8 +24,8 @@ impl<D> Clone for CheckPoint<D> {
24
24
struct CPInner < D > {
25
25
/// Block id
26
26
block_id : BlockId ,
27
- /// Data.
28
- data : D ,
27
+ /// Data (if any) .
28
+ data : Option < D > ,
29
29
/// Previous checkpoint (if any).
30
30
prev : Option < Arc < CPInner < D > > > ,
31
31
}
@@ -68,6 +68,11 @@ impl<D> Drop for CPInner<D> {
68
68
pub trait ToBlockHash {
69
69
/// Returns the [`BlockHash`] for the associated [`CheckPoint`] `data` type.
70
70
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
+ }
71
76
}
72
77
73
78
impl ToBlockHash for BlockHash {
@@ -80,6 +85,10 @@ impl ToBlockHash for Header {
80
85
fn to_blockhash ( & self ) -> BlockHash {
81
86
self . block_hash ( )
82
87
}
88
+
89
+ fn prev_blockhash ( & self ) -> Option < BlockHash > {
90
+ Some ( self . prev_blockhash )
91
+ }
83
92
}
84
93
85
94
impl < D > PartialEq for CheckPoint < D > {
@@ -92,13 +101,13 @@ impl<D> PartialEq for CheckPoint<D> {
92
101
93
102
// Methods for any `D`
94
103
impl < 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 ( )
98
107
}
99
108
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 >
102
111
where
103
112
D : Clone ,
104
113
{
@@ -170,6 +179,17 @@ impl<D> CheckPoint<D> {
170
179
self . range ( ..=height) . next ( )
171
180
}
172
181
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
+
173
193
/// Returns the checkpoint located a number of heights below this one.
174
194
///
175
195
/// This is a convenience wrapper for [`CheckPoint::floor_at`], subtracting `to_subtract` from
@@ -198,13 +218,30 @@ where
198
218
/// Construct a new base [`CheckPoint`] from given `height` and `data` at the front of a linked
199
219
/// list.
200
220
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
+
201
238
Self ( Arc :: new ( CPInner {
202
239
block_id : BlockId {
203
240
height,
204
241
hash : data. to_blockhash ( ) ,
205
242
} ,
206
- data,
207
- prev : None ,
243
+ data : Some ( data ) ,
244
+ prev,
208
245
} ) )
209
246
}
210
247
@@ -251,21 +288,30 @@ where
251
288
let mut tail = vec ! [ ] ;
252
289
let base = loop {
253
290
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" ) ;
256
305
}
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" ) ;
262
306
}
263
307
264
308
if cp. height ( ) < height {
265
309
break cp;
266
310
}
267
311
268
- tail. push ( ( cp. height ( ) , cp. data ( ) ) ) ;
312
+ if let Some ( d) = cp. data ( ) {
313
+ tail. push ( ( cp. height ( ) , d) ) ;
314
+ }
269
315
cp = cp. prev ( ) . expect ( "will break before genesis block" ) ;
270
316
} ;
271
317
@@ -277,15 +323,35 @@ where
277
323
///
278
324
/// Returns an `Err(self)` if the block you are pushing on is not at a greater height that the
279
325
/// 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.
280
329
pub fn push ( self , height : u32 , data : D ) -> Result < Self , Self > {
281
330
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
+
282
348
Ok ( Self ( Arc :: new ( CPInner {
283
349
block_id : BlockId {
284
350
height,
285
351
hash : data. to_blockhash ( ) ,
286
352
} ,
287
- data,
288
- prev : Some ( self . 0 ) ,
353
+ data : Some ( data ) ,
354
+ prev : Some ( current_cp ) ,
289
355
} ) ) )
290
356
} else {
291
357
Err ( self )
0 commit comments