use std::error::Error;
use crate::bitcoin::bip341::{
TaprootLeaf, TaprootMerkleTree, Bip341Taproot, TaprootSpend, TaprootOutput,
LeafVersion, Bip341Error,
};
use crate::bitcoin::bip340::{XOnlyPublicKey, SchnorrSignature, Bip340Schnorr};
use bitcoin::hashes::{sha256, Hash};
fn create_test_key(seed: u8) -> XOnlyPublicKey -> Result<(), Box<dyn Error>> {
let mut key_bytes = [0u8; 32];
key_bytes.iter_mut().for_each(|b| *b = seed);
XOnlyPublicKey::from_bytes(key_bytes)
}
#[test]
fn test_leaf_creation() -> Result<(), Box<dyn Error>> {
let script = vec![0x51, 0x21, 0x03]; let leaf = TaprootLeaf::new(LeafVersion::Default, script.clone());
assert_eq!(leaf.version, LeafVersion::Default);
assert_eq!(leaf.script, script);
let future_version = 0xc1;
let leaf2 = TaprootLeaf::new(LeafVersion::Future(future_version), script.clone());
assert_eq!(leaf2.version, LeafVersion::Future(future_version));
assert_eq!(leaf2.script, script);
}
#[test]
fn test_leaf_hash() -> Result<(), Box<dyn Error>> {
let script1 = vec![0x51]; let script2 = vec![0x52]; let leaf1 = TaprootLeaf::new(LeafVersion::Default, script1);
let leaf2 = TaprootLeaf::new(LeafVersion::Default, script2);
let hash1 = leaf1.compute_leaf_hash();
let hash2 = leaf2.compute_leaf_hash();
assert_ne!(hash1, hash2);
let hash1_again = leaf1.compute_leaf_hash();
assert_eq!(hash1, hash1_again);
}
#[test]
fn test_merkle_tree_creation() -> Result<(), Box<dyn Error>> {
let mut tree = TaprootMerkleTree::new();
assert!(tree.leaves.is_empty());
let script = vec![0x51]; let leaf = TaprootLeaf::new(LeafVersion::Default, script);
tree.add_leaf(0, leaf);
assert_eq!(tree.leaves.len(), 1);
}
#[test]
fn test_merkle_root_single_leaf() -> Result<(), Box<dyn Error>> {
let mut tree = TaprootMerkleTree::new();
let script = vec![0x51]; let leaf = TaprootLeaf::new(LeafVersion::Default, script);
tree.add_leaf(0, leaf.clone());
let root_hash = tree.root_hash();
let leaf_hash = leaf.compute_leaf_hash();
assert_eq!(root_hash, leaf_hash);
}
#[test]
fn test_merkle_root_multiple_leaves() -> Result<(), Box<dyn Error>> {
let mut tree = TaprootMerkleTree::new();
let script1 = vec![0x51]; let script2 = vec![0x52]; let leaf1 = TaprootLeaf::new(LeafVersion::Default, script1);
let leaf2 = TaprootLeaf::new(LeafVersion::Default, script2);
tree.add_leaf(0, leaf1);
tree.add_leaf(1, leaf2);
let root_hash = tree.root_hash();
assert!(!root_hash.iter().all(|&b| b == 0));
let root_hash2 = tree.root_hash();
assert_eq!(root_hash, root_hash2);
}
#[test]
fn test_merkle_proof() -> Result<(), Box<dyn Error>> {
let mut tree = TaprootMerkleTree::new();
for i in 0..4 {
let script = vec![(0x51 + i) as u8]; let leaf = TaprootLeaf::new(LeafVersion::Default, script);
tree.add_leaf(i, leaf);
}
let _root_hash = tree.root_hash();
let proof0 = tree.get_proof(0);
assert_eq!(proof0.len(), 2);
let proof3 = tree.get_proof(3);
assert_eq!(proof3.len(), 2);
let empty_proof = tree.get_proof(10);
assert!(empty_proof.is_empty());
}
#[test]
fn test_taproot_output_creation() -> Result<(), Box<dyn Error>> {
let taproot = Bip341Taproot::new();
let internal_key = create_test_key(42);
let mut tree = TaprootMerkleTree::new();
let script = vec![0x51]; let leaf = TaprootLeaf::new(LeafVersion::Default, script);
tree.add_leaf(0, leaf);
let merkle_root = Some(tree.root_hash());
let output = taproot.create_taproot_output(internal_key, merkle_root)?;
assert_eq!(output.internal_key.to_bytes(), internal_key.to_bytes());
assert_eq!(output.merkle_root, merkle_root);
assert_ne!(output.output_key.to_bytes(), internal_key.to_bytes());
}
#[test]
fn test_taproot_output_creation_no_scripts() -> Result<(), Box<dyn Error>> {
let taproot = Bip341Taproot::new();
let internal_key = create_test_key(42);
let output = taproot.create_taproot_output(internal_key, None)?;
assert_eq!(output.internal_key.to_bytes(), internal_key.to_bytes());
assert_eq!(output.merkle_root, None);
assert_ne!(output.output_key.to_bytes(), internal_key.to_bytes());
}
#[test]
fn test_silent_leaf_creation() -> Result<(), Box<dyn Error>> {
let taproot = Bip341Taproot::new();
let silent_leaf = taproot.create_silent_leaf();
assert_eq!(silent_leaf.version, LeafVersion::Default);
let silent_leaf_hash = taproot.silent_leaf_hash();
assert_eq!(silent_leaf_hash, silent_leaf.compute_leaf_hash());
}
#[test]
fn test_taproot_tweak() -> Result<(), Box<dyn Error>> {
let taproot = Bip341Taproot::new();
let internal_key = create_test_key(42);
let mut tree = TaprootMerkleTree::new();
let script = vec![0x51]; let leaf = TaprootLeaf::new(LeafVersion::Default, script);
tree.add_leaf(0, leaf);
let merkle_root = Some(tree.root_hash());
let tweak = taproot.compute_taproot_tweak(&internal_key, merkle_root);
assert!(!tweak.iter().all(|&b| b == 0));
let tweak2 = taproot.compute_taproot_tweak(&internal_key, merkle_root);
assert_eq!(tweak, tweak2);
let tweak_no_script = taproot.compute_taproot_tweak(&internal_key, None);
assert_ne!(tweak, tweak_no_script);
}
#[test]
fn test_key_path_spending() -> Result<(), Box<dyn Error>> {
let taproot = Bip341Taproot::new();
let schnorr = Bip340Schnorr::new();
let key_pair = schnorr.generate_key_pair();
let internal_key = key_pair.public_key;
let output = taproot.create_taproot_output(internal_key, None)?;
let message = b"Test message for key path spending";
let signature = schnorr.sign(&key_pair, message)?;
let spend = TaprootSpend::KeyPath {
output_key: output.output_key,
signature,
};
let result = taproot.verify_spend(&spend, message);
assert!(result.is_ok());
assert!(result?);
}
#[test]
fn test_version_conversions() -> Result<(), Box<dyn Error>> {
let default_version = LeafVersion::Default;
let default_byte: u8 = default_version.into();
assert_eq!(default_byte, 0xc0);
let version_from_byte = LeafVersion::from(default_byte);
assert_eq!(version_from_byte, LeafVersion::Default);
let future_byte = 0xc1;
let future_version = LeafVersion::Future(future_byte);
let byte_from_future: u8 = future_version.into();
assert_eq!(byte_from_future, 0xc1);
let version_from_future_byte = LeafVersion::from(future_byte);
match version_from_future_byte {
LeafVersion::Future(v) => assert_eq!(v, future_byte),
_ => panic!("Expected Future variant"),
}
}