use blvm_consensus::block::connect_block;
use blvm_consensus::constants::MAX_MONEY;
use blvm_consensus::economic::{calculate_fee, get_block_subsidy};
use blvm_consensus::opcodes::OP_1;
use blvm_consensus::transaction::{check_transaction, check_tx_inputs};
use blvm_consensus::*;
#[test]
fn test_input_value_overflow() {
let mut utxo_set = UtxoSet::default();
let large_value = MAX_MONEY / 2 + 1;
let outpoint1 = OutPoint {
hash: [1; 32],
index: 0,
};
let utxo1 = UTXO {
value: large_value,
script_pubkey: vec![].into(),
is_coinbase: false,
height: 0,
};
utxo_set.insert(outpoint1.clone(), std::sync::Arc::new(utxo1));
let outpoint2 = OutPoint {
hash: [2; 32],
index: 0,
};
let utxo2 = UTXO {
value: large_value, script_pubkey: vec![].into(),
is_coinbase: false,
height: 0,
};
utxo_set.insert(outpoint2.clone(), std::sync::Arc::new(utxo2));
let tx = Transaction {
version: 1,
inputs: vec![
TransactionInput {
prevout: outpoint1,
script_sig: vec![].into(),
sequence: 0xffffffff,
},
TransactionInput {
prevout: outpoint2,
script_sig: vec![],
sequence: 0xffffffff,
},
]
.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: vec![].into(),
}]
.into(),
lock_time: 0,
};
let result = check_tx_inputs(&tx, &utxo_set, 0).unwrap();
assert!(matches!(result.0, ValidationResult::Valid));
}
#[test]
fn test_output_value_overflow() {
let mut utxo_set = UtxoSet::default();
let outpoint = OutPoint {
hash: [1; 32],
index: 0,
};
let utxo = UTXO {
value: 1000000000,
script_pubkey: vec![].into(),
is_coinbase: false,
height: 0,
};
utxo_set.insert(outpoint, std::sync::Arc::new(utxo));
let large_value = i64::MAX / 2 + 1;
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: outpoint,
script_sig: vec![].into(),
sequence: 0xffffffff,
}]
.into(),
outputs: vec![
TransactionOutput {
value: large_value,
script_pubkey: vec![].into(),
},
TransactionOutput {
value: large_value, script_pubkey: vec![].into(),
},
]
.into(),
lock_time: 0,
};
let result = check_tx_inputs(&tx, &utxo_set, 0);
assert!(result.is_err());
if let Err(ConsensusError::TransactionValidation(msg)) = result {
assert!(
msg.contains("overflow"),
"Error message should mention overflow"
);
} else {
panic!("Expected TransactionValidation error for overflow");
}
}
#[test]
fn test_output_exceeds_max_money() {
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [0; 32],
index: 0xffffffff,
},
script_sig: vec![OP_1],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: MAX_MONEY + 1,
script_pubkey: vec![].into(),
}]
.into(),
lock_time: 0,
};
let result = check_transaction(&tx).unwrap();
assert!(matches!(result, ValidationResult::Invalid(_)));
}
#[test]
fn test_fee_calculation_no_overflow() {
let mut utxo_set = UtxoSet::default();
let input_value = MAX_MONEY / 2;
let output_value = MAX_MONEY / 2 - 1000;
let outpoint = OutPoint {
hash: [1; 32],
index: 0,
};
let utxo = UTXO {
value: input_value,
script_pubkey: vec![].into(),
is_coinbase: false,
height: 0,
};
utxo_set.insert(outpoint, std::sync::Arc::new(utxo));
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: outpoint,
script_sig: vec![].into(),
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: output_value,
script_pubkey: vec![].into(),
}]
.into(),
lock_time: 0,
};
let result = check_tx_inputs(&tx, &utxo_set, 0);
assert!(result.is_ok());
let (validation_result, fee) = result.unwrap();
assert!(matches!(validation_result, ValidationResult::Valid));
assert_eq!(fee, input_value - output_value);
assert!(fee > 0);
}
#[test]
fn test_coinbase_value_overflow() {
let utxo_set = UtxoSet::default();
let subsidy = get_block_subsidy(0); let large_fee = i64::MAX - subsidy + 1;
let coinbase = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [0; 32].into(),
index: 0xffffffff,
},
script_sig: vec![OP_1],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: MAX_MONEY + 1, script_pubkey: vec![].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].into(),
};
let witnesses: Vec<Vec<segwit::Witness>> = block
.transactions
.iter()
.map(|tx| tx.inputs.iter().map(|_| Vec::new()).collect())
.collect();
let ctx = blvm_consensus::block::BlockValidationContext::for_network(
blvm_consensus::types::Network::Mainnet,
);
let result = connect_block(&block, &witnesses, utxo_set, 0, &ctx);
assert!(result.is_ok());
let (validation_result, _, _) = result.unwrap();
assert!(matches!(validation_result, ValidationResult::Invalid(_)));
}
#[test]
fn test_total_fees_overflow() {
let mut utxo_set = UtxoSet::default();
let outpoint = OutPoint {
hash: [1; 32],
index: 0,
};
let utxo = UTXO {
value: MAX_MONEY / 2,
script_pubkey: vec![].into(),
is_coinbase: false,
height: 0,
};
utxo_set.insert(outpoint, std::sync::Arc::new(utxo));
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: outpoint,
script_sig: vec![].into(),
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: MAX_MONEY / 2 - 1000,
script_pubkey: vec![].into(),
}]
.into(),
lock_time: 0,
};
let result = calculate_fee(&tx, &utxo_set);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 1000);
}
#[test]
fn test_max_valid_values() {
let mut utxo_set = UtxoSet::default();
let outpoint = OutPoint {
hash: [1; 32],
index: 0,
};
let utxo = UTXO {
value: MAX_MONEY,
script_pubkey: vec![].into(),
is_coinbase: false,
height: 0,
};
utxo_set.insert(outpoint, std::sync::Arc::new(utxo));
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: outpoint,
script_sig: vec![].into(),
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: MAX_MONEY, script_pubkey: vec![].into(),
}]
.into(),
lock_time: 0,
};
let result = check_tx_inputs(&tx, &utxo_set, 0);
assert!(result.is_ok());
let (validation_result, fee) = result.unwrap();
assert!(matches!(validation_result, ValidationResult::Valid));
assert_eq!(fee, 0); }