From 7ce7577254c698043847eb2e4d32ae7f8c615798 Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 11 Feb 2021 17:01:07 -0600 Subject: [PATCH] df-tests: check that multiple RBFs and tx-sigs errors during RBF work --- tests/test_opening.py | 205 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) diff --git a/tests/test_opening.py b/tests/test_opening.py index 04eb7d4cb..30be7f12d 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -92,6 +92,73 @@ def test_v2_rbf(node_factory, bitcoind, chainparams): l1.daemon.wait_for_log('sendrawtx exit 0') +@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@unittest.skipIf(not EXPERIMENTAL_FEATURES, "dual-funding is experimental only") +def test_v2_rbf_multi(node_factory, bitcoind, chainparams): + l1, l2 = node_factory.get_nodes(2, + opts=[{'dev-force-features': '+223'}, + {'dev-force-features': '+223'}]) + + 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) + 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') + + next_feerate = find_next_feerate(l1, l2) + + # Initiate an RBF + startweight = 42 + 172 # base weight, funding output + initpsbt = l1.rpc.utxopsbt(chan_amount, next_feerate, startweight, + prev_utxos, reservedok=True, + min_witness_weight=110, + excess_as_change=True) + + # Do the bump + bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt']) + + update = l1.rpc.openchannel_update(chan_id, bump['psbt']) + assert update['commitments_secured'] + + # Sign our inputs, and continue + signed_psbt = l1.rpc.signpsbt(update['psbt'])['signed_psbt'] + l1.rpc.openchannel_signed(chan_id, signed_psbt) + + next_feerate = find_next_feerate(l1, l2) + + # Initiate an RBF, double the channel amount this time + startweight = 42 + 172 # base weight, funding output + initpsbt = l1.rpc.utxopsbt(chan_amount * 2, next_feerate, startweight, + prev_utxos, reservedok=True, + min_witness_weight=110, + excess_as_change=True) + + # Do the bump + bump = l1.rpc.openchannel_bump(chan_id, chan_amount * 2, initpsbt['psbt']) + + update = l1.rpc.openchannel_update(chan_id, bump['psbt']) + assert update['commitments_secured'] + + # Sign our inputs, and continue + signed_psbt = l1.rpc.signpsbt(update['psbt'])['signed_psbt'] + l1.rpc.openchannel_signed(chan_id, signed_psbt) + + bitcoind.generate_block(1) + sync_blockheight(bitcoind, [l1]) + l1.daemon.wait_for_log(' to CHANNELD_NORMAL') + + @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @unittest.skipIf(not EXPERIMENTAL_FEATURES, "dual-funding is experimental only") def test_rbf_reconnect_init(node_factory, bitcoind, chainparams): @@ -262,3 +329,141 @@ def test_rbf_reconnect_tx_construct(node_factory, bitcoind, chainparams): bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt']) update = l1.rpc.openchannel_update(chan_id, bump['psbt']) assert update['commitments_secured'] + + +@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@unittest.skipIf(not EXPERIMENTAL_FEATURES, "dual-funding is experimental only") +def test_rbf_reconnect_tx_sigs(node_factory, bitcoind, chainparams): + disconnects = ['=WIRE_TX_SIGNATURES', # Initial funding succeeds + '-WIRE_TX_SIGNATURES', # When we send tx-sigs, RBF + '=WIRE_TX_SIGNATURES', # When we reconnect + '@WIRE_TX_SIGNATURES', # When we RBF again + '=WIRE_TX_SIGNATURES', # When we reconnect + '+WIRE_TX_SIGNATURES'] # When we RBF again + + l1, l2 = node_factory.get_nodes(2, + opts=[{'dev-force-features': '+223', + 'disconnect': disconnects, + 'may_reconnect': True, + # On reconnects, we dont have cmd + # outstanding for the missed sigs + # so l1 logs a BROKEN. + 'allow_broken_log': True}, + {'dev-force-features': '+223', + '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) + 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('Broadcasting funding tx') + l1.daemon.wait_for_log(' to DUALOPEND_AWAITING_LOCKIN') + + next_feerate = find_next_feerate(l1, l2) + + # Initiate an RBF + startweight = 42 + 172 # base weight, funding output + initpsbt = l1.rpc.utxopsbt(chan_amount, next_feerate, startweight, + prev_utxos, reservedok=True, + min_witness_weight=110, + excess_as_change=True) + + bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt']) + update = l1.rpc.openchannel_update(chan_id, bump['psbt']) + + # Sign our inputs, and continue + signed_psbt = l1.rpc.signpsbt(update['psbt'])['signed_psbt'] + + # First time we error when we send our sigs + with pytest.raises(RpcError, match='Owning subdaemon dualopend died'): + l1.rpc.openchannel_signed(chan_id, signed_psbt) + + # We reconnect and try again. feerate should have bumped + next_feerate = find_next_feerate(l1, l2) + + # Initiate an RBF + startweight = 42 + 172 # base weight, funding output + 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) + + # l2 gets our sigs and broadcasts them + l2.daemon.wait_for_log('peer_in WIRE_CHANNEL_REESTABLISH') + l2.daemon.wait_for_log('peer_in WIRE_TX_SIGNATURES') + l2.daemon.wait_for_log('sendrawtx exit 0') + + # Wait until we've done re-establish, if we try to + # RBF again too quickly, it'll fail since they haven't + # had time to process our sigs yet + l1.daemon.wait_for_log('peer_in WIRE_CHANNEL_REESTABLISH') + l1.daemon.wait_for_log('peer_in WIRE_TX_SIGNATURES') + + # Now we initiate the RBF + bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt']) + update = l1.rpc.openchannel_update(chan_id, bump['psbt']) + + # Sign our inputs, and continue + signed_psbt = l1.rpc.signpsbt(update['psbt'])['signed_psbt'] + + # Second time we error after we send our sigs + with pytest.raises(RpcError, match='Owning subdaemon dualopend died'): + l1.rpc.openchannel_signed(chan_id, signed_psbt) + + # We reconnect and try again. feerate should have bumped + next_feerate = find_next_feerate(l1, l2) + + startweight = 42 + 172 # base weight, funding output + 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) + + # l2 gets our sigs and broadcasts them + l2.daemon.wait_for_log('peer_in WIRE_CHANNEL_REESTABLISH') + l2.daemon.wait_for_log('peer_in WIRE_TX_SIGNATURES') + l2.daemon.wait_for_log('sendrawtx exit 0') + + # Wait until we've done re-establish, if we try to + # RBF again too quickly, it'll fail since they haven't + # had time to process our sigs yet + l1.daemon.wait_for_log('peer_in WIRE_CHANNEL_REESTABLISH') + l1.daemon.wait_for_log('peer_in WIRE_TX_SIGNATURES') + + # 3rd RBF + bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt']) + update = l1.rpc.openchannel_update(chan_id, bump['psbt']) + signed_psbt = l1.rpc.signpsbt(update['psbt'])['signed_psbt'] + + # Third time we error after we send our sigs + with pytest.raises(RpcError, match='Owning subdaemon dualopend died'): + l1.rpc.openchannel_signed(chan_id, signed_psbt) + + # l2 gets our sigs + l2.daemon.wait_for_log('peer_in WIRE_TX_SIGNATURES') + l2.daemon.wait_for_log('sendrawtx exit 0') + + # mine a block? + bitcoind.generate_block(1) + sync_blockheight(bitcoind, [l1]) + l1.daemon.wait_for_log(' to CHANNELD_NORMAL') + + # Check that they have matching funding txid + l1_funding_txid = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['funding_txid'] + l2_funding_txid = only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['funding_txid'] + assert l1_funding_txid == l2_funding_txid