mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-19 15:14:23 +01:00
dual-fund: test (turned off) and plugin for executing an openchannel
Proof that v2 openchannel works, plus a plugin for sewing all the requisite bits together
This commit is contained in:
87
tests/plugins/df_opener.py
Executable file
87
tests/plugins/df_opener.py
Executable file
@@ -0,0 +1,87 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Test plugin for executing the opener v2 protocol
|
||||||
|
"""
|
||||||
|
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.method('openchannelv2')
|
||||||
|
def openchannel_v2(plugin, node_id, amount):
|
||||||
|
change_output_weight = (9 + 22) * 4
|
||||||
|
funding_output_weight = (9 + 34) * 4
|
||||||
|
core_weight = 44
|
||||||
|
feerate_val = 2000
|
||||||
|
feerate = '{}perkw'.format(feerate_val)
|
||||||
|
|
||||||
|
funding = plugin.rpc.fundpsbt(amount, feerate, funding_output_weight + core_weight)
|
||||||
|
psbt_obj = psbt_from_base64(funding['psbt'])
|
||||||
|
|
||||||
|
excess = Millisatoshi(funding['excess_msat'])
|
||||||
|
# FIXME: convert feerate ?!
|
||||||
|
change_cost = Millisatoshi(change_output_weight * feerate_val // 1000 * 1000)
|
||||||
|
dust_limit = Millisatoshi(feerate_val * 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)
|
||||||
|
|
||||||
|
resp = plugin.rpc.openchannel_init(node_id, amount,
|
||||||
|
psbt_to_base64(psbt_obj, 0),
|
||||||
|
commitment_feerate=feerate,
|
||||||
|
funding_feerate=feerate)
|
||||||
|
|
||||||
|
# We don't have an updates, so we send update until our peer is also
|
||||||
|
# finished
|
||||||
|
while not resp['commitments_secured']:
|
||||||
|
resp = plugin.rpc.openchannel_update(node_id, resp['psbt'])
|
||||||
|
|
||||||
|
# fixme: pass in array of our input indexes to signonly
|
||||||
|
signed = plugin.rpc.signpsbt(resp['psbt'])
|
||||||
|
return plugin.rpc.openchannel_signed(node_id, signed['signed_psbt'])
|
||||||
|
|
||||||
|
|
||||||
|
@plugin.init()
|
||||||
|
def init(options, configuration, plugin):
|
||||||
|
plugin.log("df_opener.py initializing")
|
||||||
|
|
||||||
|
|
||||||
|
plugin.run()
|
||||||
@@ -819,6 +819,27 @@ def test_funding_toolarge(node_factory, bitcoind):
|
|||||||
l1.rpc.fundchannel(l2.info['id'], amount)
|
l1.rpc.fundchannel(l2.info['id'], amount)
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipIf(not EXPERIMENTAL_FEATURES, "dual-funding is experimental only")
|
||||||
|
@unittest.skipIf(True, "df_opener.py requires wallycore")
|
||||||
|
def test_v2_open(node_factory, bitcoind, chainparams):
|
||||||
|
l1 = node_factory.get_node(options={'plugin': os.path.join(os.getcwd(),
|
||||||
|
'tests/plugins/df_opener.py')})
|
||||||
|
l2 = node_factory.get_node()
|
||||||
|
|
||||||
|
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
|
||||||
|
amount = 2**24
|
||||||
|
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)
|
||||||
|
|
||||||
|
l1.rpc.openchannelv2(l2.info['id'], 100000)
|
||||||
|
|
||||||
|
bitcoind.generate_block(1)
|
||||||
|
sync_blockheight(bitcoind, [l1])
|
||||||
|
l1.daemon.wait_for_log(' to CHANNELD_NORMAL')
|
||||||
|
|
||||||
|
|
||||||
def test_funding_push(node_factory, bitcoind, chainparams):
|
def test_funding_push(node_factory, bitcoind, chainparams):
|
||||||
""" Try to push peer some sats """
|
""" Try to push peer some sats """
|
||||||
# We track balances, to verify that accounting is ok.
|
# We track balances, to verify that accounting is ok.
|
||||||
|
|||||||
Reference in New Issue
Block a user