tests: plugin for dual-funding tests in ln-prototest

FIXME: requires wallycore
This commit is contained in:
niftynei
2020-09-09 19:40:28 +09:30
committed by Rusty Russell
parent 8d429ecd06
commit b3cbb0b653
2 changed files with 93 additions and 0 deletions

View File

@@ -1,3 +1,4 @@
from .bech32 import bech32_decode
from .invoice import Invoice from .invoice import Invoice
from .onion import OnionPayload, TlvPayload, LegacyOnionPayload from .onion import OnionPayload, TlvPayload, LegacyOnionPayload
from .wire import LightningConnection, LightningServerSocket from .wire import LightningConnection, LightningServerSocket
@@ -11,4 +12,5 @@ __all__ = [
"OnionPayload", "OnionPayload",
"LegacyOnionPayload", "LegacyOnionPayload",
"TlvPayload", "TlvPayload",
"bech32_decode",
] ]

91
tests/plugins/df_accepter.py Executable file
View File

@@ -0,0 +1,91 @@
#!/usr/bin/env python3
"""Test plugin for adding inputs/outputs to a dual-funded transaction
"""
from pyln.client import Plugin, Millisatoshi
from pyln.proto import bech32_decode
from typing import Iterable, List, Optional
from wallycore import psbt_add_output_at, psbt_from_base64, psbt_to_base64, tx_output_init
plugin = Plugin()
def convertbits(data: Iterable[int], frombits: int, tobits: int, pad: bool = True) -> Optional[List[int]]:
"""General power-of-2 base conversion."""
acc = 0
bits = 0
ret = []
maxv = (1 << tobits) - 1
max_acc = (1 << (frombits + tobits - 1)) - 1
for value in data:
if value < 0 or (value >> frombits):
return None
acc = ((acc << frombits) | value) & max_acc
bits += frombits
while bits >= tobits:
bits -= tobits
ret.append((acc >> bits) & maxv)
if pad:
if bits:
ret.append((acc << (tobits - bits)) & maxv)
elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
return None
return ret
def get_script(bech_addr):
hrp, data = bech32_decode(bech_addr)
# FIXME: verify hrp matches expected network
wprog = convertbits(data[1:], 5, 8, False)
wit_ver = data[0]
if wit_ver > 16:
raise ValueError("Invalid witness version {}".format(wit_ver[0]))
return bytes([wit_ver + 0x50 if wit_ver > 0 else wit_ver, len(wprog)] + wprog)
@plugin.hook('openchannel2')
def on_openchannel(openchannel2, plugin, **kwargs):
# We mirror what the peer does, wrt to funding amount
amount = openchannel2['their_funding']
feerate = openchannel2['feerate_per_kw_funding']
locktime = openchannel2['locktime']
funding = plugin.rpc.fundpsbt(amount, ''.join([str(feerate), 'perkw']), 0, reserve=True,
locktime=locktime)
psbt_obj = psbt_from_base64(funding['psbt'])
excess = Millisatoshi(funding['excess_msat'])
change_cost = Millisatoshi(164 * feerate // 1000 * 1000)
dust_limit = Millisatoshi(253 * 1000)
if excess > (dust_limit + change_cost):
addr = plugin.rpc.newaddr()['bech32']
change = excess - change_cost
output = tx_output_init(int(change.to_satoshi()), get_script(addr))
psbt_add_output_at(psbt_obj, 0, 0, output)
return {'result': 'continue', 'psbt': psbt_to_base64(psbt_obj, 0),
'accepter_funding_msat': amount}
@plugin.hook('openchannel2_changed')
def on_tx_changed(openchannel2_changed, plugin, **kwargs):
# In this example, we have nothing to add, so we
# pass back the same psbt that was forwarded in here
return {'result': 'continue', 'psbt': openchannel2_changed['psbt']}
@plugin.hook('openchannel2_sign')
def on_tx_sign(openchannel2_sign, plugin, **kwargs):
psbt = openchannel2_sign['psbt']
# We only sign the ones with our parity of a serial_id
# FIXME: find the inputs with an odd-serial, these are ours
# the key for a serial_id ::
our_inputs = [1]
signed_psbt = plugin.rpc.signpsbt(psbt, signonly=our_inputs)['signed_psbt']
return {'result': 'continue', 'psbt': signed_psbt}
plugin.run()