Add ChainNotifier and define ChainListener trait
[rust-lightning] / lightning-block-sync / src / poll.rs
1 use crate::{AsyncBlockSourceResult, BlockHeaderData, BlockSource, BlockSourceError, BlockSourceResult};
2
3 use bitcoin::blockdata::block::Block;
4 use bitcoin::hash_types::BlockHash;
5 use bitcoin::network::constants::Network;
6
7 use std::ops::DerefMut;
8
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`.
11 ///
12 /// [`ChainPoller`] adapts a single `BlockSource`, while any other implementations of `Poll` are
13 /// required to be built in terms of it to ensure chain data validity.
14 ///
15 /// [`ChainPoller`]: ../struct.ChainPoller.html
16 pub trait Poll {
17         /// Returns a chain tip in terms of its relationship to the provided chain tip.
18         fn poll_chain_tip<'a>(&'a mut self, best_known_chain_tip: ValidatedBlockHeader) ->
19                 AsyncBlockSourceResult<'a, ChainTip>;
20
21         /// Returns the header that preceded the given header in the chain.
22         fn look_up_previous_header<'a>(&'a mut self, header: &'a ValidatedBlockHeader) ->
23                 AsyncBlockSourceResult<'a, ValidatedBlockHeader>;
24
25         /// Returns the block associated with the given header.
26         fn fetch_block<'a>(&'a mut self, header: &'a ValidatedBlockHeader) ->
27                 AsyncBlockSourceResult<'a, ValidatedBlock>;
28 }
29
30 /// A chain tip relative to another chain tip in terms of block hash and chainwork.
31 #[derive(Clone, Debug, PartialEq)]
32 pub enum ChainTip {
33         /// A chain tip with the same hash as another chain's tip.
34         Common,
35
36         /// A chain tip with more chainwork than another chain's tip.
37         Better(ValidatedBlockHeader),
38
39         /// A chain tip with less or equal chainwork than another chain's tip. In either case, the
40         /// hashes of each tip will be different.
41         Worse(ValidatedBlockHeader),
42 }
43
44 /// The `Validate` trait defines behavior for validating chain data.
45 pub(crate) trait Validate {
46         /// The validated data wrapper which can be dereferenced to obtain the validated data.
47         type T: std::ops::Deref<Target = Self>;
48
49         /// Validates the chain data against the given block hash and any criteria needed to ensure that
50         /// it is internally consistent.
51         fn validate(self, block_hash: BlockHash) -> BlockSourceResult<Self::T>;
52 }
53
54 impl Validate for BlockHeaderData {
55         type T = ValidatedBlockHeader;
56
57         fn validate(self, block_hash: BlockHash) -> BlockSourceResult<Self::T> {
58                 self.header
59                         .validate_pow(&self.header.target())
60                         .or_else(|e| Err(BlockSourceError::persistent(e)))?;
61
62                 // TODO: Use the result of validate_pow instead of recomputing the block hash once upstream.
63                 if self.header.block_hash() != block_hash {
64                         return Err(BlockSourceError::persistent("invalid block hash"));
65                 }
66
67                 Ok(ValidatedBlockHeader { block_hash, inner: self })
68         }
69 }
70
71 impl Validate for Block {
72         type T = ValidatedBlock;
73
74         fn validate(self, block_hash: BlockHash) -> BlockSourceResult<Self::T> {
75                 self.header
76                         .validate_pow(&self.header.target())
77                         .or_else(|e| Err(BlockSourceError::persistent(e)))?;
78
79                 // TODO: Use the result of validate_pow instead of recomputing the block hash once upstream.
80                 if self.block_hash() != block_hash {
81                         return Err(BlockSourceError::persistent("invalid block hash"));
82                 }
83
84                 if !self.check_merkle_root() {
85                         return Err(BlockSourceError::persistent("invalid merkle root"));
86                 }
87
88                 if !self.check_witness_commitment() {
89                         return Err(BlockSourceError::persistent("invalid witness commitment"));
90                 }
91
92                 Ok(ValidatedBlock { block_hash, inner: self })
93         }
94 }
95
96 /// A block header with validated proof of work and corresponding block hash.
97 #[derive(Clone, Copy, Debug, PartialEq)]
98 pub struct ValidatedBlockHeader {
99         pub(crate) block_hash: BlockHash,
100         inner: BlockHeaderData,
101 }
102
103 impl std::ops::Deref for ValidatedBlockHeader {
104         type Target = BlockHeaderData;
105
106         fn deref(&self) -> &Self::Target {
107                 &self.inner
108         }
109 }
110
111 impl ValidatedBlockHeader {
112         /// Checks that the header correctly builds on previous_header: the claimed work differential
113         /// matches the actual PoW and the difficulty transition is possible, i.e., within 4x.
114         fn check_builds_on(&self, previous_header: &ValidatedBlockHeader, network: Network) -> BlockSourceResult<()> {
115                 if self.header.prev_blockhash != previous_header.block_hash {
116                         return Err(BlockSourceError::persistent("invalid previous block hash"));
117                 }
118
119                 if self.height != previous_header.height + 1 {
120                         return Err(BlockSourceError::persistent("invalid block height"));
121                 }
122
123                 let work = self.header.work();
124                 if self.chainwork != previous_header.chainwork + work {
125                         return Err(BlockSourceError::persistent("invalid chainwork"));
126                 }
127
128                 if let Network::Bitcoin = network {
129                         if self.height % 2016 == 0 {
130                                 let previous_work = previous_header.header.work();
131                                 if work > (previous_work << 2) || work < (previous_work >> 2) {
132                                         return Err(BlockSourceError::persistent("invalid difficulty transition"))
133                                 }
134                         } else if self.header.bits != previous_header.header.bits {
135                                 return Err(BlockSourceError::persistent("invalid difficulty"))
136                         }
137                 }
138
139                 Ok(())
140         }
141 }
142
143 /// A block with validated data against its transaction list and corresponding block hash.
144 pub struct ValidatedBlock {
145         pub(crate) block_hash: BlockHash,
146         inner: Block,
147 }
148
149 impl std::ops::Deref for ValidatedBlock {
150         type Target = Block;
151
152         fn deref(&self) -> &Self::Target {
153                 &self.inner
154         }
155 }
156
157 /// The canonical `Poll` implementation used for a single `BlockSource`.
158 ///
159 /// Other `Poll` implementations must be built using `ChainPoller` as it provides the only means of
160 /// validating chain data.
161 pub struct ChainPoller<B: DerefMut<Target=T> + Sized + Sync + Send, T: BlockSource> {
162         block_source: B,
163         network: Network,
164 }
165
166 impl<B: DerefMut<Target=T> + Sized + Sync + Send, T: BlockSource> ChainPoller<B, T> {
167         /// Creates a new poller for the given block source.
168         ///
169         /// If the `network` parameter is mainnet, then the difficulty between blocks is checked for
170         /// validity.
171         pub fn new(block_source: B, network: Network) -> Self {
172                 Self { block_source, network }
173         }
174 }
175
176 impl<B: DerefMut<Target=T> + Sized + Sync + Send, T: BlockSource> Poll for ChainPoller<B, T> {
177         fn poll_chain_tip<'a>(&'a mut self, best_known_chain_tip: ValidatedBlockHeader) ->
178                 AsyncBlockSourceResult<'a, ChainTip>
179         {
180                 Box::pin(async move {
181                         let (block_hash, height) = self.block_source.get_best_block().await?;
182                         if block_hash == best_known_chain_tip.header.block_hash() {
183                                 return Ok(ChainTip::Common);
184                         }
185
186                         let chain_tip = self.block_source
187                                 .get_header(&block_hash, height).await?
188                                 .validate(block_hash)?;
189                         if chain_tip.chainwork > best_known_chain_tip.chainwork {
190                                 Ok(ChainTip::Better(chain_tip))
191                         } else {
192                                 Ok(ChainTip::Worse(chain_tip))
193                         }
194                 })
195         }
196
197         fn look_up_previous_header<'a>(&'a mut self, header: &'a ValidatedBlockHeader) ->
198                 AsyncBlockSourceResult<'a, ValidatedBlockHeader>
199         {
200                 Box::pin(async move {
201                         if header.height == 0 {
202                                 return Err(BlockSourceError::persistent("genesis block reached"));
203                         }
204
205                         let previous_hash = &header.header.prev_blockhash;
206                         let height = header.height - 1;
207                         let previous_header = self.block_source
208                                 .get_header(previous_hash, Some(height)).await?
209                                 .validate(*previous_hash)?;
210                         header.check_builds_on(&previous_header, self.network)?;
211
212                         Ok(previous_header)
213                 })
214         }
215
216         fn fetch_block<'a>(&'a mut self, header: &'a ValidatedBlockHeader) ->
217                 AsyncBlockSourceResult<'a, ValidatedBlock>
218         {
219                 Box::pin(async move {
220                         self.block_source
221                                 .get_block(&header.block_hash).await?
222                                 .validate(header.block_hash)
223                 })
224         }
225 }
226
227 #[cfg(test)]
228 mod tests {
229         use crate::*;
230         use crate::test_utils::Blockchain;
231         use super::*;
232         use bitcoin::util::uint::Uint256;
233
234         #[tokio::test]
235         async fn poll_empty_chain() {
236                 let mut chain = Blockchain::default().with_height(0);
237                 let best_known_chain_tip = chain.tip();
238                 chain.disconnect_tip();
239
240                 let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
241                 match poller.poll_chain_tip(best_known_chain_tip).await {
242                         Err(e) => {
243                                 assert_eq!(e.kind(), BlockSourceErrorKind::Transient);
244                                 assert_eq!(e.into_inner().as_ref().to_string(), "empty chain");
245                         },
246                         Ok(_) => panic!("Expected error"),
247                 }
248         }
249
250         #[tokio::test]
251         async fn poll_chain_without_headers() {
252                 let mut chain = Blockchain::default().with_height(1).without_headers();
253                 let best_known_chain_tip = chain.at_height(0);
254
255                 let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
256                 match poller.poll_chain_tip(best_known_chain_tip).await {
257                         Err(e) => {
258                                 assert_eq!(e.kind(), BlockSourceErrorKind::Persistent);
259                                 assert_eq!(e.into_inner().as_ref().to_string(), "header not found");
260                         },
261                         Ok(_) => panic!("Expected error"),
262                 }
263         }
264
265         #[tokio::test]
266         async fn poll_chain_with_invalid_pow() {
267                 let mut chain = Blockchain::default().with_height(1);
268                 let best_known_chain_tip = chain.at_height(0);
269
270                 // Invalidate the tip by changing its target.
271                 chain.blocks.last_mut().unwrap().header.bits =
272                         BlockHeader::compact_target_from_u256(&Uint256::from_be_bytes([0; 32]));
273
274                 let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
275                 match poller.poll_chain_tip(best_known_chain_tip).await {
276                         Err(e) => {
277                                 assert_eq!(e.kind(), BlockSourceErrorKind::Persistent);
278                                 assert_eq!(e.into_inner().as_ref().to_string(), "block target correct but not attained");
279                         },
280                         Ok(_) => panic!("Expected error"),
281                 }
282         }
283
284         #[tokio::test]
285         async fn poll_chain_with_malformed_headers() {
286                 let mut chain = Blockchain::default().with_height(1).malformed_headers();
287                 let best_known_chain_tip = chain.at_height(0);
288
289                 let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
290                 match poller.poll_chain_tip(best_known_chain_tip).await {
291                         Err(e) => {
292                                 assert_eq!(e.kind(), BlockSourceErrorKind::Persistent);
293                                 assert_eq!(e.into_inner().as_ref().to_string(), "invalid block hash");
294                         },
295                         Ok(_) => panic!("Expected error"),
296                 }
297         }
298
299         #[tokio::test]
300         async fn poll_chain_with_common_tip() {
301                 let mut chain = Blockchain::default().with_height(0);
302                 let best_known_chain_tip = chain.tip();
303
304                 let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
305                 match poller.poll_chain_tip(best_known_chain_tip).await {
306                         Err(e) => panic!("Unexpected error: {:?}", e),
307                         Ok(tip) => assert_eq!(tip, ChainTip::Common),
308                 }
309         }
310
311         #[tokio::test]
312         async fn poll_chain_with_uncommon_tip_but_equal_chainwork() {
313                 let mut chain = Blockchain::default().with_height(1);
314                 let best_known_chain_tip = chain.tip();
315
316                 // Change the nonce to get a different block hash with the same chainwork.
317                 chain.blocks.last_mut().unwrap().header.nonce += 1;
318                 let worse_chain_tip = chain.tip();
319                 assert_eq!(best_known_chain_tip.chainwork, worse_chain_tip.chainwork);
320
321                 let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
322                 match poller.poll_chain_tip(best_known_chain_tip).await {
323                         Err(e) => panic!("Unexpected error: {:?}", e),
324                         Ok(tip) => assert_eq!(tip, ChainTip::Worse(worse_chain_tip)),
325                 }
326         }
327
328         #[tokio::test]
329         async fn poll_chain_with_worse_tip() {
330                 let mut chain = Blockchain::default().with_height(1);
331                 let best_known_chain_tip = chain.tip();
332
333                 chain.disconnect_tip();
334                 let worse_chain_tip = chain.tip();
335
336                 let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
337                 match poller.poll_chain_tip(best_known_chain_tip).await {
338                         Err(e) => panic!("Unexpected error: {:?}", e),
339                         Ok(tip) => assert_eq!(tip, ChainTip::Worse(worse_chain_tip)),
340                 }
341         }
342
343         #[tokio::test]
344         async fn poll_chain_with_better_tip() {
345                 let mut chain = Blockchain::default().with_height(1);
346                 let best_known_chain_tip = chain.at_height(0);
347
348                 let better_chain_tip = chain.tip();
349
350                 let mut poller = ChainPoller::new(&mut chain, Network::Bitcoin);
351                 match poller.poll_chain_tip(best_known_chain_tip).await {
352                         Err(e) => panic!("Unexpected error: {:?}", e),
353                         Ok(tip) => assert_eq!(tip, ChainTip::Better(better_chain_tip)),
354                 }
355         }
356 }