use bsv_rs::primitives::bsv::schnorr::Schnorr;
use bsv_rs::primitives::bsv::shamir::{split_private_key, KeyShares};
use bsv_rs::primitives::bsv::sighash::{
compute_sighash, SighashParams, TxInput, TxOutput, SIGHASH_ALL, SIGHASH_FORKID,
};
use bsv_rs::primitives::ec::{recover_public_key, PrivateKey};
use bsv_rs::primitives::hash::{hash160, sha256, sha256_hmac, sha256d, sha512_hmac};
use bsv_rs::primitives::p256::{P256PrivateKey, P256PublicKey};
use bsv_rs::primitives::symmetric::SymmetricKey;
use bsv_rs::primitives::{from_base58, from_base64, from_hex, to_base58, to_base64, to_hex};
#[test]
fn test_full_key_derivation_workflow() {
let sender = PrivateKey::random();
let recipient = PrivateKey::random();
let invoice = "order-12345";
let recipient_child = recipient
.derive_child(&sender.public_key(), invoice)
.unwrap();
let sender_derived_pub = recipient
.public_key()
.derive_child(&sender, invoice)
.unwrap();
assert_eq!(
recipient_child.public_key().to_compressed(),
sender_derived_pub.to_compressed(),
"BRC-42 key derivation should produce matching keys"
);
}
#[test]
fn test_key_derivation_multiple_invoices() {
let alice = PrivateKey::random();
let bob = PrivateKey::random();
let invoices = ["invoice-001", "invoice-002", "payment-abc", ""];
let mut derived_keys = Vec::new();
for invoice in invoices {
let child = alice.derive_child(&bob.public_key(), invoice).unwrap();
derived_keys.push(child.public_key().to_compressed());
}
for i in 0..derived_keys.len() {
for j in (i + 1)..derived_keys.len() {
assert_ne!(
derived_keys[i], derived_keys[j],
"Different invoices should produce different keys"
);
}
}
}
#[test]
fn test_sign_and_verify_with_derived_keys() {
let alice = PrivateKey::random();
let bob = PrivateKey::random();
let child = alice
.derive_child(&bob.public_key(), "test-invoice")
.unwrap();
let msg_hash = sha256(b"Hello, BSV!");
let sig = child.sign(&msg_hash).unwrap();
assert!(
child.public_key().verify(&msg_hash, &sig),
"Signature should verify with derived key"
);
assert!(
sig.is_low_s(),
"Signature should be low-S (BIP 62 compliant)"
);
}
#[test]
fn test_signature_recovery_with_derived_keys() {
let alice = PrivateKey::random();
let bob = PrivateKey::random();
let child = alice
.derive_child(&bob.public_key(), "recovery-test")
.unwrap();
let child_pub = child.public_key();
let msg_hash = sha256(b"test message for recovery");
let signature = child.sign(&msg_hash).unwrap();
let mut found = false;
for recovery_id in 0..2u8 {
if let Ok(recovered) = recover_public_key(&msg_hash, &signature, recovery_id) {
if recovered.to_compressed() == child_pub.to_compressed() {
found = true;
break;
}
}
}
assert!(found, "Should recover the correct public key");
}
#[test]
fn test_symmetric_encryption_with_derived_key() {
let alice = PrivateKey::random();
let bob = PrivateKey::random();
let shared = alice.derive_shared_secret(&bob.public_key()).unwrap();
let x = shared.x();
let sym_key = SymmetricKey::from_bytes(&x).unwrap();
let plaintext = b"Secret message";
let ciphertext = sym_key.encrypt(plaintext).unwrap();
let decrypted = sym_key.decrypt(&ciphertext).unwrap();
assert_eq!(
plaintext.as_slice(),
&decrypted[..],
"Decrypted message should match original"
);
}
#[test]
fn test_symmetric_encryption_bidirectional() {
let alice = PrivateKey::random();
let bob = PrivateKey::random();
let alice_shared = alice.derive_shared_secret(&bob.public_key()).unwrap();
let bob_shared = bob.derive_shared_secret(&alice.public_key()).unwrap();
assert_eq!(
alice_shared.to_compressed(),
bob_shared.to_compressed(),
"ECDH shared secrets should match"
);
let alice_key = SymmetricKey::from_bytes(&alice_shared.x()).unwrap();
let bob_key = SymmetricKey::from_bytes(&bob_shared.x()).unwrap();
let message = b"Hello from Alice";
let ciphertext = alice_key.encrypt(message).unwrap();
let decrypted = bob_key.decrypt(&ciphertext).unwrap();
assert_eq!(message.as_slice(), &decrypted[..]);
}
#[test]
fn test_wif_address_roundtrip() {
let key = PrivateKey::random();
let wif = key.to_wif();
let recovered = PrivateKey::from_wif(&wif).unwrap();
let address = recovered.public_key().to_address();
assert_eq!(
key.to_bytes(),
recovered.to_bytes(),
"WIF roundtrip should preserve key"
);
assert!(
address.starts_with('1'),
"Mainnet address should start with '1'"
);
}
#[test]
fn test_testnet_wif_address() {
let key = PrivateKey::random();
let wif_testnet = key.to_wif_with_prefix(0xEF);
let recovered = PrivateKey::from_wif(&wif_testnet).unwrap();
let address = recovered.public_key().to_address_with_prefix(0x6F);
assert_eq!(key.to_bytes(), recovered.to_bytes());
assert!(
address.starts_with('m') || address.starts_with('n'),
"Testnet address should start with 'm' or 'n'"
);
}
#[test]
fn test_schnorr_proof_with_derived_keys() {
let alice = PrivateKey::random();
let bob = PrivateKey::random();
let alice_pub = alice.public_key();
let bob_pub = bob.public_key();
let shared = alice.derive_shared_secret(&bob_pub).unwrap();
let proof = Schnorr::generate_proof(&alice, &alice_pub, &bob_pub, &shared).unwrap();
assert!(
Schnorr::verify_proof(&alice_pub, &bob_pub, &shared, &proof),
"Schnorr proof should verify"
);
}
#[test]
fn test_schnorr_proof_fails_with_wrong_secret() {
let alice = PrivateKey::random();
let bob = PrivateKey::random();
let carol = PrivateKey::random();
let shared = alice.derive_shared_secret(&bob.public_key()).unwrap();
let wrong_shared = alice.derive_shared_secret(&carol.public_key()).unwrap();
let proof =
Schnorr::generate_proof(&alice, &alice.public_key(), &bob.public_key(), &shared).unwrap();
assert!(
!Schnorr::verify_proof(
&alice.public_key(),
&bob.public_key(),
&wrong_shared,
&proof
),
"Proof should not verify with wrong shared secret"
);
}
#[test]
fn test_shamir_with_wif_export() {
let original = PrivateKey::random();
let original_wif = original.to_wif();
let shares = split_private_key(&original, 3, 5).unwrap();
let backup = shares.to_backup_format();
let restored = KeyShares::from_backup_format(&backup[0..3]).unwrap();
let recovered = restored.recover_private_key().unwrap();
assert_eq!(
original_wif,
recovered.to_wif(),
"Recovered key should have same WIF"
);
}
#[test]
fn test_shamir_different_subsets() {
let key = PrivateKey::random();
let key_bytes = key.to_bytes();
let shares = split_private_key(&key, 3, 5).unwrap();
let backup = shares.to_backup_format();
let subsets: Vec<Vec<String>> = vec![
backup[0..3].to_vec(), backup[1..4].to_vec(), backup[2..5].to_vec(), vec![backup[0].clone(), backup[2].clone(), backup[4].clone()], ];
for (i, subset) in subsets.iter().enumerate() {
let restored = KeyShares::from_backup_format(subset).unwrap();
let recovered = restored.recover_private_key().unwrap();
assert_eq!(
key_bytes,
recovered.to_bytes(),
"Subset {} should recover the same key",
i
);
}
}
#[test]
fn test_p256_sign_verify_roundtrip() {
let private_key = P256PrivateKey::random();
let public_key = private_key.public_key();
let message = b"Hello, P-256!";
let signature = private_key.sign(message);
assert!(
public_key.verify(message, &signature),
"P-256 signature should verify"
);
}
#[test]
fn test_p256_key_serialization() {
let private_key = P256PrivateKey::random();
let public_key = private_key.public_key();
let priv_hex = private_key.to_hex();
let recovered_priv = P256PrivateKey::from_hex(&priv_hex).unwrap();
assert_eq!(private_key.to_bytes(), recovered_priv.to_bytes());
let compressed = public_key.to_compressed();
let uncompressed = public_key.to_uncompressed();
let from_compressed = P256PublicKey::from_bytes(&compressed).unwrap();
let from_uncompressed = P256PublicKey::from_bytes(&uncompressed).unwrap();
assert_eq!(public_key.x(), from_compressed.x());
assert_eq!(public_key.x(), from_uncompressed.x());
}
#[test]
fn test_hash_chain_for_key_derivation() {
let seed = b"master seed for key derivation";
let h1 = sha256(seed);
let h2 = sha256d(&h1);
let h3 = hash160(&h2);
assert_ne!(h1.as_slice(), h2.as_slice());
assert_ne!(h1[..20], h3);
let h1_repeat = sha256(seed);
assert_eq!(h1, h1_repeat);
}
#[test]
fn test_hmac_for_key_derivation() {
let master_key = b"master secret key";
let data = b"child key derivation";
let hmac256 = sha256_hmac(master_key, data);
let hmac512 = sha512_hmac(master_key, data);
assert_eq!(hmac256.len(), 32);
assert_eq!(hmac512.len(), 64);
let hmac256_repeat = sha256_hmac(master_key, data);
assert_eq!(hmac256, hmac256_repeat);
}
#[test]
fn test_encoding_roundtrips() {
let data = b"test data for encoding";
let hex = to_hex(data);
let from_hex_data = from_hex(&hex).unwrap();
assert_eq!(data.as_slice(), from_hex_data.as_slice());
let b64 = to_base64(data);
let from_b64_data = from_base64(&b64).unwrap();
assert_eq!(data.as_slice(), from_b64_data.as_slice());
let b58 = to_base58(data);
let from_b58_data = from_base58(&b58).unwrap();
assert_eq!(data.as_slice(), from_b58_data.as_slice());
}
#[test]
fn test_address_encoding_integration() {
let key = PrivateKey::random();
let pub_key = key.public_key();
let h160 = pub_key.hash160();
let manual_address = pub_key.to_address();
let (version, payload) = bsv_rs::primitives::from_base58_check(&manual_address).unwrap();
assert_eq!(version, vec![0x00]);
assert_eq!(payload, h160.to_vec());
}
#[test]
fn test_sighash_with_signature() {
let inputs = vec![TxInput {
txid: [0u8; 32],
output_index: 0,
script: vec![],
sequence: 0xffffffff,
}];
let outputs = vec![TxOutput {
satoshis: 50000,
script: vec![0x76, 0xa9, 0x14], }];
let subscript = vec![0x76, 0xa9, 0x14];
let params = SighashParams {
version: 1,
inputs: &inputs,
outputs: &outputs,
locktime: 0,
input_index: 0,
subscript: &subscript,
satoshis: 100000,
scope: SIGHASH_ALL | SIGHASH_FORKID,
};
let sighash = compute_sighash(¶ms);
let key = PrivateKey::random();
let signature = key.sign(&sighash).unwrap();
assert!(key.public_key().verify(&sighash, &signature));
}
#[test]
fn test_complete_payment_workflow() {
let merchant = PrivateKey::random();
let merchant_pub = merchant.public_key();
let customer = PrivateKey::random();
let customer_pub = customer.public_key();
let invoice = "INV-2024-001";
let customer_payment_key = customer.derive_child(&merchant_pub, invoice).unwrap();
let merchant_derived_pub = customer_pub.derive_child(&merchant, invoice).unwrap();
assert_eq!(
customer_payment_key.public_key().to_compressed(),
merchant_derived_pub.to_compressed()
);
let shared_secret = customer.derive_shared_secret(&merchant_pub).unwrap();
let encryption_key = SymmetricKey::from_bytes(&shared_secret.x()).unwrap();
let payment_data = b"Payment of $100 for order #12345";
let encrypted = encryption_key.encrypt(payment_data).unwrap();
let merchant_shared = merchant.derive_shared_secret(&customer_pub).unwrap();
let merchant_key = SymmetricKey::from_bytes(&merchant_shared.x()).unwrap();
let decrypted = merchant_key.decrypt(&encrypted).unwrap();
assert_eq!(payment_data.as_slice(), decrypted.as_slice());
let payment_hash = sha256(payment_data);
let signature = customer_payment_key.sign(&payment_hash).unwrap();
assert!(merchant_derived_pub.verify(&payment_hash, &signature));
}
#[test]
fn test_key_backup_and_recovery_workflow() {
let master_key = PrivateKey::random();
let master_pub = master_key.public_key();
let shares = split_private_key(&master_key, 3, 5).unwrap();
let backup_strings = shares.to_backup_format();
assert_eq!(backup_strings.len(), 5);
let selected_backups = vec![
backup_strings[0].clone(),
backup_strings[2].clone(),
backup_strings[3].clone(),
];
let restored_shares = KeyShares::from_backup_format(&selected_backups).unwrap();
let recovered_key = restored_shares.recover_private_key().unwrap();
assert_eq!(master_key.to_bytes(), recovered_key.to_bytes());
let message_hash = sha256(b"Test message after recovery");
let signature = recovered_key.sign(&message_hash).unwrap();
assert!(master_pub.verify(&message_hash, &signature));
}
#[test]
fn test_multi_party_schnorr_verification() {
let alice = PrivateKey::random();
let bob = PrivateKey::random();
let alice_shared = alice.derive_shared_secret(&bob.public_key()).unwrap();
let bob_shared = bob.derive_shared_secret(&alice.public_key()).unwrap();
assert_eq!(alice_shared.to_compressed(), bob_shared.to_compressed());
let alice_proof = Schnorr::generate_proof(
&alice,
&alice.public_key(),
&bob.public_key(),
&alice_shared,
)
.unwrap();
let bob_proof =
Schnorr::generate_proof(&bob, &bob.public_key(), &alice.public_key(), &bob_shared).unwrap();
assert!(Schnorr::verify_proof(
&alice.public_key(),
&bob.public_key(),
&alice_shared,
&alice_proof
));
assert!(Schnorr::verify_proof(
&bob.public_key(),
&alice.public_key(),
&bob_shared,
&bob_proof
));
}