Skip to content

Commit 286d5c6

Browse files
committed
splice: Fix weight calculations & use opening feerate
Here we update `psbt_input_get_weight` to allow the caller to specify what kind of input size estimation they would like. Before it was just “zero witness bytes” which we now move to the default behavior and add an assumption option for P2WSH -> 2of2 multisig which is what we typically see in splicing. At the same time we move channeld over to using the opening feerate estimation instead of the less useful “max feerate.” We make splice script use this same feerate. After auditing the feerates for these two places against each other and the actually created transaction, we implement some fixes making them all match. While we’re here, we add relevant debug log messages. Changelog-Changed: Audited and updated splice’s feerate calculations to be precise to the byte — a critical change to accomodate splice script.
1 parent 42521f3 commit 286d5c6

File tree

9 files changed

+228
-77
lines changed

9 files changed

+228
-77
lines changed

bitcoin/psbt.c

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <ccan/tal/str/str.h>
1111
#include <common/utils.h>
1212
#include <wally_psbt.h>
13+
#include <wally_psbt_members.h>
1314
#include <wire/wire.h>
1415

1516

@@ -483,6 +484,28 @@ void psbt_input_set_witscript(struct wally_psbt *psbt, size_t in, const u8 *wscr
483484
tal_wally_end(psbt);
484485
}
485486

487+
const u8 *psbt_input_get_witscript(const tal_t *ctx,
488+
const struct wally_psbt *psbt,
489+
size_t in)
490+
{
491+
size_t witscript_len, written_len;
492+
u8 *witscript;
493+
if (wally_psbt_get_input_witness_script_len(psbt, in, &witscript_len) != WALLY_OK)
494+
abort();
495+
witscript = tal_arr(ctx, u8, witscript_len);
496+
if (wally_psbt_get_input_witness_script(psbt, in, witscript, witscript_len, &written_len) != WALLY_OK)
497+
abort();
498+
if (witscript_len != written_len)
499+
abort();
500+
return witscript;
501+
}
502+
503+
bool psbt_input_get_ecdsa_sig(const tal_t *ctx,
504+
const struct wally_psbt *psbt,
505+
size_t in,
506+
const struct pubkey *pubkey,
507+
struct bitcoin_signature **sig);
508+
486509
void psbt_elements_input_set_asset(struct wally_psbt *psbt, size_t in,
487510
struct amount_asset *asset)
488511
{
@@ -595,10 +618,16 @@ struct amount_sat psbt_input_get_amount(const struct wally_psbt *psbt,
595618
}
596619

597620
size_t psbt_input_get_weight(const struct wally_psbt *psbt,
598-
size_t in)
621+
size_t in,
622+
enum PSBT_GUESS guess)
599623
{
600624
size_t weight;
601625
const struct wally_map_item *redeem_script;
626+
struct wally_psbt_input *input = &psbt->inputs[in];
627+
struct wally_tx_output *utxo_out = NULL;
628+
629+
if (input->utxo)
630+
utxo_out = &input->utxo->outputs[input->index];
602631

603632
redeem_script = wally_map_get_integer(&psbt->inputs[in].psbt_fields, /* PSBT_IN_REDEEM_SCRIPT */ 0x04);
604633

@@ -608,8 +637,20 @@ size_t psbt_input_get_weight(const struct wally_psbt *psbt,
608637
weight +=
609638
(redeem_script->value_len +
610639
varint_size(redeem_script->value_len)) * 4;
640+
} else if ((guess & PSBT_GUESS_2OF2)
641+
&& utxo_out
642+
&& is_p2wsh(utxo_out->script, utxo_out->script_len, NULL)) {
643+
weight = bitcoin_tx_input_weight(false,
644+
bitcoin_tx_2of2_input_witness_weight());
645+
} else if (utxo_out
646+
&& is_p2wpkh(utxo_out->script, utxo_out->script_len, NULL)) {
647+
weight = bitcoin_tx_input_weight(false,
648+
bitcoin_tx_input_witness_weight(UTXO_P2SH_P2WPKH));
649+
} else if (utxo_out
650+
&& is_p2tr(utxo_out->script, utxo_out->script_len, NULL)) {
651+
weight = bitcoin_tx_input_weight(false,
652+
bitcoin_tx_input_witness_weight(UTXO_P2TR));
611653
} else {
612-
/* zero scriptSig length */
613654
weight += varint_size(0) * 4;
614655
}
615656

bitcoin/psbt.h

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@ WARN_UNUSED_RESULT bool psbt_input_get_ecdsa_sig(const tal_t *ctx,
207207

208208
void psbt_input_set_witscript(struct wally_psbt *psbt, size_t in, const u8 *wscript);
209209

210+
const u8 *psbt_input_get_witscript(const tal_t *ctx,
211+
const struct wally_psbt *psbt,
212+
size_t in);
213+
210214
/* psbt_input_set_unknown - Set the given Key-Value in the psbt's input keymap
211215
* @ctx - tal context for allocations
212216
* @in - psbt input to set key-value on
@@ -265,9 +269,20 @@ void psbt_output_set_unknown(const tal_t *ctx,
265269
struct amount_sat psbt_input_get_amount(const struct wally_psbt *psbt,
266270
size_t in);
267271

268-
/* psbt_input_get_weight - Calculate the tx weight for input index `in` */
272+
enum PSBT_GUESS {
273+
PSBT_GUESS_ZERO = 0x0, /* Assume unknown is 0 bytes (fallback) */
274+
PSBT_GUESS_2OF2 = 0x1, /* Assume P2WSH is 2of2 multisig (req prevtx) */
275+
};
276+
277+
/* psbt_input_get_weight - Calculate the tx weight for input index `in`.
278+
*
279+
* @psbt - psbt
280+
* @in - index of input who's weight you want
281+
* @guess - How to guess if we have incomplete information
282+
* */
269283
size_t psbt_input_get_weight(const struct wally_psbt *psbt,
270-
size_t in);
284+
size_t in,
285+
enum PSBT_GUESS guess);
271286

272287
/* psbt_output_get_amount - Returns the value of this output
273288
*

bitcoin/test/run-bitcoin_block_from_hex.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,15 @@ void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED)
9797
/* Generated stub for towire_u8_array */
9898
void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED)
9999
{ fprintf(stderr, "towire_u8_array called!\n"); abort(); }
100+
/* Generated stub for is_p2wsh */
101+
bool is_p2wsh(const u8 *script UNNEEDED, size_t script_len UNNEEDED, struct sha256 *addr UNNEEDED)
102+
{ fprintf(stderr, "is_p2wsh called!\n"); abort(); }
103+
/* Generated stub for is_p2tr */
104+
bool is_p2tr(const u8 *script UNNEEDED, size_t script_len UNNEEDED, u8 xonly_pubkey[32] UNNEEDED)
105+
{ fprintf(stderr, "is_p2tr called!\n"); abort(); }
106+
/* Generated stub for is_p2wpkh */
107+
bool is_p2wpkh(const u8 *script UNNEEDED, size_t script_len UNNEEDED, struct bitcoin_address *addr UNNEEDED)
108+
{ fprintf(stderr, "is_p2wpkh called!\n"); abort(); }
100109
/* AUTOGENERATED MOCKS END */
101110

