1
0
mirror of https://github.com/bitcoin/bips.git synced 2026-06-01 17:15:27 +00:00
Files
bips/bip-0449/test-vectors/src/main.rs

173 lines
5.2 KiB
Rust
Raw Normal View History

use secp256k1::{constants::CURVE_ORDER, PublicKey, Secp256k1, SecretKey, XOnlyPublicKey};
fn hex32(b: &[u8; 32]) -> String {
b.iter().map(|x| format!("{:02x}", x)).collect()
}
/// Implements OP_TWEAKADD semantics.
/// Returns None if invalid scalar, invalid x, or infinity.
fn tweak_add_xonly(pubkey32: [u8; 32], h32: [u8; 32]) -> Option<[u8; 32]> {
let secp = Secp256k1::new();
// Reject if t >= n
let scalar = secp256k1::Scalar::from_be_bytes(h32).ok()?;
// Lift pubkey from x-only
let xpk = XOnlyPublicKey::from_slice(&pubkey32).ok()?;
let (xonly, _) = xpk.add_tweak(&secp, &scalar).ok()?;
Some(xonly.serialize())
}
fn case(name: &str, pubkey_hex: &str, t_hex: &str, check_res: Option<&str>) {
let pk_bytes = hex::decode(pubkey_hex).unwrap();
let t_bytes = hex::decode(t_hex).unwrap();
let mut pk32 = [0u8; 32];
pk32.copy_from_slice(&pk_bytes);
let mut t32 = [0u8; 32];
t32.copy_from_slice(&t_bytes);
match tweak_add_xonly(pk32, t32) {
Some(out) => {
let out_hex = hex32(&out);
if let Some(check) = check_res {
assert_eq!(out_hex, check);
}
let script = format!("<{t_hex}> <{pubkey_hex}> OP_TWEAKADD <{out_hex}> OP_EQUAL");
println!("{name}\n```\n pubkey32 = {pubkey_hex}\n h32 = {t_hex}\n expect = {out_hex}\n\n script = {script}\n```")
}
None => {
let script = format!("<{t_hex}> <{pubkey_hex}> OP_TWEAKADD OP_DROP OP_1");
println!("{name}\n```\n pubkey32 = {pubkey_hex}\n h32 = {t_hex}\n expect = fail\n script = {script}\n```")
}
}
}
/// Helper: compute x-only for scalar*k*G.
fn xonly_of_scalar(k: u8) -> String {
let secp = Secp256k1::new();
let mut buf = [0u8; 32];
buf[31] = k;
let sk = SecretKey::from_slice(&buf).unwrap();
let pk = PublicKey::from_secret_key(&secp, &sk);
let (xonly, _) = pk.x_only_public_key();
hex32(&xonly.serialize())
}
fn hash_scalar(k: u8) -> [u8; 32] {
bitcoin_hashes::Sha256::hash(&[k]).to_byte_array()
}
/// Helper: compute x-only for scalar*k*G.
fn xonly_of_scalar_hash(k: [u8; 32]) -> String {
let secp = Secp256k1::new();
let sk = SecretKey::from_slice(&k).unwrap();
let pk = PublicKey::from_secret_key(&secp, &sk);
let (xonly, _) = pk.x_only_public_key();
hex32(&xonly.serialize())
}
fn main() {
println!("Curve order n = {}", hex::encode(CURVE_ORDER));
println!();
let x_g = xonly_of_scalar(1);
let x_2g = xonly_of_scalar(2);
let x_3g = xonly_of_scalar(3);
let x_5g = xonly_of_scalar(5);
let x_6g = xonly_of_scalar(6);
let x_7g = xonly_of_scalar(7);
let x_16g = xonly_of_scalar(16);
let h1 = hash_scalar(1);
let h2 = hash_scalar(2);
let h7 = hash_scalar(7);
let x_h1g = xonly_of_scalar_hash(h1);
let x_h2g = xonly_of_scalar_hash(h2);
let x_h7g = xonly_of_scalar_hash(h7);
println!("\n### Passing cases\n");
case(
"1) Identity tweak (t = 0)",
&x_g,
"0000000000000000000000000000000000000000000000000000000000000000",
Some(&x_g),
);
case(
"2) Increment by 1",
&x_g,
"0000000000000000000000000000000000000000000000000000000000000001",
Some(&x_2g),
);
case(
"3) Increment by 2",
&x_g,
"0000000000000000000000000000000000000000000000000000000000000002",
Some(&x_3g),
);
case(
"4) Increment by 5",
&x_g,
"0000000000000000000000000000000000000000000000000000000000000005",
Some(&x_6g),
);
case(
"5) Input x(2G), t = 3",
&x_2g,
"0000000000000000000000000000000000000000000000000000000000000003",
Some(&x_5g),
);
case(
"6) Input x(7G), t = 9",
&x_7g,
"0000000000000000000000000000000000000000000000000000000000000009",
Some(&x_16g),
);
case(
"7) Input x(h(1) G), t = 1",
&x_h1g,
"0000000000000000000000000000000000000000000000000000000000000001",
None,
);
case(
"8) Input x(h(2) G), t = 1",
&x_h2g,
"0000000000000000000000000000000000000000000000000000000000000001",
None,
);
case(
"9) Input x(h(7) G), t = 1",
&x_h7g,
"0000000000000000000000000000000000000000000000000000000000000001",
None,
);
case("10) Input x(G), t = 1", &x_g, &hex32(&h1), None);
case("11) Input x(G), t = h(2)", &x_g, &hex32(&h2), None);
case(
"12) Input x(G), t = h(7) (Note: differs from 9)",
&x_g,
&hex32(&h7),
None,
);
println!("\n### Failing cases\n");
case(
"A) Scalar out of range (t = n)",
&x_g,
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
None,
);
case(
"B) Invalid x (x = 0), t = 1",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000001",
None,
);
case(
"C) Infinity result (x(G), t = n-1)",
&x_g,
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
None,
);
}