#[cfg(feature = "utxo-commitments")]
use crate::utxo_commitments::data_structures::{
UtxoCommitment, UtxoCommitmentError, UtxoCommitmentResult,
};
#[cfg(feature = "utxo-commitments")]
use blvm_consensus::economic::total_supply;
#[cfg(feature = "utxo-commitments")]
use blvm_consensus::pow::check_proof_of_work;
#[cfg(feature = "utxo-commitments")]
use blvm_consensus::types::{BlockHeader, Hash, Natural};
#[cfg(feature = "utxo-commitments")]
pub fn verify_supply(commitment: &UtxoCommitment) -> UtxoCommitmentResult<bool> {
let expected_supply = total_supply(commitment.block_height) as u64;
if commitment.total_supply != expected_supply {
return Err(UtxoCommitmentError::VerificationFailed(format!(
"Supply mismatch at height {}: commitment has {} satoshis, expected {} satoshis",
commitment.block_height, commitment.total_supply, expected_supply
)));
}
Ok(true)
}
pub fn verify_header_chain(headers: &[BlockHeader]) -> UtxoCommitmentResult<bool> {
if headers.is_empty() {
return Err(UtxoCommitmentError::VerificationFailed(
"Empty header chain".to_string(),
));
}
for (i, header) in headers.iter().enumerate() {
match check_proof_of_work(header) {
Ok(is_valid) => {
if !is_valid {
return Err(UtxoCommitmentError::VerificationFailed(format!(
"Invalid proof of work at height {}",
i
)));
}
}
Err(e) => {
return Err(UtxoCommitmentError::VerificationFailed(format!(
"PoW check failed at height {}: {}",
i, e
)));
}
}
if i > 0 {
let prev_header = &headers[i - 1];
let expected_prev_hash = compute_block_hash(prev_header);
if header.prev_block_hash != expected_prev_hash {
return Err(UtxoCommitmentError::VerificationFailed(format!(
"Chain linkage broken at height {}: expected prev_hash {:?}, got {:?}",
i, expected_prev_hash, header.prev_block_hash
)));
}
}
}
Ok(true)
}
pub fn verify_commitment_block_hash(
commitment: &UtxoCommitment,
header: &BlockHeader,
) -> UtxoCommitmentResult<bool> {
let computed_hash = compute_block_hash(header);
if commitment.block_hash != computed_hash {
return Err(UtxoCommitmentError::VerificationFailed(format!(
"Block hash mismatch: commitment has {:?}, header has {:?}",
commitment.block_hash, computed_hash
)));
}
Ok(true)
}
fn compute_block_hash(header: &BlockHeader) -> Hash {
use sha2::{Digest, Sha256};
let mut bytes = Vec::with_capacity(80);
bytes.extend_from_slice(&header.version.to_le_bytes());
bytes.extend_from_slice(&header.prev_block_hash);
bytes.extend_from_slice(&header.merkle_root);
bytes.extend_from_slice(&header.timestamp.to_le_bytes());
bytes.extend_from_slice(&header.bits.to_le_bytes());
bytes.extend_from_slice(&header.nonce.to_le_bytes());
let first_hash = Sha256::digest(&bytes);
let second_hash = Sha256::digest(&first_hash);
let mut hash = [0u8; 32];
hash.copy_from_slice(&second_hash);
hash
}
pub fn verify_forward_consistency(
initial_commitment: &UtxoCommitment,
new_commitment: &UtxoCommitment,
expected_height_increase: Natural,
) -> UtxoCommitmentResult<bool> {
if new_commitment.block_height != initial_commitment.block_height + expected_height_increase {
return Err(UtxoCommitmentError::VerificationFailed(format!(
"Height mismatch: initial {}, new {}, expected increase {}",
initial_commitment.block_height, new_commitment.block_height, expected_height_increase
)));
}
if new_commitment.total_supply < initial_commitment.total_supply {
return Err(UtxoCommitmentError::VerificationFailed(format!(
"Supply decreased: initial {}, new {}",
initial_commitment.total_supply, new_commitment.total_supply
)));
}
let expected_new_supply = total_supply(new_commitment.block_height) as u64;
if new_commitment.total_supply != expected_new_supply {
return Err(UtxoCommitmentError::VerificationFailed(format!(
"New supply mismatch: commitment has {}, expected {}",
new_commitment.total_supply, expected_new_supply
)));
}
Ok(true)
}