mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 01:43:36 +01:00
splice: Reestablish when commit or sig sends fail
Adds tests for when the connection fails during 1) splice tx_signature 2) splice commitment_signed Fleshed out the reestablish flow for these two cases and implemented the fixes to make these reestablish flows work. Part of this work required changing commit process for splices: Now we send a single commit_part for the splice where previously we sent all commits, and accordingly, we no longer revoke in response. Changelog-Fixed: Implemented splicing restart logic for tx_signature and commitment_signed. Splice commitments are reworked in a manner incompatible with the last version.
This commit is contained in:
parent
eae0d1da1e
commit
a6a9e5b1e3
@ -290,6 +290,27 @@ bool psbt_input_set_signature(struct wally_psbt *psbt, size_t in,
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool psbt_input_have_signature(const struct wally_psbt *psbt,
|
||||
size_t in,
|
||||
const struct pubkey *pubkey,
|
||||
bool *signature_found)
|
||||
{
|
||||
u8 pk_der[PUBKEY_CMPR_LEN];
|
||||
size_t index_plus_one;
|
||||
bool ok;
|
||||
|
||||
assert(in < psbt->num_inputs);
|
||||
|
||||
pubkey_to_der(pk_der, pubkey);
|
||||
|
||||
ok = wally_psbt_input_find_signature(&psbt->inputs[in], pk_der,
|
||||
sizeof(pk_der),
|
||||
&index_plus_one) == WALLY_OK;
|
||||
if (ok)
|
||||
*signature_found = index_plus_one > 0;
|
||||
return ok;
|
||||
}
|
||||
|
||||
void psbt_input_set_wit_utxo(struct wally_psbt *psbt, size_t in,
|
||||
const u8 *scriptPubkey, struct amount_sat amt)
|
||||
{
|
||||
|
@ -178,6 +178,14 @@ WARN_UNUSED_RESULT bool psbt_input_set_signature(struct wally_psbt *psbt, size_t
|
||||
const struct pubkey *pubkey,
|
||||
const struct bitcoin_signature *sig);
|
||||
|
||||
/* Returns false on error. On success, *signature_found is set to true if the
|
||||
* input has a signature present for `pubkey` and false if if one was not found.
|
||||
* Only ignature presence is checked, is not validated. */
|
||||
WARN_UNUSED_RESULT bool psbt_input_have_signature(const struct wally_psbt *psbt,
|
||||
size_t in,
|
||||
const struct pubkey *pubkey,
|
||||
bool *signature_found);
|
||||
|
||||
void psbt_input_set_witscript(struct wally_psbt *psbt, size_t in, const u8 *wscript);
|
||||
|
||||
/* psbt_input_set_unknown - Set the given Key-Value in the psbt's input keymap
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -243,6 +243,10 @@ msgtype,channeld_splice_confirmed_signed,7213
|
||||
msgdata,channeld_splice_confirmed_signed,tx,bitcoin_tx,
|
||||
msgdata,channeld_splice_confirmed_signed,output_index,u32,
|
||||
|
||||
# channeld->master: Splice signatures are about to be sent
|
||||
msgtype,channeld_splice_sending_sigs,7214
|
||||
msgdata,channeld_splice_sending_sigs,tx,bitcoin_txid,
|
||||
|
||||
# channeld->master: A feerate error has occured
|
||||
msgtype,channeld_splice_feerate_error,7215
|
||||
msgdata,channeld_splice_feerate_error,fee,amount_msat,
|
||||
@ -257,6 +261,7 @@ msgdata,channeld_add_inflight,satoshis,amount_sat,
|
||||
msgdata,channeld_add_inflight,splice_amount,s64,
|
||||
msgdata,channeld_add_inflight,psbt,wally_psbt,
|
||||
msgdata,channeld_add_inflight,i_am_initiator,bool,
|
||||
msgdata,channeld_add_inflight,force_sign_first,bool,
|
||||
|
||||
# master->channeld: Inflight saved successfully
|
||||
msgtype,channeld_got_inflight,7217
|
||||
|
Can't render this file because it has a wrong number of fields in line 15.
|
@ -10,6 +10,7 @@ struct inflight *fromwire_inflight(const tal_t *ctx, const u8 **cursor, size_t *
|
||||
|
||||
fromwire_bitcoin_outpoint(cursor, max, &inflight->outpoint);
|
||||
inflight->amnt = fromwire_amount_sat(cursor, max);
|
||||
inflight->remote_tx_sigs = fromwire_bool(cursor, max);
|
||||
inflight->psbt = fromwire_wally_psbt(inflight, cursor, max);
|
||||
inflight->splice_amnt = fromwire_s64(cursor, max);
|
||||
int has_tx = fromwire_u8(cursor, max);
|
||||
@ -22,6 +23,7 @@ struct inflight *fromwire_inflight(const tal_t *ctx, const u8 **cursor, size_t *
|
||||
memset(&inflight->last_sig, 0, sizeof(inflight->last_sig));
|
||||
}
|
||||
inflight->i_am_initiator = fromwire_bool(cursor, max);
|
||||
inflight->force_sign_first = fromwire_bool(cursor, max);
|
||||
|
||||
return inflight;
|
||||
}
|
||||
@ -30,6 +32,7 @@ void towire_inflight(u8 **pptr, const struct inflight *inflight)
|
||||
{
|
||||
towire_bitcoin_outpoint(pptr, &inflight->outpoint);
|
||||
towire_amount_sat(pptr, inflight->amnt);
|
||||
towire_bool(pptr, inflight->remote_tx_sigs);
|
||||
towire_wally_psbt(pptr, inflight->psbt);
|
||||
towire_s64(pptr, inflight->splice_amnt);
|
||||
towire_u8(pptr, inflight->last_tx ? 1 : 0);
|
||||
@ -38,15 +41,18 @@ void towire_inflight(u8 **pptr, const struct inflight *inflight)
|
||||
towire_bitcoin_signature(pptr, &inflight->last_sig);
|
||||
}
|
||||
towire_bool(pptr, inflight->i_am_initiator);
|
||||
towire_bool(pptr, inflight->force_sign_first);
|
||||
}
|
||||
|
||||
void copy_inflight(struct inflight *dest, struct inflight *src)
|
||||
{
|
||||
dest->outpoint = src->outpoint;
|
||||
dest->amnt = src->amnt;
|
||||
dest->remote_tx_sigs = src->remote_tx_sigs;
|
||||
dest->psbt = src->psbt ? clone_psbt(dest, src->psbt): NULL;
|
||||
dest->splice_amnt = src->splice_amnt;
|
||||
dest->last_tx = src->last_tx ? clone_bitcoin_tx(dest, src->last_tx) : NULL;
|
||||
dest->last_sig = src->last_sig;
|
||||
dest->i_am_initiator = src->i_am_initiator;
|
||||
dest->force_sign_first = src->force_sign_first;
|
||||
}
|
||||
|
@ -8,12 +8,15 @@
|
||||
struct inflight {
|
||||
struct bitcoin_outpoint outpoint;
|
||||
struct amount_sat amnt;
|
||||
bool remote_tx_sigs;
|
||||
struct wally_psbt *psbt;
|
||||
s64 splice_amnt;
|
||||
struct bitcoin_tx *last_tx;
|
||||
/* last_sig is assumed valid if last_tx is set */
|
||||
struct bitcoin_signature last_sig;
|
||||
bool i_am_initiator;
|
||||
bool force_sign_first;
|
||||
bool is_locked;
|
||||
};
|
||||
|
||||
struct inflight *fromwire_inflight(const tal_t *ctx, const u8 **cursor, size_t *max);
|
||||
|
@ -6,8 +6,6 @@ struct splice_state *splice_state_new(const tal_t *ctx)
|
||||
{
|
||||
struct splice_state *splice_state = tal(ctx, struct splice_state);
|
||||
|
||||
splice_state->committed_count = 0;
|
||||
splice_state->revoked_count = 0;
|
||||
splice_state->count = 0;
|
||||
splice_state->locked_ready[LOCAL] = false;
|
||||
splice_state->locked_ready[REMOTE] = false;
|
||||
|
@ -21,10 +21,6 @@ struct splice_state {
|
||||
bool await_commitment_succcess;
|
||||
/* The txid of which splice inflight was confirmed */
|
||||
struct bitcoin_txid locked_txid;
|
||||
/* The number of splices that have been signed & committed */
|
||||
u32 committed_count;
|
||||
/* the number of splices that have been revoke_and_ack'ed */
|
||||
u32 revoked_count;
|
||||
/* The number of splices that are active (awaiting confirmation) */
|
||||
u32 count;
|
||||
};
|
||||
|
@ -124,6 +124,8 @@ psbt_to_witnesses(const tal_t *ctx,
|
||||
tal_arr(ctx, const struct witness *, 0);
|
||||
|
||||
for (size_t i = 0; i < psbt->num_inputs; i++) {
|
||||
struct wally_tx_witness_stack *wtx_s =
|
||||
psbt->inputs[i].final_witness;
|
||||
if (!psbt_get_serial_id(&psbt->inputs[i].unknowns,
|
||||
&serial_id))
|
||||
/* FIXME: throw an error ? */
|
||||
@ -136,9 +138,7 @@ psbt_to_witnesses(const tal_t *ctx,
|
||||
* - if is the *initiator*:
|
||||
* - MUST send even `serial_id`s
|
||||
*/
|
||||
if (serial_id % 2 == side_to_stack) {
|
||||
struct wally_tx_witness_stack *wtx_s =
|
||||
psbt->inputs[i].final_witness;
|
||||
if (wtx_s && serial_id % 2 == side_to_stack) {
|
||||
|
||||
/* BOLT-e299850cb5ebd8bd9c55763bbc498fcdf94a9567 #2:
|
||||
*
|
||||
|
@ -150,7 +150,8 @@ new_inflight(struct channel *channel,
|
||||
const struct amount_msat lease_fee,
|
||||
const struct amount_sat lease_amt,
|
||||
s64 splice_amnt,
|
||||
bool i_am_initiator)
|
||||
bool i_am_initiator,
|
||||
bool force_sign_first)
|
||||
{
|
||||
struct channel_inflight *inflight
|
||||
= tal(channel, struct channel_inflight);
|
||||
@ -183,6 +184,7 @@ new_inflight(struct channel *channel,
|
||||
inflight->lease_amt = lease_amt;
|
||||
|
||||
inflight->i_am_initiator = i_am_initiator;
|
||||
inflight->force_sign_first = force_sign_first;
|
||||
inflight->splice_locked_memonly = false;
|
||||
|
||||
list_add_tail(&channel->inflights, &inflight->list);
|
||||
|
@ -76,6 +76,9 @@ struct channel_inflight {
|
||||
/* Did I initate this splice attempt? */
|
||||
bool i_am_initiator;
|
||||
|
||||
/* On reestablish recovery; should I sign first? */
|
||||
bool force_sign_first;
|
||||
|
||||
/* Note: This field is not stored in the database.
|
||||
*
|
||||
* After splice_locked, we need a way to stop the chain watchers from
|
||||
@ -397,7 +400,8 @@ struct channel_inflight *new_inflight(struct channel *channel,
|
||||
const struct amount_msat lease_fee,
|
||||
const struct amount_sat lease_amt,
|
||||
s64 splice_amnt,
|
||||
bool i_am_initiator);
|
||||
bool i_am_initiator,
|
||||
bool force_sign_first);
|
||||
|
||||
/* Add a last_tx and sig to an inflight */
|
||||
void inflight_set_last_tx(struct channel_inflight *inflight,
|
||||
|
@ -499,9 +499,7 @@ static void send_splice_tx(struct channel *channel,
|
||||
send_splice_tx_done, info);
|
||||
}
|
||||
|
||||
/* After user signs PSBT with splice_signed, our node goes through the signing
|
||||
* process (adding it's own signatures and peers' sigs), sending the result to
|
||||
* us here: */
|
||||
/* After channeld have all the signatures it sends the result to us here */
|
||||
static void handle_splice_confirmed_signed(struct lightningd *ld,
|
||||
struct channel *channel,
|
||||
const u8 *msg)
|
||||
@ -512,7 +510,8 @@ static void handle_splice_confirmed_signed(struct lightningd *ld,
|
||||
struct channel_inflight *inflight;
|
||||
u32 output_index;
|
||||
|
||||
if (!fromwire_channeld_splice_confirmed_signed(tmpctx, msg, &tx, &output_index)) {
|
||||
if (!fromwire_channeld_splice_confirmed_signed(tmpctx, msg, &tx,
|
||||
&output_index)) {
|
||||
|
||||
channel_internal_error(channel,
|
||||
"bad splice_confirmed_signed %s",
|
||||
@ -532,23 +531,130 @@ static void handle_splice_confirmed_signed(struct lightningd *ld,
|
||||
inflight->remote_tx_sigs = true;
|
||||
wallet_inflight_save(ld->wallet, inflight);
|
||||
|
||||
if (channel->state != CHANNELD_NORMAL) {
|
||||
if (channel->state != CHANNELD_AWAITING_SPLICE) {
|
||||
log_debug(channel->log,
|
||||
"Would broadcast splice, but state %s"
|
||||
" isn't CHANNELD_NORMAL",
|
||||
" isn't CHANNELD_AWAITING_SPLICE",
|
||||
channel_state_name(channel));
|
||||
return;
|
||||
}
|
||||
|
||||
cc = splice_command_for_chan(ld, channel);
|
||||
|
||||
send_splice_tx(channel, tx, cc, output_index);
|
||||
}
|
||||
|
||||
static enum watch_result splice_depth_cb(struct lightningd *ld,
|
||||
const struct bitcoin_txid *txid,
|
||||
const struct bitcoin_tx *tx,
|
||||
unsigned int depth,
|
||||
void *param)
|
||||
{
|
||||
/* find_txwatch triggers a type warning on inflight, so we do this. */
|
||||
struct channel_inflight *inflight = param;
|
||||
struct txlocator *loc;
|
||||
struct short_channel_id scid;
|
||||
|
||||
/* What scid is this giving us? */
|
||||
loc = wallet_transaction_locate(tmpctx, ld->wallet, txid);
|
||||
if (!mk_short_channel_id(&scid,
|
||||
loc->blkheight, loc->index,
|
||||
inflight->funding->outpoint.n)) {
|
||||
channel_fail_permanent(inflight->channel,
|
||||
REASON_LOCAL,
|
||||
"Invalid funding scid %u:%u:%u",
|
||||
loc->blkheight, loc->index,
|
||||
inflight->funding->outpoint.n);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Usually, we're here because we're awaiting a splice, but
|
||||
* we could also mutual shutdown, or that weird splice_locked_memonly
|
||||
* hack... */
|
||||
if (inflight->channel->state != CHANNELD_AWAITING_SPLICE) {
|
||||
log_info(inflight->channel->log, "Splice inflight event but not"
|
||||
" in AWAITING_SPLICE, ending watch of txid %s",
|
||||
type_to_string(tmpctx, struct bitcoin_txid, txid));
|
||||
return DELETE_WATCH;
|
||||
}
|
||||
|
||||
/* Reorged out? OK, we're not committed yet. */
|
||||
if (depth == 0) {
|
||||
return KEEP_WATCHING;
|
||||
}
|
||||
|
||||
if (inflight->channel->owner) {
|
||||
log_info(inflight->channel->log, "splice_depth_cb: sending funding depth scid: %s",
|
||||
type_to_string(tmpctx, struct short_channel_id, &scid));
|
||||
subd_send_msg(inflight->channel->owner,
|
||||
take(towire_channeld_funding_depth(
|
||||
NULL, &scid,
|
||||
inflight->channel->alias[LOCAL],
|
||||
depth, true, txid)));
|
||||
}
|
||||
|
||||
/* channeld will tell us when splice is locked in: we'll clean
|
||||
* this watch up then. */
|
||||
return KEEP_WATCHING;
|
||||
}
|
||||
|
||||
void watch_splice_inflight(struct lightningd *ld,
|
||||
struct channel_inflight *inflight)
|
||||
{
|
||||
log_info(inflight->channel->log, "Watching splice inflight %s",
|
||||
type_to_string(tmpctx, struct bitcoin_txid,
|
||||
&inflight->funding->outpoint.txid));
|
||||
watch_txid(inflight, ld->topology,
|
||||
&inflight->funding->outpoint.txid,
|
||||
splice_depth_cb, inflight);
|
||||
}
|
||||
|
||||
static struct txwatch *splice_inflight_txwatch(struct channel *channel,
|
||||
struct channel_inflight *inflight)
|
||||
{
|
||||
return find_txwatch(channel->peer->ld->topology,
|
||||
&inflight->funding->outpoint.txid,
|
||||
splice_depth_cb, channel);
|
||||
}
|
||||
|
||||
static void handle_splice_sending_sigs(struct lightningd *ld,
|
||||
struct channel *channel,
|
||||
const u8 *msg)
|
||||
{
|
||||
struct splice_command *cc;
|
||||
struct bitcoin_txid txid;
|
||||
struct channel_inflight *inflight;
|
||||
|
||||
if (!fromwire_channeld_splice_sending_sigs(msg, &txid)) {
|
||||
|
||||
channel_internal_error(channel,
|
||||
"bad splice_confirmed_signed %s",
|
||||
tal_hex(channel, msg));
|
||||
return;
|
||||
}
|
||||
|
||||
inflight = channel_inflight_find(channel, &txid);
|
||||
if (!inflight)
|
||||
channel_internal_error(channel, "Unable to load inflight for"
|
||||
" splice_confirmed_signed txid %s",
|
||||
type_to_string(tmpctx,
|
||||
struct bitcoin_txid,
|
||||
&txid));
|
||||
|
||||
/* Signing a splice after it has confirmed is safe and can happen during
|
||||
* reestablish if one node is late seeing blocks */
|
||||
if (channel->state == CHANNELD_AWAITING_SPLICE)
|
||||
return;
|
||||
|
||||
cc = splice_command_for_chan(ld, channel);
|
||||
/* If matching user command found, this was a user intiated splice */
|
||||
channel_set_state(channel,
|
||||
CHANNELD_NORMAL,
|
||||
CHANNELD_AWAITING_SPLICE,
|
||||
cc ? REASON_USER : REASON_REMOTE,
|
||||
"Broadcasting splice");
|
||||
"Splice signatures sent");
|
||||
|
||||
send_splice_tx(channel, tx, cc, output_index);
|
||||
watch_splice_inflight(ld, inflight);
|
||||
}
|
||||
|
||||
bool depthcb_update_scid(struct channel *channel,
|
||||
@ -600,46 +706,6 @@ bool depthcb_update_scid(struct channel *channel,
|
||||
return true;
|
||||
}
|
||||
|
||||
static enum watch_result splice_depth_cb(struct lightningd *ld,
|
||||
const struct bitcoin_txid *txid,
|
||||
const struct bitcoin_tx *tx,
|
||||
unsigned int depth,
|
||||
struct channel_inflight *inflight)
|
||||
{
|
||||
/* Usually, we're here because we're awaiting a splice, but
|
||||
* we could also mutual shutdown, or that weird splice_locked_memonly
|
||||
* hack... */
|
||||
if (inflight->channel->state != CHANNELD_AWAITING_SPLICE)
|
||||
return DELETE_WATCH;
|
||||
|
||||
/* Reorged out? OK, we're not committed yet. */
|
||||
if (depth == 0)
|
||||
return KEEP_WATCHING;
|
||||
|
||||
if (!depthcb_update_scid(inflight->channel, txid, &inflight->funding->outpoint))
|
||||
return DELETE_WATCH;
|
||||
|
||||
if (inflight->channel->owner) {
|
||||
subd_send_msg(inflight->channel->owner,
|
||||
take(towire_channeld_funding_depth(
|
||||
NULL, inflight->channel->scid,
|
||||
inflight->channel->alias[LOCAL],
|
||||
depth, true, txid)));
|
||||
}
|
||||
|
||||
/* channeld will tell us when splice is locked in: we'll clean
|
||||
* this watch up then. */
|
||||
return KEEP_WATCHING;
|
||||
}
|
||||
|
||||
void watch_splice_inflight(struct lightningd *ld,
|
||||
struct channel_inflight *inflight)
|
||||
{
|
||||
watch_txid(inflight, ld->topology,
|
||||
&inflight->funding->outpoint.txid,
|
||||
splice_depth_cb, inflight);
|
||||
}
|
||||
|
||||
static void handle_add_inflight(struct lightningd *ld,
|
||||
struct channel *channel,
|
||||
const u8 *msg)
|
||||
@ -650,7 +716,7 @@ static void handle_add_inflight(struct lightningd *ld,
|
||||
s64 splice_amnt;
|
||||
struct wally_psbt *psbt;
|
||||
struct channel_inflight *inflight;
|
||||
bool i_am_initiator;
|
||||
bool i_am_initiator, force_sign_first;
|
||||
|
||||
if (!fromwire_channeld_add_inflight(tmpctx,
|
||||
msg,
|
||||
@ -660,7 +726,8 @@ static void handle_add_inflight(struct lightningd *ld,
|
||||
&satoshis,
|
||||
&splice_amnt,
|
||||
&psbt,
|
||||
&i_am_initiator)) {
|
||||
&i_am_initiator,
|
||||
&force_sign_first)) {
|
||||
channel_internal_error(channel,
|
||||
"bad channel_add_inflight %s",
|
||||
tal_hex(channel, msg));
|
||||
@ -681,14 +748,14 @@ static void handle_add_inflight(struct lightningd *ld,
|
||||
AMOUNT_MSAT(0),
|
||||
AMOUNT_SAT(0),
|
||||
splice_amnt,
|
||||
i_am_initiator);
|
||||
i_am_initiator,
|
||||
force_sign_first);
|
||||
|
||||
log_debug(channel->log, "lightningd adding inflight with txid %s",
|
||||
type_to_string(tmpctx, struct bitcoin_txid,
|
||||
&inflight->funding->outpoint.txid));
|
||||
|
||||
wallet_inflight_add(ld->wallet, inflight);
|
||||
watch_splice_inflight(ld, inflight);
|
||||
|
||||
subd_send_msg(channel->owner, take(towire_channeld_got_inflight(NULL)));
|
||||
}
|
||||
@ -885,6 +952,7 @@ static void handle_peer_splice_locked(struct channel *channel, const u8 *msg)
|
||||
s64 splice_amnt;
|
||||
struct channel_inflight *inflight;
|
||||
struct bitcoin_txid locked_txid;
|
||||
struct txwatch *txw;
|
||||
|
||||
if (!fromwire_channeld_got_splice_locked(msg, &funding_sats,
|
||||
&splice_amnt,
|
||||
@ -922,6 +990,9 @@ static void handle_peer_splice_locked(struct channel *channel, const u8 *msg)
|
||||
|
||||
wallet_channel_clear_inflights(channel->peer->ld->wallet, channel);
|
||||
|
||||
depthcb_update_scid(channel, &locked_txid,
|
||||
&inflight->funding->outpoint);
|
||||
|
||||
/* That freed watchers in inflights: now watch funding tx */
|
||||
channel_watch_funding(channel->peer->ld, channel);
|
||||
|
||||
@ -934,6 +1005,14 @@ static void handle_peer_splice_locked(struct channel *channel, const u8 *msg)
|
||||
list_add_tail(&channel->inflights, &inflight->list);
|
||||
|
||||
lockin_complete(channel, CHANNELD_AWAITING_SPLICE);
|
||||
|
||||
/* Turn off tx watcher for the splice */
|
||||
txw = splice_inflight_txwatch(channel, inflight);
|
||||
if (!txw)
|
||||
log_unusual(channel->log, "Can't unwatch txid %s",
|
||||
type_to_string(tmpctx, struct bitcoin_txid,
|
||||
&locked_txid));
|
||||
tal_free(txw);
|
||||
}
|
||||
|
||||
/* We were informed by channeld that channel is ready (reached mindepth) */
|
||||
@ -1318,6 +1397,9 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds)
|
||||
case WIRE_CHANNELD_SPLICE_CONFIRMED_SIGNED:
|
||||
handle_splice_confirmed_signed(sd->ld, sd->channel, msg);
|
||||
break;
|
||||
case WIRE_CHANNELD_SPLICE_SENDING_SIGS:
|
||||
handle_splice_sending_sigs(sd->ld, sd->channel, msg);
|
||||
break;
|
||||
case WIRE_CHANNELD_ADD_INFLIGHT:
|
||||
handle_add_inflight(sd->ld, sd->channel, msg);
|
||||
break;
|
||||
@ -1524,6 +1606,7 @@ bool peer_start_channeld(struct channel *channel,
|
||||
|
||||
infcopy->outpoint = inflight->funding->outpoint;
|
||||
infcopy->amnt = inflight->funding->total_funds;
|
||||
infcopy->remote_tx_sigs = inflight->remote_tx_sigs;
|
||||
infcopy->splice_amnt = inflight->funding->splice_amnt;
|
||||
if (inflight->last_tx)
|
||||
infcopy->last_tx = tal_dup(infcopy, struct bitcoin_tx, inflight->last_tx);
|
||||
@ -1531,9 +1614,12 @@ bool peer_start_channeld(struct channel *channel,
|
||||
infcopy->last_tx = NULL;
|
||||
infcopy->last_sig = inflight->last_sig;
|
||||
infcopy->i_am_initiator = inflight->i_am_initiator;
|
||||
infcopy->force_sign_first = inflight->force_sign_first;
|
||||
|
||||
tal_wally_start();
|
||||
wally_psbt_clone_alloc(inflight->funding_psbt, 0, &infcopy->psbt);
|
||||
tal_wally_end_onto(infcopy, infcopy->psbt, struct wally_psbt);
|
||||
|
||||
tal_arr_expand(&inflights, infcopy);
|
||||
}
|
||||
|
||||
@ -1643,7 +1729,7 @@ void channeld_tell_depth(struct channel *channel,
|
||||
subd_send_msg(channel->owner,
|
||||
take(towire_channeld_funding_depth(
|
||||
NULL, channel->scid, channel->alias[LOCAL], depth,
|
||||
channel->state == CHANNELD_AWAITING_SPLICE, txid)));
|
||||
false, txid)));
|
||||
}
|
||||
|
||||
/* Check if we are the fundee of this channel, the channel
|
||||
|
@ -1292,6 +1292,7 @@ wallet_update_channel(struct lightningd *ld,
|
||||
channel->push,
|
||||
lease_amt,
|
||||
0,
|
||||
false,
|
||||
false);
|
||||
wallet_inflight_add(ld->wallet, inflight);
|
||||
|
||||
@ -1510,6 +1511,7 @@ wallet_commit_channel(struct lightningd *ld,
|
||||
channel->push,
|
||||
lease_amt,
|
||||
0,
|
||||
false,
|
||||
false);
|
||||
wallet_inflight_add(ld->wallet, inflight);
|
||||
|
||||
|
@ -211,13 +211,14 @@ def test_invalid_splice(node_factory, bitcoind):
|
||||
result = l1.rpc.signpsbt(result['psbt'])
|
||||
result = l1.rpc.splice_signed(chan_id, result['signed_psbt'])
|
||||
|
||||
l2.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
||||
l1.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
|
||||
|
||||
mempool = bitcoind.rpc.getrawmempool(True)
|
||||
assert len(list(mempool.keys())) == 1
|
||||
assert result['txid'] in list(mempool.keys())
|
||||
|
||||
# Wait until nodes are reconnected
|
||||
l1.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_REESTABLISH')
|
||||
l2.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_REESTABLISH')
|
||||
|
||||
bitcoind.generate_block(6, wait_for_mempool=1)
|
||||
|
||||
l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
|
||||
|
119
tests/test_splicing_disconnect.py
Normal file
119
tests/test_splicing_disconnect.py
Normal file
@ -0,0 +1,119 @@
|
||||
from fixtures import * # noqa: F401,F403
|
||||
import pytest
|
||||
import unittest
|
||||
import time
|
||||
from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND
|
||||
from utils import (
|
||||
TEST_NETWORK, first_scid
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.openchannel('v1')
|
||||
@pytest.mark.openchannel('v2')
|
||||
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
|
||||
def test_splice_disconnect_sig(node_factory, bitcoind):
|
||||
# Dual open and splicing both use tx_sig messages. If we have dual enabled, ignore the first one.
|
||||
disconnect = ['-WIRE_TX_SIGNATURES']
|
||||
if EXPERIMENTAL_DUAL_FUND:
|
||||
disconnect = ['=WIRE_TX_SIGNATURES'] + disconnect
|
||||
|
||||
l1 = node_factory.get_node(disconnect=disconnect,
|
||||
options={'experimental-splicing': None, 'dev-no-reconnect': None},
|
||||
may_reconnect=True)
|
||||
l2 = node_factory.get_node(options={'experimental-splicing': None}, may_reconnect=True)
|
||||
l1.openchannel(l2, 1000000)
|
||||
|
||||
chan_id = l1.get_channel_id(l2)
|
||||
|
||||
# add extra sats to pay fee
|
||||
funds_result = l1.rpc.fundpsbt("109000sat", "slow", 166, excess_as_change=True)
|
||||
|
||||
result = l1.rpc.splice_init(chan_id, 100000, funds_result['psbt'])
|
||||
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
||||
result = l1.rpc.signpsbt(result['psbt'])
|
||||
result = l1.rpc.splice_signed(chan_id, result['signed_psbt'])
|
||||
|
||||
l1.daemon.wait_for_log(r'dev_disconnect: \-WIRE_TX_SIGNATURES')
|
||||
|
||||
print("Killing l1 without sending WIRE_TX_SIGNATURES")
|
||||
l1.daemon.kill()
|
||||
|
||||
# Restart l1, without disconnect stuff.
|
||||
del l1.daemon.opts['dev-no-reconnect']
|
||||
del l1.daemon.opts['dev-disconnect']
|
||||
|
||||
# Should reconnect, and reestablish the splice.
|
||||
l1.start()
|
||||
|
||||
# Wait until nodes are reconnected
|
||||
l1.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_REESTABLISH')
|
||||
l2.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_REESTABLISH')
|
||||
|
||||
bitcoind.generate_block(6, wait_for_mempool=1)
|
||||
|
||||
l1.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'private channel announcement from channeld for ' + first_scid(l1, l2))
|
||||
|
||||
inv = l2.rpc.invoice(10**2, '3', 'no_3')
|
||||
l1.rpc.pay(inv['bolt11'])
|
||||
|
||||
# Check that the splice doesn't generate a unilateral close transaction
|
||||
time.sleep(5)
|
||||
assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0
|
||||
|
||||
|
||||
@pytest.mark.openchannel('v1')
|
||||
@pytest.mark.openchannel('v2')
|
||||
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
|
||||
def test_splice_disconnect_commit(node_factory, bitcoind, executor):
|
||||
l1 = node_factory.get_node(options={'experimental-splicing': None}, may_reconnect=True)
|
||||
l2 = node_factory.get_node(disconnect=['+WIRE_COMMITMENT_SIGNED'],
|
||||
options={'experimental-splicing': None, 'dev-no-reconnect': None},
|
||||
may_reconnect=True)
|
||||
l1.openchannel(l2, 1000000)
|
||||
|
||||
chan_id = l1.get_channel_id(l2)
|
||||
|
||||
# add extra sats to pay fee
|
||||
funds_result = l1.rpc.fundpsbt("109000sat", "slow", 166, excess_as_change=True)
|
||||
|
||||
result = l1.rpc.splice_init(chan_id, 100000, funds_result['psbt'])
|
||||
print("l1 splice_update")
|
||||
result = l1.rpc.splice_update(chan_id, result['psbt'])
|
||||
print("l1 signpsbt")
|
||||
result = l1.rpc.signpsbt(result['psbt'])
|
||||
print("l1 splice_signed")
|
||||
|
||||
executor.submit(l1.rpc.splice_signed, chan_id, result['signed_psbt'])
|
||||
|
||||
print("l2 waiting for dev_disconnect msg")
|
||||
|
||||
l2.daemon.wait_for_log(r'dev_disconnect: \+WIRE_COMMITMENT_SIGNED')
|
||||
|
||||
print("Killing l2 without sending WIRE_COMMITMENT_SIGNED")
|
||||
l2.daemon.kill()
|
||||
|
||||
# Restart l1, without disconnect stuff.
|
||||
del l2.daemon.opts['dev-no-reconnect']
|
||||
del l2.daemon.opts['dev-disconnect']
|
||||
|
||||
# Should reconnect, and reestablish the splice.
|
||||
l2.start()
|
||||
|
||||
# Wait until nodes are reconnected
|
||||
l1.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_REESTABLISH')
|
||||
l2.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_REESTABLISH')
|
||||
|
||||
bitcoind.generate_block(6, wait_for_mempool=1)
|
||||
|
||||
l1.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'private channel announcement from channeld for ' + first_scid(l1, l2))
|
||||
|
||||
inv = l2.rpc.invoice(10**2, '3', 'no_3')
|
||||
l1.rpc.pay(inv['bolt11'])
|
||||
|
||||
# Check that the splice doesn't generate a unilateral close transaction
|
||||
time.sleep(5)
|
||||
assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0
|
@ -1010,6 +1010,7 @@ static struct migration dbmigrations[] = {
|
||||
{SQL("ALTER TABLE forwards ADD updated_index BIGINT DEFAULT 0"), NULL},
|
||||
{SQL("CREATE INDEX forwards_updated_idx ON forwards (updated_index)"), NULL},
|
||||
{NULL, migrate_initialize_forwards_wait_indexes},
|
||||
{SQL("ALTER TABLE channel_funding_inflights ADD force_sign_first INTEGER DEFAULT 0"), NULL},
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -201,7 +201,8 @@ struct channel_inflight *new_inflight(struct channel *channel UNNEEDED,
|
||||
const struct amount_msat lease_fee UNNEEDED,
|
||||
const struct amount_sat lease_amt UNNEEDED,
|
||||
s64 splice_amnt UNNEEDED,
|
||||
bool i_am_initiator UNNEEDED)
|
||||
bool i_am_initiator UNNEEDED,
|
||||
bool force_sign_first UNNEEDED)
|
||||
{ fprintf(stderr, "new_inflight called!\n"); abort(); }
|
||||
/* Generated stub for new_logger */
|
||||
struct logger *new_logger(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED,
|
||||
|
@ -1923,6 +1923,7 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx)
|
||||
AMOUNT_MSAT(10),
|
||||
AMOUNT_SAT(1111),
|
||||
0,
|
||||
false,
|
||||
false);
|
||||
|
||||
inflight_set_last_tx(inflight, last_tx, sig);
|
||||
@ -1949,6 +1950,7 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx)
|
||||
AMOUNT_MSAT(0),
|
||||
AMOUNT_SAT(0),
|
||||
0,
|
||||
false,
|
||||
false);
|
||||
inflight_set_last_tx(inflight, last_tx, sig);
|
||||
wallet_inflight_add(w, inflight);
|
||||
|
@ -1217,8 +1217,9 @@ void wallet_inflight_add(struct wallet *w, struct channel_inflight *inflight)
|
||||
", lease_satoshi"
|
||||
", splice_amnt"
|
||||
", i_am_initiator"
|
||||
", force_sign_first"
|
||||
") VALUES ("
|
||||
"?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"));
|
||||
"?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"));
|
||||
|
||||
db_bind_u64(stmt, inflight->channel->dbid);
|
||||
db_bind_txid(stmt, &inflight->funding->outpoint.txid);
|
||||
@ -1256,6 +1257,7 @@ void wallet_inflight_add(struct wallet *w, struct channel_inflight *inflight)
|
||||
|
||||
db_bind_s64(stmt, inflight->funding->splice_amnt);
|
||||
db_bind_int(stmt, inflight->i_am_initiator);
|
||||
db_bind_int(stmt, inflight->force_sign_first);
|
||||
|
||||
db_exec_prepared_v2(stmt);
|
||||
assert(!stmt->error);
|
||||
@ -1324,7 +1326,7 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt,
|
||||
struct bitcoin_tx *last_tx;
|
||||
struct channel_inflight *inflight;
|
||||
s64 splice_amnt;
|
||||
bool i_am_initiator;
|
||||
bool i_am_initiator, force_sign_first;
|
||||
|
||||
secp256k1_ecdsa_signature *lease_commit_sig;
|
||||
u32 lease_blockheight_start;
|
||||
@ -1362,6 +1364,7 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt,
|
||||
|
||||
splice_amnt = db_col_s64(stmt, "splice_amnt");
|
||||
i_am_initiator = db_col_int(stmt, "i_am_initiator");
|
||||
force_sign_first = db_col_int(stmt, "force_sign_first");
|
||||
|
||||
inflight = new_inflight(chan, &funding,
|
||||
db_col_int(stmt, "funding_feerate"),
|
||||
@ -1376,7 +1379,8 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt,
|
||||
lease_fee,
|
||||
lease_amt,
|
||||
splice_amnt,
|
||||
i_am_initiator);
|
||||
i_am_initiator,
|
||||
force_sign_first);
|
||||
|
||||
/* last_tx is null for not yet committed
|
||||
* channels + static channel backup recoveries */
|
||||
@ -1427,6 +1431,7 @@ static bool wallet_channel_load_inflights(struct wallet *w,
|
||||
", lease_satoshi"
|
||||
", splice_amnt"
|
||||
", i_am_initiator"
|
||||
", force_sign_first"
|
||||
" FROM channel_funding_inflights"
|
||||
" WHERE channel_id = ?"
|
||||
" ORDER BY funding_feerate"));
|
||||
|
Loading…
Reference in New Issue
Block a user