Skip to main content

blvm_protocol/utxo_commitments/
verification.rs

1//! UTXO Commitment Verification
2//!
3//! Provides utilities for verifying UTXO commitments against:
4//! - Bitcoin supply calculations
5//! - Block header chain (Proof of Work)
6//! - Peer consensus consistency
7
8#[cfg(feature = "utxo-commitments")]
9use crate::utxo_commitments::data_structures::{
10    UtxoCommitment, UtxoCommitmentError, UtxoCommitmentResult,
11};
12#[cfg(feature = "utxo-commitments")]
13use blvm_consensus::economic::total_supply;
14#[cfg(feature = "utxo-commitments")]
15use blvm_consensus::pow::check_proof_of_work;
16#[cfg(feature = "utxo-commitments")]
17use blvm_consensus::types::{BlockHeader, Hash, Natural};
18#[cfg(feature = "utxo-commitments")]
19/// Verify that a UTXO commitment's supply matches expected Bitcoin supply
20///
21/// Checks that the total supply in the commitment equals the sum of all
22/// block subsidies up to the commitment's block height.
23pub fn verify_supply(commitment: &UtxoCommitment) -> UtxoCommitmentResult<bool> {
24    let expected_supply = total_supply(commitment.block_height) as u64;
25
26    if commitment.total_supply != expected_supply {
27        return Err(UtxoCommitmentError::VerificationFailed(format!(
28            "Supply mismatch at height {}: commitment has {} satoshis, expected {} satoshis",
29            commitment.block_height, commitment.total_supply, expected_supply
30        )));
31    }
32
33    Ok(true)
34}
35
36/// Verify that a block header chain is valid (Proof of Work)
37///
38/// Verifies the chain of block headers from genesis to the commitment height,
39/// checking that each header satisfies proof of work requirements.
40pub fn verify_header_chain(headers: &[BlockHeader]) -> UtxoCommitmentResult<bool> {
41    if headers.is_empty() {
42        return Err(UtxoCommitmentError::VerificationFailed(
43            "Empty header chain".to_string(),
44        ));
45    }
46
47    // Verify each header's proof of work
48    for (i, header) in headers.iter().enumerate() {
49        match check_proof_of_work(header) {
50            Ok(is_valid) => {
51                if !is_valid {
52                    return Err(UtxoCommitmentError::VerificationFailed(format!(
53                        "Invalid proof of work at height {}",
54                        i
55                    )));
56                }
57            }
58            Err(e) => {
59                return Err(UtxoCommitmentError::VerificationFailed(format!(
60                    "PoW check failed at height {}: {}",
61                    i, e
62                )));
63            }
64        }
65
66        // Verify chain linkage (except for genesis)
67        if i > 0 {
68            let prev_header = &headers[i - 1];
69            // Compute block hash using double SHA256
70            let expected_prev_hash = compute_block_hash(prev_header);
71
72            if header.prev_block_hash != expected_prev_hash {
73                return Err(UtxoCommitmentError::VerificationFailed(format!(
74                    "Chain linkage broken at height {}: expected prev_hash {:?}, got {:?}",
75                    i, expected_prev_hash, header.prev_block_hash
76                )));
77            }
78        }
79    }
80
81    Ok(true)
82}
83
84/// Verify commitment against block header
85///
86/// Verifies that the commitment's block_hash matches the actual block hash
87/// at the given height.
88pub fn verify_commitment_block_hash(
89    commitment: &UtxoCommitment,
90    header: &BlockHeader,
91) -> UtxoCommitmentResult<bool> {
92    let computed_hash = compute_block_hash(header);
93
94    if commitment.block_hash != computed_hash {
95        return Err(UtxoCommitmentError::VerificationFailed(format!(
96            "Block hash mismatch: commitment has {:?}, header has {:?}",
97            commitment.block_hash, computed_hash
98        )));
99    }
100
101    Ok(true)
102}
103
104/// Compute block header hash (double SHA256) — 80-byte Bitcoin wire format.
105///
106/// Casts each field to its on-wire width (version/timestamp/bits/nonce = u32 LE)
107/// matching `blvm_consensus::pow::serialize_header`.
108fn compute_block_hash(header: &BlockHeader) -> Hash {
109    use sha2::{Digest, Sha256};
110
111    let mut bytes = [0u8; 80];
112    bytes[0..4].copy_from_slice(&(header.version as u32).to_le_bytes());
113    bytes[4..36].copy_from_slice(&header.prev_block_hash);
114    bytes[36..68].copy_from_slice(&header.merkle_root);
115    bytes[68..72].copy_from_slice(&(header.timestamp as u32).to_le_bytes());
116    bytes[72..76].copy_from_slice(&(header.bits as u32).to_le_bytes());
117    bytes[76..80].copy_from_slice(&(header.nonce as u32).to_le_bytes());
118
119    let first_hash = Sha256::digest(bytes);
120    let second_hash = Sha256::digest(first_hash);
121
122    let mut hash = [0u8; 32];
123    hash.copy_from_slice(&second_hash);
124    hash
125}
126
127/// Verify forward consistency
128///
129/// Verifies that applying a sequence of blocks to a commitment results in
130/// a consistent new commitment. Used to ensure commitments remain valid
131/// as the chain progresses.
132pub fn verify_forward_consistency(
133    initial_commitment: &UtxoCommitment,
134    new_commitment: &UtxoCommitment,
135    expected_height_increase: Natural,
136) -> UtxoCommitmentResult<bool> {
137    // Verify height progression
138    if new_commitment.block_height != initial_commitment.block_height + expected_height_increase {
139        return Err(UtxoCommitmentError::VerificationFailed(format!(
140            "Height mismatch: initial {}, new {}, expected increase {}",
141            initial_commitment.block_height, new_commitment.block_height, expected_height_increase
142        )));
143    }
144
145    // Verify supply progression (should only increase or stay same, never decrease)
146    if new_commitment.total_supply < initial_commitment.total_supply {
147        return Err(UtxoCommitmentError::VerificationFailed(format!(
148            "Supply decreased: initial {}, new {}",
149            initial_commitment.total_supply, new_commitment.total_supply
150        )));
151    }
152
153    // Note: We can't verify UTXO count changes without knowing transactions,
154    // but we can verify supply changes match expected block subsidies
155    let expected_new_supply = total_supply(new_commitment.block_height) as u64;
156    if new_commitment.total_supply != expected_new_supply {
157        return Err(UtxoCommitmentError::VerificationFailed(format!(
158            "New supply mismatch: commitment has {}, expected {}",
159            new_commitment.total_supply, expected_new_supply
160        )));
161    }
162
163    Ok(true)
164}
165
166// ============================================================================
167// FORMAL VERIFICATION
168// ============================================================================
169//
170// Mathematical Specification for UTXO Commitment Verification:
171// ∀ commitment ∈ UtxoCommitment, height ∈ ℕ:
172// - verify_supply(commitment) = true ⟺ commitment.total_supply = total_supply(height)
173// - verify_forward_consistency(c1, c2) = true ⟺ c2.height = c1.height + Δ ∧ c2.supply ≥ c1.supply
174//
175// Invariants:
176// - Supply verification prevents inflation
177// - Forward consistency ensures commitments progress correctly
178// - Block hash verification ensures commitment matches actual block