#[cfg(feature = "utxo-commitments")]
use crate::spam_filter::{SpamFilter, SpamSummary};
#[cfg(feature = "utxo-commitments")]
use crate::utxo_commitments::data_structures::{
UtxoCommitment, UtxoCommitmentError, UtxoCommitmentResult,
};
#[cfg(feature = "utxo-commitments")]
use blvm_consensus::types::{BlockHeader, Hash as HashType, Natural, Transaction};
#[cfg(feature = "utxo-commitments")]
#[derive(Debug, Clone)]
pub struct FilteredBlock {
pub header: BlockHeader,
pub commitment: UtxoCommitment,
pub transactions: Vec<Transaction>,
pub transaction_indices: Vec<u32>,
pub spam_summary: SpamSummary,
}
pub trait UtxoCommitmentsNetworkClient: Send + Sync {
fn request_utxo_set(
&self,
peer_id: &str,
height: Natural,
block_hash: HashType,
) -> std::pin::Pin<
Box<dyn std::future::Future<Output = UtxoCommitmentResult<UtxoCommitment>> + Send + '_>,
>;
fn request_filtered_block(
&self,
peer_id: &str,
block_hash: HashType,
) -> std::pin::Pin<
Box<dyn std::future::Future<Output = UtxoCommitmentResult<FilteredBlock>> + Send + '_>,
>;
fn request_full_block(
&self,
peer_id: &str,
block_hash: HashType,
) -> std::pin::Pin<
Box<dyn std::future::Future<Output = UtxoCommitmentResult<FullBlock>> + Send + '_>,
>;
fn get_peer_ids(&self) -> Vec<String>;
}
#[derive(Debug, Clone)]
pub struct FullBlock {
pub block: blvm_consensus::types::Block,
pub witnesses: Vec<Vec<blvm_consensus::segwit::Witness>>,
}
pub async fn request_utxo_sets_from_peers_fn<F, Fut>(
request_fn: F,
peers: &[String],
height: Natural,
block_hash: HashType,
) -> Vec<(String, UtxoCommitmentResult<UtxoCommitment>)>
where
F: Fn(&str, Natural, HashType) -> Fut,
Fut: std::future::Future<Output = UtxoCommitmentResult<UtxoCommitment>>,
{
let mut results = Vec::new();
for peer_id in peers {
let result = request_fn(peer_id, height, block_hash).await;
results.push((peer_id.clone(), result));
}
results
}
pub fn process_and_verify_filtered_block(
filtered_block: &FilteredBlock,
expected_height: Natural,
_spam_filter: &SpamFilter,
) -> UtxoCommitmentResult<bool> {
if filtered_block.commitment.block_height != expected_height {
return Err(UtxoCommitmentError::VerificationFailed(format!(
"Commitment height mismatch: expected {}, got {}",
expected_height, filtered_block.commitment.block_height
)));
}
let computed_hash = compute_block_hash(&filtered_block.header);
if filtered_block.commitment.block_hash != computed_hash {
return Err(UtxoCommitmentError::VerificationFailed(format!(
"Block hash mismatch: expected {:?}, got {:?}",
computed_hash, filtered_block.commitment.block_hash
)));
}
Ok(true)
}
fn compute_block_hash(header: &BlockHeader) -> HashType {
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
}