diff --git a/tests/test_opening.py b/tests/test_opening.py index fe82311fc..9e716d0ad 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -790,6 +790,81 @@ def test_rbf_fails_to_broadcast(node_factory, bitcoind, chainparams): assert last_txs['tx'] +@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@pytest.mark.openchannel('v2') +def test_rbf_broadcast_close_inflights(node_factory, bitcoind, chainparams): + """ + Close a channel before it's mined, and the most recent transaction + hasn't made it to the mempool. Should publish all the commitment + transactions that we have. + """ + l1, l2 = node_factory.get_nodes(2, + opts={'allow_warning': True}) + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + amount = 2**24 + chan_amount = 100000 + bitcoind.rpc.sendtoaddress(l1.rpc.newaddr()['bech32'], amount / 10**8 + 0.01) + bitcoind.generate_block(1) + # Wait for it to arrive. + wait_for(lambda: len(l1.rpc.listfunds()['outputs']) > 0) + + res = l1.rpc.fundchannel(l2.info['id'], chan_amount, feerate='7500perkw') + chan_id = res['channel_id'] + vins = bitcoind.rpc.decoderawtransaction(res['tx'])['vin'] + assert(only_one(vins)) + prev_utxos = ["{}:{}".format(vins[0]['txid'], vins[0]['vout'])] + + # Check that we're waiting for lockin + l1.daemon.wait_for_log(' to DUALOPEND_AWAITING_LOCKIN') + inflights = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['inflight'] + assert inflights[-1]['funding_txid'] in bitcoind.rpc.getrawmempool() + + # Make it such that l1 and l2 cannot broadcast transactions + # (mimics failing to reach the miner with replacement) + def censoring_sendrawtx(r): + return {'id': r['id'], 'result': {}} + + l1.daemon.rpcproxy.mock_rpc('sendrawtransaction', censoring_sendrawtx) + l2.daemon.rpcproxy.mock_rpc('sendrawtransaction', censoring_sendrawtx) + + def run_retry(): + startweight = 42 + 173 + next_feerate = find_next_feerate(l1, l2) + initpsbt = l1.rpc.utxopsbt(chan_amount, next_feerate, startweight, + prev_utxos, reservedok=True, + min_witness_weight=110, + excess_as_change=True) + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt']) + update = l1.rpc.openchannel_update(chan_id, bump['psbt']) + assert update['commitments_secured'] + + return l1.rpc.signpsbt(update['psbt'])['signed_psbt'] + + signed_psbt = run_retry() + l1.rpc.openchannel_signed(chan_id, signed_psbt) + inflights = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['inflight'] + assert inflights[-1]['funding_txid'] not in bitcoind.rpc.getrawmempool() + + cmtmt_txid = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['scratch_txid'] + assert cmtmt_txid == inflights[-1]['scratch_txid'] + + # l2 goes offline + l2.stop() + + # l1 drops to chain. + l1.daemon.rpcproxy.mock_rpc('sendrawtransaction', None) + l1.rpc.close(chan_id, 1) + l1.daemon.wait_for_logs(['Broadcasting txid {}'.format(inflights[0]['scratch_txid']), + 'Broadcasting txid {}'.format(inflights[1]['scratch_txid']), + 'sendrawtx exit 0', + 'sendrawtx exit 25']) + assert inflights[0]['scratch_txid'] in bitcoind.rpc.getrawmempool() + assert inflights[1]['scratch_txid'] not in bitcoind.rpc.getrawmempool() + + @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') def test_funder_options(node_factory, bitcoind):