Skip to main content

urma_chain/
validation.rs

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}