mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 05:12:45 +01:00
df-tests: have the df_accepter plugin keep track of attempts
Test connection/reconnection handling for v2 opens. We needed to fixup the accepter plugin so that we were freeing up inputs on disconnect/failure.
This commit is contained in:
parent
88b4b5b2bb
commit
a82bfa83ff
@ -89,6 +89,7 @@ def find_inputs(b64_psbt):
|
|||||||
def init(configuration, options, plugin):
|
def init(configuration, options, plugin):
|
||||||
# this is the max channel size, pre-wumbo
|
# this is the max channel size, pre-wumbo
|
||||||
plugin.max_fund = Millisatoshi((2 ** 24 - 1) * 1000)
|
plugin.max_fund = Millisatoshi((2 ** 24 - 1) * 1000)
|
||||||
|
plugin.inflight = {}
|
||||||
plugin.log('max funding set to {}'.format(plugin.max_fund))
|
plugin.log('max funding set to {}'.format(plugin.max_fund))
|
||||||
|
|
||||||
|
|
||||||
@ -99,6 +100,35 @@ def set_accept_funding_max(plugin, max_sats, **kwargs):
|
|||||||
return {'accepter_max_funding': plugin.max_fund}
|
return {'accepter_max_funding': plugin.max_fund}
|
||||||
|
|
||||||
|
|
||||||
|
def add_inflight(plugin, peerid, chanid, psbt):
|
||||||
|
if peerid in plugin.inflight:
|
||||||
|
chans = plugin.inflight[peerid]
|
||||||
|
else:
|
||||||
|
chans = {}
|
||||||
|
plugin.inflight[peerid] = chans
|
||||||
|
|
||||||
|
if chanid in chans:
|
||||||
|
raise ValueError("channel {} already in flight (peer {})".format(chanid, peerid))
|
||||||
|
chans[chanid] = psbt
|
||||||
|
|
||||||
|
|
||||||
|
def cleanup_inflight(plugin, chanid):
|
||||||
|
for peer, chans in plugin.inflight.items():
|
||||||
|
if chanid in chans:
|
||||||
|
psbt = chans[chanid]
|
||||||
|
del chans[chanid]
|
||||||
|
return psbt
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def cleanup_inflight_peer(plugin, peerid):
|
||||||
|
if peerid in plugin.inflight:
|
||||||
|
chans = plugin.inflight[peerid]
|
||||||
|
for chanid, psbt in chans.items():
|
||||||
|
plugin.rpc.unreserveinputs(psbt)
|
||||||
|
del plugin.inflight[peerid]
|
||||||
|
|
||||||
|
|
||||||
@plugin.hook('openchannel2')
|
@plugin.hook('openchannel2')
|
||||||
def on_openchannel(openchannel2, plugin, **kwargs):
|
def on_openchannel(openchannel2, plugin, **kwargs):
|
||||||
# We mirror what the peer does, wrt to funding amount ...
|
# We mirror what the peer does, wrt to funding amount ...
|
||||||
@ -144,8 +174,12 @@ def on_openchannel(openchannel2, plugin, **kwargs):
|
|||||||
output = tx_output_init(change.to_whole_satoshi(), get_script(addr))
|
output = tx_output_init(change.to_whole_satoshi(), get_script(addr))
|
||||||
psbt_add_output_at(psbt_obj, 0, 0, output)
|
psbt_add_output_at(psbt_obj, 0, 0, output)
|
||||||
|
|
||||||
|
psbt = psbt_to_base64(psbt_obj, 0)
|
||||||
|
add_inflight(plugin, openchannel2['id'],
|
||||||
|
openchannel2['channel_id'], psbt)
|
||||||
plugin.log("contributing {} at feerate {}".format(amount, feerate))
|
plugin.log("contributing {} at feerate {}".format(amount, feerate))
|
||||||
return {'result': 'continue', 'psbt': psbt_to_base64(psbt_obj, 0),
|
|
||||||
|
return {'result': 'continue', 'psbt': psbt,
|
||||||
'accepter_funding_msat': amount,
|
'accepter_funding_msat': amount,
|
||||||
'funding_feerate': feerate}
|
'funding_feerate': feerate}
|
||||||
|
|
||||||
@ -168,7 +202,23 @@ def on_tx_sign(openchannel2_sign, plugin, **kwargs):
|
|||||||
else:
|
else:
|
||||||
final_psbt = psbt
|
final_psbt = psbt
|
||||||
|
|
||||||
|
cleanup_inflight(plugin, openchannel2_sign['channel_id'])
|
||||||
return {'result': 'continue', 'psbt': final_psbt}
|
return {'result': 'continue', 'psbt': final_psbt}
|
||||||
|
|
||||||
|
|
||||||
|
@plugin.subscribe("channel_open_failed")
|
||||||
|
def on_open_failed(channel_open_failed, plugin, **kwargs):
|
||||||
|
channel_id = channel_open_failed['channel_id']
|
||||||
|
psbt = cleanup_inflight(plugin, channel_id)
|
||||||
|
if psbt:
|
||||||
|
plugin.log("failed to open channel {}, unreserving".format(channel_id))
|
||||||
|
plugin.rpc.unreserveinputs(psbt)
|
||||||
|
|
||||||
|
|
||||||
|
@plugin.subscribe("disconnect")
|
||||||
|
def on_peer_disconnect(id, plugin, **kwargs):
|
||||||
|
plugin.log("peer {} disconnected, removing inflights".format(id))
|
||||||
|
cleanup_inflight_peer(plugin, id)
|
||||||
|
|
||||||
|
|
||||||
plugin.run()
|
plugin.run()
|
||||||
|
@ -308,6 +308,14 @@ def test_disconnect_fundee(node_factory):
|
|||||||
disconnects = ['-WIRE_ACCEPT_CHANNEL',
|
disconnects = ['-WIRE_ACCEPT_CHANNEL',
|
||||||
'@WIRE_ACCEPT_CHANNEL',
|
'@WIRE_ACCEPT_CHANNEL',
|
||||||
'+WIRE_ACCEPT_CHANNEL']
|
'+WIRE_ACCEPT_CHANNEL']
|
||||||
|
if EXPERIMENTAL_DUAL_FUND:
|
||||||
|
disconnects = ['-WIRE_ACCEPT_CHANNEL2',
|
||||||
|
'@WIRE_ACCEPT_CHANNEL2',
|
||||||
|
'+WIRE_ACCEPT_CHANNEL2',
|
||||||
|
'-WIRE_TX_COMPLETE',
|
||||||
|
'@WIRE_TX_COMPLETE',
|
||||||
|
'+WIRE_TX_COMPLETE']
|
||||||
|
|
||||||
l1 = node_factory.get_node()
|
l1 = node_factory.get_node()
|
||||||
l2 = node_factory.get_node(disconnect=disconnects)
|
l2 = node_factory.get_node(disconnect=disconnects)
|
||||||
|
|
||||||
@ -328,6 +336,47 @@ def test_disconnect_fundee(node_factory):
|
|||||||
assert len(l2.rpc.listpeers()) == 1
|
assert len(l2.rpc.listpeers()) == 1
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1")
|
||||||
|
@unittest.skipIf(not EXPERIMENTAL_DUAL_FUND, "needs OPT_DUAL_FUND")
|
||||||
|
def test_disconnect_fundee_v2(node_factory):
|
||||||
|
# Now error on fundee side during channel open, with them funding
|
||||||
|
disconnects = ['-WIRE_ACCEPT_CHANNEL2',
|
||||||
|
'@WIRE_ACCEPT_CHANNEL2',
|
||||||
|
'+WIRE_ACCEPT_CHANNEL2',
|
||||||
|
'-WIRE_TX_ADD_INPUT',
|
||||||
|
'@WIRE_TX_ADD_INPUT',
|
||||||
|
'+WIRE_TX_ADD_INPUT',
|
||||||
|
'-WIRE_TX_ADD_OUTPUT',
|
||||||
|
'@WIRE_TX_ADD_OUTPUT',
|
||||||
|
'+WIRE_TX_ADD_OUTPUT',
|
||||||
|
'-WIRE_TX_COMPLETE',
|
||||||
|
'@WIRE_TX_COMPLETE',
|
||||||
|
'+WIRE_TX_COMPLETE']
|
||||||
|
|
||||||
|
accepter_plugin = os.path.join(os.path.dirname(__file__),
|
||||||
|
'plugins/df_accepter.py')
|
||||||
|
l1 = node_factory.get_node()
|
||||||
|
l2 = node_factory.get_node(disconnect=disconnects,
|
||||||
|
options={'plugin': accepter_plugin})
|
||||||
|
|
||||||
|
l1.fundwallet(2000000)
|
||||||
|
l2.fundwallet(2000000)
|
||||||
|
|
||||||
|
for d in disconnects:
|
||||||
|
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
|
||||||
|
with pytest.raises(RpcError):
|
||||||
|
l1.rpc.fundchannel(l2.info['id'], 25000)
|
||||||
|
assert l1.rpc.getpeer(l2.info['id']) is None
|
||||||
|
|
||||||
|
# This one will succeed.
|
||||||
|
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
|
||||||
|
l1.rpc.fundchannel(l2.info['id'], 25000)
|
||||||
|
|
||||||
|
# Should still only have one peer!
|
||||||
|
assert len(l1.rpc.listpeers()) == 1
|
||||||
|
assert len(l2.rpc.listpeers()) == 1
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1")
|
@unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1")
|
||||||
def test_disconnect_half_signed(node_factory):
|
def test_disconnect_half_signed(node_factory):
|
||||||
# Now, these are the corner cases. Fundee sends funding_signed,
|
# Now, these are the corner cases. Fundee sends funding_signed,
|
||||||
@ -2527,12 +2576,15 @@ def test_restart_many_payments(node_factory, bitcoind):
|
|||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not DEVELOPER, "need dev-disconnect")
|
@unittest.skipIf(not DEVELOPER, "need dev-disconnect")
|
||||||
@unittest.skipIf(True, "FIXME: doesn't work for opt_dual_fund, see test below")
|
|
||||||
def test_fail_unconfirmed(node_factory, bitcoind, executor):
|
def test_fail_unconfirmed(node_factory, bitcoind, executor):
|
||||||
"""Test that if we crash with an unconfirmed connection to a known
|
"""Test that if we crash with an unconfirmed connection to a known
|
||||||
peer, we don't have a dangling peer in db"""
|
peer, we don't have a dangling peer in db"""
|
||||||
|
if EXPERIMENTAL_DUAL_FUND:
|
||||||
|
disconnect = ['=WIRE_OPEN_CHANNEL2']
|
||||||
|
else:
|
||||||
|
disconnect = ['=WIRE_OPEN_CHANNEL']
|
||||||
# = is a NOOP disconnect, but sets up file.
|
# = is a NOOP disconnect, but sets up file.
|
||||||
l1 = node_factory.get_node(disconnect=['=WIRE_OPEN_CHANNEL'])
|
l1 = node_factory.get_node(disconnect=disconnect)
|
||||||
l2 = node_factory.get_node()
|
l2 = node_factory.get_node()
|
||||||
|
|
||||||
# First one, we close by mutual agreement.
|
# First one, we close by mutual agreement.
|
||||||
|
Loading…
Reference in New Issue
Block a user