urma_runtime/p2p/
bootstrap.rs1use super::headers::prove_batch;
2use crate::config::P2P_MEDIAN_TIME_SPAN;
3use crate::error::{Error, ensure};
4use bitcoin::block::Header;
5use urma_chain::observation::Chain;
6
7#[derive(Clone, Debug)]
17pub struct HeaderBootstrap {
18 chain: Chain,
19 height: u64,
20 header: Header,
21 ancestors: Vec<Header>,
22}
23
24impl HeaderBootstrap {
25 pub fn new(
29 chain: Chain,
30 height: u64,
31 header: Header,
32 ancestors: Vec<Header>,
33 ) -> Result<Self, Error> {
34 let count = context_headers(chain);
35 let expected = usize::try_from(height.min(u64::try_from(count)?))?;
36 ensure!(
37 ancestors.len() == expected,
38 "bootstrap needs exactly {expected} ancestors"
39 );
40 let oldest = ancestors.first().unwrap_or(&header);
41 if height == u64::try_from(ancestors.len())? {
42 ensure!(
43 oldest.block_hash() == chain.genesis()?.0,
44 "bootstrap prefix does not start at the configured chain genesis"
45 );
46 }
47 for (previous, next) in ancestors
48 .iter()
49 .zip(ancestors.iter().skip(1).chain(std::iter::once(&header)))
50 {
51 ensure!(
52 next.prev_blockhash == previous.block_hash(),
53 "bootstrap ancestors do not connect to the trusted anchor"
54 );
55 }
56 prove_batch(chain, &ancestors)?;
57 prove_batch(chain, std::slice::from_ref(&header))?;
58 Ok(Self {
59 chain,
60 height,
61 header,
62 ancestors,
63 })
64 }
65
66 pub fn chain(&self) -> Chain {
67 self.chain
68 }
69 pub fn height(&self) -> u64 {
70 self.height
71 }
72 pub fn header(&self) -> &Header {
73 &self.header
74 }
75 pub fn ancestors(&self) -> &[Header] {
76 &self.ancestors
77 }
78
79 pub(super) fn into_parts(self) -> (Chain, u64, Header, Vec<Header>) {
80 (self.chain, self.height, self.header, self.ancestors)
81 }
82}
83
84pub fn context_headers(chain: Chain) -> usize {
87 chain
91 .params()
92 .retarget_lookback
93 .max(chain.params().retarget_interval - 1)
94 .max((P2P_MEDIAN_TIME_SPAN - 1) as u64) as usize
95}