diff --git a/channeld/channel.c b/channeld/channel.c index 37c481f4e5d9..9199b1812bd4 100644 --- a/channeld/channel.c +++ b/channeld/channel.c @@ -1590,7 +1590,9 @@ static void peer_in(struct peer *peer, const u8 *msg) /* Must get funding_locked before almost anything. */ if (!peer->funding_locked[REMOTE]) { - if (type != WIRE_FUNDING_LOCKED && type != WIRE_PONG) { + if (type != WIRE_FUNDING_LOCKED + && type != WIRE_PONG + && type != WIRE_SHUTDOWN) { peer_failed(&peer->cs, peer->gossip_index, &peer->channel_id, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index c730704dacd1..93e6c92226bc 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -77,9 +77,9 @@ static void peer_got_shutdown(struct channel *channel, const u8 *msg) } /* If we weren't already shutting down, we are now */ - if (channel->state == CHANNELD_NORMAL) + if (channel->state != CHANNELD_SHUTTING_DOWN) channel_set_state(channel, - CHANNELD_NORMAL, CHANNELD_SHUTTING_DOWN); + channel->state, CHANNELD_SHUTTING_DOWN); /* TODO(cdecker) Selectively save updated fields to DB */ wallet_channel_save(ld->wallet, channel); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 0e6921913201..50c21b921cf0 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -853,9 +853,9 @@ static void json_close(struct command *cmd, } /* Normal case. */ - if (channel->state == CHANNELD_NORMAL) { + if (channel->state == CHANNELD_NORMAL || channel->state == CHANNELD_AWAITING_LOCKIN) { channel_set_state(channel, - CHANNELD_NORMAL, CHANNELD_SHUTTING_DOWN); + channel->state, CHANNELD_SHUTTING_DOWN); if (channel->owner) subd_send_msg(channel->owner, diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 042e64711416..754c231c0619 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -3048,6 +3048,39 @@ def test_shutdown_reconnect(self): l2.daemon.wait_for_logs(['sendrawtx exit 0', ' to CLOSINGD_COMPLETE']) assert l1.bitcoin.rpc.getmempoolinfo()['size'] == 1 + def test_shutdown_awaiting_lockin(self): + l1 = self.node_factory.get_node() + l2 = self.node_factory.get_node(options=['--anchor-confirms=3']) + + l1.rpc.connect(l2.info['id'], 'localhost', l2.info['port']) + self.give_funds(l1, 10**6 + 1000000) + l1.rpc.fundchannel(l2.info['id'], 10**6) + + # Technically, this is async to fundchannel. + l1.daemon.wait_for_log('sendrawtx exit 0') + bitcoind.generate_block(1) + + l1.rpc.close(l2.info['id']) + l1.daemon.wait_for_log('CHANNELD_AWAITING_LOCKIN to CHANNELD_SHUTTING_DOWN') + l2.daemon.wait_for_log('CHANNELD_AWAITING_LOCKIN to CHANNELD_SHUTTING_DOWN') + + l1.daemon.wait_for_log('CHANNELD_SHUTTING_DOWN to CLOSINGD_SIGEXCHANGE') + l2.daemon.wait_for_log('CHANNELD_SHUTTING_DOWN to CLOSINGD_SIGEXCHANGE') + + # And should put closing into mempool (happens async, so + # CLOSINGD_COMPLETE may come first). + l1.daemon.wait_for_logs(['sendrawtx exit 0', ' to CLOSINGD_COMPLETE']) + l2.daemon.wait_for_logs(['sendrawtx exit 0', ' to CLOSINGD_COMPLETE']) + assert bitcoind.rpc.getmempoolinfo()['size'] == 1 + + bitcoind.generate_block(1) + l1.daemon.wait_for_log(' to ONCHAIN') + l2.daemon.wait_for_log(' to ONCHAIN') + + bitcoind.generate_block(100) + wait_forget_channels(l1) + wait_forget_channels(l2) + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_closing_negotiation_reconnect(self): disconnects = ['-WIRE_CLOSING_SIGNED',