1 use crate::{AsyncBlockSourceResult, BlockHeaderData, BlockSource, BlockSourceError, BlockSourceResult};
3 use bitcoin::blockdata::block::Block;
4 use bitcoin::hash_types::BlockHash;
5 use bitcoin::network::constants::Network;
7 use std::ops::DerefMut;
9 /// The `Poll` trait defines behavior for polling block sources for a chain tip and retrieving
10 /// related chain data. It serves as an adapter for `BlockSource`.
12 /// Returns a chain tip in terms of its relationship to the provided chain tip.
13 fn poll_chain_tip<'a>(&'a mut self, best_known_chain_tip: ValidatedBlockHeader) ->
14 AsyncBlockSourceResult<'a, ChainTip>;
16 /// Returns the header that preceded the given header in the chain.
17 fn look_up_previous_header<'a>(&'a mut self, header: &'a ValidatedBlockHeader) ->
18 AsyncBlockSourceResult<'a, ValidatedBlockHeader>;
20 /// Returns the block associated with the given header.
21 fn fetch_block<'a>(&'a mut self, header: &'a ValidatedBlockHeader) ->
22 AsyncBlockSourceResult<'a, ValidatedBlock>;
25 /// A chain tip relative to another chain tip in terms of block hash and chainwork.
26 #[derive(Clone, Debug, PartialEq)]
28 /// A chain tip with the same hash as another chain's tip.
31 /// A chain tip with more chainwork than another chain's tip.
32 Better(ValidatedBlockHeader),
34 /// A chain tip with less or equal chainwork than another chain's tip. In either case, the
35 /// hashes of each tip will be different.
36 Worse(ValidatedBlockHeader),
39 /// The `Validate` trait defines behavior for validating chain data.
40 pub(crate) trait Validate {
41 /// The validated data wrapper which can be dereferenced to obtain the validated data.
42 type T: std::ops::Deref<Target = Self>;
44 /// Validates the chain data against the given block hash and any criteria needed to ensure that
45 /// it is internally consistent.
46 fn validate(self, block_hash: BlockHash) -> BlockSourceResult<Self::T>;
49 impl Validate for BlockHeaderData {
50 type T = ValidatedBlockHeader;
52 fn validate(self, block_hash: BlockHash) -> BlockSourceResult<Self::T> {
54 .validate_pow(&self.header.target())
55 .or_else(|e| Err(BlockSourceError::persistent(e)))?;
57 // TODO: Use the result of validate_pow instead of recomputing the block hash once upstream.
58 if self.header.block_hash() != block_hash {
59 return Err(BlockSourceError::persistent("invalid block hash"));
62 Ok(ValidatedBlockHeader { block_hash, inner: self })
66 impl Validate for Block {
67 type T = ValidatedBlock;
69 fn validate(self, block_hash: BlockHash) -> BlockSourceResult<Self::T> {
71 .validate_pow(&self.header.target())
72 .or_else(|e| Err(BlockSourceError::persistent(e)))?;
74 // TODO: Use the result of validate_pow instead of recomputing the block hash once upstream.
75 if self.block_hash() != block_hash {
76 return Err(BlockSourceError::persistent("invalid block hash"));
79 if !self.check_merkle_root() {
80 return Err(BlockSourceError::persistent("invalid merkle root"));
83 if !self.check_witness_commitment() {
84 return Err(BlockSourceError::persistent("invalid witness commitment"));
87 Ok(ValidatedBlock { block_hash, inner: self })
91 /// A block header with validated proof of work and corresponding block hash.
92 #[derive(Clone, Copy, Debug, PartialEq)]
93 pub struct ValidatedBlockHeader {
94 block_hash: BlockHash,
95 inner: BlockHeaderData,
98 impl std::ops::Deref for ValidatedBlockHeader {
99 type Target = BlockHeaderData;
101 fn deref(&self) -> &Self::Target {
106 impl ValidatedBlockHeader {
107 /// Checks that the header correctly builds on previous_header: the claimed work differential
108 /// matches the actual PoW and the difficulty transition is possible, i.e., within 4x.
109 fn check_builds_on(&self, previous_header: &ValidatedBlockHeader, network: Network) -> BlockSourceResult<()> {
110 if self.header.prev_blockhash != previous_header.block_hash {
111 return Err(BlockSourceError::persistent("invalid previous block hash"));
114 if self.height != previous_header.height + 1 {
115 return Err(BlockSourceError::persistent("invalid block height"));
118 let work = self.header.work();
119 if self.chainwork != previous_header.chainwork + work {
120 return Err(BlockSourceError::persistent("invalid chainwork"));
123 if let Network::Bitcoin = network {
124 if self.height % 2016 == 0 {
125 let previous_work = previous_header.header.work();
126 if work > (previous_work << 2) || work < (previous_work >> 2) {
127 return Err(BlockSourceError::persistent("invalid difficulty transition"))
129 } else if self.header.bits != previous_header.header.bits {
130 return Err(BlockSourceError::persistent("invalid difficulty"))
138 /// A block with validated data against its transaction list and corresponding block hash.
139 pub struct ValidatedBlock {
140 block_hash: BlockHash,
144 impl std::ops::Deref for ValidatedBlock {
147 fn deref(&self) -> &Self::Target {
152 pub struct ChainPoller<B: DerefMut<Target=T> + Sized + Sync + Send, T: BlockSource> {
157 impl<B: DerefMut<Target=T> + Sized + Sync + Send, T: BlockSource> ChainPoller<B, T> {
158 pub fn new(block_source: B, network: Network) -> Self {
159 Self { block_source, network }
163 impl<B: DerefMut<Target=T> + Sized + Sync + Send, T: BlockSource> Poll for ChainPoller<B, T> {
164 fn poll_chain_tip<'a>(&'a mut self, best_known_chain_tip: ValidatedBlockHeader) ->
165 AsyncBlockSourceResult<'a, ChainTip>
167 Box::pin(async move {
168 let (block_hash, height) = self.block_source.get_best_block().await?;
169 if block_hash == best_known_chain_tip.header.block_hash() {
170 return Ok(ChainTip::Common);
173 let chain_tip = self.block_source
174 .get_header(&block_hash, height).await?
175 .validate(block_hash)?;
176 if chain_tip.chainwork > best_known_chain_tip.chainwork {
177 Ok(ChainTip::Better(chain_tip))
179 Ok(ChainTip::Worse(chain_tip))
184 fn look_up_previous_header<'a>(&'a mut self, header: &'a ValidatedBlockHeader) ->
185 AsyncBlockSourceResult<'a, ValidatedBlockHeader>
187 Box::pin(async move {
188 if header.height == 0 {
189 return Err(BlockSourceError::persistent("genesis block reached"));
192 let previous_hash = &header.header.prev_blockhash;
193 let height = header.height - 1;
194 let previous_header = self.block_source
195 .get_header(previous_hash, Some(height)).await?
196 .validate(*previous_hash)?;
197 header.check_builds_on(&previous_header, self.network)?;
203 fn fetch_block<'a>(&'a mut self, header: &'a ValidatedBlockHeader) ->
204 AsyncBlockSourceResult<'a, ValidatedBlock>
206 Box::pin(async move {
208 .get_block(&header.block_hash).await?
209 .validate(header.block_hash)
217 use crate::test_utils::Blockchain;
219 use bitcoin::util::uint::Uint256;
222 async fn poll_empty_chain() {
223 let mut chain = Blockchain::default().with_height(0);
224 let best_known_chain_tip = chain.tip();
225 chain.disconnect_tip();
227 let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
228 match poller.poll_chain_tip(best_known_chain_tip).await {
230 assert_eq!(e.kind(), BlockSourceErrorKind::Transient);
231 assert_eq!(e.into_inner().as_ref().to_string(), "empty chain");
233 Ok(_) => panic!("Expected error"),
238 async fn poll_chain_without_headers() {
239 let mut chain = Blockchain::default().with_height(1).without_headers();
240 let best_known_chain_tip = chain.at_height(0);
242 let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
243 match poller.poll_chain_tip(best_known_chain_tip).await {
245 assert_eq!(e.kind(), BlockSourceErrorKind::Persistent);
246 assert_eq!(e.into_inner().as_ref().to_string(), "header not found");
248 Ok(_) => panic!("Expected error"),
253 async fn poll_chain_with_invalid_pow() {
254 let mut chain = Blockchain::default().with_height(1);
255 let best_known_chain_tip = chain.at_height(0);
257 // Invalidate the tip by changing its target.
258 chain.blocks.last_mut().unwrap().header.bits =
259 BlockHeader::compact_target_from_u256(&Uint256::from_be_bytes([0; 32]));
261 let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
262 match poller.poll_chain_tip(best_known_chain_tip).await {
264 assert_eq!(e.kind(), BlockSourceErrorKind::Persistent);
265 assert_eq!(e.into_inner().as_ref().to_string(), "block target correct but not attained");
267 Ok(_) => panic!("Expected error"),
272 async fn poll_chain_with_malformed_headers() {
273 let mut chain = Blockchain::default().with_height(1).malformed_headers();
274 let best_known_chain_tip = chain.at_height(0);
276 let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
277 match poller.poll_chain_tip(best_known_chain_tip).await {
279 assert_eq!(e.kind(), BlockSourceErrorKind::Persistent);
280 assert_eq!(e.into_inner().as_ref().to_string(), "invalid block hash");
282 Ok(_) => panic!("Expected error"),
287 async fn poll_chain_with_common_tip() {
288 let mut chain = Blockchain::default().with_height(0);
289 let best_known_chain_tip = chain.tip();
291 let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
292 match poller.poll_chain_tip(best_known_chain_tip).await {
293 Err(e) => panic!("Unexpected error: {:?}", e),
294 Ok(tip) => assert_eq!(tip, ChainTip::Common),
299 async fn poll_chain_with_uncommon_tip_but_equal_chainwork() {
300 let mut chain = Blockchain::default().with_height(1);
301 let best_known_chain_tip = chain.tip();
303 // Change the nonce to get a different block hash with the same chainwork.
304 chain.blocks.last_mut().unwrap().header.nonce += 1;
305 let worse_chain_tip = chain.tip();
306 assert_eq!(best_known_chain_tip.chainwork, worse_chain_tip.chainwork);
308 let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
309 match poller.poll_chain_tip(best_known_chain_tip).await {
310 Err(e) => panic!("Unexpected error: {:?}", e),
311 Ok(tip) => assert_eq!(tip, ChainTip::Worse(worse_chain_tip)),
316 async fn poll_chain_with_worse_tip() {
317 let mut chain = Blockchain::default().with_height(1);
318 let best_known_chain_tip = chain.tip();
320 chain.disconnect_tip();
321 let worse_chain_tip = chain.tip();
323 let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
324 match poller.poll_chain_tip(best_known_chain_tip).await {
325 Err(e) => panic!("Unexpected error: {:?}", e),
326 Ok(tip) => assert_eq!(tip, ChainTip::Worse(worse_chain_tip)),
331 async fn poll_chain_with_better_tip() {
332 let mut chain = Blockchain::default().with_height(1);
333 let best_known_chain_tip = chain.at_height(0);
335 let better_chain_tip = chain.tip();
337 let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
338 match poller.poll_chain_tip(best_known_chain_tip).await {
339 Err(e) => panic!("Unexpected error: {:?}", e),
340 Ok(tip) => assert_eq!(tip, ChainTip::Better(better_chain_tip)),