use blvm_consensus::types::OutPoint;
use blvm_consensus::{Transaction, TransactionInput, TransactionOutput};
use blvm_protocol::bip158::{
build_block_filter, match_filter, CompactBlockFilter, BIP158_M, BIP158_P,
};
fn create_test_transaction_with_outputs(output_scripts: Vec<Vec<u8>>) -> Transaction {
Transaction {
version: 1,
inputs: blvm_consensus::tx_inputs![TransactionInput {
prevout: OutPoint {
hash: [0u8; 32],
index: 0xffffffff,
},
script_sig: vec![blvm_consensus::opcodes::OP_1],
sequence: 0xffffffff,
}],
outputs: output_scripts
.into_iter()
.map(|script| TransactionOutput {
value: 1000,
script_pubkey: script,
})
.collect::<Vec<_>>()
.into(),
lock_time: 0,
}
}
fn create_coinbase_transaction() -> Transaction {
Transaction {
version: 1,
inputs: blvm_consensus::tx_inputs![TransactionInput {
prevout: OutPoint {
hash: [0u8; 32],
index: 0xffffffff,
},
script_sig: vec![0x04, 0x00, 0x00, 0x00, 0x00], sequence: 0xffffffff,
}],
outputs: blvm_consensus::tx_outputs![TransactionOutput {
value: 50_0000_0000,
script_pubkey: vec![
blvm_consensus::opcodes::OP_DUP,
blvm_consensus::opcodes::OP_HASH160,
blvm_consensus::opcodes::PUSH_20_BYTES,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00
],
}],
lock_time: 0,
}
}
#[test]
fn test_build_block_filter_basic() {
let tx = create_test_transaction_with_outputs(vec![
vec![
blvm_consensus::opcodes::OP_DUP,
blvm_consensus::opcodes::OP_HASH160,
blvm_consensus::opcodes::PUSH_20_BYTES,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
], ]);
let block = vec![tx];
let previous_scripts = vec![];
let filter = build_block_filter(&block, &previous_scripts).unwrap();
assert!(!filter.filter_data.is_empty() || filter.num_elements == 0);
}
#[test]
fn test_build_block_filter_with_multiple_transactions() {
let tx1 = create_test_transaction_with_outputs(vec![vec![
blvm_consensus::opcodes::OP_DUP,
blvm_consensus::opcodes::OP_HASH160,
blvm_consensus::opcodes::PUSH_20_BYTES,
0x11,
0x11,
0x11,
0x11,
0x11,
0x11,
0x11,
0x11,
0x11,
0x11,
0x11,
0x11,
0x11,
0x11,
0x11,
0x11,
0x11,
0x11,
0x11,
0x11,
]]);
let tx2 = create_test_transaction_with_outputs(vec![vec![
blvm_consensus::opcodes::OP_DUP,
blvm_consensus::opcodes::OP_HASH160,
blvm_consensus::opcodes::PUSH_20_BYTES,
0x22,
0x22,
0x22,
0x22,
0x22,
0x22,
0x22,
0x22,
0x22,
0x22,
0x22,
0x22,
0x22,
0x22,
0x22,
0x22,
0x22,
0x22,
0x22,
0x22,
]]);
let block = vec![tx1, tx2];
let previous_scripts = vec![];
let filter = build_block_filter(&block, &previous_scripts).unwrap();
assert!(filter.num_elements >= 2);
}
#[test]
fn test_build_block_filter_with_empty_block() {
let coinbase = create_coinbase_transaction();
let block = vec![coinbase];
let previous_scripts = vec![];
let filter = build_block_filter(&block, &previous_scripts).unwrap();
assert!(filter.num_elements >= 1);
}
#[test]
fn test_build_block_filter_with_previous_scripts() {
let tx = create_test_transaction_with_outputs(vec![vec![
blvm_consensus::opcodes::OP_DUP,
blvm_consensus::opcodes::OP_HASH160,
blvm_consensus::opcodes::PUSH_20_BYTES,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
]]);
let block = vec![tx];
let previous_scripts = vec![vec![
blvm_consensus::opcodes::OP_DUP,
blvm_consensus::opcodes::OP_HASH160,
blvm_consensus::opcodes::PUSH_20_BYTES,
0x33,
0x33,
0x33,
0x33,
0x33,
0x33,
0x33,
0x33,
0x33,
0x33,
0x33,
0x33,
0x33,
0x33,
0x33,
0x33,
0x33,
0x33,
0x33,
0x33,
]];
let filter = build_block_filter(&block, &previous_scripts).unwrap();
assert!(filter.num_elements >= 2);
}
#[test]
fn test_match_filter_positive_match() {
let script = vec![
blvm_consensus::opcodes::OP_DUP,
blvm_consensus::opcodes::OP_HASH160,
blvm_consensus::opcodes::PUSH_20_BYTES,
0x44,
0x44,
0x44,
0x44,
0x44,
0x44,
0x44,
0x44,
0x44,
0x44,
0x44,
0x44,
0x44,
0x44,
0x44,
0x44,
0x44,
0x44,
0x44,
0x44,
];
let tx = create_test_transaction_with_outputs(vec![script.clone()]);
let block = vec![tx];
let previous_scripts = vec![];
let filter = build_block_filter(&block, &previous_scripts).unwrap();
assert!(match_filter(&filter, &script));
}
#[test]
fn test_match_filter_negative_match() {
let script_in_block = vec![
blvm_consensus::opcodes::OP_DUP,
blvm_consensus::opcodes::OP_HASH160,
blvm_consensus::opcodes::PUSH_20_BYTES,
0x55,
0x55,
0x55,
0x55,
0x55,
0x55,
0x55,
0x55,
0x55,
0x55,
0x55,
0x55,
0x55,
0x55,
0x55,
0x55,
0x55,
0x55,
0x55,
0x55,
];
let script_not_in_block = vec![
blvm_consensus::opcodes::OP_DUP,
blvm_consensus::opcodes::OP_HASH160,
blvm_consensus::opcodes::PUSH_20_BYTES,
0x66,
0x66,
0x66,
0x66,
0x66,
0x66,
0x66,
0x66,
0x66,
0x66,
0x66,
0x66,
0x66,
0x66,
0x66,
0x66,
0x66,
0x66,
0x66,
0x66,
];
let tx = create_test_transaction_with_outputs(vec![script_in_block]);
let block = vec![tx];
let previous_scripts = vec![];
let filter = build_block_filter(&block, &previous_scripts).unwrap();
let _matches = match_filter(&filter, &script_not_in_block);
assert!(filter.num_elements > 0);
}
#[test]
fn test_match_filter_with_empty_filter() {
let empty_filter = CompactBlockFilter {
filter_data: Vec::new(),
num_elements: 0,
};
let script = vec![
blvm_consensus::opcodes::OP_DUP,
blvm_consensus::opcodes::OP_HASH160,
blvm_consensus::opcodes::PUSH_20_BYTES,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
];
assert!(!match_filter(&empty_filter, &script));
}
#[test]
fn test_match_filter_with_previous_scripts() {
let script_in_output = vec![
blvm_consensus::opcodes::OP_DUP,
blvm_consensus::opcodes::OP_HASH160,
blvm_consensus::opcodes::PUSH_20_BYTES,
0x77,
0x77,
0x77,
0x77,
0x77,
0x77,
0x77,
0x77,
0x77,
0x77,
0x77,
0x77,
0x77,
0x77,
0x77,
0x77,
0x77,
0x77,
0x77,
0x77,
];
let script_in_previous = vec![
blvm_consensus::opcodes::OP_DUP,
blvm_consensus::opcodes::OP_HASH160,
blvm_consensus::opcodes::PUSH_20_BYTES,
0x88,
0x88,
0x88,
0x88,
0x88,
0x88,
0x88,
0x88,
0x88,
0x88,
0x88,
0x88,
0x88,
0x88,
0x88,
0x88,
0x88,
0x88,
0x88,
0x88,
];
let tx = create_test_transaction_with_outputs(vec![script_in_output]);
let block = vec![tx];
let previous_scripts = vec![script_in_previous.clone()];
let filter = build_block_filter(&block, &previous_scripts).unwrap();
assert!(filter.num_elements >= 2);
let matches = match_filter(&filter, &script_in_previous);
if !matches {
assert!(
filter.num_elements >= 1,
"Filter should contain at least the previous script"
);
}
}
#[test]
fn test_filter_roundtrip() {
let scripts = vec![
vec![
blvm_consensus::opcodes::OP_DUP,
blvm_consensus::opcodes::OP_HASH160,
blvm_consensus::opcodes::PUSH_20_BYTES,
0x99,
0x99,
0x99,
0x99,
0x99,
0x99,
0x99,
0x99,
0x99,
0x99,
0x99,
0x99,
0x99,
0x99,
0x99,
0x99,
0x99,
0x99,
0x99,
0x99,
],
vec![
blvm_consensus::opcodes::OP_DUP,
blvm_consensus::opcodes::OP_HASH160,
blvm_consensus::opcodes::PUSH_20_BYTES,
0xaa,
0xaa,
0xaa,
0xaa,
0xaa,
0xaa,
0xaa,
0xaa,
0xaa,
0xaa,
0xaa,
0xaa,
0xaa,
0xaa,
0xaa,
0xaa,
0xaa,
0xaa,
0xaa,
0xaa,
],
];
let tx = create_test_transaction_with_outputs(scripts.clone());
let block = vec![tx];
let previous_scripts = vec![];
let filter = build_block_filter(&block, &previous_scripts).unwrap();
assert!(filter.num_elements >= 1);
for script in &scripts {
let matches = match_filter(&filter, script);
if !matches {
assert!(filter.num_elements > 0, "Filter should contain elements");
}
}
}
#[test]
fn test_filter_with_duplicate_scripts() {
let script = vec![
blvm_consensus::opcodes::OP_DUP,
blvm_consensus::opcodes::OP_HASH160,
blvm_consensus::opcodes::PUSH_20_BYTES,
0xbb,
0xbb,
0xbb,
0xbb,
0xbb,
0xbb,
0xbb,
0xbb,
0xbb,
0xbb,
0xbb,
0xbb,
0xbb,
0xbb,
0xbb,
0xbb,
0xbb,
0xbb,
0xbb,
0xbb,
];
let tx = create_test_transaction_with_outputs(vec![script.clone(), script.clone()]);
let block = vec![tx];
let previous_scripts = vec![];
let filter = build_block_filter(&block, &previous_scripts).unwrap();
assert!(match_filter(&filter, &script));
assert_eq!(filter.num_elements, 1);
}
#[test]
fn test_filter_constants() {
assert_eq!(BIP158_P, 19);
assert_eq!(BIP158_M, 1 << 19); assert_eq!(BIP158_M, 524288);
}