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.
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.
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.
16 use crate::{BlockHeaderData, BlockSource, BlockSourceRespErr};
18 use bitcoin::hash_types::BlockHash;
19 use bitcoin::util::hash::BitcoinHash;
20 use bitcoin::util::uint::Uint256;
22 use bitcoin::blockdata::block::{Block, BlockHeader};
23 use bitcoin::consensus::encode;
25 use std::future::Future;
27 use std::net::{IpAddr, Ipv6Addr};
29 #[cfg(not(feature = "tokio"))]
30 use std::net::ToSocketAddrs;
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>>;
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.
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],
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 {
58 retarget_headers: Vec::new(),
59 recent_headers: [None, None, None, None, None, None],
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();
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);
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);
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>> {
85 let header = self.inner.get_header(height).await?;
86 let hash = header.bitcoin_hash();
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?;
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);
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?;
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)
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()
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))
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;
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),
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());
151 let (hash, header) = self.fetch_header_at_height(height).await?;
152 if hash == *header_hash {
155 Err(BlockSourceRespErr::NoResponse)
158 Err(BlockSourceRespErr::NoResponse)
163 fn get_block<'a>(&'a mut self, _header_hash: &'a BlockHash) -> Pin<Box<dyn Future<Output = Result<Block, BlockSourceRespErr>> + 'a + Send>> {
165 Err(BlockSourceRespErr::NoResponse)
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;
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;
190 if highest_height == 0 {
191 return Err(BlockSourceRespErr::NoResponse);
195 // We only care about finding the highest header, so walk forward until we get
199 match self.fetch_header_at_height(highest_height + 1).await {
202 res = Some((hash, Some(highest_height)));
204 Err(BlockSourceRespErr::NoResponse) => {
206 // We previously got a response. Most likely we just found the tip and
208 return Ok(res.unwrap());
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
216 return self.fetch_header_at_height(highest_height).await.map(|(hash, _)| (hash, Some(highest_height)));
219 Err(BlockSourceRespErr::BogusData) => return Err(BlockSourceRespErr::BogusData),
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 {
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 {
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))
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; }
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 } {
256 header[offs/2] |= (ip.octets()[i/2 + 2] & 0x0f) >> 0;
258 header[offs/2] |= (ip.octets()[i/2 + 2] & 0xf0) >> 4;
261 header[offs/2] <<= 4;
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),
283 return Err(BlockSourceRespErr::NoResponse);
285 let data = map_addrs_to_header(&mut ips).ok_or(BlockSourceRespErr::NoResponse)?;
286 let header: BlockHeader = encode::deserialize(&data).map_err(|_| BlockSourceRespErr::NoResponse)?;
294 fn test_map_addrs() {
295 use std::str::FromStr;
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());
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][..]);
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());
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][..]);