use blvm_consensus::types::{Hash, Natural};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UtxoCommitment {
pub merkle_root: Hash,
pub total_supply: u64,
pub utxo_count: u64,
pub block_height: Natural,
pub block_hash: Hash,
}
impl UtxoCommitment {
pub fn new(
merkle_root: Hash,
total_supply: u64,
utxo_count: u64,
block_height: Natural,
block_hash: Hash,
) -> Self {
Self {
merkle_root,
total_supply,
utxo_count,
block_height,
block_hash,
}
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(88);
bytes.extend_from_slice(&self.merkle_root);
bytes.extend_from_slice(&self.total_supply.to_be_bytes());
bytes.extend_from_slice(&self.utxo_count.to_be_bytes());
bytes.extend_from_slice(&self.block_height.to_be_bytes());
bytes.extend_from_slice(&self.block_hash);
bytes
}
pub fn from_bytes(data: &[u8]) -> Result<Self, UtxoCommitmentError> {
if data.len() != 88 {
return Err(UtxoCommitmentError::InvalidSize(data.len()));
}
let mut offset = 0;
let merkle_root: Hash = {
let mut hash = [0u8; 32];
hash.copy_from_slice(&data[offset..offset + 32]);
offset += 32;
hash
};
let total_supply = u64::from_be_bytes(
data[offset..offset + 8]
.try_into()
.map_err(|_| UtxoCommitmentError::InvalidSize(data.len()))?,
);
offset += 8;
let utxo_count = u64::from_be_bytes(
data[offset..offset + 8]
.try_into()
.map_err(|_| UtxoCommitmentError::InvalidSize(data.len()))?,
);
offset += 8;
let block_height = u64::from_be_bytes(
data[offset..offset + 8]
.try_into()
.map_err(|_| UtxoCommitmentError::InvalidSize(data.len()))?,
);
offset += 8;
let block_hash: Hash = {
let mut hash = [0u8; 32];
hash.copy_from_slice(&data[offset..offset + 32]);
hash
};
Ok(Self {
merkle_root,
total_supply,
utxo_count,
block_height,
block_hash,
})
}
pub fn verify_supply(&self, expected_supply: u64) -> bool {
self.total_supply == expected_supply
}
pub fn verify_count(&self, expected_count: u64) -> bool {
self.utxo_count == expected_count
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum UtxoCommitmentError {
InvalidSize(usize),
MerkleTreeError(String),
InvalidUtxo(String),
VerificationFailed(String),
SerializationError(String),
TransactionApplication(String),
}
impl std::fmt::Display for UtxoCommitmentError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
UtxoCommitmentError::InvalidSize(size) => {
write!(f, "Invalid commitment size: {} (expected 88)", size)
}
UtxoCommitmentError::MerkleTreeError(msg) => {
write!(f, "Merkle tree error: {}", msg)
}
UtxoCommitmentError::InvalidUtxo(msg) => {
write!(f, "Invalid UTXO: {}", msg)
}
UtxoCommitmentError::VerificationFailed(msg) => {
write!(f, "Verification failed: {}", msg)
}
UtxoCommitmentError::SerializationError(msg) => {
write!(f, "Serialization error: {}", msg)
}
UtxoCommitmentError::TransactionApplication(msg) => {
write!(f, "Transaction application error: {}", msg)
}
}
}
}
impl std::error::Error for UtxoCommitmentError {}
pub type UtxoCommitmentResult<T> = Result<T, UtxoCommitmentError>;
#[doc(hidden)]
const _UTXO_COMMITMENT_SPEC: () = ();