pyln-client: Add facility to register featurebits from plugins

we have 4 venues in which we can add features, 3 of which are unilaterally
controlled (`init`, `node_announcement`, and `invoices`) the
`channel_announcement` is co-signed by both parties, so we can't add
featurebits without additional coordination overhead.

Each location is encoded as a key-value pair in a dict called `featurebits` in
the manifest (omitted if no custom featurebits are set).
This commit is contained in:
Christian Decker 2020-01-29 17:02:47 +01:00 committed by Rusty Russell
parent 27c7707874
commit a13591400a

View File

@ -1,3 +1,4 @@
from binascii import hexlify
from collections import OrderedDict
from enum import Enum
from .lightning import LightningRpc, Millisatoshi
@ -5,6 +6,7 @@ from threading import RLock
import inspect
import json
import math
import os
import re
import sys
@ -99,10 +101,37 @@ class Plugin(object):
"""
def __init__(self, stdout=None, stdin=None, autopatch=True, dynamic=True):
def __init__(self, stdout=None, stdin=None, autopatch=True, dynamic=True,
init_features=None, node_features=None, invoice_features=None):
self.methods = {'init': Method('init', self._init, MethodType.RPCMETHOD)}
self.options = {}
def convert_featurebits(bits):
"""Convert the featurebits into the bytes required to hexencode.
"""
if bits is None:
return None
elif isinstance(bits, int):
bitlen = math.ceil(math.log(bits, 256))
return hexlify(bits.to_bytes(bitlen, 'big')).decode('ASCII')
elif isinstance(bits, str):
# Assume this is already hex encoded
return bits
elif isinstance(bits, bytes):
return hexlify(bits).decode('ASCII')
else:
raise ValueError("Could not convert featurebits to hex-encoded string")
self.featurebits = {
'init': convert_featurebits(init_features),
'node': convert_featurebits(node_features),
'invoice': convert_featurebits(invoice_features),
}
# A dict from topics to handler functions
self.subscriptions = {}
@ -523,14 +552,21 @@ class Plugin(object):
if method.long_desc:
methods[len(methods) - 1]["long_description"] = method.long_desc
return {
manifest = {
'options': list(self.options.values()),
'rpcmethods': methods,
'subscriptions': list(self.subscriptions.keys()),
'hooks': hooks,
'dynamic': self.dynamic
'dynamic': self.dynamic,
}
# Compact the features a bit, not important.
features = {k: v for k, v in self.featurebits.items() if v is not None}
if features is not None:
manifest['featurebits'] = features
return manifest
def _init(self, options, configuration, request):
self.rpc_filename = configuration['rpc-file']
self.lightning_dir = configuration['lightning-dir']