#[cfg(feature = "production")]
use blvm_protocol::rayon::iter::IntoParallelRefIterator;
use crate::utils::current_timestamp;
use anyhow::Result;
use blvm_protocol::block::connect_block;
use blvm_protocol::segwit::Witness;
use blvm_protocol::types::Network;
use blvm_protocol::{Block, BlockHeader, UtxoSet, ValidationResult};
#[derive(Debug, Clone)]
pub struct BlockValidationContext {
pub block: Block,
pub height: u64,
pub prev_utxo_set: UtxoSet,
pub prev_block_hash: [u8; 32],
}
pub struct ParallelBlockValidator {
max_parallel_depth: usize,
}
impl Default for ParallelBlockValidator {
fn default() -> Self {
Self::new(100)
}
}
impl ParallelBlockValidator {
pub fn new(max_parallel_depth: usize) -> Self {
Self { max_parallel_depth }
}
pub fn validate_block(
&self,
context: &BlockValidationContext,
network: Network,
) -> Result<(ValidationResult, UtxoSet)> {
let witnesses: Vec<Vec<Witness>> = context
.block
.transactions
.iter()
.map(|tx| tx.inputs.iter().map(|_| Vec::new()).collect())
.collect();
let network_time = current_timestamp();
let consensus_ctx = blvm_protocol::block::block_validation_context_for_connect_ibd(
None::<&[BlockHeader]>,
network_time,
network,
);
let (result, new_utxo_set, _undo_log) = connect_block(
&context.block,
&witnesses,
context.prev_utxo_set.clone(),
context.height,
&consensus_ctx,
)?;
Ok((result, new_utxo_set))
}
#[cfg(feature = "production")]
pub fn validate_blocks_parallel(
&self,
contexts: &[BlockValidationContext],
depth_from_tip: usize,
network: Network,
) -> Result<Vec<(ValidationResult, UtxoSet)>> {
if depth_from_tip <= self.max_parallel_depth {
return self.validate_blocks_sequential(contexts, network);
}
#[cfg(feature = "production")]
let results: Vec<_> = {
use blvm_protocol::rayon::iter::IntoParallelRefIterator;
use blvm_protocol::rayon::prelude::*;
contexts
.par_iter()
.map(|context| {
let witnesses: Vec<Vec<Witness>> = context
.block
.transactions
.iter()
.map(|tx| tx.inputs.iter().map(|_| Vec::new()).collect())
.collect();
let network_time = current_timestamp();
let consensus_ctx =
blvm_protocol::block::block_validation_context_for_connect_ibd(
None::<&[BlockHeader]>,
network_time,
network,
);
let (result, new_utxo_set, _undo_log) = connect_block(
&context.block,
witnesses.as_slice(),
context.prev_utxo_set.clone(),
context.height,
&consensus_ctx,
)?;
Ok::<_, anyhow::Error>((result, new_utxo_set))
})
.collect()
};
#[cfg(not(feature = "production"))]
let results: Vec<_> = {
contexts
.iter()
.map(|context| {
let witnesses: Vec<Vec<Witness>> = context
.block
.transactions
.iter()
.map(|tx| tx.inputs.iter().map(|_| Vec::new()).collect())
.collect();
let network_time = current_timestamp();
let consensus_ctx =
blvm_protocol::block::block_validation_context_for_connect_ibd(
None::<&[BlockHeader]>,
network_time,
network,
);
let (result, new_utxo_set, _undo_log) = connect_block(
&context.block,
witnesses.as_slice(),
context.prev_utxo_set.clone(),
context.height,
&consensus_ctx,
)?;
Ok::<_, anyhow::Error>((result, new_utxo_set))
})
.collect()
};
let mut validated_results = Vec::new();
for result in results {
validated_results.push(result?);
}
Ok(validated_results)
}
pub fn validate_blocks_sequential(
&self,
contexts: &[BlockValidationContext],
network: Network,
) -> Result<Vec<(ValidationResult, UtxoSet)>> {
let mut results = Vec::new();
for context in contexts {
let witnesses: Vec<Vec<Witness>> = context
.block
.transactions
.iter()
.map(|tx| tx.inputs.iter().map(|_| Vec::new()).collect())
.collect();
let network_time = current_timestamp();
let consensus_ctx = blvm_protocol::block::block_validation_context_for_connect_ibd(
None::<&[BlockHeader]>,
network_time,
network,
);
let (result, new_utxo_set, _undo_log) = connect_block(
&context.block,
&witnesses,
context.prev_utxo_set.clone(),
context.height,
&consensus_ctx,
)?;
results.push((result, new_utxo_set));
}
Ok(results)
}
pub fn validate_blocks(
&self,
contexts: &[BlockValidationContext],
depth_from_tip: usize,
network: Network,
) -> Result<Vec<(ValidationResult, UtxoSet)>> {
#[cfg(feature = "production")]
{
if depth_from_tip > self.max_parallel_depth {
return self.validate_blocks_parallel(contexts, depth_from_tip, network);
}
}
self.validate_blocks_sequential(contexts, network)
}
}
#[derive(Debug, Clone)]
pub struct BlockValidationResult {
pub validation_result: ValidationResult,
pub utxo_set: UtxoSet,
pub height: u64,
pub block_hash: [u8; 32],
pub validation_time_ms: u64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parallel_validator_creation() {
let validator = ParallelBlockValidator::default();
assert_eq!(validator.max_parallel_depth, 100);
}
#[test]
fn test_sequential_validation() {
let validator = ParallelBlockValidator::default();
let contexts = vec![]; let results = validator.validate_blocks_sequential(&contexts, Network::Mainnet);
assert!(results.is_ok());
}
}