Skip to content

Commit a899049

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

File tree

1 file changed

+129
-97
lines changed

1 file changed

+129
-97
lines changed

crates/core/src/checkpoint.rs

+129-97
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,69 @@ 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> PartialEq for CheckPoint<B>
33+
where
34+
B: Copy + Clone + core::cmp::PartialEq,
35+
{
2536
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());
37+
let self_cps = self.iter().map(|cp| *cp.inner());
38+
let other_cps = other.iter().map(|cp| *cp.inner());
2839
self_cps.eq(other_cps)
2940
}
3041
}
3142

32-
impl CheckPoint {
33-
/// Construct a new base block at the front of a linked list.
43+
impl CheckPoint<BlockHash> {
44+
/// Construct a new [`CheckPoint`] at the front of a linked list.
3445
pub fn new(block: BlockId) -> Self {
35-
Self(Arc::new(CPInner { block, prev: None }))
46+
Self(Arc::new(CPInner {
47+
height: block.height,
48+
data: block.hash,
49+
prev: None,
50+
}))
51+
}
52+
53+
/// Construct a checkpoint from the given `header` and block `height`.
54+
///
55+
/// If `header` is of the genesis block, the checkpoint won't have a [`prev`] node. Otherwise,
56+
/// we return a checkpoint linked with the previous block.
57+
///
58+
/// [`prev`]: CheckPoint::prev
59+
pub fn from_header(header: &bitcoin::block::Header, height: u32) -> Self {
60+
let hash = header.block_hash();
61+
let this_block_id = BlockId { height, hash };
62+
63+
let prev_height = match height.checked_sub(1) {
64+
Some(h) => h,
65+
None => return Self::new(this_block_id),
66+
};
67+
68+
let prev_block_id = BlockId {
69+
height: prev_height,
70+
hash: header.prev_blockhash,
71+
};
72+
73+
CheckPoint::new(prev_block_id)
74+
.push(this_block_id)
75+
.expect("must construct checkpoint")
3676
}
3777

3878
/// Construct a checkpoint from a list of [`BlockId`]s in ascending height order.
@@ -50,36 +90,75 @@ impl CheckPoint {
5090
block_ids: impl IntoIterator<Item = BlockId>,
5191
) -> Result<Self, Option<Self>> {
5292
let mut blocks = block_ids.into_iter();
53-
let mut acc = CheckPoint::new(blocks.next().ok_or(None)?);
93+
let block = blocks.next().ok_or(None)?;
94+
let mut acc = CheckPoint::new(block);
5495
for id in blocks {
5596
acc = acc.push(id).map_err(Some)?;
5697
}
5798
Ok(acc)
5899
}
59100

60-
/// Construct a checkpoint from the given `header` and block `height`.
101+
/// Extends the checkpoint linked list by a iterator of block ids.
61102
///
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.
103+
/// Returns an `Err(self)` if there is block which does not have a greater height than the
104+
/// previous one.
105+
pub fn extend(self, blockdata: impl IntoIterator<Item = BlockId>) -> Result<Self, Self> {
106+
let mut curr = self.clone();
107+
for block in blockdata {
108+
curr = curr.push(block).map_err(|_| self.clone())?;
109+
}
110+
Ok(curr)
111+
}
112+
113+
/// Get the block hash of the checkpoint.
114+
pub fn hash(&self) -> BlockHash {
115+
self.0.data
116+
}
117+
118+
/// Get the [`BlockId`] of the checkpoint.
119+
pub fn block_id(&self) -> BlockId {
120+
BlockId {
121+
height: self.height(),
122+
hash: self.hash(),
123+
}
124+
}
125+
126+
/// Inserts `block_id` at its height within the chain.
64127
///
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 };
128+
/// The effect of `insert` depends on whether a height already exists. If it doesn't the
129+
/// `block_id` we inserted and all pre-existing blocks higher than it will be re-inserted after
130+
/// it. If the height already existed and has a conflicting block hash then it will be purged
131+
/// along with all block followin it. The returned chain will have a tip of the `block_id`
132+
/// passed in. Of course, if the `block_id` was already present then this just returns `self`.
133+
#[must_use]
134+
pub fn insert(self, block_id: BlockId) -> Self {
135+
assert_ne!(block_id.height, 0, "cannot insert the genesis block");
69136

70-
let prev_height = match height.checked_sub(1) {
71-
Some(h) => h,
72-
None => return Self::new(this_block_id),
73-
};
137+
let mut cp = self.clone();
138+
let mut tail = vec![];
139+
let base = loop {
140+
if cp.height() == block_id.height {
141+
if cp.hash() == block_id.hash {
142+
return self;
143+
}
144+
// if we have a conflict we just return the inserted block because the tail is by
145+
// implication invalid.
146+
tail = vec![];
147+
break cp.prev().expect("can't be called on genesis block");
148+
}
74149

75-
let prev_block_id = BlockId {
76-
height: prev_height,
77-
hash: header.prev_blockhash,
150+
if cp.height() < block_id.height {
151+
break cp;
152+
}
153+
154+
tail.push(cp.block_id());
155+
cp = cp.prev().expect("will break before genesis block");
78156
};
79157

80-
CheckPoint::new(prev_block_id)
81-
.push(this_block_id)
82-
.expect("must construct checkpoint")
158+
let new_cp = core::iter::once(block_id).chain(tail.into_iter().rev());
159+
//.map(|block| (block.height, block.hash));
160+
161+
base.extend(new_cp).expect("tail is in order")
83162
}
84163

85164
/// Puts another checkpoint onto the linked list representing the blockchain.
@@ -89,48 +168,37 @@ impl CheckPoint {
89168
pub fn push(self, block: BlockId) -> Result<Self, Self> {
90169
if self.height() < block.height {
91170
Ok(Self(Arc::new(CPInner {
92-
block,
171+
height: block.height,
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 + Clone,
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 {
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
192+
self.0.height
125193
}
126194

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
{
@@ -164,55 +232,19 @@ impl CheckPoint {
164232
})
165233
}
166234

167-
/// Inserts `block_id` at its height within the chain.
168-
///
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`.
174-
#[must_use]
175-
pub fn insert(self, block_id: BlockId) -> Self {
176-
assert_ne!(block_id.height, 0, "cannot insert the genesis block");
177-
178-
let mut cp = self.clone();
179-
let mut tail = vec![];
180-
let base = loop {
181-
if cp.height() == block_id.height {
182-
if cp.hash() == block_id.hash {
183-
return self;
184-
}
185-
// if we have a conflict we just return the inserted block because the tail is by
186-
// implication invalid.
187-
tail = vec![];
188-
break cp.prev().expect("can't be called on genesis block");
189-
}
190-
191-
if cp.height() < block_id.height {
192-
break cp;
193-
}
194-
195-
tail.push(cp.block_id());
196-
cp = cp.prev().expect("will break before genesis block");
197-
};
198-
199-
base.extend(core::iter::once(block_id).chain(tail.into_iter().rev()))
200-
.expect("tail is in order")
201-
}
202-
203235
/// This method tests for `self` and `other` to have equal internal pointers.
204236
pub fn eq_ptr(&self, other: &Self) -> bool {
205237
Arc::as_ptr(&self.0) == Arc::as_ptr(&other.0)
206238
}
207239
}
208240

209241
/// Iterates over checkpoints backwards.
210-
pub struct CheckPointIter {
211-
current: Option<Arc<CPInner>>,
242+
pub struct CheckPointIter<B = BlockHash> {
243+
current: Option<Arc<CPInner<B>>>,
212244
}
213245

214-
impl Iterator for CheckPointIter {
215-
type Item = CheckPoint;
246+
impl<B> Iterator for CheckPointIter<B> {
247+
type Item = CheckPoint<B>;
216248

217249
fn next(&mut self) -> Option<Self::Item> {
218250
let current = self.current.clone()?;
@@ -221,9 +253,9 @@ impl Iterator for CheckPointIter {
221253
}
222254
}
223255

224-
impl IntoIterator for CheckPoint {
225-
type Item = CheckPoint;
226-
type IntoIter = CheckPointIter;
256+
impl<B> IntoIterator for CheckPoint<B> {
257+
type Item = CheckPoint<B>;
258+
type IntoIter = CheckPointIter<B>;
227259

228260
fn into_iter(self) -> Self::IntoIter {
229261
CheckPointIter {

0 commit comments

Comments
 (0)