102111
static const char block[] =

bitcoin/test/run-psbt-from-tx.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,15 @@ size_t varint_put(u8 buf[VARINT_MAX_LEN] UNNEEDED, varint_t v UNNEEDED)
8080
/* Generated stub for varint_size */
8181
size_t varint_size(varint_t v UNNEEDED)
8282
{ fprintf(stderr, "varint_size called!\n"); abort(); }
83+
/* Generated stub for is_p2wsh */
84+
bool is_p2wsh(const u8 *script UNNEEDED, size_t script_len UNNEEDED, struct sha256 *addr UNNEEDED)
85+
{ fprintf(stderr, "is_p2wsh called!\n"); abort(); }
86+
/* Generated stub for is_p2tr */
87+
bool is_p2tr(const u8 *script UNNEEDED, size_t script_len UNNEEDED, u8 xonly_pubkey[32] UNNEEDED)
88+
{ fprintf(stderr, "is_p2tr called!\n"); abort(); }
89+
/* Generated stub for is_p2wpkh */
90+
bool is_p2wpkh(const u8 *script UNNEEDED, size_t script_len UNNEEDED, struct bitcoin_address *addr UNNEEDED)
91+
{ fprintf(stderr, "is_p2wpkh called!\n"); abort(); }
8392
/* AUTOGENERATED MOCKS END */
8493

