use blvm_consensus::constants::*;
use blvm_consensus::economic::get_block_subsidy;
use blvm_consensus::test_utils::create_coinbase_tx;
use blvm_consensus::transaction::is_coinbase;
use blvm_consensus::types::*;
#[test]
fn test_coinbase_script_sig_minimum_length() {
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [0; 32],
index: 0xffffffff,
},
script_sig: vec![0x51, 0x52], sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 50_000_000,
script_pubkey: vec![0x51],
}]
.into(),
lock_time: 0,
};
assert!(
is_coinbase(&tx),
"Transaction should be identified as coinbase"
);
}
#[test]
fn test_coinbase_script_sig_below_minimum() {
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [0; 32],
index: 0xffffffff,
},
script_sig: vec![0x51], sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 50_000_000,
script_pubkey: vec![0x51],
}]
.into(),
lock_time: 0,
};
use blvm_consensus::transaction::check_transaction;
let result = check_transaction(&tx);
assert!(result.is_ok(), "check_transaction should succeed");
let validation = result.unwrap();
assert!(
matches!(validation, ValidationResult::Invalid(_)),
"Coinbase with scriptSig < 2 bytes should be invalid"
);
}
#[test]
fn test_coinbase_script_sig_maximum_length() {
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [0; 32],
index: 0xffffffff,
},
script_sig: vec![0x51; 100], sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 50_000_000,
script_pubkey: vec![0x51],
}]
.into(),
lock_time: 0,
};
use blvm_consensus::transaction::check_transaction;
let result = check_transaction(&tx);
assert!(result.is_ok(), "check_transaction should succeed");
let validation = result.unwrap();
assert!(
matches!(validation, ValidationResult::Valid),
"Coinbase with scriptSig = 100 bytes should be valid"
);
}
#[test]
fn test_coinbase_script_sig_above_maximum() {
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [0; 32],
index: 0xffffffff,
},
script_sig: vec![0x51; 101], sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 50_000_000,
script_pubkey: vec![0x51],
}]
.into(),
lock_time: 0,
};
use blvm_consensus::transaction::check_transaction;
let result = check_transaction(&tx);
assert!(result.is_ok(), "check_transaction should succeed");
let validation = result.unwrap();
assert!(
matches!(validation, ValidationResult::Invalid(_)),
"Coinbase with scriptSig > 100 bytes should be invalid"
);
}
#[test]
fn test_coinbase_output_exact_subsidy() {
let height = 100;
let subsidy = get_block_subsidy(height);
let coinbase = create_coinbase_tx(subsidy);
assert!(is_coinbase(&coinbase), "Should be coinbase");
assert_eq!(
coinbase.outputs[0].value, subsidy,
"Output should equal subsidy"
);
}
#[test]
fn test_coinbase_output_exceeds_subsidy() {
let height = 100;
let subsidy = get_block_subsidy(height);
let coinbase = create_coinbase_tx(subsidy + 1);
assert!(is_coinbase(&coinbase), "Should be coinbase");
assert!(
coinbase.outputs[0].value > subsidy,
"Output exceeds subsidy"
);
}
#[test]
fn test_coinbase_output_with_fees() {
let height = 100;
let subsidy = get_block_subsidy(height);
let fees = 1_000_000;
let coinbase = create_coinbase_tx(subsidy + fees);
assert!(is_coinbase(&coinbase), "Should be coinbase");
assert_eq!(
coinbase.outputs[0].value,
subsidy + fees,
"Output should equal subsidy + fees"
);
}
#[test]
fn test_coinbase_output_exceeds_max_money() {
let coinbase = create_coinbase_tx(MAX_MONEY + 1);
use blvm_consensus::transaction::check_transaction;
let result = check_transaction(&coinbase);
assert!(result.is_ok(), "check_transaction should succeed");
let validation = result.unwrap();
assert!(
matches!(validation, ValidationResult::Invalid(_)),
"Coinbase output > MAX_MONEY should be invalid"
);
}
#[test]
fn test_coinbase_identification_valid() {
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [0; 32],
index: 0xffffffff,
},
script_sig: vec![0x51, 0x52],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 50_000_000,
script_pubkey: vec![0x51],
}]
.into(),
lock_time: 0,
};
assert!(is_coinbase(&tx), "Valid coinbase should be identified");
}
#[test]
fn test_coinbase_identification_multiple_inputs() {
let tx = Transaction {
version: 1,
inputs: vec![
TransactionInput {
prevout: OutPoint {
hash: [1; 32],
index: 0,
},
script_sig: vec![0x51],
sequence: 0xffffffff,
},
TransactionInput {
prevout: OutPoint {
hash: [2; 32],
index: 0,
},
script_sig: vec![0x52],
sequence: 0xfffffffe,
},
]
.into(),
outputs: vec![TransactionOutput {
value: 50_000_000,
script_pubkey: vec![0x51],
}]
.into(),
lock_time: 0,
};
assert!(
!is_coinbase(&tx),
"Transaction with multiple inputs should not be coinbase"
);
}
#[test]
fn test_coinbase_identification_non_null_prevout() {
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32], index: 0,
},
script_sig: vec![0x51],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 50_000_000,
script_pubkey: vec![0x51],
}]
.into(),
lock_time: 0,
};
assert!(
!is_coinbase(&tx),
"Transaction with non-null prevout should not be coinbase"
);
}