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) .
17
+ struct CPInner < B > {
18
+ /// Block data .
19
19
block : BlockId ,
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 ToBlockHash for BlockHash {
33
+ fn to_blockhash ( & self ) -> BlockHash {
34
+ * self
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
+ block,
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,61 @@ 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
+ /// Inserts `block_id` at its height within the chain.
64
128
///
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 } ;
129
+ /// The effect of `insert` depends on whether a height already exists. If it doesn't the
130
+ /// `block_id` we inserted and all pre-existing blocks higher than it will be re-inserted after
131
+ /// it. If the height already existed and has a conflicting block hash then it will be purged
132
+ /// along with all block followin it. The returned chain will have a tip of the `block_id`
133
+ /// passed in. Of course, if the `block_id` was already present then this just returns `self`.
134
+ #[ must_use]
135
+ pub fn insert ( self , block_id : BlockId ) -> Self {
136
+ assert_ne ! ( block_id. height, 0 , "cannot insert the genesis block" ) ;
69
137
70
- let prev_height = match height. checked_sub ( 1 ) {
71
- Some ( h) => h,
72
- None => return Self :: new ( this_block_id) ,
73
- } ;
138
+ let mut cp = self . clone ( ) ;
139
+ let mut tail = vec ! [ ] ;
140
+ let base = loop {
141
+ if cp. height ( ) == block_id. height {
142
+ if cp. hash ( ) == block_id. hash {
143
+ return self ;
144
+ }
145
+ // if we have a conflict we just return the inserted block because the tail is by
146
+ // implication invalid.
147
+ tail = vec ! [ ] ;
148
+ break cp. prev ( ) . expect ( "can't be called on genesis block" ) ;
149
+ }
74
150
75
- let prev_block_id = BlockId {
76
- height : prev_height,
77
- hash : header. prev_blockhash ,
151
+ if cp. height ( ) < block_id. height {
152
+ break cp;
153
+ }
154
+
155
+ tail. push ( cp. block_id ( ) ) ;
156
+ cp = cp. prev ( ) . expect ( "will break before genesis block" ) ;
78
157
} ;
79
158
80
- CheckPoint :: new ( prev_block_id )
81
- . push ( this_block_id )
82
- . expect ( "must construct checkpoint " )
159
+ let new_cp = core :: iter :: once ( block_id ) . chain ( tail . into_iter ( ) . rev ( ) ) ;
160
+
161
+ base . extend ( new_cp ) . expect ( "tail is in order " )
83
162
}
84
163
85
164
/// Puts another checkpoint onto the linked list representing the blockchain.
@@ -90,47 +169,36 @@ impl CheckPoint {
90
169
if self . height ( ) < block. height {
91
170
Ok ( Self ( Arc :: new ( CPInner {
92
171
block,
172
+ data : block. hash ,
93
173
prev : Some ( self . 0 ) ,
94
174
} ) ) )
95
175
} else {
96
176
Err ( self )
97
177
}
98
178
}
179
+ }
99
180
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)
110
- }
111
-
112
- /// Get the [`BlockId`] of the checkpoint.
113
- pub fn block_id ( & self ) -> BlockId {
114
- self . 0 . block
181
+ impl < B > CheckPoint < B >
182
+ where
183
+ B : Copy ,
184
+ {
185
+ /// Get reference to the inner type.
186
+ pub fn inner ( & self ) -> & B {
187
+ & self . 0 . data
115
188
}
116
189
117
190
/// Get the height of the checkpoint.
118
191
pub fn height ( & self ) -> u32 {
119
192
self . 0 . block . height
120
193
}
121
194
122
- /// Get the block hash of the checkpoint.
123
- pub fn hash ( & self ) -> BlockHash {
124
- self . 0 . block . hash
125
- }
126
-
127
195
/// Get the previous checkpoint in the chain
128
- pub fn prev ( & self ) -> Option < CheckPoint > {
196
+ pub fn prev ( & self ) -> Option < CheckPoint < B > > {
129
197
self . 0 . prev . clone ( ) . map ( CheckPoint )
130
198
}
131
199
132
200
/// Iterate from this checkpoint in descending height.
133
- pub fn iter ( & self ) -> CheckPointIter {
201
+ pub fn iter ( & self ) -> CheckPointIter < B > {
134
202
self . clone ( ) . into_iter ( )
135
203
}
136
204
@@ -145,7 +213,7 @@ impl CheckPoint {
145
213
///
146
214
/// Note that we always iterate checkpoints in reverse height order (iteration starts at tip
147
215
/// height).
148
- pub fn range < R > ( & self , range : R ) -> impl Iterator < Item = CheckPoint >
216
+ pub fn range < R > ( & self , range : R ) -> impl Iterator < Item = CheckPoint < B > >
149
217
where
150
218
R : RangeBounds < u32 > ,
151
219
{
@@ -163,6 +231,48 @@ impl CheckPoint {
163
231
core:: ops:: Bound :: Unbounded => true ,
164
232
} )
165
233
}
234
+ }
235
+
236
+ impl < B > CheckPoint < B >
237
+ where
238
+ B : Copy + core:: fmt:: Debug + ToBlockHash ,
239
+ {
240
+ /// Get the [`BlockId`] of the checkpoint.
241
+ pub fn block_id ( & self ) -> BlockId {
242
+ BlockId {
243
+ height : self . height ( ) ,
244
+ hash : self . hash ( ) ,
245
+ }
246
+ }
247
+
248
+ /// Construct a new [`CheckPoint`] at the front of a linked list.
249
+ pub fn from_data ( height : u32 , data : B ) -> Self {
250
+ Self ( Arc :: new ( CPInner {
251
+ block : BlockId {
252
+ height,
253
+ hash : data. to_blockhash ( ) ,
254
+ } ,
255
+ data,
256
+ prev : None ,
257
+ } ) )
258
+ }
259
+
260
+ /// Get the block hash of the checkpoint.
261
+ pub fn hash ( & self ) -> BlockHash {
262
+ self . 0 . data . to_blockhash ( )
263
+ }
264
+
265
+ /// Extends the checkpoint linked list by a iterator of block ids.
266
+ ///
267
+ /// Returns an `Err(self)` if there is block which does not have a greater height than the
268
+ /// previous one.
269
+ pub fn extend_data ( self , blockdata : impl IntoIterator < Item = ( u32 , B ) > ) -> Result < Self , Self > {
270
+ let mut curr = self . clone ( ) ;
271
+ for ( height, data) in blockdata {
272
+ curr = curr. push_data ( height, data) . map_err ( |_| self . clone ( ) ) ?;
273
+ }
274
+ Ok ( curr)
275
+ }
166
276
167
277
/// Inserts `block_id` at its height within the chain.
168
278
///
@@ -172,14 +282,14 @@ impl CheckPoint {
172
282
/// along with all block followin it. The returned chain will have a tip of the `block_id`
173
283
/// passed in. Of course, if the `block_id` was already present then this just returns `self`.
174
284
#[ must_use]
175
- pub fn insert ( self , block_id : BlockId ) -> Self {
176
- assert_ne ! ( block_id . height, 0 , "cannot insert the genesis block" ) ;
285
+ pub fn insert_data ( self , height : u32 , data : B ) -> Self {
286
+ assert_ne ! ( height, 0 , "cannot insert the genesis block" ) ;
177
287
178
288
let mut cp = self . clone ( ) ;
179
289
let mut tail = vec ! [ ] ;
180
290
let base = loop {
181
- if cp. height ( ) == block_id . height {
182
- if cp. hash ( ) == block_id . hash {
291
+ if cp. height ( ) == height {
292
+ if cp. hash ( ) == data . to_blockhash ( ) {
183
293
return self ;
184
294
}
185
295
// if we have a conflict we just return the inserted block because the tail is by
@@ -188,16 +298,36 @@ impl CheckPoint {
188
298
break cp. prev ( ) . expect ( "can't be called on genesis block" ) ;
189
299
}
190
300
191
- if cp. height ( ) < block_id . height {
301
+ if cp. height ( ) < height {
192
302
break cp;
193
303
}
194
304
195
- tail. push ( cp . block_id ( ) ) ;
305
+ tail. push ( ( height , data ) ) ;
196
306
cp = cp. prev ( ) . expect ( "will break before genesis block" ) ;
197
307
} ;
198
308
199
- base. extend ( core:: iter:: once ( block_id) . chain ( tail. into_iter ( ) . rev ( ) ) )
200
- . expect ( "tail is in order" )
309
+ let new_cp = core:: iter:: once ( ( height, data) ) . chain ( tail. into_iter ( ) . rev ( ) ) ;
310
+
311
+ base. extend_data ( new_cp) . expect ( "tail is in order" )
312
+ }
313
+
314
+ /// Puts another checkpoint onto the linked list representing the blockchain.
315
+ ///
316
+ /// Returns an `Err(self)` if the block you are pushing on is not at a greater height that the one you
317
+ /// are pushing on to.
318
+ pub fn push_data ( self , height : u32 , data : B ) -> Result < Self , Self > {
319
+ if self . height ( ) < height {
320
+ Ok ( Self ( Arc :: new ( CPInner {
321
+ block : BlockId {
322
+ height,
323
+ hash : data. to_blockhash ( ) ,
324
+ } ,
325
+ data,
326
+ prev : Some ( self . 0 ) ,
327
+ } ) ) )
328
+ } else {
329
+ Err ( self )
330
+ }
201
331
}
202
332
203
333
/// This method tests for `self` and `other` to have equal internal pointers.
@@ -207,12 +337,12 @@ impl CheckPoint {
207
337
}
208
338
209
339
/// Iterates over checkpoints backwards.
210
- pub struct CheckPointIter {
211
- current : Option < Arc < CPInner > > ,
340
+ pub struct CheckPointIter < B = BlockHash > {
341
+ current : Option < Arc < CPInner < B > > > ,
212
342
}
213
343
214
- impl Iterator for CheckPointIter {
215
- type Item = CheckPoint ;
344
+ impl < B > Iterator for CheckPointIter < B > {
345
+ type Item = CheckPoint < B > ;
216
346
217
347
fn next ( & mut self ) -> Option < Self :: Item > {
218
348
let current = self . current . clone ( ) ?;
@@ -221,9 +351,9 @@ impl Iterator for CheckPointIter {
221
351
}
222
352
}
223
353
224
- impl IntoIterator for CheckPoint {
225
- type Item = CheckPoint ;
226
- type IntoIter = CheckPointIter ;
354
+ impl < B > IntoIterator for CheckPoint < B > {
355
+ type Item = CheckPoint < B > ;
356
+ type IntoIter = CheckPointIter < B > ;
227
357
228
358
fn into_iter ( self ) -> Self :: IntoIter {
229
359
CheckPointIter {
0 commit comments