use blvm_consensus::economic::*;
use blvm_consensus::pow::*;
use blvm_consensus::script::*;
use blvm_consensus::transaction::*;
use blvm_consensus::*;
#[test]
fn test_check_transaction_valid() {
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![blvm_consensus::opcodes::OP_1],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: vec![blvm_consensus::opcodes::OP_1].into(),
}]
.into(),
lock_time: 0,
};
let result = check_transaction(&tx).unwrap();
assert!(matches!(result, ValidationResult::Valid));
}
#[test]
fn test_check_transaction_empty_inputs() {
let tx = Transaction {
version: 1,
inputs: vec![].into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: vec![blvm_consensus::opcodes::OP_1].into(),
}]
.into(),
lock_time: 0,
};
let result = check_transaction(&tx).unwrap();
assert!(matches!(result, ValidationResult::Invalid(_)));
}
#[test]
fn test_check_transaction_too_many_inputs() {
let mut inputs = Vec::new();
for i in 0..=MAX_INPUTS {
inputs.push(TransactionInput {
prevout: OutPoint {
hash: [i as u8; 32],
index: 0,
},
script_sig: vec![blvm_consensus::opcodes::OP_1],
sequence: 0xffffffff,
});
}
let tx = Transaction {
version: 1,
inputs: inputs.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: vec![blvm_consensus::opcodes::OP_1].into(),
}]
.into(),
lock_time: 0,
};
let result = check_transaction(&tx).unwrap();
assert!(matches!(result, ValidationResult::Invalid(_)));
}
#[test]
fn test_check_transaction_too_many_outputs() {
let mut outputs = Vec::new();
for _ in 0..=MAX_OUTPUTS {
outputs.push(TransactionOutput {
value: 1000,
script_pubkey: vec![0x51].into(),
});
}
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![blvm_consensus::opcodes::OP_1],
sequence: 0xffffffff,
}]
.into(),
outputs: outputs.into(),
lock_time: 0,
};
let result = check_transaction(&tx).unwrap();
assert!(matches!(result, ValidationResult::Invalid(_)));
}
#[test]
fn test_check_transaction_negative_output() {
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![blvm_consensus::opcodes::OP_1],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: -1000, script_pubkey: vec![blvm_consensus::opcodes::OP_1].into(),
}]
.into(),
lock_time: 0,
};
let result = check_transaction(&tx).unwrap();
assert!(matches!(result, ValidationResult::Invalid(_)));
}
#[test]
fn test_check_transaction_excessive_output() {
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![blvm_consensus::opcodes::OP_1],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: MAX_MONEY + 1, script_pubkey: vec![blvm_consensus::opcodes::OP_1].into(),
}]
.into(),
lock_time: 0,
};
let result = check_transaction(&tx).unwrap();
assert!(matches!(result, ValidationResult::Invalid(_)));
}
#[test]
fn test_is_coinbase() {
let coinbase_tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [0; 32].into(),
index: 0xffffffff,
},
script_sig: vec![blvm_consensus::opcodes::OP_1],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 5000000000,
script_pubkey: vec![blvm_consensus::opcodes::OP_1].into(),
}]
.into(),
lock_time: 0,
};
assert!(is_coinbase(&coinbase_tx));
let regular_tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![blvm_consensus::opcodes::OP_1],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: vec![blvm_consensus::opcodes::OP_1].into(),
}]
.into(),
lock_time: 0,
};
assert!(!is_coinbase(®ular_tx));
}
#[test]
fn test_calculate_transaction_size() {
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![0x51, 0x52],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: vec![0x51, 0x52, 0x53].into(),
}]
.into(),
lock_time: 0,
};
let result = check_transaction(&tx).unwrap();
assert!(matches!(result, ValidationResult::Valid));
}
#[test]
fn test_eval_script_simple() {
let script = vec![0x51, 0x52]; let mut stack = Vec::new();
let result = eval_script(&script, &mut stack, 0, crate::script::SigVersion::Base).unwrap();
let _ = result;
}
#[test]
fn test_eval_script_overflow() {
let mut script = Vec::new();
for _ in 0..=MAX_STACK_SIZE {
script.push(0x51); }
let mut stack = Vec::new();
let result = eval_script(&script, &mut stack, 0, crate::script::SigVersion::Base);
assert!(result.is_err());
}
#[test]
fn test_verify_script_simple() {
let script_sig = vec![0x51]; let script_pubkey = vec![0x51];
let result = verify_script(&script_sig, &script_pubkey, None, 0).unwrap();
let _ = result;
}
#[test]
fn test_verify_script_with_witness() {
let script_sig = vec![0x51]; let script_pubkey = vec![0x51]; let witness = Some(vec![0x52]);
let result = verify_script(&script_sig, &script_pubkey, witness.as_ref(), 0).unwrap();
let _ = result;
}
#[test]
fn test_verify_script_empty() {
let script_sig = vec![];
let script_pubkey = vec![];
let result = verify_script(&script_sig, &script_pubkey, None, 0).unwrap();
let _ = result;
}
#[test]
fn test_verify_script_large_scripts() {
let mut script_sig = Vec::new();
let mut script_pubkey = Vec::new();
for _ in 0..=MAX_SCRIPT_SIZE {
script_sig.push(0x51);
script_pubkey.push(0x51);
}
let result = verify_script(&script_sig, &script_pubkey, None, 0);
assert!(result.is_err());
}
#[test]
fn test_calculate_fee() {
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![blvm_consensus::opcodes::OP_1],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 800,
script_pubkey: vec![blvm_consensus::opcodes::OP_1].into(),
}]
.into(),
lock_time: 0,
};
let mut utxo_set = UtxoSet::default();
let outpoint = OutPoint {
hash: [1; 32],
index: 0,
};
let utxo = UTXO {
value: 1000,
script_pubkey: vec![0x51].into(),
height: 100,
is_coinbase: false,
};
utxo_set.insert(outpoint, std::sync::Arc::new(utxo));
let fee = calculate_fee(&tx, &utxo_set).unwrap();
assert_eq!(fee, 200);
}
#[test]
fn test_calculate_fee_negative() {
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![blvm_consensus::opcodes::OP_1],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 800,
script_pubkey: vec![blvm_consensus::opcodes::OP_1].into(),
}]
.into(),
lock_time: 0,
};
let mut utxo_set = UtxoSet::default();
let outpoint = OutPoint {
hash: [1; 32],
index: 0,
};
let utxo = UTXO {
value: 500, script_pubkey: vec![0x51].into(),
height: 100,
is_coinbase: false,
};
utxo_set.insert(outpoint, std::sync::Arc::new(utxo));
let result = calculate_fee(&tx, &utxo_set);
assert!(result.is_err());
}
#[test]
fn test_calculate_fee_zero() {
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![blvm_consensus::opcodes::OP_1],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: vec![blvm_consensus::opcodes::OP_1].into(),
}]
.into(),
lock_time: 0,
};
let mut utxo_set = UtxoSet::default();
let outpoint = OutPoint {
hash: [1; 32],
index: 0,
};
let utxo = UTXO {
value: 1000,
script_pubkey: vec![0x51].into(),
height: 100,
is_coinbase: false,
};
utxo_set.insert(outpoint, std::sync::Arc::new(utxo));
let fee = calculate_fee(&tx, &utxo_set).unwrap();
assert_eq!(fee, 0);
}
#[test]
fn test_validate_supply_limit_excessive() {
use blvm_consensus::orange_paper_constants::H;
let excessive_height = H * 100; let result = validate_supply_limit(excessive_height);
match result {
Ok(valid) => assert!(valid),
Err(_) => {
}
}
}
#[test]
fn test_get_next_work_required_insufficient_headers() {
let current_header = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1231006505,
bits: 0x1d00ffff,
nonce: 0,
};
let prev_headers = vec![];
let result = get_next_work_required(¤t_header, &prev_headers);
assert!(result.is_err());
}
#[test]
fn test_get_next_work_required_normal_adjustment() {
let current_header = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1231006505 + (DIFFICULTY_ADJUSTMENT_INTERVAL * TARGET_TIME_PER_BLOCK),
bits: 0x1d00ffff,
nonce: 0,
};
let mut prev_headers = Vec::new();
for i in 0..DIFFICULTY_ADJUSTMENT_INTERVAL {
prev_headers.push(BlockHeader {
version: 1,
prev_block_hash: [i as u8; 32],
merkle_root: [0; 32],
timestamp: 1231006505 + (i * TARGET_TIME_PER_BLOCK),
bits: 0x1d00ffff,
nonce: 0,
});
}
let result = get_next_work_required(¤t_header, &prev_headers).unwrap();
let expected = 0x1d00ffff;
let diff = result.abs_diff(expected);
assert!(
diff <= 100,
"Expected difficulty close to 0x1d00ffff, got {result} (diff: {diff})"
);
}
#[test]
fn test_check_proof_of_work_genesis() {
let header = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1231006505,
bits: 0x0300ffff, nonce: 0,
};
let result = check_proof_of_work(&header).unwrap();
let _ = result;
}
#[test]
fn test_check_proof_of_work_invalid_target() {
let header = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1231006505,
bits: 0x2100ffff,
nonce: 0,
};
let result = check_proof_of_work(&header);
assert!(result.is_err());
}
#[test]
fn test_transaction_size_boundaries() {
let mut large_script = Vec::new();
for _ in 0..MAX_SCRIPT_SIZE {
large_script.push(0x51);
}
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: large_script.clone(),
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: large_script,
}]
.into(),
lock_time: 0,
};
let result = check_transaction(&tx).unwrap();
assert!(matches!(
result,
ValidationResult::Valid | ValidationResult::Invalid(_)
));
}
#[test]
fn test_maximum_input_output_counts() {
let mut inputs = Vec::new();
for i in 0..MAX_INPUTS {
inputs.push(TransactionInput {
prevout: OutPoint {
hash: [i as u8; 32],
index: 0,
},
script_sig: vec![blvm_consensus::opcodes::OP_1],
sequence: 0xffffffff,
});
}
let tx_max_inputs = Transaction {
version: 1,
inputs: inputs.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: vec![blvm_consensus::opcodes::OP_1].into(),
}]
.into(),
lock_time: 0,
};
let result = check_transaction(&tx_max_inputs).unwrap();
match result {
ValidationResult::Valid => {
}
ValidationResult::Invalid(reason) => {
eprintln!("Transaction validation result: {reason}");
}
}
let num_outputs = MAX_TX_SIZE / 12; let mut outputs = Vec::new();
for _ in 0..num_outputs {
outputs.push(TransactionOutput {
value: 1000,
script_pubkey: vec![0x51].into(),
});
}
let tx_max_outputs = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![blvm_consensus::opcodes::OP_1],
sequence: 0xffffffff,
}]
.into(),
outputs: outputs.into(),
lock_time: 0,
};
let result = check_transaction(&tx_max_outputs).unwrap();
assert!(matches!(result, ValidationResult::Valid));
}
#[test]
fn test_monetary_boundaries() {
let tx_max_money = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![blvm_consensus::opcodes::OP_1],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: MAX_MONEY,
script_pubkey: vec![blvm_consensus::opcodes::OP_1].into(),
}]
.into(),
lock_time: 0,
};
let result = check_transaction(&tx_max_money).unwrap();
assert!(matches!(result, ValidationResult::Valid));
let tx_excess_money = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![blvm_consensus::opcodes::OP_1],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: MAX_MONEY + 1,
script_pubkey: vec![blvm_consensus::opcodes::OP_1].into(),
}]
.into(),
lock_time: 0,
};
let result = check_transaction(&tx_excess_money).unwrap();
assert!(matches!(result, ValidationResult::Invalid(_)));
}
#[test]
fn test_script_operation_limits() {
let mut script = Vec::new();
for _ in 0..MAX_SCRIPT_OPS {
script.push(0x61); }
let empty: Vec<u8> = vec![];
let result = verify_script(&script, &empty, None, 0).unwrap();
let _ = result;
let mut large_script = Vec::new();
for _ in 0..=MAX_SCRIPT_OPS {
large_script.push(0x61); }
let result = verify_script(&large_script, &empty, None, 0);
assert!(result.is_err());
}
#[test]
fn test_stack_size_limits() {
let mut script = Vec::new();
for _ in 0..=MAX_STACK_SIZE {
script.push(0x51); }
let result = verify_script(&script, &script, None, 0);
assert!(result.is_err());
}
#[test]
fn test_difficulty_adjustment_boundaries() {
let current_header = BlockHeader {
version: 1,
prev_block_hash: [0; 32],
merkle_root: [0; 32],
timestamp: 1231006505,
bits: 0x1d00ffff,
nonce: 0,
};
let mut fast_headers = Vec::new();
for i in 0..DIFFICULTY_ADJUSTMENT_INTERVAL {
fast_headers.push(BlockHeader {
version: 1,
prev_block_hash: [i as u8; 32],
merkle_root: [0; 32],
timestamp: 1231006505 + i, bits: 0x1d00ffff,
nonce: 0,
});
}
let result = get_next_work_required(¤t_header, &fast_headers).unwrap();
assert!(result < 0x1d00ffff);
let mut slow_headers = Vec::new();
for i in 0..DIFFICULTY_ADJUSTMENT_INTERVAL {
slow_headers.push(BlockHeader {
version: 1,
prev_block_hash: [i as u8; 32],
merkle_root: [0; 32],
timestamp: 1231006505 + (i * 3600), bits: 0x1d00ffff,
nonce: 0,
});
}
let result = get_next_work_required(¤t_header, &slow_headers).unwrap();
assert!(
result >= 0x1d00ffff,
"Slow blocks should decrease difficulty (increase bits), got {result} (expected >= 0x1d00ffff)"
);
}
#[test]
fn test_sequence_number_boundaries() {
let tx_max_sequence = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![blvm_consensus::opcodes::OP_1],
sequence: 0xffffffff, }]
.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: vec![blvm_consensus::opcodes::OP_1].into(),
}]
.into(),
lock_time: 0,
};
let result = check_transaction(&tx_max_sequence).unwrap();
assert!(matches!(result, ValidationResult::Valid));
let tx_rbf = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![blvm_consensus::opcodes::OP_1],
sequence: SEQUENCE_RBF as u64, }]
.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: vec![blvm_consensus::opcodes::OP_1].into(),
}]
.into(),
lock_time: 0,
};
let result = check_transaction(&tx_rbf).unwrap();
assert!(matches!(result, ValidationResult::Valid));
}