mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 09:54:16 +01:00
lightningd: new state AWAITING_UNILATERAL.
When in this state, we send a canned error "Awaiting unilateral close". We enter this both when we drop to chain, and when we're trying to get them to drop to chain due to option_data_loss_protect. As this state (unlike channel errors) is saved to the database, it means we will *never* talk to a peer again in this state, so they can't confuse us. Since we set this state in channel_fail_permanent() (which is the only place we call drop_to_chain for a unilateral close), we don't need to save to the db: channel_set_state() does that for us. This state change has a subtle effect: we return WIRE_UNKNOWN_NEXT_PEER instead of WIRE_TEMPORARY_CHANNEL_FAILURE as soon as we get a failure with a peer. To provoke a temporary failure in test_pay_disconnect we take the node offline. Reported-by: Christian Decker @cdecker Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
a5ecc95c42
commit
36b1cac6e6
@ -1894,8 +1894,7 @@ static void check_future_dataloss_fields(struct peer *peer,
|
||||
remote_current_per_commitment_point)));
|
||||
|
||||
/* We have to send them an error to trigger dropping to chain. */
|
||||
peer_failed(&peer->cs, &peer->channel_id,
|
||||
"Catastrophic failure: please close channel");
|
||||
peer_failed(&peer->cs, &peer->channel_id, "Awaiting unilateral close");
|
||||
}
|
||||
|
||||
/* BOLT #2:
|
||||
|
@ -356,6 +356,10 @@ void channel_fail_permanent(struct channel *channel, const char *fmt, ...)
|
||||
channel_set_owner(channel, NULL, false);
|
||||
/* Drop non-cooperatively (unilateral) to chain. */
|
||||
drop_to_chain(ld, channel, false);
|
||||
|
||||
if (channel_active(channel))
|
||||
channel_set_state(channel, channel->state, AWAITING_UNILATERAL);
|
||||
|
||||
tal_free(why);
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,6 @@ static void peer_got_shutdown(struct channel *channel, const u8 *msg)
|
||||
static void channel_fail_fallen_behind(struct channel *channel, const u8 *msg)
|
||||
{
|
||||
struct pubkey per_commitment_point;
|
||||
struct channel_id cid;
|
||||
|
||||
if (!fromwire_channel_fail_fallen_behind(msg, &per_commitment_point)) {
|
||||
channel_internal_error(channel,
|
||||
@ -117,17 +116,8 @@ static void channel_fail_fallen_behind(struct channel *channel, const u8 *msg)
|
||||
channel->future_per_commitment_point
|
||||
= tal_dup(channel, struct pubkey, &per_commitment_point);
|
||||
|
||||
/* TODO(cdecker) Selectively save updated fields to DB */
|
||||
wallet_channel_save(channel->peer->ld->wallet, channel);
|
||||
|
||||
/* We don't fail yet, since we want daemon to send them an error
|
||||
* to trigger rebroadcasting. But make sure we set error now in
|
||||
* case something else goes wrong! */
|
||||
derive_channel_id(&cid,
|
||||
&channel->funding_txid,
|
||||
channel->funding_outnum);
|
||||
channel->error = towire_errorfmt(channel, &cid,
|
||||
"Catastrophic failure: please close channel");
|
||||
/* Peer sees this, so send a generic msg about unilateral close. */
|
||||
channel_fail_permanent(channel, "Awaiting unilateral close");
|
||||
}
|
||||
|
||||
static void peer_start_closingd_after_shutdown(struct channel *channel,
|
||||
|
@ -19,6 +19,9 @@ enum channel_state {
|
||||
/* Waiting for onchain event. */
|
||||
CLOSINGD_COMPLETE,
|
||||
|
||||
/* Waiting for unilateral close to hit blockchain. */
|
||||
AWAITING_UNILATERAL,
|
||||
|
||||
/* We've seen the funding spent, we're waiting for onchaind. */
|
||||
FUNDING_SPEND_SEEN,
|
||||
|
||||
|
@ -503,6 +503,19 @@ void peer_connected(struct lightningd *ld, const u8 *msg,
|
||||
/* Channel is supposed to be active! */
|
||||
abort();
|
||||
|
||||
/* We consider this "active" but we only send an error */
|
||||
case AWAITING_UNILATERAL: {
|
||||
struct channel_id cid;
|
||||
derive_channel_id(&cid,
|
||||
&channel->funding_txid,
|
||||
channel->funding_outnum);
|
||||
/* channel->error is not saved in db, so this can
|
||||
* happen if we restart. */
|
||||
error = towire_errorfmt(tmpctx, &cid,
|
||||
"Awaiting unilateral close");
|
||||
goto send_error;
|
||||
}
|
||||
|
||||
case CHANNELD_AWAITING_LOCKIN:
|
||||
case CHANNELD_NORMAL:
|
||||
case CHANNELD_SHUTTING_DOWN:
|
||||
|
@ -1205,7 +1205,7 @@ def test_dataloss_protection(node_factory, bitcoind):
|
||||
l2.start()
|
||||
|
||||
# l2 should freak out!
|
||||
l2.daemon.wait_for_log("Catastrophic failure: please close channel")
|
||||
l2.daemon.wait_for_log("Peer permanent failure in CHANNELD_NORMAL: Awaiting unilateral close")
|
||||
|
||||
# l1 should drop to chain.
|
||||
l1.daemon.wait_for_log('sendrawtx exit 0')
|
||||
|
@ -85,7 +85,8 @@ def test_pay0(node_factory):
|
||||
@unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1")
|
||||
def test_pay_disconnect(node_factory, bitcoind):
|
||||
"""If the remote node has disconnected, we fail payment, but can try again when it reconnects"""
|
||||
l1, l2 = node_factory.line_graph(2, opts={'dev-max-fee-multiplier': 5})
|
||||
l1, l2 = node_factory.line_graph(2, opts={'dev-max-fee-multiplier': 5,
|
||||
'may_reconnect': True})
|
||||
|
||||
inv = l2.rpc.invoice(123000, 'test_pay_disconnect', 'description')
|
||||
rhash = inv['payment_hash']
|
||||
@ -93,21 +94,27 @@ def test_pay_disconnect(node_factory, bitcoind):
|
||||
# Can't use `pay` since that'd notice that we can't route, due to disabling channel_update
|
||||
route = l1.rpc.getroute(l2.info['id'], 123000, 1)["route"]
|
||||
|
||||
# Make l2 upset by asking for crazy fee.
|
||||
l1.rpc.dev_setfees('150000')
|
||||
# Wait for l1 notice
|
||||
l1.daemon.wait_for_log(r'Peer permanent failure in CHANNELD_NORMAL: lightning_channeld: received ERROR channel .*: update_fee 150000 outside range 1875-75000')
|
||||
l2.stop()
|
||||
l1.daemon.wait_for_log('Disabling channel .*, active 1 -> 0')
|
||||
|
||||
# Can't pay while its offline.
|
||||
with pytest.raises(RpcError):
|
||||
l1.rpc.sendpay(route, rhash)
|
||||
l1.daemon.wait_for_log('failed: WIRE_TEMPORARY_CHANNEL_FAILURE \\(First peer not ready\\)')
|
||||
|
||||
# Should fail due to temporary channel fail
|
||||
l2.start()
|
||||
l1.daemon.wait_for_log('peer_out WIRE_CHANNEL_REESTABLISH')
|
||||
|
||||
# Make l2 upset by asking for crazy fee.
|
||||
l1.rpc.dev_setfees('150000')
|
||||
# Wait for l1 notice
|
||||
l1.daemon.wait_for_log(r'Peer permanent failure in CHANNELD_NORMAL: lightning_channeld: received ERROR channel .*: update_fee 150000 outside range 1875-75000')
|
||||
|
||||
# Should fail due to permenant channel fail
|
||||
with pytest.raises(RpcError):
|
||||
l1.rpc.sendpay(route, rhash)
|
||||
|
||||
l1.daemon.wait_for_log('failed: WIRE_TEMPORARY_CHANNEL_FAILURE \\(First peer not ready\\)')
|
||||
l1.daemon.wait_for_log('failed: WIRE_UNKNOWN_NEXT_PEER \\(First peer not ready\\)')
|
||||
assert not l1.daemon.is_in_log('Payment is still in progress')
|
||||
|
||||
# After it sees block, someone should close channel.
|
||||
|
Loading…
Reference in New Issue
Block a user