From 9d9fdfdfa6824522bdbe93014f2218ba27e5dbc0 Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 20 May 2021 16:55:45 -0500 Subject: [PATCH] rbf-test: confirm that rbf'd inflight is opened and closed as expected We weren't testing that a non-tip inflight would get opened correctly. Until now, that is. --- tests/test_opening.py | 98 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/tests/test_opening.py b/tests/test_opening.py index 9e716d0ad..a983d1df8 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -865,6 +865,104 @@ def test_rbf_broadcast_close_inflights(node_factory, bitcoind, chainparams): 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_rbf_non_last_mined(node_factory, bitcoind, chainparams): + """ + What happens if a 'non-tip' RBF transaction is mined? + """ + l1, l2 = node_factory.get_nodes(2, + opts={'allow_warning': True, + 'may_reconnect': 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() + + 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'] + + # Make a second inflight + signed_psbt = run_retry() + l1.rpc.openchannel_signed(chan_id, signed_psbt) + + # 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) + + # Make a 3rd inflight that won't make it into the mempool + signed_psbt = run_retry() + l1.rpc.openchannel_signed(chan_id, signed_psbt) + + l1.daemon.rpcproxy.mock_rpc('sendrawtransaction', None) + l2.daemon.rpcproxy.mock_rpc('sendrawtransaction', None) + + # We fetch out our inflights list + inflights = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['inflight'] + + # l2 goes offline + l2.stop() + + # The funding transaction gets mined (should be the 2nd inflight) + bitcoind.generate_block(6, wait_for_mempool=1) + + # l2 comes back up + l2.start() + + # everybody's got the right things now + l1.daemon.wait_for_log(r'to CHANNELD_NORMAL') + l2.daemon.wait_for_log(r'to CHANNELD_NORMAL') + + channel = only_one(only_one(l1.rpc.listpeers()['peers'])['channels']) + assert channel['funding_txid'] == inflights[1]['funding_txid'] + assert channel['scratch_txid'] == inflights[1]['scratch_txid'] + + # We delete inflights when the channel is in normal ops + assert 'inflights' not in channel + + # l2 stops, again + l2.stop() + + # l1 drops to chain. + l1.rpc.close(chan_id, 1) + l1.daemon.wait_for_log('Broadcasting txid {}'.format(channel['scratch_txid'])) + + # The funding transaction gets mined (should be the 2nd inflight) + bitcoind.generate_block(1, wait_for_mempool=1) + l1.daemon.wait_for_log(r'to ONCHAIN') + + @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') def test_funder_options(node_factory, bitcoind):