gossipd: don't close non-local channels immediately, add 12 block delay.

This adds a new "chan_dying" message to the gossip_store, but since we
already changed the minor version in this PR, we don't bump it again.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: Protocol: We now delay forgetting funding-spent channels for 12 blocks (as per latest BOLTs, to support splicing in future).
This commit is contained in:
Rusty Russell
2022-09-14 13:20:32 +09:30
parent f0fa42bd73
commit a1f62ba0e7
12 changed files with 204 additions and 35 deletions

View File

@@ -4085,7 +4085,7 @@ def test_multichan(node_factory, executor, bitcoind):
l2.rpc.close(l3.info['id'])
l2.rpc.close(scid23b)
bitcoind.generate_block(1, wait_for_mempool=1)
bitcoind.generate_block(13, wait_for_mempool=1)
sync_blockheight(bitcoind, [l1, l2, l3])
# Gossip works as expected.
@@ -4130,14 +4130,14 @@ def test_multichan(node_factory, executor, bitcoind):
"state": "RCVD_REMOVE_ACK_REVOCATION"},
{"short_channel_id": scid12,
"id": 2,
"expiry": 123,
"expiry": 135,
"direction": "out",
"amount_msat": Millisatoshi(100001001),
"payment_hash": inv3['payment_hash'],
"state": "RCVD_REMOVE_ACK_REVOCATION"},
{"short_channel_id": scid12,
"id": 3,
"expiry": 123,
"expiry": 135,
"direction": "out",
"amount_msat": Millisatoshi(100001001),
"payment_hash": inv4['payment_hash'],

View File

@@ -96,7 +96,7 @@ def test_block_backfill(node_factory, bitcoind, chainparams):
# Now close the channel and make sure `l3` cleans up correctly:
txid = l1.rpc.close(l2.info['id'])['txid']
bitcoind.generate_block(1, wait_for_mempool=txid)
bitcoind.generate_block(13, wait_for_mempool=txid)
wait_for(lambda: len(l3.rpc.listchannels()['channels']) == 0)

View File

@@ -559,8 +559,9 @@ def test_gossip_persistence(node_factory, bitcoind):
# channel from their network view
l1.rpc.dev_fail(l2.info['id'])
# We need to wait for the unilateral close to hit the mempool
bitcoind.generate_block(1, wait_for_mempool=1)
# We need to wait for the unilateral close to hit the mempool,
# and 12 blocks for nodes to actually forget it.
bitcoind.generate_block(13, wait_for_mempool=1)
wait_for(lambda: active(l1) == [scid23, scid23])
wait_for(lambda: active(l2) == [scid23, scid23])
@@ -1391,7 +1392,7 @@ def test_gossip_notices_close(node_factory, bitcoind):
txid = l2.rpc.close(l3.info['id'])['txid']
wait_for(lambda: only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'][0]['state'] == 'CLOSINGD_COMPLETE')
bitcoind.generate_block(1, txid)
bitcoind.generate_block(13, txid)
wait_for(lambda: l1.rpc.listchannels()['channels'] == [])
wait_for(lambda: l1.rpc.listnodes()['nodes'] == [])
@@ -2097,7 +2098,7 @@ def test_topology_leak(node_factory, bitcoind):
# Close and wait for gossip to catchup.
txid = l2.rpc.close(l3.info['id'])['txid']
bitcoind.generate_block(1, txid)
bitcoind.generate_block(13, txid)
wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 2)
@@ -2122,3 +2123,45 @@ def test_parms_listforwards(node_factory):
assert len(forwards_new) == 0
assert len(forwards_dep) == 0
@pytest.mark.developer("gossip without DEVELOPER=1 is slow")
def test_close_12_block_delay(node_factory, bitcoind):
l1, l2, l3, l4 = node_factory.line_graph(4, wait_for_announce=True)
# Close l1-l2
txid = l1.rpc.close(l2.info['id'])['txid']
bitcoind.generate_block(1, txid)
# But l4 doesn't believe it immediately.
l4.daemon.wait_for_log("channel .* closing soon due to the funding outpoint being spent")
# Close l2-l3 one block later.
txid = l2.rpc.close(l3.info['id'])['txid']
bitcoind.generate_block(1, txid)
l4.daemon.wait_for_log("channel .* closing soon due to the funding outpoint being spent")
# BOLT #7:
# - once its funding output has been spent OR reorganized out:
# - SHOULD forget a channel after a 12-block delay.
# That implies 12 blocks *after* spending, i.e. 13 blocks deep!
# 12 blocks deep, l4 still sees it
bitcoind.generate_block(10)
sync_blockheight(bitcoind, [l4])
assert len(l4.rpc.listchannels(source=l1.info['id'])['channels']) == 1
# 13 blocks deep does it.
bitcoind.generate_block(1)
wait_for(lambda: l4.rpc.listchannels(source=l1.info['id'])['channels'] == [])
# Other channel still visible.
assert len(l4.rpc.listchannels(source=l2.info['id'])['channels']) == 1
# Restart: it remembers channel is dying.
l4.restart()
# One more block, it's forgotten too.
bitcoind.generate_block(1)
wait_for(lambda: l4.rpc.listchannels(source=l2.info['id'])['channels'] == [])

View File

@@ -363,7 +363,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind):
# It will use an explicit exposeprivatechannels even if it thinks its a dead-end
l0.rpc.close(l1.info['id'])
l0.wait_for_channel_onchain(l1.info['id'])
bitcoind.generate_block(1)
bitcoind.generate_block(13)
wait_for(lambda: l2.rpc.listchannels(scid_dummy)['channels'] == [])
inv = l2.rpc.invoice(amount_msat=123456, label="inv7", description="?", exposeprivatechannels=scid)