82398a4b39cd3ea97f3a77135ebe43b6b94f60c4
[rust-lightning] / lightning-block-sync / src / poll.rs
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(crate) 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                 // TODO: Use the result of validate_pow instead of recomputing the block hash once upstream.
56                 if self.header.block_hash() != block_hash {
57                         return Err(BlockSourceError::persistent("invalid block hash"));
58                 }
59
60                 Ok(ValidatedBlockHeader { block_hash, inner: self })
61         }
62 }
63
64 impl Validate for Block {
65         type T = ValidatedBlock;
66
67         fn validate(self, block_hash: BlockHash) -> BlockSourceResult<Self::T> {
68                 self.header
69                         .validate_pow(&self.header.target())
70                         .or_else(|e| Err(BlockSourceError::persistent(e)))?;
71
72                 // TODO: Use the result of validate_pow instead of recomputing the block hash once upstream.
73                 if self.block_hash() != block_hash {
74                         return Err(BlockSourceError::persistent("invalid block hash"));
75                 }
76
77                 if !self.check_merkle_root() {
78                         return Err(BlockSourceError::persistent("invalid merkle root"));
79                 }
80
81                 if !self.check_witness_commitment() {
82                         return Err(BlockSourceError::persistent("invalid witness commitment"));
83                 }
84
85                 Ok(ValidatedBlock { block_hash, inner: self })
86         }
87 }
88
89 /// A block header with validated proof of work and corresponding block hash.
90 #[derive(Clone, Copy, Debug, PartialEq)]
91 pub struct ValidatedBlockHeader {
92         block_hash: BlockHash,
93         inner: BlockHeaderData,
94 }
95
96 impl std::ops::Deref for ValidatedBlockHeader {
97         type Target = BlockHeaderData;
98
99         fn deref(&self) -> &Self::Target {
100                 &self.inner
101         }
102 }
103
104 impl ValidatedBlockHeader {
105         /// Checks that the header correctly builds on previous_header: the claimed work differential
106         /// matches the actual PoW and the difficulty transition is possible, i.e., within 4x.
107         fn check_builds_on(&self, previous_header: &ValidatedBlockHeader, network: Network) -> BlockSourceResult<()> {
108                 if self.header.prev_blockhash != previous_header.block_hash {
109                         return Err(BlockSourceError::persistent("invalid previous block hash"));
110                 }
111
112                 if self.height != previous_header.height + 1 {
113                         return Err(BlockSourceError::persistent("invalid block height"));
114                 }
115
116                 let work = self.header.work();
117                 if self.chainwork != previous_header.chainwork + work {
118                         return Err(BlockSourceError::persistent("invalid chainwork"));
119                 }
120
121                 if let Network::Bitcoin = network {
122                         if self.height % 2016 == 0 {
123                                 let previous_work = previous_header.header.work();
124                                 if work > (previous_work << 2) || work < (previous_work >> 2) {
125                                         return Err(BlockSourceError::persistent("invalid difficulty transition"))
126                                 }
127                         } else if self.header.bits != previous_header.header.bits {
128                                 return Err(BlockSourceError::persistent("invalid difficulty"))
129                         }
130                 }
131
132                 Ok(())
133         }
134 }
135
136 /// A block with validated data against its transaction list and corresponding block hash.
137 pub struct ValidatedBlock {
138         block_hash: BlockHash,
139         inner: Block,
140 }
141
142 impl std::ops::Deref for ValidatedBlock {
143         type Target = Block;
144
145         fn deref(&self) -> &Self::Target {
146                 &self.inner
147         }
148 }