#[cfg(feature = "production")]
mod tests {
use blvm_consensus::*;
use blvm_consensus::block::*;
use blvm_consensus::script::*;
fn create_multi_input_transaction() -> Transaction {
Transaction {
version: 1,
inputs: vec![
TransactionInput {
prevout: OutPoint { hash: [1; 32].into(), index: 0 },
script_sig: vec![0x51],
sequence: 0xffffffff,
},
TransactionInput {
prevout: OutPoint { hash: [2; 32], index: 0 },
script_sig: vec![0x51],
sequence: 0xffffffff,
},
TransactionInput {
prevout: OutPoint { hash: [3; 32], index: 0 },
script_sig: vec![0x51],
sequence: 0xffffffff,
},
].into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: vec![0x51].into(),
}].into(),
lock_time: 0,
}
}
fn create_multi_input_utxo_set() -> UtxoSet {
let mut utxo_set = UtxoSet::default();
for i in 1..=3 {
let outpoint = OutPoint { hash: [i as u8; 32], index: 0 };
let utxo = UTXO {
value: 10000,
script_pubkey: vec![0x51].into(),
height: 0,
};
utxo_set.insert(outpoint, std::sync::Arc::new(utxo));
}
utxo_set
}
#[test]
fn test_parallel_script_verification_single_tx() {
let tx = create_multi_input_transaction();
let utxo_set = create_multi_input_utxo_set();
let block = Block {
header: BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1231006505,
bits: 0x1d00ffff,
nonce: 0,
},
transactions: vec![
Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint { hash: [0; 32].into(), index: 0xffffffff },
script_sig: vec![0x51],
sequence: 0xffffffff,
}].into(),
outputs: vec![TransactionOutput {
value: 50_000_000_000,
script_pubkey: vec![0x51].into(),
}].into(),
lock_time: 0,
},
tx.clone(),
],
};
let witnesses: Vec<segwit::Witness> = block.transactions.iter().map(|_| Vec::new()).collect();
let (result, _) = { let ctx = block::BlockValidationContext::for_network(crate::types::Network::Mainnet); connect_block(&block, &witnesses, utxo_set, 0, &ctx) }.unwrap();
assert!(matches!(result, ValidationResult::Valid | ValidationResult::Invalid(_)),
"Parallel verification must produce deterministic result");
}
#[test]
fn test_parallel_script_verification_ordering() {
let tx = create_multi_input_transaction();
let utxo_set = create_multi_input_utxo_set();
let input_utxos: Vec<(usize, Option<&ByteString>)> = tx.inputs
.iter()
.enumerate()
.map(|(j, input)| (j, utxo_set.get(&input.prevout).map(|u| &u.script_pubkey)))
.collect();
assert_eq!(input_utxos.len(), 3);
assert_eq!(input_utxos[0].0, 0);
assert_eq!(input_utxos[1].0, 1);
assert_eq!(input_utxos[2].0, 2);
}
#[test]
fn test_parallel_script_verification_error_handling() {
let mut utxo_set = create_multi_input_utxo_set();
let tx = Transaction {
version: 1,
inputs: vec![
TransactionInput {
prevout: OutPoint { hash: [1; 32].into(), index: 0 },
script_sig: vec![0xff; MAX_SCRIPT_OPS + 1], sequence: 0xffffffff,
},
].into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: vec![0x51].into(),
}].into(),
lock_time: 0,
};
let block = Block {
header: BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1231006505,
bits: 0x1d00ffff,
nonce: 0,
},
transactions: vec![
Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint { hash: [0; 32].into(), index: 0xffffffff },
script_sig: vec![0x51],
sequence: 0xffffffff,
}].into(),
outputs: vec![TransactionOutput {
value: 50_000_000_000,
script_pubkey: vec![0x51].into(),
}].into(),
lock_time: 0,
},
tx,
],
};
let witnesses: Vec<segwit::Witness> = block.transactions.iter().map(|_| Vec::new()).collect();
let result = { let ctx = block::BlockValidationContext::for_network(crate::types::Network::Mainnet); connect_block(&block, &witnesses, utxo_set, 0, &ctx) };
assert!(result.is_ok() || result.is_err(),
"Parallel verification must handle errors correctly");
}
#[test]
fn test_parallel_utxo_prelookup() {
let tx = create_multi_input_transaction();
let utxo_set = create_multi_input_utxo_set();
let input_utxos: Vec<(usize, Option<&ByteString>)> = tx.inputs
.iter()
.enumerate()
.map(|(j, input)| (j, utxo_set.get(&input.prevout).map(|u| &u.script_pubkey)))
.collect();
assert_eq!(input_utxos.len(), 3);
for (idx, opt_script) in &input_utxos {
assert!(opt_script.is_some(),
"UTXO pre-lookup must find all inputs (input {})", idx);
}
}
#[test]
fn test_parallel_empty_transactions() {
let coinbase_tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint { hash: [0; 32].into(), index: 0xffffffff },
script_sig: vec![0x51],
sequence: 0xffffffff,
}].into(),
outputs: vec![TransactionOutput {
value: 50_000_000_000,
script_pubkey: vec![0x51].into(),
}].into(),
lock_time: 0,
};
let block = Block {
header: BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1231006505,
bits: 0x1d00ffff,
nonce: 0,
},
transactions: vec![coinbase_tx].into(),
};
let witnesses: Vec<segwit::Witness> = block.transactions.iter().map(|_| Vec::new()).collect();
let (result, _) = { let ctx = block::BlockValidationContext::for_network(crate::types::Network::Mainnet); connect_block(&block, &witnesses, UtxoSet::default(), 0, &ctx) }.unwrap();
assert!(matches!(result, ValidationResult::Valid | ValidationResult::Invalid(_)));
}
#[test]
fn test_parallel_single_input() {
let mut utxo_set = UtxoSet::default();
let outpoint = OutPoint { hash: [1; 32], index: 0 };
let utxo = UTXO {
value: 10000,
script_pubkey: vec![0x51].into(),
height: 0,
};
utxo_set.insert(outpoint, std::sync::Arc::new(utxo));
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint { hash: [1; 32].into(), index: 0 },
script_sig: vec![0x51],
sequence: 0xffffffff,
}].into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: vec![0x51].into(),
}].into(),
lock_time: 0,
};
let block = Block {
header: BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1231006505,
bits: 0x1d00ffff,
nonce: 0,
},
transactions: vec![
Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint { hash: [0; 32].into(), index: 0xffffffff },
script_sig: vec![0x51],
sequence: 0xffffffff,
}].into(),
outputs: vec![TransactionOutput {
value: 50_000_000_000,
script_pubkey: vec![0x51].into(),
}].into(),
lock_time: 0,
},
tx,
],
};
let witnesses: Vec<segwit::Witness> = block.transactions.iter().map(|_| Vec::new()).collect();
let (result, _) = { let ctx = block::BlockValidationContext::for_network(crate::types::Network::Mainnet); connect_block(&block, &witnesses, utxo_set, 0, &ctx) }.unwrap();
assert!(matches!(result, ValidationResult::Valid | ValidationResult::Invalid(_)));
}
#[test]
fn test_parallel_deterministic_results() {
let tx = create_multi_input_transaction();
let utxo_set = create_multi_input_utxo_set();
let block = Block {
header: BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1231006505,
bits: 0x1d00ffff,
nonce: 0,
},
transactions: vec![
Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint { hash: [0; 32].into(), index: 0xffffffff },
script_sig: vec![0x51],
sequence: 0xffffffff,
}].into(),
outputs: vec![TransactionOutput {
value: 50_000_000_000,
script_pubkey: vec![0x51].into(),
}].into(),
lock_time: 0,
},
tx.clone(),
],
};
let witnesses: Vec<Vec<Witness>> = block
.transactions
.iter()
.map(|tx| (0..tx.inputs.len()).map(|_| vec![]).collect())
.collect();
let ctx = block::BlockValidationContext::for_network(crate::types::Network::Mainnet);
let (result1, _) = connect_block(&block, &witnesses, utxo_set.clone(), 0, &ctx).unwrap();
let (result2, _) = connect_block(&block, &witnesses, utxo_set, 0, &ctx).unwrap();
assert_eq!(format!("{:?}", result1), format!("{:?}", result2),
"Parallel verification must be deterministic");
}
}