1use bitcoin::{Block, BlockHash, Network};
2
3#[derive(Debug)]
4pub enum BlockValidationError {
5 HashMismatch,
6 MerkleRootMismatch,
7 WitnessCommitmentMismatch,
8 ProofOfWork(bitcoin::block::ValidationError),
9 UnsupportedNetwork,
10 TargetLimit,
11}
12
13impl std::fmt::Display for BlockValidationError {
14 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15 match self {
16 Self::HashMismatch => f.write_str("block hash mismatch"),
17 Self::MerkleRootMismatch => f.write_str("transaction Merkle root mismatch"),
18 Self::WitnessCommitmentMismatch => f.write_str("witness commitment mismatch"),
19 Self::ProofOfWork(cause) => write!(f, "block proof of work: {cause}"),
20 Self::UnsupportedNetwork => f.write_str("only regtest and testnet4 are supported"),
21 Self::TargetLimit => f.write_str("block target exceeds network proof-of-work limit"),
22 }
23 }
24}
25
26impl std::error::Error for BlockValidationError {
27 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
28 match self {
29 Self::ProofOfWork(cause) => Some(cause),
30 _ => None,
31 }
32 }
33}
34
35pub fn validate_block_integrity(
36 block: &Block,
37 expected_hash: BlockHash,
38) -> Result<(), BlockValidationError> {
39 if block.block_hash() != expected_hash {
40 return Err(BlockValidationError::HashMismatch);
41 }
42 if !block.check_merkle_root() {
43 return Err(BlockValidationError::MerkleRootMismatch);
44 }
45 if !block.check_witness_commitment() {
46 return Err(BlockValidationError::WitnessCommitmentMismatch);
47 }
48 Ok(())
49}
50
51pub fn validate_block(block: &Block, expected_hash: BlockHash) -> Result<(), BlockValidationError> {
52 validate_block_integrity(block, expected_hash)?;
53 block
54 .header
55 .validate_pow(block.header.target())
56 .map_err(BlockValidationError::ProofOfWork)?;
57 Ok(())
58}
59
60pub fn validate_block_for_network(
61 block: &Block,
62 expected_hash: BlockHash,
63 network: Network,
64) -> Result<(), BlockValidationError> {
65 if !matches!(network, Network::Regtest | Network::Testnet4) {
66 return Err(BlockValidationError::UnsupportedNetwork);
67 }
68 if block.header.target() > network.params().max_attainable_target {
69 return Err(BlockValidationError::TargetLimit);
70 }
71 validate_block(block, expected_hash)
72}