Skip to content

Commit cfca26b

Browse files
committed
common: calculate correct weight of Taproot spends
utxo_spend_weight(…) was assuming that witnesses always include a pubkey, but that is not true of Taproot (key-path) spends. The effect was that wallet- funded commands (e.g., withdraw, fundchannel, multifundchannel, etc.) were overpaying on fees due to calculating Taproot input weights as 34 sipa greater than they actually are. Also as a consequence of the miscalculation, users who carefully select a set of UTxOs to spend so as to avoid producing a change output would experience an error when trying to spend them. As an example, I tried to spend a tiny 1390-sat P2TR output to a new 1275-sat output at minimum feerate. The actual weight of the transaction is 451 sipa, so the 115-sat fee yields a feerate of 255 sat/kw. However, CLN refuses the transaction, claiming: { "code": 301, "message": "Could not afford 1275sat using UTXOs totalling 1390sat with weight 485 at feerate 253" } The reported weight of 485 sipa is wrong, as it assumes the input will include a 34-sipa public key when in fact it will not. This commit amends the bitcoin_tx_simple_input_witness_weight() and bitcoin_tx_simple_input_weight(…) functions to take a parameter specifying the scriptPubKey type of the spend. The implementation of the former is corrected so that P2TR spends are not charged for a public key that they actually lack. An enum type is introduced to enumerate the known scriptPubKey types, and a new scriptpubkey_type(…) utility function is implemented to discern the type of a given scriptPubKey. The existing is_known_scripttype(…) function is reimplemented as an inline wrapper around the new function. The default_lease_rates(…) function in plugins/funder_policy.c is amended so as to state explicitly an assumption that it has been making: that the two inputs it assumes for its default max weight calculation will not be Taproot inputs. Fixes: #8164 Changelog-Fixed: P2TR inputs are assessed the correct weight in fee calculations.
1 parent 427c4c5 commit cfca26b

File tree

6 files changed

+54
-19
lines changed

6 files changed

+54
-19
lines changed

bitcoin/script.c

+13-7
Original file line numberDiff line numberDiff line change
@@ -550,13 +550,19 @@ bool is_p2tr(const u8 *script, size_t script_len, u8 xonly_pubkey[32])
550550
return true;
551551
}
552552

