Add a Headers-over-DNS client to lightning-block-sync.
[rust-lightning] / lightning-block-sync / src / dns_headers.rs
1 //! A headers-only BlockSource which fetches headers by doing AAAA DNS queries. This should provide
2 //! some additional robustness by being easy to tunnel over other protocols (eg DNS-over-TLS or
3 //! DNS-over-HTTPS) and simply by nature of being a protocol which is not Bitcoin-specific.
4 //!
5 //! 80-byte Bitcoin headers are encoded in six IPv6 addresses as follows:
6 //! The first two bytes of each address are ignored.
7 //! The next 4 bits in each address indicate the ordering of the addresses
8 //! (as DNS resolvers/servers often shuffle the addresses)
9 //! The first 8 bits (ie the second half of the 3rd byte and first half of the 4th)
10 //! of the first address are interpreted as a version and must currently be 0.
11 //! The remaining bits are placed into the 80 byte result in order.
12 //!
13 //! Hostnames are height.(height / 10,000).domain_suffix to keep zones at a more manageable size.
14 //! bitcoinheaders.net can be used as domain_suffic to get a public copy of the header chain.
15
16 use crate::{BlockHeaderData, BlockSource, BlockSourceRespErr};
17
18 use bitcoin::hash_types::BlockHash;
19 use bitcoin::util::hash::BitcoinHash;
20 use bitcoin::util::uint::Uint256;
21
22 use bitcoin::blockdata::block::{Block, BlockHeader};
23 use bitcoin::consensus::encode;
24
25 use std::future::Future;
26 use std::pin::Pin;
27 use std::net::{IpAddr, Ipv6Addr};
28
29 #[cfg(not(feature = "tokio"))]
30 use std::net::ToSocketAddrs;
31
32 /// A trait for a barebones version of a BlockSource which only allows queries from height to
33 /// header hash, eg our headers-over-DNS protocol.
34 pub trait SimpleHeadersClient {
35         /// Gets the header at a given height
36         fn get_header<'a>(&'a self, height: u32) -> Pin<Box<dyn Future<Output = Result<BlockHeader, BlockSourceRespErr>> + 'a + Send>>;
37 }
38 /// Adapts a SimpleHeadersClient to a BlockSource (which always returns NoResponse for full block
39 /// requests) by caching headers on difficulty adjustments and a few recent headers.
40 ///
41 /// Caching should be under 20KB for a million headers on mainnet (though note that every block is
42 /// a difficulty adjustment block on testnet so the size is much larger).
43 pub struct CachingHeadersClient<C: SimpleHeadersClient> {
44         /// We track only the headers which fall on a retarget, storing only their hash and nBits field
45         /// so that we can recalculate the chainwork from any point.
46         retarget_headers: Vec<(BlockHash, u32)>,
47         /// Cache the last 6 full block headers and their hashes since almost all requests should just
48         /// be for the latest one or two.
49         recent_headers: [Option<(BlockHash, BlockHeaderData)>; 6],
50         mainnet: bool,
51         inner: C,
52 }
53 impl<C: SimpleHeadersClient + Send + Sync> CachingHeadersClient<C> {
54         /// Creates a new CachingHeadersClient with the given SimpleHeadersClient and a flag to
55         /// indicate whether reargets happen every 2016 blocks, or every block (eg on testnet3).
56         pub fn new(inner: C, mainnet: bool) -> Self {
57                 Self {
58                         retarget_headers: Vec::new(),
59                         recent_headers: [None, None, None, None, None, None],
60                         mainnet, inner
61                 }
62         }
63
64         fn calculate_chainwork_to(&self, height: u32) -> Uint256 {
65                 let interval = if self.mainnet { 2016usize } else { 1 };
66                 if (height as usize) < interval {
67                         return Uint256::from_u64(4295032833 * height as u64).unwrap();
68                 }
69                 // First difficulty adjustment period is always:
70                 let mut chainwork = Uint256::from_u64(4295032833 * 2015).unwrap();
71                 for (_, bits) in self.retarget_headers.iter().take((height as usize / interval) - 1) {
72                         chainwork = chainwork + BlockHeader { version: 0, prev_blockhash: Default::default(), merkle_root: Default::default(),
73                                         time: 0, bits: *bits, nonce: 0 }
74                                 .target().mul_u32(interval as u32);
75                 }
76                 chainwork = chainwork + BlockHeader { version: 0, prev_blockhash: Default::default(), merkle_root: Default::default(),
77                                 time: 0, bits: self.retarget_headers[(height as usize / interval) - 1].1, nonce: 0 }
78                         .target().mul_u32((height % interval as u32) + 1);
79                 chainwork
80         }
81
82         /// Ask inner for the header at the given height, updating relevant caches
83         fn fetch_header_at_height<'a>(&'a mut self, height: u32) -> Pin<Box<dyn Future<Output = Result<(BlockHash, BlockHeaderData), BlockSourceRespErr>> + 'a + Send>> {
84                 Box::pin(async move {
85                         let header = self.inner.get_header(height).await?;
86                         let hash = header.bitcoin_hash();
87
88                         let interval = if self.mainnet { 2016usize } else { 1 };
89                         // Make sure our retarget_headers is filled appropriately:
90                         if height % interval as u32 == 0 {
91                                 // This block has a fresh nBits that the previous did not, update it in
92                                 // retarget_headers (noting that we have a strange - 1 in a few places as we do
93                                 // not store the genesis header, and thus the first header in retarget_headers
94                                 // is block 2016 stored at pos 0).
95                                 if self.retarget_headers.len() < (height as usize / interval) - 1 {
96                                         self.fetch_header_at_height((height - 1) / interval as u32 * interval as u32).await?;
97                                 }
98                                 if self.retarget_headers.len() == (height as usize / interval) - 1 || self.retarget_headers[height as usize / interval - 1].0 != hash {
99                                         self.retarget_headers.resize(height as usize / interval, (hash, header.bits));
100                                         self.retarget_headers[height as usize / interval - 1] = (hash, header.bits);
101                                 }
102                         } else if height as usize > interval && self.retarget_headers.len() < (height as usize / interval) {
103                                 self.fetch_header_at_height(height / interval as u32 * interval as u32).await?;
104                         }
105
106                         let chainwork = if self.recent_headers[(height as usize - 1) % self.recent_headers.len()]
107                                         .as_ref().map(|(hash, _)| hash) == Some(&header.prev_blockhash) {
108                                 // Prev matches our prev hash, use it to calculate chainwork and return!
109                                 self.recent_headers[(height as usize - 1) % self.recent_headers.len()]
110                                         .as_ref().unwrap().1.chainwork + header.work()
111                         } else if height as usize > interval {
112                                 // Prev is unrelated to current, fetch the last difficulty adjustment block
113                                 // and recalculate the chainwork.
114                                 self.fetch_header_at_height((height - 1) / interval as u32 * interval as u32).await?;
115                                 self.calculate_chainwork_to(height)
116                         } else {
117                                 // Prev is unrelated to current but we're in the first difficulty window,
118                                 // so diff must be 1!
119                                 Uint256::from_u64(4295032833 * height as u64).unwrap()
120                         };
121                         let headerdata = BlockHeaderData { chainwork, height, header };
122                         self.recent_headers[height as usize % self.recent_headers.len()] = Some((hash, headerdata.clone()));
123                         Ok((hash, headerdata))
124                 })
125         }
126
127         /// Load all available difficulty adjustment blocks
128         async fn sync_diff_adjustments(&mut self) -> Result<(), BlockSourceRespErr> {
129                 // Load blocks jumping up 2016 at a time to get the retarget blocks:
130                 let interval = if self.mainnet { 2016 } else { 1 };
131                 let mut height = interval;
132                 loop {
133                         match self.fetch_header_at_height(height).await {
134                                 Ok(_) => height += interval,
135                                 Err(BlockSourceRespErr::NoResponse) => return Ok(()),
136                                 Err(BlockSourceRespErr::BogusData) => return Err(BlockSourceRespErr::BogusData),
137                         }
138                 }
139         }
140 }
141
142 impl<C: SimpleHeadersClient + Send + Sync + 'static> BlockSource for CachingHeadersClient<C> {
143         fn get_header<'a>(&'a mut self, header_hash: &'a BlockHash, height_hint: Option<u32>) -> Pin<Box<dyn Future<Output = Result<BlockHeaderData, BlockSourceRespErr>> + 'a + Send>> {
144                 Box::pin(async move {
145                         if let Some(height) = height_hint {
146                                 if let &Some((ref hash, ref header)) = &self.recent_headers[height as usize % self.recent_headers.len()] {
147                                         if hash == header_hash {
148                                                 return Ok(header.clone());
149                                         }
150                                 }
151                                 let (hash, header) = self.fetch_header_at_height(height).await?;
152                                 if hash == *header_hash {
153                                         Ok(header)
154                                 } else {
155                                         Err(BlockSourceRespErr::NoResponse)
156                                 }
157                         } else {
158                                 Err(BlockSourceRespErr::NoResponse)
159                         }
160                 })
161         }
162
163         fn get_block<'a>(&'a mut self, _header_hash: &'a BlockHash) -> Pin<Box<dyn Future<Output = Result<Block, BlockSourceRespErr>> + 'a + Send>> {
164                 Box::pin(async {
165                         Err(BlockSourceRespErr::NoResponse)
166                 })
167         }
168
169         fn get_best_block<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(BlockHash, Option<u32>), BlockSourceRespErr>> + 'a + Send>> {
170                 Box::pin(async move {
171                         let mut highest_height = 0;
172                         for hdr in self.recent_headers.iter() {
173                                 if let Some(header) = hdr {
174                                         if header.1.height > highest_height {
175                                                 highest_height = header.1.height;
176                                         }
177                                 }
178                         }
179                         if highest_height == 0 {
180                                 // If we're just starting, load the difficulty adjustment blocks to get us near the
181                                 // tip, then check for the highest header again:
182                                 self.sync_diff_adjustments().await?;
183                                 for hdr in self.recent_headers.iter() {
184                                         if let Some(header) = hdr {
185                                                 if header.1.height > highest_height {
186                                                         highest_height = header.1.height;
187                                                 }
188                                         }
189                                 }
190                                 if highest_height == 0 {
191                                         return Err(BlockSourceRespErr::NoResponse);
192                                 }
193                         }
194
195                         // We only care about finding the highest header, so walk forward until we get
196                         // NoResponse:
197                         let mut res = None;
198                         loop {
199                                 match self.fetch_header_at_height(highest_height + 1).await {
200                                         Ok((hash, _)) => {
201                                                 highest_height += 1;
202                                                 res = Some((hash, Some(highest_height)));
203                                         },
204                                         Err(BlockSourceRespErr::NoResponse) => {
205                                                 if res.is_some() {
206                                                         // We previously got a response. Most likely we just found the tip and
207                                                         // should return it.
208                                                         return Ok(res.unwrap());
209                                                 } else {
210                                                         // Probably no new headers, check the current tip is the same and
211                                                         // return. If we get another NoResponse just give up - while its
212                                                         // possible there was a reorg to a lower height, this probably requires
213                                                         // a multi-block reorg around a retarget with a huge difference in
214                                                         // timestamps between forks....so we can just wait for another block to
215                                                         // be mined.
216                                                         return self.fetch_header_at_height(highest_height).await.map(|(hash, _)| (hash, Some(highest_height)));
217                                                 }
218                                         },
219                                         Err(BlockSourceRespErr::BogusData) => return Err(BlockSourceRespErr::BogusData),
220                                 }
221                         }
222                 })
223         }
224 }
225
226 /// A client which fetches headers over DNS from a specific provider, implementing
227 /// SimpleHeadersClient. You probably want to create one of these and then wrap it in a
228 /// CachingHeadersClient.
229 pub struct DNSHeadersClient {
230         domain_str: String,
231 }
232
233 impl DNSHeadersClient {
234         /// Creates a new DNSHeadersClient which fetches headers by doing AAAA (IPv6) DNS queries to
235         /// prefixes on a given hostname (see the module documentation for info on the exact format).
236         pub fn new(domain_str: String) -> Self {
237                 Self { domain_str }
238         }
239 }
240
241 fn map_addrs_to_header(ips: &mut [Ipv6Addr]) -> Option<[u8; 80]> {
242         if ips.len() != 6 { return None; }
243         ips.sort_unstable_by(|a, b| {
244                 // Sort based on the first 4 bits in the 3rd byte...
245                 (&(a.octets()[2] & 0xf0)).cmp(&(b.octets()[2] & 0xf0))
246         });
247         if ips.len() != 6 { unreachable!(); }
248         let version = (ips[0].octets()[2] & 0x0f) | (ips[0].octets()[3] & 0xf0);
249         if version != 0 { return None; }
250
251         let mut header = [0u8; 80];
252         let mut offs = 0; // in bytes * 2
253         for (idx, ip) in ips.iter().enumerate() {
254                 for i in if idx == 0 { 3..14*2 } else { 1..14*2 } {
255                         if i % 2 == 1 {
256                                 header[offs/2] |= (ip.octets()[i/2 + 2] & 0x0f) >> 0;
257                         } else {
258                                 header[offs/2] |= (ip.octets()[i/2 + 2] & 0xf0) >> 4;
259                         }
260                         if offs % 2 == 0 {
261                                 header[offs/2] <<= 4;
262                         }
263                         offs += 1;
264                 }
265         }
266         Some(header)
267 }
268
269 impl SimpleHeadersClient for DNSHeadersClient {
270         fn get_header<'a>(&'a self, height: u32) -> Pin<Box<dyn Future<Output = Result<BlockHeader, BlockSourceRespErr>> + 'a + Send>> {
271                 Box::pin(async move {
272                         let domain = format!("{}.{}.{}", height, height / 10000, self.domain_str);
273                         #[cfg(not(feature = "tokio"))]
274                         let lookup_res = (domain.as_str(), 0u16).to_socket_addrs();
275                         #[cfg(feature = "tokio")]
276                         let lookup_res = tokio::net::lookup_host((domain.as_str(), 0u16)).await;
277                         let mut ips: Vec<_> = lookup_res.map_err(|_| BlockSourceRespErr::NoResponse)?
278                                 .filter_map(|a| match a.ip() {
279                                         IpAddr::V6(a) => Some(a),
280                                         _ => None,
281                                 }).collect();
282                         if ips.len() != 6 {
283                                 return Err(BlockSourceRespErr::NoResponse);
284                         }
285                         let data = map_addrs_to_header(&mut ips).ok_or(BlockSourceRespErr::NoResponse)?;
286                         let header: BlockHeader = encode::deserialize(&data).map_err(|_| BlockSourceRespErr::NoResponse)?;
287                         Ok(header)
288                 })
289         }
290 }
291
292 #[cfg(test)]
293 #[test]
294 fn test_map_addrs() {
295         use std::str::FromStr;
296
297         let mut ips = Vec::new();
298         // The genesis header:
299         ips.push(Ipv6Addr::from_str("2001:0000:1000:0000:0000:0000:0000:0000").unwrap());
300         ips.push(Ipv6Addr::from_str("2001:1000:0000:0000:0000:0000:0000:0000").unwrap());
301         ips.push(Ipv6Addr::from_str("2001:2000:0000:0000:0000:0000:03ba:3edf").unwrap());
302         ips.push(Ipv6Addr::from_str("2001:3d7a:7b12:b27a:c72c:3e67:768f:617f").unwrap());
303         ips.push(Ipv6Addr::from_str("2001:4c81:bc38:88a5:1323:a9fb:8aa4:b1e5").unwrap());
304         ips.push(Ipv6Addr::from_str("2001:5e4a:29ab:5f49:ffff:001d:1dac:2b7c").unwrap());
305
306         assert_eq!(&map_addrs_to_header(&mut ips).unwrap()[..],
307                 &[0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2, 0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61, 0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32, 0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a, 0x29, 0xab, 0x5f, 0x49, 0xff, 0xff, 0x0, 0x1d, 0x1d, 0xac, 0x2b, 0x7c][..]);
308
309         // Block 100,000
310         ips.clear();
311         ips.push(Ipv6Addr::from_str("2001:2cc1:f1cd:20::665:7a92").unwrap());
312         ips.push(Ipv6Addr::from_str("2001:352a:acd5:c0b2:9409:96ec:ff95:2228").unwrap());
313         ips.push(Ipv6Addr::from_str("2001:4c30:67cc:38d4:885e:fb5a:4ac4:247e").unwrap());
314         ips.push(Ipv6Addr::from_str("2001:0:1000:5:120:1191:72a6:1042").unwrap());
315         ips.push(Ipv6Addr::from_str("2001:11a6:c301:1dd3:30d9:df07:b636:16c2").unwrap());
316         ips.push(Ipv6Addr::from_str("2001:59f3:3722:1b4d:4c86:41b:f2b:5710").unwrap());
317
318         assert_eq!(&map_addrs_to_header(&mut ips).unwrap()[..],
319                 &[0x01, 0x00, 0x00, 0x00, 0x50, 0x12, 0x01, 0x19, 0x17, 0x2a, 0x61, 0x04, 0x21, 0xa6, 0xc3, 0x01, 0x1d, 0xd3, 0x30, 0xd9, 0xdf, 0x07, 0xb6, 0x36, 0x16, 0xc2, 0xcc, 0x1f, 0x1c, 0xd0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x57, 0xa9, 0x25, 0x2a, 0xac, 0xd5, 0xc0, 0xb2, 0x94, 0x09, 0x96, 0xec, 0xff, 0x95, 0x22, 0x28, 0xc3, 0x06, 0x7c, 0xc3, 0x8d, 0x48, 0x85, 0xef, 0xb5, 0xa4, 0xac, 0x42, 0x47, 0xe9, 0xf3, 0x37, 0x22, 0x1b, 0x4d, 0x4c, 0x86, 0x04, 0x1b, 0x0f, 0x2b, 0x57, 0x10][..]);
320 }