use bsv_rs::primitives::ec::{PrivateKey, PublicKey};
use bsv_rs::primitives::hash::sha256;
use serde::Deserialize;
use std::fs;
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct Brc42PrivateVector {
sender_public_key: String,
recipient_private_key: String,
invoice_number: String,
private_key: String,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct Brc42PublicVector {
sender_private_key: String,
recipient_public_key: String,
invoice_number: String,
public_key: String,
}
fn load_brc42_private_vectors() -> Vec<Brc42PrivateVector> {
let data = fs::read_to_string("tests/vectors/brc42_private.json")
.expect("Failed to read brc42_private.json");
serde_json::from_str(&data).expect("Failed to parse brc42_private.json")
}
fn load_brc42_public_vectors() -> Vec<Brc42PublicVector> {
let data = fs::read_to_string("tests/vectors/brc42_public.json")
.expect("Failed to read brc42_public.json");
serde_json::from_str(&data).expect("Failed to parse brc42_public.json")
}
#[test]
fn test_brc42_private_derivation() {
let vectors = load_brc42_private_vectors();
for (i, v) in vectors.iter().enumerate() {
let sender_pub = PublicKey::from_hex(&v.sender_public_key)
.unwrap_or_else(|e| panic!("Vector {}: Failed to parse sender public key: {}", i, e));
let recipient_priv = PrivateKey::from_hex(&v.recipient_private_key).unwrap_or_else(|e| {
panic!("Vector {}: Failed to parse recipient private key: {}", i, e)
});
let invoice_str = &v.invoice_number;
let derived = recipient_priv
.derive_child(&sender_pub, invoice_str)
.unwrap_or_else(|e| panic!("Vector {}: Failed to derive child key: {}", i, e));
assert_eq!(
derived.to_hex(),
v.private_key,
"Vector {}: Derived private key does not match expected",
i
);
}
}
#[test]
fn test_brc42_public_derivation() {
let vectors = load_brc42_public_vectors();
for (i, v) in vectors.iter().enumerate() {
let sender_priv = PrivateKey::from_hex(&v.sender_private_key)
.unwrap_or_else(|e| panic!("Vector {}: Failed to parse sender private key: {}", i, e));
let recipient_pub = PublicKey::from_hex(&v.recipient_public_key).unwrap_or_else(|e| {
panic!("Vector {}: Failed to parse recipient public key: {}", i, e)
});
let invoice_str = &v.invoice_number;
let derived = recipient_pub
.derive_child(&sender_priv, invoice_str)
.unwrap_or_else(|e| panic!("Vector {}: Failed to derive child key: {}", i, e));
assert_eq!(
derived.to_hex(),
v.public_key,
"Vector {}: Derived public key does not match expected",
i
);
}
}
#[test]
fn test_brc42_consistency() {
let vectors = load_brc42_private_vectors();
for (i, v) in vectors.iter().enumerate() {
let sender_pub = PublicKey::from_hex(&v.sender_public_key).unwrap();
let recipient_priv = PrivateKey::from_hex(&v.recipient_private_key).unwrap();
let invoice_str = &v.invoice_number;
let derived_priv = recipient_priv
.derive_child(&sender_pub, invoice_str)
.unwrap();
let derived_pub_from_priv = derived_priv.public_key();
assert!(
derived_pub_from_priv.to_hex().len() == 66,
"Vector {}: Derived public key has wrong length",
i
);
}
}
#[test]
fn test_wif_roundtrip_known_vectors() {
let test_cases = [
(
"KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn",
"0000000000000000000000000000000000000000000000000000000000000001",
),
(
"KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ",
"1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd",
),
];
for (wif, expected_hex) in test_cases {
let key = PrivateKey::from_wif(wif).expect("Failed to parse WIF");
assert_eq!(
key.to_hex(),
expected_hex,
"WIF {} decoded incorrectly",
wif
);
assert_eq!(key.to_wif(), wif, "WIF roundtrip failed");
}
}
#[test]
fn test_public_key_known_vectors() {
let test_cases = [
(
"0000000000000000000000000000000000000000000000000000000000000001",
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
),
(
"0000000000000000000000000000000000000000000000000000000000000002",
"02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
),
(
"0000000000000000000000000000000000000000000000000000000000000003",
"02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9",
),
];
for (priv_hex, expected_pub_hex) in test_cases {
let priv_key = PrivateKey::from_hex(priv_hex).expect("Failed to parse private key");
let pub_key = priv_key.public_key();
assert_eq!(
pub_key.to_hex(),
expected_pub_hex,
"Public key mismatch for private key {}",
priv_hex
);
}
}
#[test]
fn test_address_generation_known_vectors() {
let test_cases = [
(
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH",
),
];
for (pub_hex, expected_address) in test_cases {
let pub_key = PublicKey::from_hex(pub_hex).expect("Failed to parse public key");
let address = pub_key.to_address();
assert_eq!(
address, expected_address,
"Address mismatch for {}",
pub_hex
);
}
}
#[test]
fn test_sign_and_verify_roundtrip() {
let private_key = PrivateKey::random();
let public_key = private_key.public_key();
let messages = [
b"Hello, BSV!".to_vec(),
b"".to_vec(),
vec![0u8; 100],
vec![0xff; 256],
];
for msg in &messages {
let msg_hash = sha256(msg);
let signature = private_key.sign(&msg_hash).expect("Signing failed");
assert!(
public_key.verify(&msg_hash, &signature),
"Signature verification failed for message of length {}",
msg.len()
);
assert!(
signature.is_low_s(),
"Signature is not low-S for message of length {}",
msg.len()
);
}
}
#[test]
fn test_ecdh_shared_secret() {
let alice = PrivateKey::random();
let bob = PrivateKey::random();
let alice_shared = alice
.derive_shared_secret(&bob.public_key())
.expect("Alice shared secret failed");
let bob_shared = bob
.derive_shared_secret(&alice.public_key())
.expect("Bob shared secret failed");
assert_eq!(
alice_shared.to_compressed(),
bob_shared.to_compressed(),
"ECDH shared secrets do not match"
);
}
#[test]
fn test_deterministic_signatures() {
let private_key =
PrivateKey::from_hex("0000000000000000000000000000000000000000000000000000000000000001")
.unwrap();
let msg_hash = sha256(b"test message");
let sig1 = private_key.sign(&msg_hash).unwrap();
let sig2 = private_key.sign(&msg_hash).unwrap();
assert_eq!(sig1.r(), sig2.r(), "R components should be equal");
assert_eq!(sig1.s(), sig2.s(), "S components should be equal");
}
#[test]
fn test_public_key_recovery() {
use bsv_rs::primitives::ec::recover_public_key;
let private_key = PrivateKey::random();
let public_key = private_key.public_key();
let msg_hash = sha256(b"test message for recovery");
let signature = private_key.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() == public_key.to_compressed() {
found = true;
break;
}
}
}
assert!(found, "Failed to recover the correct public key");
}
#[test]
fn test_rfc6979_vector_1() {
let key =
PrivateKey::from_hex("cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50")
.unwrap();
let hash = sha256(b"sample");
let sig = key.sign(&hash).unwrap();
let der_hex = hex::encode(sig.to_der());
assert_eq!(
der_hex,
"3045022100af340daf02cc15c8d5d08d7735dfe6b98a474ed373bdb5fbecf7571be52b384202205009fb27f37034a9b24b707b7c6b79ca23ddef9e25f7282e8a797efe53a8f124",
"RFC 6979 vector 1 DER mismatch"
);
assert!(key.public_key().verify(&hash, &sig));
}
#[test]
fn test_rfc6979_vector_2_satoshi_nakamoto() {
let key =
PrivateKey::from_hex("0000000000000000000000000000000000000000000000000000000000000001")
.unwrap();
let hash = sha256(b"Satoshi Nakamoto");
let sig = key.sign(&hash).unwrap();
let der_hex = hex::encode(sig.to_der());
assert_eq!(
der_hex,
"3045022100934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d802202442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5",
"RFC 6979 vector 2 (Satoshi Nakamoto) DER mismatch"
);
assert!(key.public_key().verify(&hash, &sig));
}
#[test]
fn test_rfc6979_vector_3_n_minus_1() {
let key =
PrivateKey::from_hex("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140")
.unwrap();
let hash = sha256(b"Satoshi Nakamoto");
let sig = key.sign(&hash).unwrap();
let der_hex = hex::encode(sig.to_der());
assert_eq!(
der_hex,
"3045022100fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d002206b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5",
"RFC 6979 vector 3 (n-1) DER mismatch"
);
assert!(key.public_key().verify(&hash, &sig));
}
#[test]
fn test_rfc6979_vector_4_alan_turing() {
let key =
PrivateKey::from_hex("f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181")
.unwrap();
let hash = sha256(b"Alan Turing");
let sig = key.sign(&hash).unwrap();
let der_hex = hex::encode(sig.to_der());
assert_eq!(
der_hex,
"304402207063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c022058dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea",
"RFC 6979 vector 4 (Alan Turing) DER mismatch"
);
assert!(key.public_key().verify(&hash, &sig));
}
#[test]
fn test_rfc6979_vector_5_tears_in_rain() {
let key =
PrivateKey::from_hex("0000000000000000000000000000000000000000000000000000000000000001")
.unwrap();
let hash =
sha256(b"All those moments will be lost in time, like tears in rain. Time to die...");
let sig = key.sign(&hash).unwrap();
let der_hex = hex::encode(sig.to_der());
assert_eq!(
der_hex,
"30450221008600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b0220547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21",
"RFC 6979 vector 5 (tears in rain) DER mismatch"
);
assert!(key.public_key().verify(&hash, &sig));
}
#[test]
fn test_rfc6979_vector_6_computer_disease() {
let key =
PrivateKey::from_hex("e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2")
.unwrap();
let hash = sha256(
b"There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!",
);
let sig = key.sign(&hash).unwrap();
let der_hex = hex::encode(sig.to_der());
assert_eq!(
der_hex,
"3045022100b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b0220279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6",
"RFC 6979 vector 6 (computer disease) DER mismatch"
);
assert!(key.public_key().verify(&hash, &sig));
}
#[test]
fn test_malformed_der_empty() {
use bsv_rs::primitives::ec::Signature;
let result = Signature::from_der(&[]);
assert!(result.is_err(), "Empty DER should fail");
}
#[test]
fn test_malformed_der_bad_magic() {
use bsv_rs::primitives::ec::Signature;
let sig: Vec<u8> = vec![
0x31, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1,
0xd3, 0xa1, 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, 0xc6, 0xc6, 0x15,
0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07,
0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac,
0x46, 0x22, 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09,
];
let result = Signature::from_der(&sig);
assert!(result.is_err(), "Bad magic byte 0x31 should fail");
}
#[test]
fn test_malformed_der_bad_r_marker() {
use bsv_rs::primitives::ec::Signature;
let sig: Vec<u8> = vec![
0x30, 0x44, 0x03, 0x20, 0x4e, 0x45, 0xe1, 0x69, 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1,
0xd3, 0xa1, 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, 0xc6, 0xc6, 0x15,
0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07,
0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac,
0x46, 0x22, 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09,
];
let result = Signature::from_der(&sig);
assert!(result.is_err(), "Bad R integer marker 0x03 should fail");
}
#[test]
fn test_malformed_der_bad_s_marker() {
use bsv_rs::primitives::ec::Signature;
let sig: Vec<u8> = vec![
0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1,
0xd3, 0xa1, 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, 0xc6, 0xc6, 0x15,
0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, 0x03, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07,
0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac,
0x46, 0x22, 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09,
];
let result = Signature::from_der(&sig);
assert!(result.is_err(), "Bad S integer marker 0x03 should fail");
}
#[test]
fn test_malformed_der_short_total_len() {
use bsv_rs::primitives::ec::Signature;
let sig: Vec<u8> = vec![
0x30, 0x43, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1,
0xd3, 0xa1, 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, 0xc6, 0xc6, 0x15,
0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07,
0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac,
0x46, 0x22, 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09,
];
let _result = Signature::from_der(&sig);
}
#[test]
fn test_malformed_der_invalid_message_length() {
use bsv_rs::primitives::ec::Signature;
let sig: Vec<u8> = vec![0x30, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00];
let result = Signature::from_der(&sig);
if let Ok(parsed) = result {
assert_eq!(parsed.r(), &[0u8; 32], "R should be all zeros");
assert_eq!(parsed.s(), &[0u8; 32], "S should be all zeros");
}
}
#[test]
fn test_malformed_der_long_total_len() {
use bsv_rs::primitives::ec::Signature;
let sig: Vec<u8> = vec![
0x30, 0x45, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1,
0xd3, 0xa1, 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, 0xc6, 0xc6, 0x15,
0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07,
0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac,
0x46, 0x22, 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09,
];
let result = Signature::from_der(&sig);
assert!(
result.is_err(),
"Long total length (exceeds actual data) should fail"
);
}
#[test]
fn test_malformed_der_long_r() {
use bsv_rs::primitives::ec::Signature;
let sig: Vec<u8> = vec![
0x30, 0x44, 0x02, 0x42, 0x4e, 0x45, 0xe1, 0x69, 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1,
0xd3, 0xa1, 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, 0xc6, 0xc6, 0x15,
0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07,
0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac,
0x46, 0x22, 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09,
];
let result = Signature::from_der(&sig);
assert!(result.is_err(), "Long R (0x42 > actual) should fail");
}
#[test]
fn test_malformed_der_long_s() {
use bsv_rs::primitives::ec::Signature;
let sig: Vec<u8> = vec![
0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1,
0xd3, 0xa1, 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, 0xc6, 0xc6, 0x15,
0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, 0x02, 0x21, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07,
0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac,
0x46, 0x22, 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09,
];
let result = Signature::from_der(&sig);
assert!(
result.is_err(),
"Long S (0x21 overflows buffer) should fail"
);
}
#[test]
fn test_malformed_der_short_s() {
use bsv_rs::primitives::ec::Signature;
let sig: Vec<u8> = vec![
0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1,
0xd3, 0xa1, 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, 0xc6, 0xc6, 0x15,
0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, 0x02, 0x19, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07,
0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac,
0x46, 0x22, 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09,
];
let _result = Signature::from_der(&sig);
}
#[test]
fn test_malformed_der_zero_len_r() {
use bsv_rs::primitives::ec::Signature;
let sig: Vec<u8> = vec![
0x30, 0x24, 0x02, 0x00, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07, 0xde, 0x48,
0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22,
0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09,
];
let _result = Signature::from_der(&sig);
}
#[test]
fn test_malformed_der_zero_len_s() {
use bsv_rs::primitives::ec::Signature;
let sig: Vec<u8> = vec![
0x30, 0x24, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1,
0xd3, 0xa1, 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, 0xc6, 0xc6, 0x15,
0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, 0x02, 0x00,
];
let _result = Signature::from_der(&sig);
}
#[test]
fn test_malformed_der_valid_signature_parses_ok() {
use bsv_rs::primitives::ec::Signature;
let sig: Vec<u8> = vec![
0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1,
0xd3, 0xa1, 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, 0xc6, 0xc6, 0x15,
0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07,
0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac,
0x46, 0x22, 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09,
];
let parsed = Signature::from_der(&sig).expect("Valid DER should parse successfully");
assert_eq!(
hex::encode(parsed.r()),
"4e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd41",
"R component mismatch"
);
assert_eq!(
hex::encode(parsed.s()),
"181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d09",
"S component mismatch"
);
}
#[test]
fn test_malformed_der_trailing_data_accepted() {
use bsv_rs::primitives::ec::Signature;
let sig: Vec<u8> = vec![
0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1,
0xd3, 0xa1, 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, 0xc6, 0xc6, 0x15,
0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07,
0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac,
0x46, 0x22, 0x08, 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09,
0x01, ];
let parsed = Signature::from_der(&sig);
assert!(
parsed.is_ok(),
"Trailing data should be accepted (blockchain compat)"
);
}
#[test]
fn test_known_answer_ecdsa_sign() {
let key =
PrivateKey::from_hex("cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50")
.unwrap();
let public_key = key.public_key();
let msg_hash = sha256(b"sample");
let sig = key.sign(&msg_hash).unwrap();
assert_eq!(
hex::encode(sig.r()),
"af340daf02cc15c8d5d08d7735dfe6b98a474ed373bdb5fbecf7571be52b3842",
"R component mismatch for known-answer ECDSA sign"
);
assert_eq!(
hex::encode(sig.s()),
"5009fb27f37034a9b24b707b7c6b79ca23ddef9e25f7282e8a797efe53a8f124",
"S component mismatch for known-answer ECDSA sign"
);
assert!(sig.is_low_s(), "Signature should be low-S");
let der = sig.to_der();
let recovered = bsv_rs::primitives::ec::Signature::from_der(&der).unwrap();
assert_eq!(recovered.r(), sig.r(), "DER roundtrip R mismatch");
assert_eq!(recovered.s(), sig.s(), "DER roundtrip S mismatch");
assert!(
public_key.verify(&msg_hash, &sig),
"Signature verification failed for known-answer test"
);
}
#[cfg(feature = "transaction")]
mod custom_k_tests {
use bsv_rs::primitives::ec::PrivateKey;
use bsv_rs::primitives::BigNumber;
use bsv_rs::script::templates::{RPuzzle, RPuzzleType};
use bsv_rs::script::{ScriptTemplate, SignOutputs};
use bsv_rs::transaction::{Transaction, TransactionOutput};
#[test]
fn test_compute_r_from_k_one_gives_generator_x() {
let k =
BigNumber::from_hex("0000000000000000000000000000000000000000000000000000000000000001")
.unwrap();
let r = RPuzzle::compute_r_from_k(&k).unwrap();
let expected_hex = "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798";
assert_eq!(
hex::encode(r).to_uppercase(),
expected_hex,
"k=1 should produce the generator point x-coordinate"
);
}
#[test]
fn test_compute_r_from_k_n_minus_1_is_valid() {
let k =
BigNumber::from_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140")
.unwrap();
let r = RPuzzle::compute_r_from_k(&k).unwrap();
assert_eq!(r.len(), 32, "R should be 32 bytes");
assert!(
r.iter().any(|&b| b != 0),
"R from k=n-1 should not be all zeros"
);
let k_one =
BigNumber::from_hex("0000000000000000000000000000000000000000000000000000000000000001")
.unwrap();
let r_one = RPuzzle::compute_r_from_k(&k_one).unwrap();
assert_eq!(
r, r_one,
"k=n-1 and k=1 should produce the same R (same x-coordinate)"
);
}
#[test]
fn test_compute_r_from_k_zero_errors() {
let k =
BigNumber::from_hex("0000000000000000000000000000000000000000000000000000000000000000")
.unwrap();
let result = RPuzzle::compute_r_from_k(&k);
assert!(result.is_err(), "k=0 should return an error");
let err_msg = result.unwrap_err().to_string();
assert!(
err_msg.contains("Invalid K value"),
"Error should mention invalid K value, got: {}",
err_msg
);
}
#[test]
fn test_compute_r_from_k_curve_order_errors() {
let k =
BigNumber::from_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141")
.unwrap();
let result = RPuzzle::compute_r_from_k(&k);
assert!(result.is_err(), "k=n (curve order) should return an error");
let err_msg = result.unwrap_err().to_string();
assert!(
err_msg.contains("Invalid K value"),
"Error should mention invalid K value, got: {}",
err_msg
);
}
#[tokio::test]
async fn test_rpuzzle_lock_unlock_e2e() {
use bsv_rs::script::LockingScript;
let k =
BigNumber::from_hex("0000000000000000000000000000000000000000000000000000000000000002")
.unwrap();
let r_value = RPuzzle::compute_r_from_k(&k).unwrap();
let template = RPuzzle::new(RPuzzleType::Raw);
let locking_script = template.lock(&r_value).unwrap();
let mut source_tx = Transaction::new();
source_tx
.add_output(TransactionOutput::new(100_000, locking_script.clone()))
.expect("add_output should work");
let mut spend_tx = Transaction::new();
let private_key = PrivateKey::from_hex(
"0000000000000000000000000000000000000000000000000000000000000042",
)
.unwrap();
let unlock_template = RPuzzle::unlock(&k, &private_key, SignOutputs::All, false);
spend_tx
.add_input_from_tx(source_tx, 0, unlock_template)
.expect("add_input_from_tx should work");
spend_tx
.add_output(TransactionOutput::new(99_000, LockingScript::new()))
.expect("add_output should work");
spend_tx.sign().await.expect("sign() should work");
let input = &spend_tx.inputs[0];
assert!(
input.unlocking_script.is_some(),
"Unlocking script should be set after signing"
);
let unlocking = input.unlocking_script.as_ref().unwrap();
let chunks = unlocking.chunks();
assert_eq!(
chunks.len(),
2,
"RPuzzle unlocking should have 2 chunks (sig + pubkey)"
);
let sig_data = chunks[0]
.data
.as_ref()
.expect("Signature data should be present");
let r_len = sig_data[3] as usize;
let r_start = 4;
let r_bytes = &sig_data[r_start..r_start + r_len];
let r_trimmed: Vec<u8> = r_bytes.iter().copied().skip_while(|&b| b == 0).collect();
let expected_trimmed: Vec<u8> = r_value.iter().copied().skip_while(|&b| b == 0).collect();
assert_eq!(
r_trimmed, expected_trimmed,
"R value in signature should match compute_r_from_k(k=2)"
);
}
#[tokio::test]
async fn test_rpuzzle_hash160_lock_unlock_e2e() {
use bsv_rs::primitives::hash::hash160;
use bsv_rs::script::LockingScript;
let k =
BigNumber::from_hex("0000000000000000000000000000000000000000000000000000000000000003")
.unwrap();
let r_value = RPuzzle::compute_r_from_k(&k).unwrap();
let r_hash = hash160(&r_value);
let template = RPuzzle::new(RPuzzleType::Hash160);
let locking_script = template.lock(&r_hash).unwrap();
let asm = locking_script.to_asm();
assert!(
asm.contains("OP_HASH160"),
"Hash160 RPuzzle locking script should contain OP_HASH160"
);
let mut source_tx = Transaction::new();
source_tx
.add_output(TransactionOutput::new(50_000, locking_script.clone()))
.expect("add_output should work");
let mut spend_tx = Transaction::new();
let private_key = PrivateKey::random();
let unlock_template = RPuzzle::unlock(&k, &private_key, SignOutputs::All, false);
spend_tx
.add_input_from_tx(source_tx, 0, unlock_template)
.expect("add_input_from_tx should work");
spend_tx
.add_output(TransactionOutput::new(49_000, LockingScript::new()))
.expect("add_output should work");
spend_tx.sign().await.expect("sign() should work");
assert!(
spend_tx.inputs[0].unlocking_script.is_some(),
"Unlocking script should be set after signing with Hash160 RPuzzle"
);
}
}