8594
/* This transaction has scriptSig data in it.

bitcoin/test/run-tx-encode.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED)
9898
/* Generated stub for towire_u8_array */
9999
void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED)
100100
{ fprintf(stderr, "towire_u8_array called!\n"); abort(); }
101+
/* Generated stub for is_p2wsh */
102+
bool is_p2wsh(const u8 *script UNNEEDED, size_t script_len UNNEEDED, struct sha256 *addr UNNEEDED)
103+
{ fprintf(stderr, "is_p2wsh called!\n"); abort(); }
104+
/* Generated stub for is_p2tr */
105+
bool is_p2tr(const u8 *script UNNEEDED, size_t script_len UNNEEDED, u8 xonly_pubkey[32] UNNEEDED)
106+
{ fprintf(stderr, "is_p2tr called!\n"); abort(); }
107+
/* Generated stub for is_p2wpkh */
108+
bool is_p2wpkh(const u8 *script UNNEEDED, size_t script_len UNNEEDED, struct bitcoin_address *addr UNNEEDED)
109+
{ fprintf(stderr, "is_p2wpkh called!\n"); abort(); }
101110
/* AUTOGENERATED MOCKS END */
102111

103112
const char extended_tx[] =

channeld/channeld.c

Lines changed: 86 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3153,47 +3153,70 @@ static struct wally_psbt_output *find_channel_output(struct peer *peer,
31533153
return NULL;
31543154
}
31553155

3156-
static size_t calc_weight(enum tx_role role, const struct wally_psbt *psbt)
3156+
static size_t calc_weight(enum tx_role role, const struct wally_psbt *psbt,
3157+
bool log_math)
31573158
{
3158-
size_t weight = 0;
3159+
size_t lweight = 0, weight = 0;
31593160

3160-
/* BOLT #2:
3161-
* The *initiator* is responsible for paying the fees for the following fields,
3162-
* to be referred to as the `common fields`.
3163-
*
3164-
* - version
3165-
* - segwit marker + flag
3166-
* - input count
3167-
* - output count
3168-
* - locktime
3169-
*/
3170-
if (role == TX_INITIATOR)
3171-
weight += bitcoin_tx_core_weight(psbt->num_inputs,
3172-
psbt->num_outputs);
3161+
if (log_math)
3162+
status_debug("Counting tx weight;");
31733163

31743164
/* BOLT #2:
31753165
* The rest of the transaction bytes' fees are the responsibility of
31763166
* the peer who contributed that input or output via `tx_add_input` or
31773167
* `tx_add_output`, at the agreed upon `feerate`.
31783168
*/
3179-
for (size_t i = 0; i < psbt->num_inputs; i++)
3169+
for (size_t i = 0; i < psbt->num_inputs; i++) {
31803170
if (is_initiators_serial(&psbt->inputs[i].unknowns)) {
31813171
if (role == TX_INITIATOR)
3182-
weight += psbt_input_get_weight(psbt, i);
3172+
weight += psbt_input_get_weight(psbt, i, PSBT_GUESS_2OF2);
31833173
}
3184-
else
3174+
else {
31853175
if (role != TX_INITIATOR)
3186-
weight += psbt_input_get_weight(psbt, i);
3176+
weight += psbt_input_get_weight(psbt, i, PSBT_GUESS_2OF2);
3177+
}
3178+
if (log_math)
3179+
status_debug(" Adding input"
3180+
" %lu; weight: %lu", i, weight - lweight);
3181+
lweight = weight;
3182+
}
31873183

3188-
for (size_t i = 0; i < psbt->num_outputs; i++)
3184+
for (size_t i = 0; i < psbt->num_outputs; i++) {
31893185
if (is_initiators_serial(&psbt->outputs[i].unknowns)) {
31903186
if (role == TX_INITIATOR)
31913187
weight += psbt_output_get_weight(psbt, i);
31923188
}
3193-
else
3189+
else {
31943190
if (role != TX_INITIATOR)
31953191
weight += psbt_output_get_weight(psbt, i);
3192+
}
3193+
if (log_math)
3194+
status_debug(" Adding output"
3195+
" %lu; weight: %lu", i, weight - lweight);
3196+
lweight = weight;
3197+
}
31963198

3199+
/* BOLT #2:
3200+
* The *initiator* is responsible for paying the fees for the following fields,
3201+
* to be referred to as the `common fields`.
3202+
*
3203+
* - version
3204+
* - segwit marker + flag
3205+
* - input count
3206+
* - output count
3207+
* - locktime
3208+
*/
3209+
if (role == TX_INITIATOR) {
3210+
weight += bitcoin_tx_core_weight(psbt->num_inputs,
3211+
psbt->num_outputs);
3212+
if (log_math)
3213+
status_debug(" Adding bitcoin_tx_core_weight;"
3214+
" weight: %lu", weight - lweight);
3215+
lweight = weight;
3216+
}
3217+
3218+
if (log_math)
3219+
status_debug("Total weight: %lu", weight);
31973220
return weight;
31983221
}
31993222

