diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index fac3cfeee..835f2d642 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -1442,6 +1442,18 @@ static bool peer_sending_revocation(struct channel *channel, return true; } +struct deferred_commitsig { + struct channel *channel; + const u8 *msg; +}; + +static void retry_deferred_commitsig(struct chain_topology *topo, + struct deferred_commitsig *d) +{ + peer_got_commitsig(d->channel, d->msg); + tal_free(d); +} + /* This also implies we're sending revocation */ void peer_got_commitsig(struct channel *channel, const u8 *msg) { @@ -1458,6 +1470,24 @@ void peer_got_commitsig(struct channel *channel, const u8 *msg) size_t i; struct lightningd *ld = channel->peer->ld; + /* If we're not synced with bitcoin network, we can't accept + * any HTLCs. We stall at this point, in the hope that it + * won't take long! */ + if (!topology_synced(ld->topology)) { + struct deferred_commitsig *d; + + log_unusual(channel->log, + "Deferring incoming commit until we sync"); + + /* If subdaemon dies, we want to forget this. */ + d = tal(channel->owner, struct deferred_commitsig); + d->channel = channel; + d->msg = tal_dup_arr(d, u8, msg, tal_count(msg), 0); + topology_add_sync_waiter(d, ld->topology, + retry_deferred_commitsig, d); + return; + } + if (!fromwire_channel_got_commitsig(msg, msg, &commitnum, &feerate, diff --git a/tests/test_misc.py b/tests/test_misc.py index e36c91fcd..38689253d 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -154,10 +154,14 @@ def test_lightningd_still_loading(node_factory, bitcoind, executor): # Start it, establish channel. l1, l2 = node_factory.line_graph(2, opts={'may_reconnect': True}) + # Balance channel. + l1.pay(l2, 10**9 // 2) l1.stop() # Now make sure it's behind. bitcoind.generate_block(2) + # Make sure l2 is synced + sync_blockheight(bitcoind, [l2]) # Make it slow grabbing the final block. slow_blockid = bitcoind.rpc.getblockhash(bitcoind.rpc.getblockcount()) @@ -173,9 +177,17 @@ def test_lightningd_still_loading(node_factory, bitcoind, executor): with pytest.raises(RpcError, match=r'TEMPORARY_CHANNEL_FAILURE'): l1.pay(l2, 1000) - # Release the mock, and it will recover. + # This will work, but will be delayed until synced. + fut = executor.submit(l2.pay, l1, 1000) + l1.daemon.wait_for_log("Deferring incoming commit until we sync") + + # Release the mock. mock_release.set() - wait_for(lambda: 'warning_lightningd_sync' not in l1.rpc.getinfo()) + fut.result() + + assert 'warning_lightningd_sync' not in l1.rpc.getinfo() + + # This will now work normally. l1.pay(l2, 1000) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 32a363616..986396bc6 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -527,6 +527,13 @@ void subd_req_(const tal_t *ctx UNNEEDED, /* Generated stub for subd_send_msg */ void subd_send_msg(struct subd *sd UNNEEDED, const u8 *msg_out UNNEEDED) { fprintf(stderr, "subd_send_msg called!\n"); abort(); } +/* Generated stub for topology_add_sync_waiter_ */ +void topology_add_sync_waiter_(const tal_t *ctx UNNEEDED, + struct chain_topology *topo UNNEEDED, + void (*cb)(struct chain_topology *topo UNNEEDED, + void *arg) UNNEEDED, + void *arg UNNEEDED) +{ fprintf(stderr, "topology_add_sync_waiter_ called!\n"); abort(); } /* Generated stub for towire_channel_dev_memleak */ u8 *towire_channel_dev_memleak(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channel_dev_memleak called!\n"); abort(); }