Browse Source
Proof that v2 openchannel works, plus a plugin for sewing all the requisite bits togethertravis-experimental
niftynei
4 years ago
committed by
Rusty Russell
2 changed files with 108 additions and 0 deletions
@ -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() |
Loading…
Reference in new issue