@@ -3294,8 +3317,7 @@ static struct amount_sat check_balances(struct peer *peer,
32943317
{
32953318
struct amount_sat min_initiator_fee, min_accepter_fee,
32963319
max_initiator_fee, max_accepter_fee,
3297-
funding_amount_res, min_multiplied,
3298-
initiator_penalty_fee, accepter_penalty_fee;
3320+
funding_amount_res;
32993321
struct amount_msat funding_amount,
33003322
initiator_fee, accepter_fee;
33013323
struct amount_msat in[NUM_TX_ROLES], out[NUM_TX_ROLES],
@@ -3444,33 +3466,26 @@ static struct amount_sat check_balances(struct peer *peer,
34443466
"amount_sat_less / amount_sat_sub mismtach");
34453467

34463468
min_initiator_fee = amount_tx_fee(peer->splicing->feerate_per_kw,
3447-
calc_weight(TX_INITIATOR, psbt));
3469+
calc_weight(TX_INITIATOR, psbt, false));
34483470
min_accepter_fee = amount_tx_fee(peer->splicing->feerate_per_kw,
3449-
calc_weight(TX_ACCEPTER, psbt));
3471+
calc_weight(TX_ACCEPTER, psbt, false));
34503472

34513473
/* As a safeguard max feerate is checked (only) locally, if it's
34523474
* particularly high we fail and tell the user but allow them to
34533475
* override with `splice_force_feerate` */
3454-
max_accepter_fee = amount_tx_fee(peer->feerate_max,
3455-
calc_weight(TX_ACCEPTER, psbt));
3456-
max_initiator_fee = amount_tx_fee(peer->feerate_max,
3457-
calc_weight(TX_INITIATOR, psbt));
3458-
initiator_penalty_fee = amount_tx_fee(peer->feerate_penalty,
3459-
calc_weight(TX_INITIATOR, psbt));
3460-
accepter_penalty_fee = amount_tx_fee(peer->feerate_penalty,
3461-
calc_weight(TX_ACCEPTER, psbt));
3462-
3463-
/* Sometimes feerate_max is some absurdly high value, in that case we
3464-
* give a fee warning based of a multiple of the min value. */
3465-
amount_sat_mul(&min_multiplied, min_accepter_fee, 5);
3466-
max_accepter_fee = SAT_MIN(min_multiplied, max_accepter_fee);
3467-
if (amount_sat_greater(accepter_penalty_fee, max_accepter_fee))
3468-
max_accepter_fee = accepter_penalty_fee;
3469-
3470-
amount_sat_mul(&min_multiplied, min_initiator_fee, 5);
3471-
max_initiator_fee = SAT_MIN(min_multiplied, max_initiator_fee);
3472-
if (amount_sat_greater(initiator_penalty_fee, max_initiator_fee))
3473-
max_initiator_fee = initiator_penalty_fee;
3476+
max_accepter_fee = amount_tx_fee(peer->feerate_opening,
3477+
calc_weight(TX_ACCEPTER, psbt, false));
3478+
max_initiator_fee = amount_tx_fee(peer->feerate_opening,
3479+
calc_weight(TX_INITIATOR, psbt, opener));
3480+
3481+
if (opener) {
3482+
status_debug("User specified fee of %s. Opening feerate %"PRIu32
3483+
" * weight %lu / 1000 = %s",
3484+
fmt_amount_m_as_sat(tmpctx, initiator_fee),
3485+
peer->feerate_opening,
3486+
calc_weight(TX_INITIATOR, psbt, false),
3487+
fmt_amount_sat(tmpctx, max_initiator_fee));
3488+
}
34743489

