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