mirror of
https://github.com/bitcoin/bips.git
synced 2026-06-01 17:15:27 +00:00
BIP449: OP_TWEAKADD (#1944)
* BIP: OP_TWEAKADD * BIP TweakAdd: note on commutativity of tweaking and add test cases * BIP TweakAdd: Invert Argument Order * BIP Tweakadd: fix typo & add note on even-y tweaking * BIP TweakAdd -- add mailing list discussion * BIP TweakAdd: Add Alpen and MATT mentions * BIP TweakAdd Formatting Edits * BIP TWEAKADD remove conventions section * BIP TWEAKADD formatting fix * BIP TWEAKADD Move Vectors to end * BIP TweakAdd: Condense compatibility section * [BIP-0449] Updates post assignemnt * [BIP-0449] Normalize Metadata * Update bip-0449.md Link Text to point to OP of ML Thread
This commit is contained in:
9
bip-0449/test-vectors/Cargo.toml
Normal file
9
bip-0449/test-vectors/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "test-vectors"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
secp256k1 = "0.29"
|
||||
hex = "0.4"
|
||||
bitcoin_hashes = "0.16.0"
|
||||
172
bip-0449/test-vectors/src/main.rs
Normal file
172
bip-0449/test-vectors/src/main.rs
Normal file
@@ -0,0 +1,172 @@
|
||||
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,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user