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