11use core:: ops:: RangeBounds ;
22
33use alloc:: sync:: Arc ;
4- use bitcoin:: BlockHash ;
4+ use bitcoin:: { block :: Header , consensus :: Encodable , hashes :: Hash , BlockHash } ;
55
66use crate :: BlockId ;
77
@@ -10,29 +10,83 @@ use crate::BlockId;
1010/// Checkpoints are cheaply cloneable and are useful to find the agreement point between two sparse
1111/// block chains.
1212#[ derive( Debug , Clone ) ]
13- pub struct CheckPoint ( Arc < CPInner > ) ;
13+ pub struct CheckPoint < B = BlockHash > ( Arc < CPInner < B > > ) ;
1414
1515/// The internal contents of [`CheckPoint`].
1616#[ 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 ,
2022 /// Previous checkpoint (if any).
21- prev : Option < Arc < CPInner > > ,
23+ prev : Option < Arc < CPInner < B > > > ,
2224}
2325
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+ {
2550 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 ( ) ) ;
2853 self_cps. eq ( other_cps)
2954 }
3055}
3156
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.
3459 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" )
3690 }
3791
3892 /// Construct a checkpoint from a list of [`BlockId`]s in ascending height order.
@@ -50,36 +104,74 @@ impl CheckPoint {
50104 block_ids : impl IntoIterator < Item = BlockId > ,
51105 ) -> Result < Self , Option < Self > > {
52106 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) ;
54109 for id in blocks {
55110 acc = acc. push ( id) . map_err ( Some ) ?;
56111 }
57112 Ok ( acc)
58113 }
59114
60- /// Construct a checkpoint from the given `header` and block `height` .
115+ /// Extends the checkpoint linked list by a iterator of block ids .
61116 ///
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.
64141 ///
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" ) ;
69150
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+ }
74163
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" ) ;
78170 } ;
79171
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 " )
83175 }
84176
85177 /// Puts another checkpoint onto the linked list representing the blockchain.
@@ -89,48 +181,46 @@ impl CheckPoint {
89181 pub fn push ( self , block : BlockId ) -> Result < Self , Self > {
90182 if self . height ( ) < block. height {
91183 Ok ( Self ( Arc :: new ( CPInner {
92- block,
184+ height : block. height ,
185+ data : block. hash ,
93186 prev : Some ( self . 0 ) ,
94187 } ) ) )
95188 } else {
96189 Err ( self )
97190 }
98191 }
192+ }
99193
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+ } ) )
110205 }
111206
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
115210 }
116211
117212 /// Get the height of the checkpoint.
118213 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
125215 }
126216
127217 /// Get the previous checkpoint in the chain
128- pub fn prev ( & self ) -> Option < CheckPoint > {
218+ pub fn prev ( & self ) -> Option < CheckPoint < B > > {
129219 self . 0 . prev . clone ( ) . map ( CheckPoint )
130220 }
131221
132222 /// Iterate from this checkpoint in descending height.
133- pub fn iter ( & self ) -> CheckPointIter {
223+ pub fn iter ( & self ) -> CheckPointIter < B > {
134224 self . clone ( ) . into_iter ( )
135225 }
136226
@@ -145,7 +235,7 @@ impl CheckPoint {
145235 ///
146236 /// Note that we always iterate checkpoints in reverse height order (iteration starts at tip
147237 /// 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 > >
149239 where
150240 R : RangeBounds < u32 > ,
151241 {
@@ -164,6 +254,28 @@ impl CheckPoint {
164254 } )
165255 }
166256
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+
167279 /// Inserts `block_id` at its height within the chain.
168280 ///
169281 /// The effect of `insert` depends on whether a height already exists. If it doesn't the
@@ -172,14 +284,14 @@ impl CheckPoint {
172284 /// along with all block followin it. The returned chain will have a tip of the `block_id`
173285 /// passed in. Of course, if the `block_id` was already present then this just returns `self`.
174286 #[ 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" ) ;
177289
178290 let mut cp = self . clone ( ) ;
179291 let mut tail = vec ! [ ] ;
180292 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 ( ) {
183295 return self ;
184296 }
185297 // if we have a conflict we just return the inserted block because the tail is by
@@ -188,31 +300,50 @@ impl CheckPoint {
188300 break cp. prev ( ) . expect ( "can't be called on genesis block" ) ;
189301 }
190302
191- if cp. height ( ) < block_id . height {
303+ if cp. height ( ) < height {
192304 break cp;
193305 }
194306
195- tail. push ( cp. block_id ( ) ) ;
307+ tail. push ( BlockId {
308+ height,
309+ hash : data. to_blockhash ( ) ,
310+ } ) ;
196311 cp = cp. prev ( ) . expect ( "will break before genesis block" ) ;
197312 } ;
198313
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" )
201321 }
202322
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+ }
206337 }
207338}
208339
209340/// 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 > > > ,
212343}
213344
214- impl Iterator for CheckPointIter {
215- type Item = CheckPoint ;
345+ impl < B > Iterator for CheckPointIter < B > {
346+ type Item = CheckPoint < B > ;
216347
217348 fn next ( & mut self ) -> Option < Self :: Item > {
218349 let current = self . current . clone ( ) ?;
@@ -221,9 +352,9 @@ impl Iterator for CheckPointIter {
221352 }
222353}
223354
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 > ;
227358
228359 fn into_iter ( self ) -> Self :: IntoIter {
229360 CheckPointIter {
0 commit comments