Skip to content

Commit 2bf6e69

Browse files
committed
Define a Poll trait as an adaptor on BlockSource
SPV clients need to poll one or more block sources for the best chain tip and to retrieve related chain data. The Poll trait serves as an adaptor interface for BlockSource. Implementations may define an appropriate polling strategy.
1 parent 89be625 commit 2bf6e69

File tree

1 file changed

+142
-0
lines changed

1 file changed

+142
-0
lines changed

lightning-block-sync/src/poll.rs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
use crate::{AsyncBlockSourceResult, BlockHeaderData, BlockSourceError, BlockSourceResult};
2+
3+
use bitcoin::blockdata::block::Block;
4+
use bitcoin::hash_types::BlockHash;
5+
use bitcoin::network::constants::Network;
6+
7+
/// The `Poll` trait defines behavior for polling block sources for a chain tip and retrieving
8+
/// related chain data. It serves as an adapter for `BlockSource`.
9+
pub trait Poll {
10+
/// Returns a chain tip in terms of its relationship to the provided chain tip.
11+
fn poll_chain_tip<'a>(&'a mut self, best_known_chain_tip: ValidatedBlockHeader) ->
12+
AsyncBlockSourceResult<'a, ChainTip>;
13+
14+
/// Returns the header that preceded the given header in the chain.
15+
fn look_up_previous_header<'a>(&'a mut self, header: &'a ValidatedBlockHeader) ->
16+
AsyncBlockSourceResult<'a, ValidatedBlockHeader>;
17+
18+
/// Returns the block associated with the given header.
19+
fn fetch_block<'a>(&'a mut self, header: &'a ValidatedBlockHeader) ->
20+
AsyncBlockSourceResult<'a, ValidatedBlock>;
21+
}
22+
23+
/// A chain tip relative to another chain tip in terms of block hash and chainwork.
24+
#[derive(Clone, Debug, PartialEq)]
25+
pub enum ChainTip {
26+
/// A chain tip with the same hash as another chain's tip.
27+
Common,
28+
29+
/// A chain tip with more chainwork than another chain's tip.
30+
Better(ValidatedBlockHeader),
31+
32+
/// A chain tip with less or equal chainwork than another chain's tip. In either case, the
33+
/// hashes of each tip will be different.
34+
Worse(ValidatedBlockHeader),
35+
}
36+
37+
/// The `Validate` trait defines behavior for validating chain data.
38+
pub trait Validate {
39+
/// The validated data wrapper which can be dereferenced to obtain the validated data.
40+
type T: std::ops::Deref<Target = Self>;
41+
42+
/// Validates the chain data against the given block hash and any criteria needed to ensure that
43+
/// it is internally consistent.
44+
fn validate(self, block_hash: BlockHash) -> BlockSourceResult<Self::T>;
45+
}
46+
47+
impl Validate for BlockHeaderData {
48+
type T = ValidatedBlockHeader;
49+
50+
fn validate(self, block_hash: BlockHash) -> BlockSourceResult<Self::T> {
51+
self.header
52+
.validate_pow(&self.header.target())
53+
.or_else(|e| Err(BlockSourceError::persistent(e)))?;
54+
55+
if self.header.block_hash() != block_hash {
56+
return Err(BlockSourceError::persistent("invalid block hash"));
57+
}
58+
59+
Ok(ValidatedBlockHeader { block_hash, inner: self })
60+
}
61+
}
62+
63+
impl Validate for Block {
64+
type T = ValidatedBlock;
65+
66+
fn validate(self, block_hash: BlockHash) -> BlockSourceResult<Self::T> {
67+
if self.block_hash() != block_hash {
68+
return Err(BlockSourceError::persistent("invalid block hash"));
69+
}
70+
71+
if !self.check_merkle_root() {
72+
return Err(BlockSourceError::persistent("invalid merkle root"));
73+
}
74+
75+
if !self.check_witness_commitment() {
76+
return Err(BlockSourceError::persistent("invalid witness commitment"));
77+
}
78+
79+
Ok(ValidatedBlock { block_hash, inner: self })
80+
}
81+
}
82+
83+
/// A block header with validated proof of work and corresponding block hash.
84+
#[derive(Clone, Copy, Debug, PartialEq)]
85+
pub struct ValidatedBlockHeader {
86+
block_hash: BlockHash,
87+
inner: BlockHeaderData,
88+
}
89+
90+
impl std::ops::Deref for ValidatedBlockHeader {
91+
type Target = BlockHeaderData;
92+
93+
fn deref(&self) -> &Self::Target {
94+
&self.inner
95+
}
96+
}
97+
98+
impl ValidatedBlockHeader {
99+
/// Checks that the header correctly builds on previous_header: the claimed work differential
100+
/// matches the actual PoW and the difficulty transition is possible, i.e., within 4x.
101+
fn check_builds_on(&self, previous_header: &ValidatedBlockHeader, network: Network) -> BlockSourceResult<()> {
102+
if self.header.prev_blockhash != previous_header.block_hash {
103+
return Err(BlockSourceError::persistent("invalid previous block hash"));
104+
}
105+
106+
if self.height != previous_header.height + 1 {
107+
return Err(BlockSourceError::persistent("invalid block height"));
108+
}
109+
110+
let work = self.header.work();
111+
if self.chainwork != previous_header.chainwork + work {
112+
return Err(BlockSourceError::persistent("invalid chainwork"));
113+
}
114+
115+
if let Network::Bitcoin = network {
116+
if self.height % 2016 == 0 {
117+
let previous_work = previous_header.header.work();
118+
if work > previous_work << 2 || work < previous_work >> 2 {
119+
return Err(BlockSourceError::persistent("invalid difficulty transition"))
120+
}
121+
} else if self.header.bits != previous_header.header.bits {
122+
return Err(BlockSourceError::persistent("invalid difficulty"))
123+
}
124+
}
125+
126+
Ok(())
127+
}
128+
}
129+
130+
/// A block with validated data against its transaction list and corresponding block hash.
131+
pub struct ValidatedBlock {
132+
block_hash: BlockHash,
133+
inner: Block,
134+
}
135+
136+
impl std::ops::Deref for ValidatedBlock {
137+
type Target = Block;
138+
139+
fn deref(&self) -> &Self::Target {
140+
&self.inner
141+
}
142+
}

0 commit comments

Comments
 (0)