From 313354329d692f9647e4b48527ac85889ff4c598 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 26 Jun 2023 08:35:21 +0930 Subject: [PATCH] bitcoin/psbt: handle anchor spends. Turns out it's a single sig, identical to the already-handled case where we spend a to_remote output. We also close a temporary memleak: stack was unused, but tallocated off the psbt, so it lives as long as the PSBT. Signed-off-by: Rusty Russell --- bitcoin/psbt.c | 26 ++++++++++++++++++++--- bitcoin/script.c | 22 +++++++++++++++++++ bitcoin/script.h | 3 +++ bitcoin/test/run-bitcoin_block_from_hex.c | 3 +++ bitcoin/test/run-psbt-from-tx.c | 3 +++ bitcoin/test/run-tx-encode.c | 3 +++ 6 files changed, 57 insertions(+), 3 deletions(-) diff --git a/bitcoin/psbt.c b/bitcoin/psbt.c index 446edf67a126..92014e2e3288 100644 --- a/bitcoin/psbt.c +++ b/bitcoin/psbt.c @@ -586,7 +586,7 @@ bool psbt_finalize(struct wally_psbt *psbt) tal_wally_start(); /* Wally doesn't know how to finalize P2WSH; this happens with - * option_anchor_outputs, and finalizing is trivial. */ + * option_anchor_outputs, and finalizing those two cases is trivial. */ /* FIXME: miniscript! miniscript! miniscript! */ for (size_t i = 0; i < psbt->num_inputs; i++) { struct wally_psbt_input *input = &psbt->inputs[i]; @@ -594,10 +594,16 @@ bool psbt_finalize(struct wally_psbt *psbt) const struct wally_map_item *iws; iws = wally_map_get_integer(&input->psbt_fields, /* PSBT_IN_WITNESS_SCRIPT */ 0x05); - if (!iws || !is_to_remote_anchored_witness_script(iws->value, - iws->value_len)) + if (!iws) continue; + if (!is_to_remote_anchored_witness_script(iws->value, + iws->value_len) + && !is_anchor_witness_script(iws->value, + iws->value_len)) { + continue; + } + if (input->signatures.num_items != 1) continue; @@ -615,6 +621,19 @@ bool psbt_finalize(struct wally_psbt *psbt) * * */ + /* BOLT #3: + * #### `to_local_anchor` and `to_remote_anchor` Output (option_anchors) + *... + * OP_CHECKSIG OP_IFDUP + * OP_NOTIF + * OP_16 OP_CHECKSEQUENCEVERIFY + * OP_ENDIF + *... + * Spending of the output requires the following witness: + * + */ + + /* i.e. in both cases, this is the same thing */ wally_tx_witness_stack_init_alloc(2, &stack); wally_tx_witness_stack_add(stack, input->signatures.items[0].value, @@ -623,6 +642,7 @@ bool psbt_finalize(struct wally_psbt *psbt) iws->value, iws->value_len); wally_psbt_input_set_final_witness(input, stack); + wally_tx_witness_stack_free(stack); } ok = (wally_psbt_finalize(psbt, 0 /* flags */) == WALLY_OK); diff --git a/bitcoin/script.c b/bitcoin/script.c index a127ce96c004..ca8c4ad5e767 100644 --- a/bitcoin/script.c +++ b/bitcoin/script.c @@ -874,9 +874,31 @@ u8 *bitcoin_wscript_anchor(const tal_t *ctx, add_op(&script, OP_CHECKSEQUENCEVERIFY); add_op(&script, OP_ENDIF); + assert(is_anchor_witness_script(script, tal_bytelen(script))); return script; } +bool is_anchor_witness_script(const u8 *script, size_t script_len) +{ + if (script_len != 34 + 1 + 1 + 1 + 1 + 1 + 1) + return false; + if (script[0] != OP_PUSHBYTES(33)) + return false; + if (script[34] != OP_CHECKSIG) + return false; + if (script[35] != OP_IFDUP) + return false; + if (script[36] != OP_NOTIF) + return false; + if (script[37] != 0x50 + 16) + return false; + if (script[38] != OP_CHECKSEQUENCEVERIFY) + return false; + if (script[39] != OP_ENDIF) + return false; + return true; +} + bool scripteq(const u8 *s1, const u8 *s2) { memcheck(s1, tal_count(s1)); diff --git a/bitcoin/script.h b/bitcoin/script.h index a00f12cc2425..1ef219c67256 100644 --- a/bitcoin/script.h +++ b/bitcoin/script.h @@ -159,6 +159,9 @@ bool is_known_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); +/* Is this an anchor witness script? */ +bool is_anchor_witness_script(const u8 *script, size_t script_len); + /* Are these two scripts equal? */ bool scripteq(const u8 *s1, const u8 *s2); diff --git a/bitcoin/test/run-bitcoin_block_from_hex.c b/bitcoin/test/run-bitcoin_block_from_hex.c index 0495e5af12c2..bb05350a6c7c 100644 --- a/bitcoin/test/run-bitcoin_block_from_hex.c +++ b/bitcoin/test/run-bitcoin_block_from_hex.c @@ -69,6 +69,9 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u8_array */ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for is_anchor_witness_script */ +bool is_anchor_witness_script(const u8 *script UNNEEDED, size_t script_len UNNEEDED) +{ fprintf(stderr, "is_anchor_witness_script called!\n"); abort(); } /* Generated stub for is_to_remote_anchored_witness_script */ bool is_to_remote_anchored_witness_script(const u8 *script UNNEEDED, size_t script_len UNNEEDED) { fprintf(stderr, "is_to_remote_anchored_witness_script called!\n"); abort(); } diff --git a/bitcoin/test/run-psbt-from-tx.c b/bitcoin/test/run-psbt-from-tx.c index 9fe77ea4f992..8809636efb41 100644 --- a/bitcoin/test/run-psbt-from-tx.c +++ b/bitcoin/test/run-psbt-from-tx.c @@ -50,6 +50,9 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) void fromwire_sha256_double(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256_double *sha256d UNNEEDED) { fprintf(stderr, "fromwire_sha256_double called!\n"); abort(); } +/* Generated stub for is_anchor_witness_script */ +bool is_anchor_witness_script(const u8 *script UNNEEDED, size_t script_len UNNEEDED) +{ fprintf(stderr, "is_anchor_witness_script called!\n"); abort(); } /* Generated stub for is_to_remote_anchored_witness_script */ bool is_to_remote_anchored_witness_script(const u8 *script UNNEEDED, size_t script_len UNNEEDED) { fprintf(stderr, "is_to_remote_anchored_witness_script called!\n"); abort(); } diff --git a/bitcoin/test/run-tx-encode.c b/bitcoin/test/run-tx-encode.c index d99c838a496c..d2b0a0c2cdb2 100644 --- a/bitcoin/test/run-tx-encode.c +++ b/bitcoin/test/run-tx-encode.c @@ -70,6 +70,9 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u8_array */ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for is_anchor_witness_script */ +bool is_anchor_witness_script(const u8 *script UNNEEDED, size_t script_len UNNEEDED) +{ fprintf(stderr, "is_anchor_witness_script called!\n"); abort(); } /* Generated stub for is_to_remote_anchored_witness_script */ bool is_to_remote_anchored_witness_script(const u8 *script UNNEEDED, size_t script_len UNNEEDED) { fprintf(stderr, "is_to_remote_anchored_witness_script called!\n"); abort(); }