1
1
use core:: ops:: RangeBounds ;
2
2
3
3
use alloc:: sync:: Arc ;
4
- use bitcoin:: BlockHash ;
4
+ use bitcoin:: { block :: Header , consensus :: Encodable , hashes :: Hash , BlockHash } ;
5
5
6
6
use crate :: BlockId ;
7
7
@@ -10,29 +10,83 @@ use crate::BlockId;
10
10
/// Checkpoints are cheaply cloneable and are useful to find the agreement point between two sparse
11
11
/// block chains.
12
12
#[ derive( Debug , Clone ) ]
13
- pub struct CheckPoint ( Arc < CPInner > ) ;
13
+ pub struct CheckPoint < B = BlockHash > ( Arc < CPInner < B > > ) ;
14
14
15
15
/// The internal contents of [`CheckPoint`].
16
16
#[ derive( Debug , Clone ) ]
17
- struct CPInner {
18
- /// Block id (hash and height).
19
- block : BlockId ,
17
+ struct CPInner < B > {
18
+ /// Block height.
19
+ height : u32 ,
20
+ /// Data.
21
+ data : B ,
20
22
/// Previous checkpoint (if any).
21
- prev : Option < Arc < CPInner > > ,
23
+ prev : Option < Arc < CPInner < B > > > ,
22
24
}
23
25
24
- impl PartialEq for CheckPoint {
26
+ /// TODO: ToBlockHash doc
27
+ pub trait ToBlockHash {
28
+ /// TODO: to_blockhash doc
29
+ fn to_blockhash ( & self ) -> BlockHash ;
30
+ }
31
+
32
+ impl < B : ToBlockHash > ToBlockHash for CheckPoint < B > {
33
+ fn to_blockhash ( & self ) -> BlockHash {
34
+ self . 0 . data . to_blockhash ( )
35
+ }
36
+ }
37
+
38
+ impl ToBlockHash for Header {
39
+ fn to_blockhash ( & self ) -> BlockHash {
40
+ let mut bytes = vec ! [ ] ;
41
+ self . consensus_encode ( & mut bytes) . unwrap_or_default ( ) ;
42
+ BlockHash :: hash ( & bytes)
43
+ }
44
+ }
45
+
46
+ impl < B > PartialEq for CheckPoint < B >
47
+ where
48
+ B : Copy + core:: cmp:: PartialEq ,
49
+ {
25
50
fn eq ( & self , other : & Self ) -> bool {
26
- let self_cps = self . iter ( ) . map ( |cp| cp. block_id ( ) ) ;
27
- let other_cps = other. iter ( ) . map ( |cp| cp. block_id ( ) ) ;
51
+ let self_cps = self . iter ( ) . map ( |cp| * cp. inner ( ) ) ;
52
+ let other_cps = other. iter ( ) . map ( |cp| * cp. inner ( ) ) ;
28
53
self_cps. eq ( other_cps)
29
54
}
30
55
}
31
56
32
- impl CheckPoint {
33
- /// Construct a new base block at the front of a linked list.
57
+ impl CheckPoint < BlockHash > {
58
+ /// Construct a new [`CheckPoint`] at the front of a linked list.
34
59
pub fn new ( block : BlockId ) -> Self {
35
- Self ( Arc :: new ( CPInner { block, prev : None } ) )
60
+ Self ( Arc :: new ( CPInner {
61
+ height : block. height ,
62
+ data : block. hash ,
63
+ prev : None ,
64
+ } ) )
65
+ }
66
+
67
+ /// Construct a checkpoint from the given `header` and block `height`.
68
+ ///
69
+ /// If `header` is of the genesis block, the checkpoint won't have a [`prev`] node. Otherwise,
70
+ /// we return a checkpoint linked with the previous block.
71
+ ///
72
+ /// [`prev`]: CheckPoint::prev
73
+ pub fn from_header ( header : & bitcoin:: block:: Header , height : u32 ) -> Self {
74
+ let hash = header. block_hash ( ) ;
75
+ let this_block_id = BlockId { height, hash } ;
76
+
77
+ let prev_height = match height. checked_sub ( 1 ) {
78
+ Some ( h) => h,
79
+ None => return Self :: new ( this_block_id) ,
80
+ } ;
81
+
82
+ let prev_block_id = BlockId {
83
+ height : prev_height,
84
+ hash : header. prev_blockhash ,
85
+ } ;
86
+
87
+ CheckPoint :: new ( prev_block_id)
88
+ . push ( this_block_id)
89
+ . expect ( "must construct checkpoint" )
36
90
}
37
91
38
92
/// Construct a checkpoint from a list of [`BlockId`]s in ascending height order.
@@ -50,36 +104,74 @@ impl CheckPoint {
50
104
block_ids : impl IntoIterator < Item = BlockId > ,
51
105
) -> Result < Self , Option < Self > > {
52
106
let mut blocks = block_ids. into_iter ( ) ;
53
- let mut acc = CheckPoint :: new ( blocks. next ( ) . ok_or ( None ) ?) ;
107
+ let block = blocks. next ( ) . ok_or ( None ) ?;
108
+ let mut acc = CheckPoint :: new ( block) ;
54
109
for id in blocks {
55
110
acc = acc. push ( id) . map_err ( Some ) ?;
56
111
}
57
112
Ok ( acc)
58
113
}
59
114
60
- /// Construct a checkpoint from the given `header` and block `height` .
115
+ /// Extends the checkpoint linked list by a iterator of block ids .
61
116
///
62
- /// If `header` is of the genesis block, the checkpoint won't have a [`prev`] node. Otherwise,
63
- /// we return a checkpoint linked with the previous block.
117
+ /// Returns an `Err(self)` if there is block which does not have a greater height than the
118
+ /// previous one.
119
+ pub fn extend ( self , blockdata : impl IntoIterator < Item = BlockId > ) -> Result < Self , Self > {
120
+ let mut curr = self . clone ( ) ;
121
+ for block in blockdata {
122
+ curr = curr. push ( block) . map_err ( |_| self . clone ( ) ) ?;
123
+ }
124
+ Ok ( curr)
125
+ }
126
+
127
+ /// Get the block hash of the checkpoint.
128
+ pub fn hash ( & self ) -> BlockHash {
129
+ self . 0 . data
130
+ }
131
+
132
+ /// Get the [`BlockId`] of the checkpoint.
133
+ pub fn block_id ( & self ) -> BlockId {
134
+ BlockId {
135
+ height : self . height ( ) ,
136
+ hash : self . hash ( ) ,
137
+ }
138
+ }
139
+
140
+ /// Inserts `block_id` at its height within the chain.
64
141
///
65
- /// [`prev`]: CheckPoint::prev
66
- pub fn from_header ( header : & bitcoin:: block:: Header , height : u32 ) -> Self {
67
- let hash = header. block_hash ( ) ;
68
- let this_block_id = BlockId { height, hash } ;
142
+ /// The effect of `insert` depends on whether a height already exists. If it doesn't the
143
+ /// `block_id` we inserted and all pre-existing blocks higher than it will be re-inserted after
144
+ /// it. If the height already existed and has a conflicting block hash then it will be purged
145
+ /// along with all block followin it. The returned chain will have a tip of the `block_id`
146
+ /// passed in. Of course, if the `block_id` was already present then this just returns `self`.
147
+ #[ must_use]
148
+ pub fn insert ( self , block_id : BlockId ) -> Self {
149
+ assert_ne ! ( block_id. height, 0 , "cannot insert the genesis block" ) ;
69
150
70
- let prev_height = match height. checked_sub ( 1 ) {
71
- Some ( h) => h,
72
- None => return Self :: new ( this_block_id) ,
73
- } ;
151
+ let mut cp = self . clone ( ) ;
152
+ let mut tail = vec ! [ ] ;
153
+ let base = loop {
154
+ if cp. height ( ) == block_id. height {
155
+ if cp. hash ( ) == block_id. hash {
156
+ return self ;
157
+ }
158
+ // if we have a conflict we just return the inserted block because the tail is by
159
+ // implication invalid.
160
+ tail = vec ! [ ] ;
161
+ break cp. prev ( ) . expect ( "can't be called on genesis block" ) ;
162
+ }
74
163
75
- let prev_block_id = BlockId {
76
- height : prev_height,
77
- hash : header. prev_blockhash ,
164
+ if cp. height ( ) < block_id. height {
165
+ break cp;
166
+ }
167
+
168
+ tail. push ( cp. block_id ( ) ) ;
169
+ cp = cp. prev ( ) . expect ( "will break before genesis block" ) ;
78
170
} ;
79
171
80
- CheckPoint :: new ( prev_block_id )
81
- . push ( this_block_id )
82
- . expect ( "must construct checkpoint " )
172
+ let new_cp = core :: iter :: once ( block_id ) . chain ( tail . into_iter ( ) . rev ( ) ) ;
173
+
174
+ base . extend ( new_cp ) . expect ( "tail is in order " )
83
175
}
84
176
85
177
/// Puts another checkpoint onto the linked list representing the blockchain.
@@ -89,48 +181,46 @@ impl CheckPoint {
89
181
pub fn push ( self , block : BlockId ) -> Result < Self , Self > {
90
182
if self . height ( ) < block. height {
91
183
Ok ( Self ( Arc :: new ( CPInner {
92
- block,
184
+ height : block. height ,
185
+ data : block. hash ,
93
186
prev : Some ( self . 0 ) ,
94
187
} ) ) )
95
188
} else {
96
189
Err ( self )
97
190
}
98
191
}
192
+ }
99
193
100
- /// Extends the checkpoint linked list by a iterator of block ids.
101
- ///
102
- /// Returns an `Err(self)` if there is block which does not have a greater height than the
103
- /// previous one.
104
- pub fn extend ( self , blocks : impl IntoIterator < Item = BlockId > ) -> Result < Self , Self > {
105
- let mut curr = self . clone ( ) ;
106
- for block in blocks {
107
- curr = curr. push ( block) . map_err ( |_| self . clone ( ) ) ?;
108
- }
109
- Ok ( curr)
194
+ impl < B > CheckPoint < B >
195
+ where
196
+ B : Copy ,
197
+ {
198
+ /// Construct a new [`CheckPoint`] at the front of a linked list.
199
+ pub fn from_data ( height : u32 , data : B ) -> Self {
200
+ Self ( Arc :: new ( CPInner {
201
+ height,
202
+ data,
203
+ prev : None ,
204
+ } ) )
110
205
}
111
206
112
- /// Get the [`BlockId`] of the checkpoint .
113
- pub fn block_id ( & self ) -> BlockId {
114
- self . 0 . block
207
+ /// Get reference to the inner type .
208
+ pub fn inner ( & self ) -> & B {
209
+ & self . 0 . data
115
210
}
116
211
117
212
/// Get the height of the checkpoint.
118
213
pub fn height ( & self ) -> u32 {
119
- self . 0 . block . height
120
- }
121
-
122
- /// Get the block hash of the checkpoint.
123
- pub fn hash ( & self ) -> BlockHash {
124
- self . 0 . block . hash
214
+ self . 0 . height
125
215
}
126
216
127
217
/// Get the previous checkpoint in the chain
128
- pub fn prev ( & self ) -> Option < CheckPoint > {
218
+ pub fn prev ( & self ) -> Option < CheckPoint < B > > {
129
219
self . 0 . prev . clone ( ) . map ( CheckPoint )
130
220
}
131
221
132
222
/// Iterate from this checkpoint in descending height.
133
- pub fn iter ( & self ) -> CheckPointIter {
223
+ pub fn iter ( & self ) -> CheckPointIter < B > {
134
224
self . clone ( ) . into_iter ( )
135
225
}
136
226
@@ -145,7 +235,7 @@ impl CheckPoint {
145
235
///
146
236
/// Note that we always iterate checkpoints in reverse height order (iteration starts at tip
147
237
/// height).
148
- pub fn range < R > ( & self , range : R ) -> impl Iterator < Item = CheckPoint >
238
+ pub fn range < R > ( & self , range : R ) -> impl Iterator < Item = CheckPoint < B > >
149
239
where
150
240
R : RangeBounds < u32 > ,
151
241
{
@@ -164,6 +254,28 @@ impl CheckPoint {
164
254
} )
165
255
}
166
256
257
+ /// This method tests for `self` and `other` to have equal internal pointers.
258
+ pub fn eq_ptr ( & self , other : & Self ) -> bool {
259
+ Arc :: as_ptr ( & self . 0 ) == Arc :: as_ptr ( & other. 0 )
260
+ }
261
+ }
262
+
263
+ impl < B > CheckPoint < B >
264
+ where
265
+ B : Copy + core:: fmt:: Debug + ToBlockHash + From < BlockHash > ,
266
+ {
267
+ /// Extends the checkpoint linked list by a iterator of block ids.
268
+ ///
269
+ /// Returns an `Err(self)` if there is block which does not have a greater height than the
270
+ /// previous one.
271
+ pub fn extend_data ( self , blockdata : impl IntoIterator < Item = ( u32 , B ) > ) -> Result < Self , Self > {
272
+ let mut curr = self . clone ( ) ;
273
+ for ( height, data) in blockdata {
274
+ curr = curr. push_data ( height, data) . map_err ( |_| self . clone ( ) ) ?;
275
+ }
276
+ Ok ( curr)
277
+ }
278
+
167
279
/// Inserts `block_id` at its height within the chain.
168
280
///
169
281
/// The effect of `insert` depends on whether a height already exists. If it doesn't the
@@ -172,14 +284,14 @@ impl CheckPoint {
172
284
/// along with all block followin it. The returned chain will have a tip of the `block_id`
173
285
/// passed in. Of course, if the `block_id` was already present then this just returns `self`.
174
286
#[ must_use]
175
- pub fn insert ( self , block_id : BlockId ) -> Self {
176
- assert_ne ! ( block_id . height, 0 , "cannot insert the genesis block" ) ;
287
+ pub fn insert_data ( self , height : u32 , data : B ) -> Self {
288
+ assert_ne ! ( height, 0 , "cannot insert the genesis block" ) ;
177
289
178
290
let mut cp = self . clone ( ) ;
179
291
let mut tail = vec ! [ ] ;
180
292
let base = loop {
181
- if cp. height ( ) == block_id . height {
182
- if cp. hash ( ) == block_id . hash {
293
+ if cp. height ( ) == height {
294
+ if cp. to_blockhash ( ) == data . to_blockhash ( ) {
183
295
return self ;
184
296
}
185
297
// if we have a conflict we just return the inserted block because the tail is by
@@ -188,31 +300,50 @@ impl CheckPoint {
188
300
break cp. prev ( ) . expect ( "can't be called on genesis block" ) ;
189
301
}
190
302
191
- if cp. height ( ) < block_id . height {
303
+ if cp. height ( ) < height {
192
304
break cp;
193
305
}
194
306
195
- tail. push ( cp. block_id ( ) ) ;
307
+ tail. push ( BlockId {
308
+ height,
309
+ hash : data. to_blockhash ( ) ,
310
+ } ) ;
196
311
cp = cp. prev ( ) . expect ( "will break before genesis block" ) ;
197
312
} ;
198
313
199
- base. extend ( core:: iter:: once ( block_id) . chain ( tail. into_iter ( ) . rev ( ) ) )
200
- . expect ( "tail is in order" )
314
+ let new_cp = core:: iter:: once ( ( height, data) ) . chain (
315
+ tail. into_iter ( )
316
+ . rev ( )
317
+ . map ( |block| ( block. height , B :: from ( block. hash ) ) ) ,
318
+ ) ;
319
+
320
+ base. extend_data ( new_cp) . expect ( "tail is in order" )
201
321
}
202
322
203
- /// This method tests for `self` and `other` to have equal internal pointers.
204
- pub fn eq_ptr ( & self , other : & Self ) -> bool {
205
- Arc :: as_ptr ( & self . 0 ) == Arc :: as_ptr ( & other. 0 )
323
+ /// Puts another checkpoint onto the linked list representing the blockchain.
324
+ ///
325
+ /// Returns an `Err(self)` if the block you are pushing on is not at a greater height that the one you
326
+ /// are pushing on to.
327
+ pub fn push_data ( self , height : u32 , data : B ) -> Result < Self , Self > {
328
+ if self . height ( ) < height {
329
+ Ok ( Self ( Arc :: new ( CPInner {
330
+ height,
331
+ data,
332
+ prev : Some ( self . 0 ) ,
333
+ } ) ) )
334
+ } else {
335
+ Err ( self )
336
+ }
206
337
}
207
338
}
208
339
209
340
/// Iterates over checkpoints backwards.
210
- pub struct CheckPointIter {
211
- current : Option < Arc < CPInner > > ,
341
+ pub struct CheckPointIter < B = BlockHash > {
342
+ current : Option < Arc < CPInner < B > > > ,
212
343
}
213
344
214
- impl Iterator for CheckPointIter {
215
- type Item = CheckPoint ;
345
+ impl < B > Iterator for CheckPointIter < B > {
346
+ type Item = CheckPoint < B > ;
216
347
217
348
fn next ( & mut self ) -> Option < Self :: Item > {
218
349
let current = self . current . clone ( ) ?;
@@ -221,9 +352,9 @@ impl Iterator for CheckPointIter {
221
352
}
222
353
}
223
354
224
- impl IntoIterator for CheckPoint {
225
- type Item = CheckPoint ;
226
- type IntoIter = CheckPointIter ;
355
+ impl < B > IntoIterator for CheckPoint < B > {
356
+ type Item = CheckPoint < B > ;
357
+ type IntoIter = CheckPointIter < B > ;
227
358
228
359
fn into_iter ( self ) -> Self :: IntoIter {
229
360
CheckPointIter {
0 commit comments