use blvm_consensus::constants::*;
use blvm_consensus::test_utils::create_test_utxo_set_two_outputs;
use blvm_consensus::transaction::check_tx_inputs;
use blvm_consensus::types::ValidationResult;
use blvm_consensus::types::*;
#[test]
fn test_zero_fee_transaction() {
let utxo_set = create_test_utxo_set_two_outputs();
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: 100_000_000, script_pubkey: vec![0x51].into(),
}]
.into(),
lock_time: 0,
};
let result = check_tx_inputs(&tx, &utxo_set, 200);
assert!(result.is_ok(), "check_tx_inputs should succeed");
let (validation, fee) = result.unwrap();
assert!(
matches!(validation, ValidationResult::Valid),
"Zero fee transaction should be valid"
);
assert_eq!(fee, 0, "Fee should be zero");
}
#[test]
fn test_positive_fee_transaction() {
let utxo_set = create_test_utxo_set_two_outputs();
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: 99_000_000, script_pubkey: vec![0x51].into(),
}]
.into(),
lock_time: 0,
};
let result = check_tx_inputs(&tx, &utxo_set, 200);
assert!(result.is_ok(), "check_tx_inputs should succeed");
let (validation, fee) = result.unwrap();
assert!(
matches!(validation, ValidationResult::Valid),
"Positive fee transaction should be valid"
);
assert_eq!(
fee, 1_000_000,
"Fee should be 0.01 BTC (1,000,000 satoshis)"
);
}
#[test]
fn test_negative_fee_transaction() {
let utxo_set = create_test_utxo_set_two_outputs();
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: 101_000_000, script_pubkey: vec![0x51].into(),
}]
.into(),
lock_time: 0,
};
let result = check_tx_inputs(&tx, &utxo_set, 200);
assert!(result.is_ok(), "check_tx_inputs should succeed");
let (validation, _fee) = result.unwrap();
assert!(
matches!(validation, ValidationResult::Invalid(_)),
"Negative fee transaction should be invalid"
);
}
#[test]
fn test_fee_multiple_inputs() {
let utxo_set = create_test_utxo_set_two_outputs();
let tx = 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].into(),
index: 0,
},
script_sig: vec![0x52],
sequence: 0xfffffffe,
},
]
.into(),
outputs: vec![TransactionOutput {
value: 140_000_000, script_pubkey: vec![0x51].into(),
}]
.into(),
lock_time: 0,
};
let result = check_tx_inputs(&tx, &utxo_set, 200);
assert!(result.is_ok(), "check_tx_inputs should succeed");
let (validation, fee) = result.unwrap();
assert!(
matches!(validation, ValidationResult::Valid),
"Transaction with multiple inputs should be valid"
);
assert_eq!(
fee, 10_000_000,
"Fee should be 0.1 BTC (10,000,000 satoshis)"
);
}
#[test]
fn test_fee_multiple_outputs() {
let utxo_set = create_test_utxo_set_two_outputs();
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: 50_000_000,
script_pubkey: vec![0x51].into(),
},
TransactionOutput {
value: 49_000_000, script_pubkey: vec![0x52].into(),
},
]
.into(),
lock_time: 0,
};
let result = check_tx_inputs(&tx, &utxo_set, 200);
assert!(result.is_ok(), "check_tx_inputs should succeed");
let (validation, fee) = result.unwrap();
assert!(
matches!(validation, ValidationResult::Valid),
"Transaction with multiple outputs should be valid"
);
assert_eq!(
fee, 1_000_000,
"Fee should be 0.01 BTC (1,000,000 satoshis)"
);
}
#[test]
fn test_fee_maximum_money() {
let mut utxo_set = UtxoSet::default();
utxo_set.insert(
OutPoint {
hash: [1; 32].into(),
index: 0,
},
std::sync::Arc::new(UTXO {
value: MAX_MONEY,
script_pubkey: vec![0x51].into(),
height: 100,
is_coinbase: false,
}),
);
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: MAX_MONEY - 1, script_pubkey: vec![0x51].into(),
}]
.into(),
lock_time: 0,
};
let result = check_tx_inputs(&tx, &utxo_set, 200);
assert!(result.is_ok(), "check_tx_inputs should succeed");
let (validation, fee) = result.unwrap();
assert!(
matches!(validation, ValidationResult::Valid),
"Transaction with maximum money should be valid"
);
assert_eq!(fee, 1, "Fee should be 1 satoshi");
}
#[test]
fn test_fee_overflow_protection() {
let mut utxo_set = UtxoSet::default();
use blvm_consensus::constants::MAX_MONEY;
let large_value = MAX_MONEY / 2;
for i in 0..10 {
utxo_set.insert(
OutPoint {
hash: [i as u8; 32].into(),
index: 0,
},
std::sync::Arc::new(UTXO {
value: large_value, script_pubkey: vec![0x51].into(),
height: 100,
is_coinbase: false,
}),
);
}
let mut inputs = Vec::new();
for i in 0..10 {
inputs.push(TransactionInput {
prevout: OutPoint {
hash: [i as u8; 32].into(),
index: 0,
},
script_sig: vec![0x51],
sequence: 0xffffffff,
});
}
let tx = Transaction {
version: 1,
inputs: inputs.into(),
outputs: vec![TransactionOutput {
value: 1,
script_pubkey: vec![0x51].into(),
}]
.into(),
lock_time: 0,
};
let result = check_tx_inputs(&tx, &utxo_set, 200);
assert!(
result.is_ok() || result.is_err(),
"Fee calculation should handle overflow gracefully"
);
}
#[test]
fn test_coinbase_fee() {
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [0; 32].into(),
index: 0xffffffff, },
script_sig: vec![0x03, 0x01, 0x00, 0x00], sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 50_000_000,
script_pubkey: vec![0x51].into(),
}]
.into(),
lock_time: 0,
};
let utxo_set = UtxoSet::default();
let result = check_tx_inputs(&tx, &utxo_set, 200);
assert!(result.is_ok(), "check_tx_inputs should succeed");
let (validation, fee) = result.unwrap();
assert!(
matches!(validation, ValidationResult::Valid),
"Coinbase transaction should be valid"
);
assert_eq!(fee, 0, "Coinbase transaction fee should be 0");
}