34753490
/* Check initiator fee */
34763491
if (amount_msat_less_sat(initiator_fee, min_initiator_fee)) {
@@ -3487,23 +3502,38 @@ static struct amount_sat check_balances(struct peer *peer,
34873502
&& amount_msat_greater_sat(initiator_fee, max_initiator_fee)) {
34883503
msg = towire_channeld_splice_feerate_error(NULL, initiator_fee,
34893504
true);
3505+
status_debug("Our own fee (%s) is too high to use without"
3506+
" forcing. Opening feerate %"PRIu32
3507+
" x weight %lu / 1000 = %s (max)",
3508+
fmt_amount_m_as_sat(tmpctx, initiator_fee),
3509+
peer->feerate_opening,
3510+
calc_weight(TX_INITIATOR, psbt, false),
3511+
fmt_amount_sat(tmpctx, max_initiator_fee));
3512+
34903513
wire_sync_write(MASTER_FD, take(msg));
3514+
34913515
splice_abort(peer,
3492-
"Our own fee (%s) was too high, max without"
3493-
" forcing is %s.",
3494-
fmt_amount_msat(tmpctx, initiator_fee),
3495-
fmt_amount_sat(tmpctx, max_initiator_fee));
3516+
"Our own fee (%s) is too high to use without"
3517+
" forcing. Opening feerate %"PRIu32
3518+
" x weight %lu / 1000 = %s (max)",
3519+
fmt_amount_m_as_sat(tmpctx, initiator_fee),
3520+
peer->feerate_opening,
3521+
calc_weight(TX_INITIATOR, psbt, false),
3522+
fmt_amount_sat(tmpctx, max_initiator_fee));
34963523
}
34973524
/* Check accepter fee */
34983525
if (amount_msat_less_sat(accepter_fee, min_accepter_fee)) {
34993526
msg = towire_channeld_splice_feerate_error(NULL, accepter_fee,
35003527
false);
35013528
wire_sync_write(MASTER_FD, take(msg));
35023529
splice_abort(peer,
3503-
"%s fee (%s) was too low, must be at least %s",
3504-
opener ? "Your" : "Our",
3505-
fmt_amount_msat(tmpctx, accepter_fee),
3506-
fmt_amount_sat(tmpctx, min_accepter_fee));
3530+
"%s fee (%s) was too low, must be at least %s"
3531+
" weight: %"PRIu64", feerate_max: %"PRIu32,
3532+
opener ? "Your" : "Our",
3533+
fmt_amount_msat(tmpctx, accepter_fee),
3534+
fmt_amount_sat(tmpctx, min_accepter_fee),
3535+
calc_weight(TX_INITIATOR, psbt, false),
3536+
peer->feerate_opening);
35073537
}
35083538
if (!peer->splicing->force_feerate && !opener
35093539
&& amount_msat_greater_sat(accepter_fee, max_accepter_fee)) {
@@ -3956,6 +3986,9 @@ static void resume_splice_negotiation(struct peer *peer,
39563986

39573987
peer->splicing = tal_free(peer->splicing);
39583988

3989+
if (our_role == TX_INITIATOR)
3990+
calc_weight(TX_INITIATOR, current_psbt, true);
3991+
39593992
final_tx = bitcoin_tx_with_psbt(tmpctx, current_psbt);
39603993
msg = towire_channeld_splice_confirmed_signed(tmpctx, final_tx,
39613994
new_output_index);

lightningd/channel_control.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -596,9 +596,10 @@ static void send_splice_tx(struct channel *channel,
596596
u8* tx_bytes = linearize_tx(tmpctx, tx);
597597

598598
log_debug(channel->log,
599-
"Broadcasting splice tx %s for channel %s.",
599+
"Broadcasting splice tx %s for channel %s. Final weight %lu",
600600
tal_hex(tmpctx, tx_bytes),
601-
fmt_channel_id(tmpctx, &channel->cid));
601+
fmt_channel_id(tmpctx, &channel->cid),
602+
bitcoin_tx_weight(tx));
602603

603604
struct send_splice_info *info = tal(NULL, struct send_splice_info);
604605

openingd/dualopend.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -819,14 +819,14 @@ static char *check_balances(const tal_t *ctx,
819819
assert(ok);
820820

821821
initiator_weight +=
822-
psbt_input_get_weight(psbt, i);
822+
psbt_input_get_weight(psbt, i, PSBT_GUESS_ZERO);
823823
} else {
824824
ok = amount_sat_add(&accepter_inputs,
825825
accepter_inputs, amt);
826826
assert(ok);
827827

828828
accepter_weight +=
829-
psbt_input_get_weight(psbt, i);
829+
psbt_input_get_weight(psbt, i, PSBT_GUESS_ZERO);
830830
}
831831
}
832832
tot_output_amt = AMOUNT_SAT(0);

0 commit comments

Comments
 (0)