|
| 1 | +use crate::{AsyncBlockSourceResult, BlockHeaderData, BlockSourceError, BlockSourceResult}; |
| 2 | + |
| 3 | +use bitcoin::blockdata::block::Block; |
| 4 | +use bitcoin::hash_types::BlockHash; |
| 5 | +use bitcoin::network::constants::Network; |
| 6 | + |
| 7 | +/// The `Poll` trait defines behavior for polling block sources for a chain tip and retrieving |
| 8 | +/// related chain data. It serves as an adapter for `BlockSource`. |
| 9 | +pub trait Poll { |
| 10 | + /// Returns a chain tip in terms of its relationship to the provided chain tip. |
| 11 | + fn poll_chain_tip<'a>(&'a mut self, best_known_chain_tip: ValidatedBlockHeader) -> |
| 12 | + AsyncBlockSourceResult<'a, ChainTip>; |
| 13 | + |
| 14 | + /// Returns the header that preceded the given header in the chain. |
| 15 | + fn look_up_previous_header<'a>(&'a mut self, header: &'a ValidatedBlockHeader) -> |
| 16 | + AsyncBlockSourceResult<'a, ValidatedBlockHeader>; |
| 17 | + |
| 18 | + /// Returns the block associated with the given header. |
| 19 | + fn fetch_block<'a>(&'a mut self, header: &'a ValidatedBlockHeader) -> |
| 20 | + AsyncBlockSourceResult<'a, ValidatedBlock>; |
| 21 | +} |
| 22 | + |
| 23 | +/// A chain tip relative to another chain tip in terms of block hash and chainwork. |
| 24 | +#[derive(Clone, Debug, PartialEq)] |
| 25 | +pub enum ChainTip { |
| 26 | + /// A chain tip with the same hash as another chain's tip. |
| 27 | + Common, |
| 28 | + |
| 29 | + /// A chain tip with more chainwork than another chain's tip. |
| 30 | + Better(ValidatedBlockHeader), |
| 31 | + |
| 32 | + /// A chain tip with less or equal chainwork than another chain's tip. In either case, the |
| 33 | + /// hashes of each tip will be different. |
| 34 | + Worse(ValidatedBlockHeader), |
| 35 | +} |
| 36 | + |
| 37 | +/// The `Validate` trait defines behavior for validating chain data. |
| 38 | +pub(crate) trait Validate { |
| 39 | + /// The validated data wrapper which can be dereferenced to obtain the validated data. |
| 40 | + type T: std::ops::Deref<Target = Self>; |
| 41 | + |
| 42 | + /// Validates the chain data against the given block hash and any criteria needed to ensure that |
| 43 | + /// it is internally consistent. |
| 44 | + fn validate(self, block_hash: BlockHash) -> BlockSourceResult<Self::T>; |
| 45 | +} |
| 46 | + |
| 47 | +impl Validate for BlockHeaderData { |
| 48 | + type T = ValidatedBlockHeader; |
| 49 | + |
| 50 | + fn validate(self, block_hash: BlockHash) -> BlockSourceResult<Self::T> { |
| 51 | + self.header |
| 52 | + .validate_pow(&self.header.target()) |
| 53 | + .or_else(|e| Err(BlockSourceError::persistent(e)))?; |
| 54 | + |
| 55 | + // TODO: Use the result of validate_pow instead of recomputing the block hash once upstream. |
| 56 | + if self.header.block_hash() != block_hash { |
| 57 | + return Err(BlockSourceError::persistent("invalid block hash")); |
| 58 | + } |
| 59 | + |
| 60 | + Ok(ValidatedBlockHeader { block_hash, inner: self }) |
| 61 | + } |
| 62 | +} |
| 63 | + |
| 64 | +impl Validate for Block { |
| 65 | + type T = ValidatedBlock; |
| 66 | + |
| 67 | + fn validate(self, block_hash: BlockHash) -> BlockSourceResult<Self::T> { |
| 68 | + self.header |
| 69 | + .validate_pow(&self.header.target()) |
| 70 | + .or_else(|e| Err(BlockSourceError::persistent(e)))?; |
| 71 | + |
| 72 | + // TODO: Use the result of validate_pow instead of recomputing the block hash once upstream. |
| 73 | + if self.block_hash() != block_hash { |
| 74 | + return Err(BlockSourceError::persistent("invalid block hash")); |
| 75 | + } |
| 76 | + |
| 77 | + if !self.check_merkle_root() { |
| 78 | + return Err(BlockSourceError::persistent("invalid merkle root")); |
| 79 | + } |
| 80 | + |
| 81 | + if !self.check_witness_commitment() { |
| 82 | + return Err(BlockSourceError::persistent("invalid witness commitment")); |
| 83 | + } |
| 84 | + |
| 85 | + Ok(ValidatedBlock { block_hash, inner: self }) |
| 86 | + } |
| 87 | +} |
| 88 | + |
| 89 | +/// A block header with validated proof of work and corresponding block hash. |
| 90 | +#[derive(Clone, Copy, Debug, PartialEq)] |
| 91 | +pub struct ValidatedBlockHeader { |
| 92 | + block_hash: BlockHash, |
| 93 | + inner: BlockHeaderData, |
| 94 | +} |
| 95 | + |
| 96 | +impl std::ops::Deref for ValidatedBlockHeader { |
| 97 | + type Target = BlockHeaderData; |
| 98 | + |
| 99 | + fn deref(&self) -> &Self::Target { |
| 100 | + &self.inner |
| 101 | + } |
| 102 | +} |
| 103 | + |
| 104 | +impl ValidatedBlockHeader { |
| 105 | + /// Checks that the header correctly builds on previous_header: the claimed work differential |
| 106 | + /// matches the actual PoW and the difficulty transition is possible, i.e., within 4x. |
| 107 | + fn check_builds_on(&self, previous_header: &ValidatedBlockHeader, network: Network) -> BlockSourceResult<()> { |
| 108 | + if self.header.prev_blockhash != previous_header.block_hash { |
| 109 | + return Err(BlockSourceError::persistent("invalid previous block hash")); |
| 110 | + } |
| 111 | + |
| 112 | + if self.height != previous_header.height + 1 { |
| 113 | + return Err(BlockSourceError::persistent("invalid block height")); |
| 114 | + } |
| 115 | + |
| 116 | + let work = self.header.work(); |
| 117 | + if self.chainwork != previous_header.chainwork + work { |
| 118 | + return Err(BlockSourceError::persistent("invalid chainwork")); |
| 119 | + } |
| 120 | + |
| 121 | + if let Network::Bitcoin = network { |
| 122 | + if self.height % 2016 == 0 { |
| 123 | + let previous_work = previous_header.header.work(); |
| 124 | + if work > (previous_work << 2) || work < (previous_work >> 2) { |
| 125 | + return Err(BlockSourceError::persistent("invalid difficulty transition")) |
| 126 | + } |
| 127 | + } else if self.header.bits != previous_header.header.bits { |
| 128 | + return Err(BlockSourceError::persistent("invalid difficulty")) |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | + Ok(()) |
| 133 | + } |
| 134 | +} |
| 135 | + |
| 136 | +/// A block with validated data against its transaction list and corresponding block hash. |
| 137 | +pub struct ValidatedBlock { |
| 138 | + block_hash: BlockHash, |
| 139 | + inner: Block, |
| 140 | +} |
| 141 | + |
| 142 | +impl std::ops::Deref for ValidatedBlock { |
| 143 | + type Target = Block; |
| 144 | + |
| 145 | + fn deref(&self) -> &Self::Target { |
| 146 | + &self.inner |
| 147 | + } |
| 148 | +} |
0 commit comments