nakamoto_common/block/
tree.rs1#![warn(missing_docs)]
3use std::collections::BTreeMap;
4
5use bitcoin::blockdata::block::BlockHeader;
6use bitcoin::consensus::params::Params;
7use bitcoin::hash_types::BlockHash;
8
9use thiserror::Error;
10
11use crate::block::store;
12use crate::block::time::Clock;
13use crate::block::{Bits, BlockTime, Height, Target, Work};
14use crate::nonempty::NonEmpty;
15
16#[derive(Debug, Error)]
18pub enum Error {
19 #[error("invalid block proof-of-work")]
21 InvalidBlockPoW,
22
23 #[error("invalid block difficulty target: {0}, expected {1}")]
25 InvalidBlockTarget(Target, Target),
26
27 #[error("invalid checkpoint block hash {0} at height {1}")]
29 InvalidBlockHash(BlockHash, Height),
30
31 #[error("block height {0} is prior to last checkpoint")]
33 InvalidBlockHeight(Height),
34
35 #[error("block timestamp {0} is invalid")]
37 InvalidBlockTime(BlockTime, std::cmp::Ordering),
38
39 #[error("duplicate block {0}")]
41 DuplicateBlock(BlockHash),
42
43 #[error("block missing: {0}")]
45 BlockMissing(BlockHash),
46
47 #[error("block import aborted at height {2}: {0} ({1} block(s) imported)")]
49 BlockImportAborted(Box<Self>, usize, Height),
50
51 #[error("stored genesis header doesn't match network genesis")]
53 GenesisMismatch,
54
55 #[error("storage error: {0}")]
57 Store(#[from] store::Error),
58
59 #[error("the operation was interrupted")]
61 Interrupted,
62}
63
64pub trait Header {
66 fn work(&self) -> Work;
68}
69
70impl Header for BlockHeader {
71 fn work(&self) -> Work {
72 self.work()
73 }
74}
75
76#[allow(clippy::large_enum_variant)]
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub enum ImportResult {
80 TipChanged(
86 BlockHeader,
87 BlockHash,
88 Height,
89 Vec<(Height, BlockHeader)>,
90 NonEmpty<(Height, BlockHeader)>,
91 ),
92 TipUnchanged, }
96
97#[derive(Debug, Clone)]
99pub struct Branch<'a, H: Header>(pub &'a [H]);
100
101impl<'a, H: Header> Branch<'a, H> {
102 pub fn work(&self) -> Work {
104 let mut work = Work::default();
105 for header in self.0.iter() {
106 work = work + header.work();
107 }
108 work
109 }
110}
111
112pub trait BlockTree: BlockReader {
114 fn import_blocks<I: Iterator<Item = BlockHeader>, C: Clock>(
116 &mut self,
117 chain: I,
118 context: &C,
119 ) -> Result<ImportResult, Error>;
120 fn extend_tip<C: Clock>(
123 &mut self,
124 header: BlockHeader,
125 context: &C,
126 ) -> Result<ImportResult, Error>;
127}
128
129pub trait BlockReader {
131 fn get_block(&self, hash: &BlockHash) -> Option<(Height, &BlockHeader)>;
133 fn get_block_by_height(&self, height: Height) -> Option<&BlockHeader>;
135 fn find_branch(&self, to: &BlockHash) -> Option<(Height, NonEmpty<BlockHeader>)>;
142 fn chain<'a>(&'a self) -> Box<dyn Iterator<Item = BlockHeader> + 'a> {
144 Box::new(self.iter().map(|(_, h)| h))
145 }
146 fn iter<'a>(&'a self) -> Box<dyn DoubleEndedIterator<Item = (Height, BlockHeader)> + 'a>;
148 fn range<'a>(
150 &'a self,
151 range: std::ops::Range<Height>,
152 ) -> Box<dyn Iterator<Item = (Height, BlockHash)> + 'a> {
153 Box::new(
154 self.iter()
155 .map(|(height, header)| (height, header.block_hash()))
156 .skip(range.start as usize)
157 .take((range.end - range.start) as usize),
158 )
159 }
160 fn height(&self) -> Height;
162 fn tip(&self) -> (BlockHash, BlockHeader);
164 fn best_block(&self) -> (Height, &BlockHeader) {
166 let height = self.height();
167 (
168 height,
169 self.get_block_by_height(height)
170 .expect("the best block is always present"),
171 )
172 }
173 fn last_checkpoint(&self) -> Height;
175 fn checkpoints(&self) -> BTreeMap<Height, BlockHash>;
177 fn genesis(&self) -> &BlockHeader {
179 self.get_block_by_height(0)
180 .expect("the genesis block is always present")
181 }
182 fn is_known(&self, hash: &BlockHash) -> bool;
184 fn contains(&self, hash: &BlockHash) -> bool;
186 fn locate_headers(
188 &self,
189 locators: &[BlockHash],
190 stop_hash: BlockHash,
191 max_headers: usize,
192 ) -> Vec<BlockHeader>;
193 fn locator_hashes(&self, from: Height) -> Vec<BlockHash>;
195 fn next_difficulty_target(
197 &self,
198 last_height: Height,
199 last_time: BlockTime,
200 last_target: Target,
201 params: &Params,
202 ) -> Bits {
203 if (last_height + 1) % params.difficulty_adjustment_interval() != 0 {
206 return BlockHeader::compact_target_from_u256(&last_target);
207 }
208
209 let last_adjustment_height =
210 last_height.saturating_sub(params.difficulty_adjustment_interval() - 1);
211 let last_adjustment_block = self
212 .get_block_by_height(last_adjustment_height)
213 .unwrap_or_else(|| self.genesis());
214 let last_adjustment_time = last_adjustment_block.time;
215
216 if params.no_pow_retargeting {
217 return last_adjustment_block.bits;
218 }
219
220 let actual_timespan = last_time - last_adjustment_time;
221 let mut adjusted_timespan = actual_timespan;
222
223 if actual_timespan < params.pow_target_timespan as BlockTime / 4 {
224 adjusted_timespan = params.pow_target_timespan as BlockTime / 4;
225 } else if actual_timespan > params.pow_target_timespan as BlockTime * 4 {
226 adjusted_timespan = params.pow_target_timespan as BlockTime * 4;
227 }
228
229 let mut target = last_target;
230
231 target = target.mul_u32(adjusted_timespan);
232 target = target / Target::from_u64(params.pow_target_timespan).unwrap();
233
234 if target > params.pow_limit {
236 target = params.pow_limit;
237 }
238
239 BlockHeader::compact_target_from_u256(&target)
240 }
241}