diff --git a/bitcoin/psbt.c b/bitcoin/psbt.c index dff12b040fad..c255dc183c35 100644 --- a/bitcoin/psbt.c +++ b/bitcoin/psbt.c @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -483,6 +484,28 @@ void psbt_input_set_witscript(struct wally_psbt *psbt, size_t in, const u8 *wscr tal_wally_end(psbt); } +const u8 *psbt_input_get_witscript(const tal_t *ctx, + const struct wally_psbt *psbt, + size_t in) +{ + size_t witscript_len, written_len; + u8 *witscript; + if (wally_psbt_get_input_witness_script_len(psbt, in, &witscript_len) != WALLY_OK) + abort(); + witscript = tal_arr(ctx, u8, witscript_len); + if (wally_psbt_get_input_witness_script(psbt, in, witscript, witscript_len, &written_len) != WALLY_OK) + abort(); + if (witscript_len != written_len) + abort(); + return witscript; +} + +bool psbt_input_get_ecdsa_sig(const tal_t *ctx, + const struct wally_psbt *psbt, + size_t in, + const struct pubkey *pubkey, + struct bitcoin_signature **sig); + void psbt_elements_input_set_asset(struct wally_psbt *psbt, size_t in, struct amount_asset *asset) { @@ -595,10 +618,16 @@ struct amount_sat psbt_input_get_amount(const struct wally_psbt *psbt, } size_t psbt_input_get_weight(const struct wally_psbt *psbt, - size_t in) + size_t in, + enum PSBT_GUESS guess) { size_t weight; const struct wally_map_item *redeem_script; + struct wally_psbt_input *input = &psbt->inputs[in]; + struct wally_tx_output *utxo_out = NULL; + + if (input->utxo) + utxo_out = &input->utxo->outputs[input->index]; redeem_script = wally_map_get_integer(&psbt->inputs[in].psbt_fields, /* PSBT_IN_REDEEM_SCRIPT */ 0x04); @@ -608,8 +637,20 @@ size_t psbt_input_get_weight(const struct wally_psbt *psbt, weight += (redeem_script->value_len + varint_size(redeem_script->value_len)) * 4; + } else if ((guess & PSBT_GUESS_2OF2) + && utxo_out + && is_p2wsh(utxo_out->script, utxo_out->script_len, NULL)) { + weight = bitcoin_tx_input_weight(false, + bitcoin_tx_2of2_input_witness_weight()); + } else if (utxo_out + && is_p2wpkh(utxo_out->script, utxo_out->script_len, NULL)) { + weight = bitcoin_tx_input_weight(false, + bitcoin_tx_input_witness_weight(UTXO_P2SH_P2WPKH)); + } else if (utxo_out + && is_p2tr(utxo_out->script, utxo_out->script_len, NULL)) { + weight = bitcoin_tx_input_weight(false, + bitcoin_tx_input_witness_weight(UTXO_P2TR)); } else { - /* zero scriptSig length */ weight += varint_size(0) * 4; } diff --git a/bitcoin/psbt.h b/bitcoin/psbt.h index a44bba06636e..b5b186c2ee7d 100644 --- a/bitcoin/psbt.h +++ b/bitcoin/psbt.h @@ -207,6 +207,10 @@ WARN_UNUSED_RESULT bool psbt_input_get_ecdsa_sig(const tal_t *ctx, void psbt_input_set_witscript(struct wally_psbt *psbt, size_t in, const u8 *wscript); +const u8 *psbt_input_get_witscript(const tal_t *ctx, + const struct wally_psbt *psbt, + size_t in); + /* psbt_input_set_unknown - Set the given Key-Value in the psbt's input keymap * @ctx - tal context for allocations * @in - psbt input to set key-value on @@ -265,9 +269,20 @@ void psbt_output_set_unknown(const tal_t *ctx, struct amount_sat psbt_input_get_amount(const struct wally_psbt *psbt, size_t in); -/* psbt_input_get_weight - Calculate the tx weight for input index `in` */ +enum PSBT_GUESS { + PSBT_GUESS_ZERO = 0x0, /* Assume unknown is 0 bytes (fallback) */ + PSBT_GUESS_2OF2 = 0x1, /* Assume P2WSH is 2of2 multisig (req prevtx) */ +}; + +/* psbt_input_get_weight - Calculate the tx weight for input index `in`. + * + * @psbt - psbt + * @in - index of input who's weight you want + * @guess - How to guess if we have incomplete information + * */ size_t psbt_input_get_weight(const struct wally_psbt *psbt, - size_t in); + size_t in, + enum PSBT_GUESS guess); /* psbt_output_get_amount - Returns the value of this output * diff --git a/bitcoin/test/run-bitcoin_block_from_hex.c b/bitcoin/test/run-bitcoin_block_from_hex.c index 5f6bb9bb7bfc..ae32aa5d2a96 100644 --- a/bitcoin/test/run-bitcoin_block_from_hex.c +++ b/bitcoin/test/run-bitcoin_block_from_hex.c @@ -97,6 +97,15 @@ void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) /* Generated stub for towire_u8_array */ void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "towire_u8_array called!\n"); abort(); } +/* Generated stub for is_p2wsh */ +bool is_p2wsh(const u8 *script UNNEEDED, size_t script_len UNNEEDED, struct sha256 *addr UNNEEDED) +{ fprintf(stderr, "is_p2wsh called!\n"); abort(); } +/* Generated stub for is_p2tr */ +bool is_p2tr(const u8 *script UNNEEDED, size_t script_len UNNEEDED, u8 xonly_pubkey[32] UNNEEDED) +{ fprintf(stderr, "is_p2tr called!\n"); abort(); } +/* Generated stub for is_p2wpkh */ +bool is_p2wpkh(const u8 *script UNNEEDED, size_t script_len UNNEEDED, struct bitcoin_address *addr UNNEEDED) +{ fprintf(stderr, "is_p2wpkh called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ static const char block[] = diff --git a/bitcoin/test/run-psbt-from-tx.c b/bitcoin/test/run-psbt-from-tx.c index 2b55c420b0a2..d60eceba8971 100644 --- a/bitcoin/test/run-psbt-from-tx.c +++ b/bitcoin/test/run-psbt-from-tx.c @@ -80,6 +80,15 @@ size_t varint_put(u8 buf[VARINT_MAX_LEN] UNNEEDED, varint_t v UNNEEDED) /* Generated stub for varint_size */ size_t varint_size(varint_t v UNNEEDED) { fprintf(stderr, "varint_size called!\n"); abort(); } +/* Generated stub for is_p2wsh */ +bool is_p2wsh(const u8 *script UNNEEDED, size_t script_len UNNEEDED, struct sha256 *addr UNNEEDED) +{ fprintf(stderr, "is_p2wsh called!\n"); abort(); } +/* Generated stub for is_p2tr */ +bool is_p2tr(const u8 *script UNNEEDED, size_t script_len UNNEEDED, u8 xonly_pubkey[32] UNNEEDED) +{ fprintf(stderr, "is_p2tr called!\n"); abort(); } +/* Generated stub for is_p2wpkh */ +bool is_p2wpkh(const u8 *script UNNEEDED, size_t script_len UNNEEDED, struct bitcoin_address *addr UNNEEDED) +{ fprintf(stderr, "is_p2wpkh called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ /* This transaction has scriptSig data in it. diff --git a/bitcoin/test/run-tx-encode.c b/bitcoin/test/run-tx-encode.c index b28f3191c00c..8a70cf2ace54 100644 --- a/bitcoin/test/run-tx-encode.c +++ b/bitcoin/test/run-tx-encode.c @@ -98,6 +98,15 @@ void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) /* Generated stub for towire_u8_array */ void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "towire_u8_array called!\n"); abort(); } +/* Generated stub for is_p2wsh */ +bool is_p2wsh(const u8 *script UNNEEDED, size_t script_len UNNEEDED, struct sha256 *addr UNNEEDED) +{ fprintf(stderr, "is_p2wsh called!\n"); abort(); } +/* Generated stub for is_p2tr */ +bool is_p2tr(const u8 *script UNNEEDED, size_t script_len UNNEEDED, u8 xonly_pubkey[32] UNNEEDED) +{ fprintf(stderr, "is_p2tr called!\n"); abort(); } +/* Generated stub for is_p2wpkh */ +bool is_p2wpkh(const u8 *script UNNEEDED, size_t script_len UNNEEDED, struct bitcoin_address *addr UNNEEDED) +{ fprintf(stderr, "is_p2wpkh called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ const char extended_tx[] = diff --git a/bitcoin/tx.c b/bitcoin/tx.c index 4c1a63de6c0a..7852ce738e5c 100644 --- a/bitcoin/tx.c +++ b/bitcoin/tx.c @@ -950,8 +950,7 @@ size_t bitcoin_tx_2of2_input_witness_weight(void) /* BOLT #03: * Signatures are 73 bytes long (the maximum length). */ - return 1 + /* Prefix: 4 elements to push on stack */ - (1 + 0) + /* [0]: witness-marker-and-flag */ + return (1 + 0) + /* [0]: witness-marker-and-flag */ (1 + 73) + /* [1] Party A signature and length prefix */ (1 + 73) + /* [2] Party B signature and length prefix */ (1 + 1 + /* [3] length prefix and numpushes (2) */ diff --git a/channeld/channeld.c b/channeld/channeld.c index fe7f7707f264..a74fcd53f3d9 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -88,6 +88,9 @@ struct peer { /* Feerate to be used when creating penalty transactions. */ u32 feerate_penalty; + /* Feerate to be used when opening (or splicing) a channel. */ + u32 feerate_opening; + /* Local next per-commit point. */ struct pubkey next_local_per_commit; @@ -3150,47 +3153,70 @@ static struct wally_psbt_output *find_channel_output(struct peer *peer, return NULL; } -static size_t calc_weight(enum tx_role role, const struct wally_psbt *psbt) +static size_t calc_weight(enum tx_role role, const struct wally_psbt *psbt, + bool log_math) { - size_t weight = 0; + size_t lweight = 0, weight = 0; - /* BOLT #2: - * The *initiator* is responsible for paying the fees for the following fields, - * to be referred to as the `common fields`. - * - * - version - * - segwit marker + flag - * - input count - * - output count - * - locktime - */ - if (role == TX_INITIATOR) - weight += bitcoin_tx_core_weight(psbt->num_inputs, - psbt->num_outputs); + if (log_math) + status_debug("Counting tx weight;"); /* BOLT #2: * The rest of the transaction bytes' fees are the responsibility of * the peer who contributed that input or output via `tx_add_input` or * `tx_add_output`, at the agreed upon `feerate`. */ - for (size_t i = 0; i < psbt->num_inputs; i++) + for (size_t i = 0; i < psbt->num_inputs; i++) { if (is_initiators_serial(&psbt->inputs[i].unknowns)) { if (role == TX_INITIATOR) - weight += psbt_input_get_weight(psbt, i); + weight += psbt_input_get_weight(psbt, i, PSBT_GUESS_2OF2); } - else + else { if (role != TX_INITIATOR) - weight += psbt_input_get_weight(psbt, i); + weight += psbt_input_get_weight(psbt, i, PSBT_GUESS_2OF2); + } + if (log_math) + status_debug(" Adding input" + " %lu; weight: %lu", i, weight - lweight); + lweight = weight; + } - for (size_t i = 0; i < psbt->num_outputs; i++) + for (size_t i = 0; i < psbt->num_outputs; i++) { if (is_initiators_serial(&psbt->outputs[i].unknowns)) { if (role == TX_INITIATOR) weight += psbt_output_get_weight(psbt, i); } - else + else { if (role != TX_INITIATOR) weight += psbt_output_get_weight(psbt, i); + } + if (log_math) + status_debug(" Adding output" + " %lu; weight: %lu", i, weight - lweight); + lweight = weight; + } + /* BOLT #2: + * The *initiator* is responsible for paying the fees for the following fields, + * to be referred to as the `common fields`. + * + * - version + * - segwit marker + flag + * - input count + * - output count + * - locktime + */ + if (role == TX_INITIATOR) { + weight += bitcoin_tx_core_weight(psbt->num_inputs, + psbt->num_outputs); + if (log_math) + status_debug(" Adding bitcoin_tx_core_weight;" + " weight: %lu", weight - lweight); + lweight = weight; + } + + if (log_math) + status_debug("Total weight: %lu", weight); return weight; } @@ -3291,8 +3317,7 @@ static struct amount_sat check_balances(struct peer *peer, { struct amount_sat min_initiator_fee, min_accepter_fee, max_initiator_fee, max_accepter_fee, - funding_amount_res, min_multiplied, - initiator_penalty_fee, accepter_penalty_fee; + funding_amount_res; struct amount_msat funding_amount, initiator_fee, accepter_fee; struct amount_msat in[NUM_TX_ROLES], out[NUM_TX_ROLES], @@ -3441,33 +3466,26 @@ static struct amount_sat check_balances(struct peer *peer, "amount_sat_less / amount_sat_sub mismtach"); min_initiator_fee = amount_tx_fee(peer->splicing->feerate_per_kw, - calc_weight(TX_INITIATOR, psbt)); + calc_weight(TX_INITIATOR, psbt, false)); min_accepter_fee = amount_tx_fee(peer->splicing->feerate_per_kw, - calc_weight(TX_ACCEPTER, psbt)); + calc_weight(TX_ACCEPTER, psbt, false)); /* As a safeguard max feerate is checked (only) locally, if it's * particularly high we fail and tell the user but allow them to * override with `splice_force_feerate` */ - max_accepter_fee = amount_tx_fee(peer->feerate_max, - calc_weight(TX_ACCEPTER, psbt)); - max_initiator_fee = amount_tx_fee(peer->feerate_max, - calc_weight(TX_INITIATOR, psbt)); - initiator_penalty_fee = amount_tx_fee(peer->feerate_penalty, - calc_weight(TX_INITIATOR, psbt)); - accepter_penalty_fee = amount_tx_fee(peer->feerate_penalty, - calc_weight(TX_ACCEPTER, psbt)); - - /* Sometimes feerate_max is some absurdly high value, in that case we - * give a fee warning based of a multiple of the min value. */ - amount_sat_mul(&min_multiplied, min_accepter_fee, 5); - max_accepter_fee = SAT_MIN(min_multiplied, max_accepter_fee); - if (amount_sat_greater(accepter_penalty_fee, max_accepter_fee)) - max_accepter_fee = accepter_penalty_fee; - - amount_sat_mul(&min_multiplied, min_initiator_fee, 5); - max_initiator_fee = SAT_MIN(min_multiplied, max_initiator_fee); - if (amount_sat_greater(initiator_penalty_fee, max_initiator_fee)) - max_initiator_fee = initiator_penalty_fee; + max_accepter_fee = amount_tx_fee(peer->feerate_opening, + calc_weight(TX_ACCEPTER, psbt, false)); + max_initiator_fee = amount_tx_fee(peer->feerate_opening, + calc_weight(TX_INITIATOR, psbt, opener)); + + if (opener) { + status_debug("User specified fee of %s. Opening feerate %"PRIu32 + " * weight %lu / 1000 = %s", + fmt_amount_m_as_sat(tmpctx, initiator_fee), + peer->feerate_opening, + calc_weight(TX_INITIATOR, psbt, false), + fmt_amount_sat(tmpctx, max_initiator_fee)); + } /* Check initiator fee */ if (amount_msat_less_sat(initiator_fee, min_initiator_fee)) { @@ -3484,12 +3502,24 @@ static struct amount_sat check_balances(struct peer *peer, && amount_msat_greater_sat(initiator_fee, max_initiator_fee)) { msg = towire_channeld_splice_feerate_error(NULL, initiator_fee, true); + status_debug("Our own fee (%s) is too high to use without" + " forcing. Opening feerate %"PRIu32 + " x weight %lu / 1000 = %s (max)", + fmt_amount_m_as_sat(tmpctx, initiator_fee), + peer->feerate_opening, + calc_weight(TX_INITIATOR, psbt, false), + fmt_amount_sat(tmpctx, max_initiator_fee)); + wire_sync_write(MASTER_FD, take(msg)); + splice_abort(peer, - "Our own fee (%s) was too high, max without" - " forcing is %s.", - fmt_amount_msat(tmpctx, initiator_fee), - fmt_amount_sat(tmpctx, max_initiator_fee)); + "Our own fee (%s) is too high to use without" + " forcing. Opening feerate %"PRIu32 + " x weight %lu / 1000 = %s (max)", + fmt_amount_m_as_sat(tmpctx, initiator_fee), + peer->feerate_opening, + calc_weight(TX_INITIATOR, psbt, false), + fmt_amount_sat(tmpctx, max_initiator_fee)); } /* Check accepter fee */ if (amount_msat_less_sat(accepter_fee, min_accepter_fee)) { @@ -3497,10 +3527,13 @@ static struct amount_sat check_balances(struct peer *peer, false); wire_sync_write(MASTER_FD, take(msg)); splice_abort(peer, - "%s fee (%s) was too low, must be at least %s", - opener ? "Your" : "Our", - fmt_amount_msat(tmpctx, accepter_fee), - fmt_amount_sat(tmpctx, min_accepter_fee)); + "%s fee (%s) was too low, must be at least %s" + " weight: %"PRIu64", feerate_max: %"PRIu32, + opener ? "Your" : "Our", + fmt_amount_msat(tmpctx, accepter_fee), + fmt_amount_sat(tmpctx, min_accepter_fee), + calc_weight(TX_INITIATOR, psbt, false), + peer->feerate_opening); } if (!peer->splicing->force_feerate && !opener && amount_msat_greater_sat(accepter_fee, max_accepter_fee)) { @@ -3953,6 +3986,9 @@ static void resume_splice_negotiation(struct peer *peer, peer->splicing = tal_free(peer->splicing); + if (our_role == TX_INITIATOR) + calc_weight(TX_INITIATOR, current_psbt, true); + final_tx = bitcoin_tx_with_psbt(tmpctx, current_psbt); msg = towire_channeld_splice_confirmed_signed(tmpctx, final_tx, new_output_index); @@ -6282,7 +6318,8 @@ static void handle_feerates(struct peer *peer, const u8 *inmsg) if (!fromwire_channeld_feerates(inmsg, &feerate, &peer->feerate_min, &peer->feerate_max, - &peer->feerate_penalty)) + &peer->feerate_penalty, + &peer->feerate_opening)) master_badmsg(WIRE_CHANNELD_FEERATES, inmsg); /* BOLT #2: @@ -6653,6 +6690,7 @@ static void init_channel(struct peer *peer) &peer->feerate_min, &peer->feerate_max, &peer->feerate_penalty, + &peer->feerate_opening, &peer->their_commit_sig, &funding_pubkey[REMOTE], &points[REMOTE], diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index be5a5c6dee36..47c234392568 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -31,6 +31,7 @@ msgdata,channeld_init,fee_states,fee_states, msgdata,channeld_init,feerate_min,u32, msgdata,channeld_init,feerate_max,u32, msgdata,channeld_init,feerate_penalty,u32, +msgdata,channeld_init,feerate_opening,u32, msgdata,channeld_init,first_commit_sig,bitcoin_signature, msgdata,channeld_init,remote_fundingkey,pubkey, msgdata,channeld_init,remote_basepoints,basepoints, @@ -329,6 +330,7 @@ msgdata,channeld_feerates,feerate,u32, msgdata,channeld_feerates,min_feerate,u32, msgdata,channeld_feerates,max_feerate,u32, msgdata,channeld_feerates,penalty_feerate,u32, +msgdata,channeld_feerates,opening_feerate,u32, # master -> channeld: do you have a memleak? msgtype,channeld_dev_memleak,1033 diff --git a/common/amount.c b/common/amount.c index 1eae69cefc8c..03a06f40856b 100644 --- a/common/amount.c +++ b/common/amount.c @@ -7,6 +7,7 @@ #include #include #include +#include #include bool amount_sat_to_msat(struct amount_msat *msat, @@ -64,6 +65,24 @@ char *fmt_amount_msat(const tal_t *ctx, struct amount_msat msat) return tal_fmt(ctx, "%"PRIu64"msat", msat.millisatoshis); } +#define LSIZE 5 + +char *fmt_amount_m_as_sat(const tal_t *ctx, struct amount_msat msat) +{ + char last[LSIZE] = {0}; + + if (msat.millisatoshis % MSAT_PER_SAT) { + snprintf(last, LSIZE, ".%03"PRIu64, + msat.millisatoshis % MSAT_PER_SAT); + while(last[strlen(last) - 1] == '0') + last[strlen(last) - 1] = 0; + } + + return tal_fmt(ctx, "%"PRIu64"%ssat", + msat.millisatoshis / MSAT_PER_SAT, + last); +} + const char *fmt_amount_sat_btc(const tal_t *ctx, struct amount_sat sat, bool append_unit) diff --git a/common/amount.h b/common/amount.h index b1cdbac1d570..a5bd6a027e90 100644 --- a/common/amount.h +++ b/common/amount.h @@ -223,6 +223,9 @@ const char *fmt_amount_msat_btc(const tal_t *ctx, /* => 1234msat */ char *fmt_amount_msat(const tal_t *ctx, struct amount_msat msat); +/* => 1234.12sat */ +char *fmt_amount_m_as_sat(const tal_t *ctx, struct amount_msat msat); + /* => 1.23456789btc (8 decimals!) */ const char *fmt_amount_sat_btc(const tal_t *ctx, struct amount_sat sat, diff --git a/common/splice_script.c b/common/splice_script.c index 453bc6ca5f3e..fb9f653b6ac2 100644 --- a/common/splice_script.c +++ b/common/splice_script.c @@ -1114,12 +1114,6 @@ static struct splice_script_error *type_data(const tal_t *ctx, input[i]->type = TOK_NODEID; input[i]->node_id = tal(input[i], struct node_id); - if (!node_id_from_hexstr(input[i]->str, - strlen(input[i]->str), - input[i]->node_id)) - return new_error(ctx, INVALID_NODEID, - input[i], - "type_data"); /* Rare corner case where channel begins with * prefix of 02 or 03 */ if (autocomplete_chan_id(input[i], channels, @@ -1137,6 +1131,12 @@ static struct splice_script_error *type_data(const tal_t *ctx, "type_data"); input[i]->type = TOK_CHANID; input[i]->node_id = tal_free(input[i]->node_id); + } else if (!node_id_from_hexstr(input[i]->str, + strlen(input[i]->str), + input[i]->node_id)) { + return new_error(ctx, INVALID_NODEID, + input[i], + "type_data"); } } else if (is_bitcoin_address(input[i]->str)) { input[i]->type = TOK_BTCADDR; diff --git a/common/test/run-amount.c b/common/test/run-amount.c index 0e9f295dc0c2..25a3f7f960a4 100644 --- a/common/test/run-amount.c +++ b/common/test/run-amount.c @@ -346,6 +346,57 @@ int main(int argc, char *argv[]) /* Overflowingly big. */ FAIL_SAT(&sat, "21000000000000000000000000.00000000btc"); + const char *partial_sats[] = + { + "10.111sat", + "10.11sat", + "10.1sat", + "10sat", + "10.001sat", + "10.01sat", + "10.1sat", + "1.111sat", + "1.11sat", + "1.1sat", + "1sat", + "0.111sat", + "0.011sat", + "0.001sat", + "0sat", + NULL, + }; + + u64 msat_amnts[] = + { + 10111, + 10110, + 10100, + 10000, + 10001, + 10010, + 10100, + 1111, + 1110, + 1100, + 1000, + 111, + 11, + 1, + 0, + }; + + assert(sizeof(partial_sats) / sizeof(partial_sats[0]) - 1 + == sizeof(msat_amnts) / sizeof(msat_amnts[0])); + + for (int i = 0; partial_sats[i]; i++) { + msat.millisatoshis = msat_amnts[i]; + printf("Does '%s' equal '%s'\n", + fmt_amount_m_as_sat(tmpctx, msat), + partial_sats[i]); + assert(streq(fmt_amount_m_as_sat(tmpctx, msat), + partial_sats[i])); + } + /* Test fmt_amount_msat_btc, fmt_amount_msat */ for (u64 i = 0; i <= UINT64_MAX / 10; i = i ? i * 10 : 1) { const char *with, *without; diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 93fb13ec16da..1c9ef05d770e 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -94,7 +94,8 @@ void channel_update_feerates(struct lightningd *ld, const struct channel *channe msg = towire_channeld_feerates(NULL, feerate, min_feerate, max_feerate, - penalty_feerate(ld->topology)); + penalty_feerate(ld->topology), + opening_feerate(ld->topology)); subd_send_msg(channel->owner, take(msg)); } @@ -595,9 +596,10 @@ static void send_splice_tx(struct channel *channel, u8* tx_bytes = linearize_tx(tmpctx, tx); log_debug(channel->log, - "Broadcasting splice tx %s for channel %s.", + "Broadcasting splice tx %s for channel %s. Final weight %lu", tal_hex(tmpctx, tx_bytes), - fmt_channel_id(tmpctx, &channel->cid)); + fmt_channel_id(tmpctx, &channel->cid), + bitcoin_tx_weight(tx)); struct send_splice_info *info = tal(NULL, struct send_splice_info); @@ -1863,6 +1865,7 @@ bool peer_start_channeld(struct channel *channel, min_feerate, max_feerate, penalty_feerate(ld->topology), + opening_feerate(ld->topology), &channel->last_sig, &channel->channel_info.remote_fundingkey, &channel->channel_info.theirbase, @@ -2654,7 +2657,8 @@ static struct command_result *json_dev_feerate(struct command *cmd, msg = towire_channeld_feerates(NULL, *feerate, feerate_min(cmd->ld, NULL), feerate_max(cmd->ld, NULL), - penalty_feerate(cmd->ld->topology)); + penalty_feerate(cmd->ld->topology), + opening_feerate(cmd->ld->topology)); subd_send_msg(channel->owner, take(msg)); response = json_stream_success(cmd); diff --git a/openingd/dualopend.c b/openingd/dualopend.c index d140d37b6c4b..995767ff3271 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -819,14 +819,14 @@ static char *check_balances(const tal_t *ctx, assert(ok); initiator_weight += - psbt_input_get_weight(psbt, i); + psbt_input_get_weight(psbt, i, PSBT_GUESS_ZERO); } else { ok = amount_sat_add(&accepter_inputs, accepter_inputs, amt); assert(ok); accepter_weight += - psbt_input_get_weight(psbt, i); + psbt_input_get_weight(psbt, i, PSBT_GUESS_ZERO); } } tot_output_amt = AMOUNT_SAT(0); diff --git a/plugins/spender/splice.c b/plugins/spender/splice.c index 1c8ce72fa9ff..f2b67e962381 100644 --- a/plugins/spender/splice.c +++ b/plugins/spender/splice.c @@ -74,9 +74,11 @@ static struct command_result *unreserve_get_result(struct command *cmd, &splice_cmd->final_txid); } - json_array_start(response, "log"); - debug_log_to_json(response, splice_cmd->debug_log); - json_array_end(response); + if (splice_cmd->debug_log) { + json_array_start(response, "log"); + debug_log_to_json(response, splice_cmd->debug_log); + json_array_end(response); + } tal_free(abort_pkg); return command_finished(cmd, response); @@ -122,8 +124,8 @@ static struct command_result *do_fail(struct command *cmd, splice_cmd->wetrun = false; plugin_log(cmd->plugin, LOG_DBG, - "splice_error(psbt:%p, splice_cmd_stat:%p)", - splice_cmd->psbt, splice_cmd); + "splice_error(psbt:%p, splice_cmd:%p, str: %s)", + splice_cmd->psbt, splice_cmd, str ?: ""); abort_pkg = tal(cmd->plugin, struct abort_pkg); abort_pkg->splice_cmd = tal_steal(abort_pkg, splice_cmd); @@ -179,7 +181,11 @@ static struct command_result *splice_error_pkg(struct command *cmd, const jsmntok_t *error, struct splice_index_pkg *pkg) { - return splice_error(cmd, methodname, buf, error, pkg->splice_cmd); + struct command_result *res = splice_error(cmd, methodname, buf, error, pkg->splice_cmd); + + tal_free(pkg); + + return res; } static struct command_result *calc_in_ppm_and_fee(struct command *cmd, @@ -457,41 +463,71 @@ static size_t calc_weight(struct splice_cmd *splice_cmd, bool simulate_wallet_outputs) { struct splice_script_result *action; + struct plugin *plugin = splice_cmd->cmd->plugin; struct wally_psbt *psbt = splice_cmd->psbt; - size_t weight = 0; + size_t lweight = 0, weight = 0; size_t extra_inputs = 0; size_t extra_outputs = 0; + plugin_log(plugin, LOG_DBG, "Counting potenetial tx weight;"); + /* BOLT #2: * The rest of the transaction bytes' fees are the responsibility of * the peer who contributed that input or output via `tx_add_input` or * `tx_add_output`, at the agreed upon `feerate`. */ - for (size_t i = 0; i < psbt->num_inputs; i++) - weight += psbt_input_get_weight(psbt, i); + for (size_t i = 0; i < psbt->num_inputs; i++) { + weight += psbt_input_get_weight(psbt, i, PSBT_GUESS_2OF2); + plugin_log(plugin, LOG_DBG, " Adding input; weight: %lu", + weight - lweight); + lweight = weight; + } - for (size_t i = 0; i < psbt->num_outputs; i++) - weight += psbt_output_get_weight(psbt, i); + /* Count the splice input manually */ + for (size_t i = 0; i < tal_count(splice_cmd->actions); i++) { + action = splice_cmd->actions[i]; + if (splice_cmd->actions[i]->channel_id) { + weight += bitcoin_tx_input_weight(false, + bitcoin_tx_2of2_input_witness_weight()); + plugin_log(plugin, LOG_DBG, " Adding input" + " (simulated channel); weight:" + " %lu", weight - lweight); + lweight = weight; + extra_inputs++; + } + } - /* Count the splice input & outputs manually */ + /* Count the splice outputs manually */ for (size_t i = 0; i < tal_count(splice_cmd->actions); i++) { action = splice_cmd->actions[i]; if (simulate_wallet_outputs && action->onchain_wallet) { if (!amount_sat_is_zero(action->in_sat) || action->in_ppm) { weight += bitcoin_tx_output_weight(BITCOIN_SCRIPTPUBKEY_P2TR_LEN); extra_outputs++; + plugin_log(plugin, LOG_DBG, " Adding output" + " (simulated wallet); weight:" + " %lu", weight - lweight); + lweight = weight; } - - } else if (splice_cmd->actions[i]->channel_id) { + assert(!splice_cmd->actions[i]->channel_id); + } + if (splice_cmd->actions[i]->channel_id) { weight += bitcoin_tx_output_weight(BITCOIN_SCRIPTPUBKEY_P2WSH_LEN); - weight += bitcoin_tx_input_weight(true, - bitcoin_tx_2of2_input_witness_weight()); - extra_inputs++; + plugin_log(plugin, LOG_DBG, " Adding output" + " (simulated channel); weight:" + " %lu", weight - lweight); + lweight = weight; + extra_outputs++; } } - /* DTODO make a test to confirm weight calculation is correct */ + for (size_t i = 0; i < psbt->num_outputs; i++) { + weight += psbt_output_get_weight(psbt, i); + plugin_log(plugin, LOG_DBG, " Adding output; weight: %lu", + weight - lweight); + lweight = weight; + } /* BOLT #2: * The *initiator* is responsible for paying the fees for the following fields, @@ -505,7 +541,11 @@ static size_t calc_weight(struct splice_cmd *splice_cmd, */ weight += bitcoin_tx_core_weight(psbt->num_inputs + extra_inputs, psbt->num_outputs + extra_outputs); + plugin_log(plugin, LOG_DBG, " Adding bitcoin_tx_core_weight;" + " weight: %lu", weight - lweight); + lweight = weight; + plugin_log(plugin, LOG_DBG, " Total weight: %lu", weight); return weight; } @@ -747,6 +787,8 @@ static struct command_result *splice_signed_error_pkg(struct command *cmd, error->end - error->start); abort_pkg->code = -1; + tal_free(pkg); + return make_error(cmd, abort_pkg, "splice_signed_error"); } @@ -912,11 +954,11 @@ static struct command_result *continue_splice(struct command *cmd, plugin_log(cmd->plugin, LOG_INFORM, "Splice fee is %s at %"PRIu32" perkw (%.02f sat/vB) " - "on tx where our personal vbytes are %.02f", + "on tx where our weight units are %lu", fmt_amount_sat(tmpctx, onchain_fee), splice_cmd->feerate_per_kw, 4 * splice_cmd->feerate_per_kw / 1000.0f, - weight / 4.0f); + weight); result = calc_in_ppm_and_fee(cmd, splice_cmd, onchain_fee); if (result) @@ -1261,7 +1303,6 @@ validate_splice_cmd(struct splice_cmd *splice_cmd) { struct splice_script_result *action; int paying_fee_count = 0; - int channels = 0; for (size_t i = 0; i < tal_count(splice_cmd->actions); i++) { action = splice_cmd->actions[i]; /* Taking fee from onchain wallet requires recursive looping @@ -1297,14 +1338,6 @@ validate_splice_cmd(struct splice_cmd *splice_cmd) JSONRPC2_INVALID_PARAMS, "Dynamic bitcoin address amounts" " not supported for now"); - if (action->channel_id) { - if (channels) - return command_fail(splice_cmd->cmd, - JSONRPC2_INVALID_PARAMS, - "Multi-channel splice not" - "supported for now"); - channels++; - } if (action->bitcoin_address) return command_fail(splice_cmd->cmd, JSONRPC2_INVALID_PARAMS, diff --git a/tests/plugins/channeld_fakenet.c b/tests/plugins/channeld_fakenet.c index fc08d2bf1352..51c702305548 100644 --- a/tests/plugins/channeld_fakenet.c +++ b/tests/plugins/channeld_fakenet.c @@ -963,10 +963,10 @@ static void handle_offer_htlc(struct info *info, const u8 *inmsg) static void handle_feerates(struct info *info, const u8 *inmsg) { - u32 feerate, min, max, penalty; + u32 feerate, min, max, penalty, opening; if (!fromwire_channeld_feerates(inmsg, &feerate, - &min, &max, &penalty)) + &min, &max, &penalty, &opening)) master_badmsg(WIRE_CHANNELD_FEERATES, inmsg); /* BOLT #2: @@ -1058,7 +1058,7 @@ static struct channel *handle_init(struct info *info, const u8 *init_msg) struct secret last_remote_per_commit_secret; struct penalty_base *pbases; struct channel_type *channel_type; - u32 feerate_min, feerate_max, feerate_penalty; + u32 feerate_min, feerate_max, feerate_penalty, feerate_opening; struct pubkey remote_per_commit; struct pubkey old_remote_per_commit; u32 commit_msec; @@ -1101,6 +1101,7 @@ static struct channel *handle_init(struct info *info, const u8 *init_msg) &feerate_min, &feerate_max, &feerate_penalty, + &feerate_opening, &their_commit_sig, &funding_pubkey[REMOTE], &points[REMOTE], diff --git a/tests/test_splice.py b/tests/test_splice.py index a26c983166ad..3ff8a0ee9173 100644 --- a/tests/test_splice.py +++ b/tests/test_splice.py @@ -195,3 +195,35 @@ def test_script_splice_in(node_factory, bitcoind, chainparams): l1.wait_for_channel_onchain(l2.info['id']) account_info = only_one([acct for acct in l1.rpc.bkpr_listbalances()['accounts'] if acct['account'] == account_id]) assert not account_info['account_closed'] + + +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +def test_script_two_chan_splice_in(node_factory, bitcoind): + l1, l2, l3 = node_factory.line_graph(3, fundamount=1000000, wait_for_announce=True, opts={'experimental-splicing': None}) + + chan_id1 = l2.get_channel_id(l1) + chan_id2 = l2.get_channel_id(l3) + + # l2 will splice funds into the channels with l1 and l3 at the same time + result = l2.rpc.splice(f"wallet -> 200999; 100000 -> {chan_id1}; 100000 -> {chan_id2}") + + l3.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE') + l2.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE') + l1.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE') + + wait_for(lambda: len(list(bitcoind.rpc.getrawmempool(True).keys())) == 1) + assert result['txid'] in list(bitcoind.rpc.getrawmempool(True).keys()) + + bitcoind.generate_block(6, wait_for_mempool=1) + + l3.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL') + l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL') + l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL') + + inv = l2.rpc.invoice(10**2, '1', 'no_1') + l1.rpc.pay(inv['bolt11']) + + inv = l3.rpc.invoice(10**2, '2', 'no_2') + l2.rpc.pay(inv['bolt11'])