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