From 3c689cdaffee88c07646b429d286dfd29fae45df Mon Sep 17 00:00:00 2001 From: Greg Sanders Date: Tue, 11 Jul 2023 01:54:43 +0930 Subject: [PATCH] p2tr script utilities --- bitcoin/script.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++- bitcoin/script.h | 24 ++++++++++++- 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/bitcoin/script.c b/bitcoin/script.c index c682a64f9..54c53d82e 100644 --- a/bitcoin/script.c +++ b/bitcoin/script.c @@ -310,6 +310,69 @@ u8 *scriptpubkey_witness_raw(const tal_t *ctx, u8 version, return script; } +u8 *scriptpubkey_raw_p2tr(const tal_t *ctx, const struct pubkey *output_pubkey) +{ + int ok; + secp256k1_xonly_pubkey x_key; + unsigned char x_key_bytes[32]; + u8 *script = tal_arr(ctx, u8, 0); + + add_op(&script, OP_1); + + ok = secp256k1_xonly_pubkey_from_pubkey(secp256k1_ctx, + &x_key, + /* pk_parity */ NULL, + &(output_pubkey->pubkey)); + assert(ok); + + ok = secp256k1_xonly_pubkey_serialize(secp256k1_ctx, + x_key_bytes, + &x_key); + assert(ok); + + script_push_bytes(&script, x_key_bytes, sizeof(x_key_bytes)); + assert(tal_count(script) == BITCOIN_SCRIPTPUBKEY_P2TR_LEN); + return script; +} + +u8 *scriptpubkey_raw_p2tr_derkey(const tal_t *ctx, const u8 output_der[33]) +{ + struct pubkey tr_key; + if (!pubkey_from_der(output_der, 33, &tr_key)) { + abort(); + } + return scriptpubkey_raw_p2tr(ctx, &tr_key); +} + +u8 *scriptpubkey_p2tr(const tal_t *ctx, const struct pubkey *inner_pubkey) +{ + unsigned char key_bytes[33]; + unsigned char tweaked_key_bytes[33]; + size_t out_len = sizeof(key_bytes); + u8 *script = tal_arr(ctx, u8, 0); + + add_op(&script, OP_1); + + secp256k1_ec_pubkey_serialize(secp256k1_ctx, key_bytes, &out_len, &inner_pubkey->pubkey, SECP256K1_EC_COMPRESSED); + /* Only commit to inner pubkey in tweak */ + if (wally_ec_public_key_bip341_tweak(key_bytes, 33, /* merkle_root*/ NULL, 0, 0 /* flags */, tweaked_key_bytes, sizeof(tweaked_key_bytes)) != WALLY_OK) + abort(); + + /* Cut off the first byte from the serialized compressed key */ + script_push_bytes(&script, tweaked_key_bytes + 1, sizeof(tweaked_key_bytes) - 1); + assert(tal_count(script) == BITCOIN_SCRIPTPUBKEY_P2TR_LEN); + return script; +} + +u8 *scriptpubkey_p2tr_derkey(const tal_t *ctx, const u8 inner_der[33]) +{ + struct pubkey tr_key; + if (!pubkey_from_der(inner_der, 33, &tr_key)) { + abort(); + } + return scriptpubkey_p2tr(ctx, &tr_key); +} + /* BOLT #3: * * #### `to_remote` Output @@ -481,10 +544,33 @@ bool is_p2wpkh(const u8 *script, struct bitcoin_address *addr) return true; } +bool is_p2tr(const u8 *script, u8 xonly_pubkey[32]) +{ + size_t script_len = tal_count(script); + + if (script_len != BITCOIN_SCRIPTPUBKEY_P2TR_LEN) + return false; + if (script[0] != OP_1) + return false; + /* x-only pubkey */ + if (script[1] != OP_PUSHBYTES(32)) + return false; + if (xonly_pubkey) + memcpy(xonly_pubkey, script+2, 32); + return true; +} + bool is_known_scripttype(const u8 *script) { return is_p2wpkh(script, NULL) || is_p2wsh(script, NULL) - || is_p2sh(script, NULL) || is_p2pkh(script, NULL); + || is_p2sh(script, NULL) || is_p2pkh(script, NULL) + || is_p2tr(script, NULL); +} + +bool is_known_segwit_scripttype(const u8 *script) +{ + return is_p2wpkh(script, NULL) || is_p2wsh(script, NULL) + || is_p2tr(script, NULL); } u8 **bitcoin_witness_sig_and_element(const tal_t *ctx, diff --git a/bitcoin/script.h b/bitcoin/script.h index 04c289b74..660c83ad6 100644 --- a/bitcoin/script.h +++ b/bitcoin/script.h @@ -63,6 +63,19 @@ u8 *scriptpubkey_p2wpkh_derkey(const tal_t *ctx, const u8 der[33]); u8 *scriptpubkey_witness_raw(const tal_t *ctx, u8 version, const u8 *wprog, size_t wprog_size); +/* Create an output script for a "raw"(perhaps already tweaked) taproot output pubkey */ +u8 *scriptpubkey_raw_p2tr(const tal_t *ctx, const struct pubkey *output_pubkey); + +/* Same as above, but compressed key is DER-encoded. */ +u8 *scriptpubkey_raw_p2tr_derkey(const tal_t *ctx, const u8 output_der[33]); + +/* Create an output script for an internal taproot pubkey. Results in different script than + * scriptpubkey_raw_p2tr! TODO support merkle root tweaking */ +u8 *scriptpubkey_p2tr(const tal_t *ctx, const struct pubkey *inner_pubkey); + +/* Same as above, but compressed key is DER-encoded. TODO support merkle root tweaking */ +u8 *scriptpubkey_p2tr_derkey(const tal_t *ctx, const u8 inner_der[33]); + /* To-remotekey with csv max(lease_expiry - blockheight, 1) delay. */ u8 *bitcoin_wscript_to_remote_anchored(const tal_t *ctx, const struct pubkey *remote_key, @@ -157,9 +170,15 @@ bool is_p2wsh(const u8 *script, struct sha256 *addr); /* Is this (version 0) pay to witness pubkey hash? (extract addr if not NULL) */ bool is_p2wpkh(const u8 *script, struct bitcoin_address *addr); -/* Is this one of the four above script types? */ +/* Is this a taproot output? (extract xonly_pubkey bytes if not NULL) */ +bool is_p2tr(const u8 *script, u8 xonly_pubkey[32]); + +/* Is this one of the above script types? */ bool is_known_scripttype(const u8 *script); +/* Is this a witness script type? */ +bool is_known_segwit_scripttype(const u8 *script); + /* Is this a to-remote witness script (used for option_anchor_outputs)? */ bool is_to_remote_anchored_witness_script(const u8 *script, size_t script_len); @@ -184,4 +203,7 @@ void script_push_bytes(u8 **scriptp, const void *mem, size_t len); /* OP_0 + PUSH(32-byte-hash) */ #define BITCOIN_SCRIPTPUBKEY_P2WSH_LEN (1 + 1 + 32) +/* OP_1 + PUSH(32-byte-key) */ +#define BITCOIN_SCRIPTPUBKEY_P2TR_LEN (1 + 1 + 32) + #endif /* LIGHTNING_BITCOIN_SCRIPT_H */