553-
bool is_known_scripttype(const u8 *script, size_t script_len)
554-
{
555-
return is_p2wpkh(script, script_len, NULL)
556-
|| is_p2wsh(script, script_len, NULL)
557-
|| is_p2sh(script, script_len, NULL)
558-
|| is_p2pkh(script, script_len, NULL)
559-
|| is_p2tr(script, script_len, NULL);
553+
enum scriptpubkey_type scriptpubkey_type(const u8 *script, size_t script_len)
554+
{
555+
if (is_p2wpkh(script, script_len, NULL))
556+
return scriptpubkey_type_p2wpkh;
557+
if (is_p2wsh(script, script_len, NULL))
558+
return scriptpubkey_type_p2wsh;
559+
if (is_p2sh(script, script_len, NULL))
560+
return scriptpubkey_type_p2sh;
561+
if (is_p2pkh(script, script_len, NULL))
562+
return scriptpubkey_type_p2pkh;
563+
if (is_p2tr(script, script_len, NULL))
564+
return scriptpubkey_type_p2tr;
565+
return scriptpubkey_type_unknown;
560566
}
561567

562568
bool is_known_segwit_scripttype(const u8 *script, size_t script_len)

bitcoin/script.h

+17-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ struct ripemd160;
1313
struct rel_locktime;
1414
struct abs_locktime;
1515

16+
enum scriptpubkey_type {
17+
scriptpubkey_type_unknown = 0,
18+
scriptpubkey_type_p2pk,
19+
scriptpubkey_type_p2pkh,
20+
scriptpubkey_type_p2sh,
21+
scriptpubkey_type_p2wpkh,
22+
scriptpubkey_type_p2wsh,
23+
scriptpubkey_type_p2tr,
24+
};
25+
1626
/* tal_count() gives the length of the script. */
1727
u8 *bitcoin_redeem_2of2(const tal_t *ctx,
1828
const struct pubkey *key1,
@@ -173,8 +183,14 @@ bool is_p2wpkh(const u8 *script, size_t script_len, struct bitcoin_address *addr
173183
/* Is this a taproot output? (extract xonly_pubkey bytes if not NULL) */
174184
bool is_p2tr(const u8 *script, size_t script_len, u8 xonly_pubkey[32]);
175185

186+
/* What type of script is this? */
187+
enum scriptpubkey_type scriptpubkey_type(const u8 *script, size_t script_len);
188+
176189
/* Is this one of the above script types? */
177-
bool is_known_scripttype(const u8 *script, size_t script_len);
190+
static inline bool is_known_scripttype(const u8 *script, size_t script_len)
191+
{
192+
return scriptpubkey_type(script, script_len) != scriptpubkey_type_unknown;
193+
}
178194

179195
/* Is this a witness script type? */
180196
bool is_known_segwit_scripttype(const u8 *script, size_t script_len);

bitcoin/tx.c

+15-7
Original file line numberDiff line numberDiff line change
@@ -912,17 +912,25 @@ size_t bitcoin_tx_input_weight(bool p2sh, size_t witness_weight)
912912
return weight;
913913
}
914914

915-
size_t bitcoin_tx_simple_input_witness_weight(void)
915+
size_t bitcoin_tx_simple_input_witness_weight(enum scriptpubkey_type spend_type)
916916
{
917-
/* Account for witness (1 byte count + sig + key) */
918-
return 1 + (bitcoin_tx_input_sig_weight() + 1 + 33);
917+
size_t witness_weight = 1; /* byte count */
918+
919+
/* All spend types include a signature */
920+
witness_weight += bitcoin_tx_input_sig_weight();
921+
922+
/* All spend types except P2TR include a public key */
923+
if (spend_type != scriptpubkey_type_p2tr)
924+
witness_weight += 1 + 33;
925+
926+
return witness_weight;
919927
}
920928

921-
/* We only do segwit inputs, and we assume witness is sig + key */
922-
size_t bitcoin_tx_simple_input_weight(bool p2sh)
929+
/* We only do segwit inputs */
930+
size_t bitcoin_tx_simple_input_weight(enum scriptpubkey_type spend_type)
923931
{
924-
return bitcoin_tx_input_weight(p2sh,
925-
bitcoin_tx_simple_input_witness_weight());
932+
return bitcoin_tx_input_weight(spend_type == scriptpubkey_type_p2sh,
933+
bitcoin_tx_simple_input_witness_weight(spend_type));
926934
}
927935

928936
size_t bitcoin_tx_2of2_input_witness_weight(void)

bitcoin/tx.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
struct wally_psbt;
1919
struct ripemd160;
20+
enum scriptpubkey_type;
2021

2122
struct bitcoin_txid {
2223
struct sha256_double shad;
@@ -319,10 +320,10 @@ size_t bitcoin_tx_input_sig_weight(void);
319320
size_t bitcoin_tx_input_weight(bool p2sh, size_t witness_weight);
320321

321322
/* The witness weight for a simple (sig + key) input */
322-
size_t bitcoin_tx_simple_input_witness_weight(void);
323+
size_t bitcoin_tx_simple_input_witness_weight(enum scriptpubkey_type spend_type);
323324

324325
/* We only do segwit inputs, and we assume witness is sig + key */
325-
size_t bitcoin_tx_simple_input_weight(bool p2sh);
326+
size_t bitcoin_tx_simple_input_weight(enum scriptpubkey_type spend_type);
326327

327328
/* The witness for our 2of2 input (closing or commitment tx). */
328329
size_t bitcoin_tx_2of2_input_witness_weight(void);

common/utxo.c

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "config.h"
2+
#include <bitcoin/script.h>
23
#include <common/utxo.h>
34
#include <wire/wire.h>
45

@@ -64,7 +65,9 @@ struct utxo *fromwire_utxo(const tal_t *ctx, const u8 **ptr, size_t *max)
6465

6566
size_t utxo_spend_weight(const struct utxo *utxo, size_t min_witness_weight)
6667
{
67-
size_t wit_weight = bitcoin_tx_simple_input_witness_weight();
68+
size_t wit_weight = bitcoin_tx_simple_input_witness_weight(
69+
scriptpubkey_type(utxo->scriptPubkey,
70+
tal_bytelen(utxo->scriptPubkey)));
6871
/* If the min is less than what we'd use for a 'normal' tx,
6972
* we return the value with the greater added/calculated */
7073
if (wit_weight < min_witness_weight)

plugins/funder_policy.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,9 @@ default_lease_rates(const tal_t *ctx)
128128

129129
/* Let's set our default max weight to two inputs + an output
130130
* (use helpers b/c elements) */
131+
/* Assume worst case although Taproot inputs will be lighter */
131132
rates->funding_weight
132-
= 2 * bitcoin_tx_simple_input_weight(false)
133+
= 2 * bitcoin_tx_simple_input_weight(scriptpubkey_type_p2wpkh)
133134
+ bitcoin_tx_output_weight(BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN);
134135

135136
return rates;

0 commit comments

Comments
 (0)