df-tests: check that multiple RBFs and tx-sigs errors during RBF work

This commit is contained in:
niftynei 2021-02-11 17:01:07 -06:00 committed by Rusty Russell
parent c1fcc1c819
commit 7ce7577254

View File

@ -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