Skip to content

Couldn't find channel funds in wallet after unilateral close #8881

@enaples

Description

@enaples

Scenario

  1. Two nodes establish a channel between them (l1 --> l2)
  2. l2 goes offline for a while ( > 200 blocks)
  3. Meanwhile, l1 closes unilaterally the channel and withdraws funds
  4. l1 successfully see all their funds
  5. l2 comes back online and successfully sync and resolve the unilaterally closed channel

Issue

l2 does not see their (if any) channel funds in their wallet.

The only "unusual" log I see is the following:

plugin-cln-grpc: Failed to parse notification from lightningd Error("unknown variant `CLOSED`, expected one of `OPENINGD`, `CHANNELD_AWAITING_LOCKIN`, `CHANNELD_NORMAL`, `CHANNELD_SHUTTING_DOWN`, `CLOSINGD_SIGEXCHANGE`, `CLOSINGD_COMPLETE`, `AWAITING_UNILATERAL`, `FUNDING_SPEND_SEEN`, `ONCHAIN`, `DUALOPEND_OPEN_INIT`, `DUALOPEND_AWAITING_LOCKIN`, `CHANNELD_AWAITING_SPLICE`, `DUALOPEND_OPEN_COMMITTED`, `DUALOPEND_OPEN_COMMIT_READY`", line: 0, column: 0)

Here the test to reproduce the issue:

def test_channel_lifecycle_with_shutdown_and_restart(node_factory, bitcoind):
    """
    Test channel lifecycle including unilateral close, withdrawal with node restart after a while.
    """
    l1, l2 = node_factory.get_nodes(2)
    l1.fundwallet(10000000)
    l2.fundwallet(10000000)

    l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
    l1.rpc.fundchannel(l2.info['id'], 500000, minconf=0)

    bitcoind.generate_block(1, wait_for_mempool=1)

    l1.daemon.wait_for_log(' to CHANNELD_NORMAL')

    chan12 = l1.get_channel_scid(l2)

    inv = l2.rpc.invoice(100000, 'test1', 'description1')
    l1.rpc.pay(inv['bolt11'])

    wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['htlcs'] == [])
    wait_for(lambda: only_one(l2.rpc.listpeerchannels()['channels'])['htlcs'] == [])

    l2.stop()

    l1.rpc.close(chan12, 1)

    bitcoind.generate_block(1, wait_for_mempool=1)
    sync_blockheight(bitcoind, [l1])

    l1_channel = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])
    assert all('ONCHAIN' in status for status in l1_channel['status'])

    sync_blockheight(bitcoind, [l1])

    l1_addr = l1.rpc.newaddr()['p2tr']

    assert len(l1.rpc.listfunds()['outputs']) > 0, "l1 has no outputs to withdraw"

    l1.rpc.withdraw(l1_addr, 'all')

    bitcoind.generate_block(310, wait_for_mempool=1)
    sync_blockheight(bitcoind, [l1])

    l2.start()

    funds_l1 = l1.rpc.listfunds()
    total_funds_l1 = sum([output['amount_msat'] for output in funds_l1['outputs']])
    assert 950000000 <= total_funds_l1 < 10000000000, f"Total funds {total_funds_l1} not in expected range"

    funds_l2 = l2.rpc.listfunds()
    total_funds_l2 = sum([output['amount_msat'] for output in funds_l2['outputs']])

    for channel in funds_l2['channels']:
        if channel['state'] == 'ONCHAIN':
            total_funds_l2 += channel['our_amount_msat']
    assert total_funds_l2 > 10000000000, f"Total funds {total_funds_l2} not in expected range"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions