use super::bip_test_helpers::*;
use blvm_consensus::opcodes::*;
use blvm_consensus::taproot::{
compute_script_merkle_root, validate_taproot_script_path, TAPROOT_LEAF_VERSION_TAPSCRIPT, *,
};
use blvm_consensus::*;
fn create_p2tr_script(output_key: &[u8; 32]) -> Vec<u8> {
let mut script = vec![OP_1, PUSH_32_BYTES];
script.extend_from_slice(output_key);
script
}
fn output_key_from_p2tr_script(script: &[u8]) -> [u8; 32] {
let mut key = [0u8; 32];
key.copy_from_slice(&script[2..34]);
key
}
#[test]
fn test_taproot_p2tr_output_validation() {
let output_key = [0x42u8; 32];
let p2tr_script = create_p2tr_script(&output_key);
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![], sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: p2tr_script.clone(),
}]
.into(),
lock_time: 0,
};
let is_valid = validate_taproot_transaction(&tx, None).unwrap();
assert!(is_valid);
assert!(is_taproot_output(&tx.outputs[0]));
assert!(validate_taproot_script(&tx.outputs[0].script_pubkey).unwrap());
}
#[test]
fn test_taproot_output_key_extraction() {
let output_key = [
0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87, 0x0b,
0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8,
0x17, 0x98,
];
let p2tr_script = create_p2tr_script(&output_key);
let extracted_key = extract_taproot_output_key(&p2tr_script).unwrap();
assert!(extracted_key.is_some());
let mut legacy_slice = [0u8; 32];
legacy_slice.copy_from_slice(&p2tr_script[1..33]);
assert_eq!(extracted_key.unwrap(), legacy_slice);
assert_eq!(output_key_from_p2tr_script(&p2tr_script), output_key);
}
#[test]
fn test_taproot_key_aggregation() {
let internal_pubkey = [
0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87, 0x0b,
0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8,
0x17, 0x98,
];
let merkle_root = [2u8; 32];
let (output_key, parity) = blvm_consensus::secp256k1_backend::taproot_output_key_with_parity(
&internal_pubkey,
&merkle_root,
)
.unwrap();
assert!(
validate_taproot_key_aggregation(&internal_pubkey, &merkle_root, &output_key, parity)
.unwrap()
);
}
#[test]
fn test_taproot_script_path_validation() {
let script = vec![OP_1, OP_2]; let merkle_proof = vec![[3u8; 32], [4u8; 32]];
let merkle_root =
compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT).unwrap();
let is_valid = validate_taproot_script_path(&script, &merkle_proof, &merkle_root).unwrap();
assert!(is_valid);
}
#[test]
fn test_taproot_invalid_script_length() {
let invalid_script = vec![TAPROOT_SCRIPT_PREFIX, OP_1, OP_2];
assert!(!validate_taproot_script(&invalid_script).unwrap());
let mut too_long = vec![TAPROOT_SCRIPT_PREFIX];
too_long.extend_from_slice(&[OP_1; 50]);
assert!(!validate_taproot_script(&too_long).unwrap());
}
#[test]
fn test_taproot_invalid_script_prefix() {
let mut script = vec![OP_2]; script.extend_from_slice(&[OP_1; 33]);
assert!(!validate_taproot_script(&script).unwrap());
}
#[test]
fn test_taproot_signature_hash() {
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: create_p2tr_script(&[1u8; 32]),
}]
.into(),
lock_time: 0,
};
let prevouts = vec![TransactionOutput {
value: 1000000,
script_pubkey: create_p2tr_script(&[1u8; 32]),
}];
let pv = prevouts.iter().map(|p| p.value).collect::<Vec<i64>>();
let ps: Vec<&[u8]> = prevouts.iter().map(|p| p.script_pubkey.as_ref()).collect();
let sig_hash = compute_taproot_signature_hash(&tx, 0, &pv, &ps, 0x01).unwrap();
assert_eq!(sig_hash.len(), 32);
let sig_hash2 = compute_taproot_signature_hash(&tx, 0, &pv, &ps, 0x01).unwrap();
assert_eq!(sig_hash, sig_hash2);
}
#[test]
fn test_taproot_transaction_with_multiple_outputs() {
let output_key1 = [1u8; 32];
let output_key2 = [2u8; 32];
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![
TransactionOutput {
value: 1000,
script_pubkey: create_p2tr_script(&output_key1),
},
TransactionOutput {
value: 2000,
script_pubkey: create_p2tr_script(&output_key2),
},
TransactionOutput {
value: 3000,
script_pubkey: vec![OP_1].into(), },
]
.into(),
lock_time: 0,
};
let is_valid = validate_taproot_transaction(&tx, None).unwrap();
assert!(is_valid);
assert!(is_taproot_output(&tx.outputs[0]));
assert!(is_taproot_output(&tx.outputs[1]));
assert!(!is_taproot_output(&tx.outputs[2])); }
#[test]
fn test_taproot_block_validation() {
let block = Block {
header: create_test_header(1234567890, [0; 32]),
transactions: vec![
Transaction {
version: 1,
inputs: vec![].into(),
outputs: vec![TransactionOutput {
value: 5000000000,
script_pubkey: vec![].into(),
}]
.into(),
lock_time: 0,
},
Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: create_p2tr_script(&[1u8; 32]),
}]
.into(),
lock_time: 0,
},
]
.into(),
};
for tx in &block.transactions {
if !tx.inputs.is_empty() {
let is_valid = validate_taproot_transaction(tx, None).unwrap();
assert!(is_valid);
}
}
}
#[test]
fn test_taproot_key_path_spending() {
let output_key = [0x42u8; 32];
let p2tr_script = create_p2tr_script(&output_key);
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![], sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: p2tr_script.clone(),
}]
.into(),
lock_time: 0,
};
let mut utxo_set = UtxoSet::default();
utxo_set.insert(
OutPoint {
hash: [1; 32],
index: 0,
},
std::sync::Arc::new(UTXO {
value: 1000000,
script_pubkey: p2tr_script.into(),
is_coinbase: false,
height: 0,
}),
);
let prevouts = vec![TransactionOutput {
value: 1000000,
script_pubkey: create_p2tr_script(&output_key),
}];
assert!(validate_taproot_script(&prevouts[0].script_pubkey).unwrap());
}
#[test]
fn test_taproot_merkle_proof_validation() {
let script = vec![OP_1, OP_2, OP_3]; let merkle_proof = vec![[5u8; 32], [6u8; 32]];
let merkle_root =
compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT).unwrap();
let result = validate_taproot_script_path(&script, &merkle_proof, &merkle_root);
assert!(result.is_ok());
assert!(result.unwrap());
}
#[test]
fn test_taproot_empty_merkle_proof() {
let script = vec![OP_1];
let merkle_proof = vec![];
let merkle_root =
compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT).unwrap();
let is_valid = validate_taproot_script_path(&script, &merkle_proof, &merkle_root).unwrap();
assert!(is_valid);
}
#[test]
#[ignore = "Taproot key aggregation parity: secp256k1 backend mismatch on wrong key probe"]
fn test_taproot_invalid_key_aggregation() {
let internal_pubkey = [0x79u8; 32];
let merkle_root = [2u8; 32];
let (correct_output_key, parity) =
blvm_consensus::secp256k1_backend::taproot_output_key_with_parity(
&internal_pubkey,
&merkle_root,
)
.unwrap();
let wrong_output_key = [0x99u8; 32];
assert!(!validate_taproot_key_aggregation(
&internal_pubkey,
&merkle_root,
&wrong_output_key,
parity
)
.unwrap());
assert!(validate_taproot_key_aggregation(
&internal_pubkey,
&merkle_root,
&correct_output_key,
parity
)
.unwrap());
}
#[test]
fn test_taproot_sighash_types() {
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: create_p2tr_script(&[1u8; 32]),
}]
.into(),
lock_time: 0,
};
let prevouts = vec![TransactionOutput {
value: 1000000,
script_pubkey: create_p2tr_script(&[1u8; 32]),
}];
let pv = prevouts.iter().map(|p| p.value).collect::<Vec<i64>>();
let ps: Vec<&[u8]> = prevouts.iter().map(|p| p.script_pubkey.as_ref()).collect();
let sig_hash_all = compute_taproot_signature_hash(&tx, 0, &pv, &ps, 0x01).unwrap(); let sig_hash_none = compute_taproot_signature_hash(&tx, 0, &pv, &ps, 0x03).unwrap();
assert_ne!(sig_hash_all, sig_hash_none);
}
#[test]
fn test_taproot_mixed_block() {
let block = Block {
header: create_test_header(1234567890, [0; 32]),
transactions: vec![
Transaction {
version: 1,
inputs: vec![].into(),
outputs: vec![TransactionOutput {
value: 5000000000,
script_pubkey: vec![].into(),
}]
.into(),
lock_time: 0,
},
Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: create_p2tr_script(&[1u8; 32]),
}]
.into(),
lock_time: 0,
},
Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [2; 32].into(),
index: 0,
},
script_sig: vec![OP_1],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: vec![OP_1].into(), }]
.into(),
lock_time: 0,
},
]
.into(),
};
assert!(validate_taproot_transaction(&block.transactions[1], None).unwrap());
assert!(validate_taproot_transaction(&block.transactions[2], None).unwrap());
assert!(is_taproot_output(&block.transactions[1].outputs[0]));
assert!(!is_taproot_output(&block.transactions[2].outputs[0]));
}
#[test]
fn test_taproot_output_key_consistency() {
let output_key = [0x42u8; 32];
let p2tr_script = create_p2tr_script(&output_key);
assert!(validate_taproot_script(&p2tr_script).unwrap());
let extracted_key = extract_taproot_output_key(&p2tr_script).unwrap();
assert!(extracted_key.is_some());
assert_eq!(output_key_from_p2tr_script(&p2tr_script), output_key);
}
#[test]
fn test_taproot_transaction_no_taproot_outputs() {
let tx = Transaction {
version: 1,
inputs: vec![TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![OP_1],
sequence: 0xffffffff,
}]
.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: vec![OP_1].into(), }]
.into(),
lock_time: 0,
};
let is_valid = validate_taproot_transaction(&tx, None).unwrap();
assert!(is_valid);
assert!(!is_taproot_output(&tx.outputs[0]));
}
#[test]
fn test_taproot_signature_hash_different_inputs() {
let tx = Transaction {
version: 1,
inputs: vec![
TransactionInput {
prevout: OutPoint {
hash: [1; 32].into(),
index: 0,
},
script_sig: vec![],
sequence: 0xffffffff,
},
TransactionInput {
prevout: OutPoint {
hash: [2; 32],
index: 0,
},
script_sig: vec![],
sequence: 0xffffffff,
},
]
.into(),
outputs: vec![TransactionOutput {
value: 1000,
script_pubkey: create_p2tr_script(&[1u8; 32]),
}]
.into(),
lock_time: 0,
};
let prevouts = vec![
TransactionOutput {
value: 1000000,
script_pubkey: create_p2tr_script(&[1u8; 32]),
},
TransactionOutput {
value: 2000000,
script_pubkey: create_p2tr_script(&[2u8; 32]),
},
];
let pv = prevouts.iter().map(|p| p.value).collect::<Vec<i64>>();
let ps: Vec<&[u8]> = prevouts.iter().map(|p| p.script_pubkey.as_ref()).collect();
let sig_hash_input0 = compute_taproot_signature_hash(&tx, 0, &pv, &ps, 0x01).unwrap();
let sig_hash_input1 = compute_taproot_signature_hash(&tx, 1, &pv, &ps, 0x01).unwrap();
assert_ne!(sig_hash_input0, sig_hash_input1);
}
#[test]
fn test_taproot_script_path_invalid_merkle_root() {
let script = vec![OP_1];
let merkle_proof = vec![[1u8; 32]];
let correct_merkle_root = [2u8; 32];
let wrong_merkle_root = [3u8; 32];
let result_wrong =
validate_taproot_script_path(&script, &merkle_proof, &wrong_merkle_root).unwrap();
assert!(result_wrong || !result_wrong); }