Skip to content

Commit bf90ea5

Browse files
committed
refactor(core): CheckPoint takes a generic WIP
1 parent 775e4ae commit bf90ea5

File tree

1 file changed

+197
-67
lines changed

1 file changed

+197
-67
lines changed

crates/core/src/checkpoint.rs

Lines changed: 197 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use core::ops::RangeBounds;
22

33
use alloc::sync::Arc;
4-
use bitcoin::BlockHash;
4+
use bitcoin::{block::Header, consensus::Encodable, hashes::Hash, BlockHash};
55

66
use 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).
17+
struct CPInner<B> {
18+
/// Block data.
1919
block: BlockId,
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 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+
{
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+
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")
3690
}
3791

3892
/// Construct a checkpoint from a list of [`BlockId`]s in ascending height order.
@@ -50,36 +104,61 @@ 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+
/// Inserts `block_id` at its height within the chain.
64128
///
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");
69137

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+
}
74150

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");
78157
};
79158

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")
83162
}
84163

85164
/// Puts another checkpoint onto the linked list representing the blockchain.
@@ -90,47 +169,36 @@ impl CheckPoint {
90169
if self.height() < block.height {
91170
Ok(Self(Arc::new(CPInner {
92171
block,
172+
data: block.hash,
93173
prev: Some(self.0),
94174
})))
95175
} else {
96176
Err(self)
97177
}
98178
}
179+
}
99180

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
115188
}
116189

117190
/// Get the height of the checkpoint.
118191
pub fn height(&self) -> u32 {
119192
self.0.block.height
120193
}
121194

122-
/// Get the block hash of the checkpoint.
123-
pub fn hash(&self) -> BlockHash {
124-
self.0.block.hash
125-
}
126-
127195
/// Get the previous checkpoint in the chain
128-
pub fn prev(&self) -> Option<CheckPoint> {
196+
pub fn prev(&self) -> Option<CheckPoint<B>> {
129197
self.0.prev.clone().map(CheckPoint)
130198
}
131199

132200
/// Iterate from this checkpoint in descending height.
133-
pub fn iter(&self) -> CheckPointIter {
201+
pub fn iter(&self) -> CheckPointIter<B> {
134202
self.clone().into_iter()
135203
}
136204

@@ -145,7 +213,7 @@ impl CheckPoint {
145213
///
146214
/// Note that we always iterate checkpoints in reverse height order (iteration starts at tip
147215
/// 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>>
149217
where
150218
R: RangeBounds<u32>,
151219
{
@@ -163,6 +231,48 @@ impl CheckPoint {
163231
core::ops::Bound::Unbounded => true,
164232
})
165233
}
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+
}
166276

167277
/// Inserts `block_id` at its height within the chain.
168278
///
@@ -172,14 +282,14 @@ impl CheckPoint {
172282
/// along with all block followin it. The returned chain will have a tip of the `block_id`
173283
/// passed in. Of course, if the `block_id` was already present then this just returns `self`.
174284
#[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");
177287

178288
let mut cp = self.clone();
179289
let mut tail = vec![];
180290
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() {
183293
return self;
184294
}
185295
// if we have a conflict we just return the inserted block because the tail is by
@@ -188,16 +298,36 @@ impl CheckPoint {
188298
break cp.prev().expect("can't be called on genesis block");
189299
}
190300

191-
if cp.height() < block_id.height {
301+
if cp.height() < height {
192302
break cp;
193303
}
194304

195-
tail.push(cp.block_id());
305+
tail.push((height, data));
196306
cp = cp.prev().expect("will break before genesis block");
197307
};
198308

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+
}
201331
}
202332

203333
/// This method tests for `self` and `other` to have equal internal pointers.
@@ -207,12 +337,12 @@ impl CheckPoint {
207337
}
208338

209339
/// 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>>>,
212342
}
213343

214-
impl Iterator for CheckPointIter {
215-
type Item = CheckPoint;
344+
impl<B> Iterator for CheckPointIter<B> {
345+
type Item = CheckPoint<B>;
216346

217347
fn next(&mut self) -> Option<Self::Item> {
218348
let current = self.current.clone()?;
@@ -221,9 +351,9 @@ impl Iterator for CheckPointIter {
221351
}
222352
}
223353

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>;
227357

228358
fn into_iter(self) -> Self::IntoIter {
229359
CheckPointIter {

0 commit comments

Comments
 (0)