Esempio n. 1
0
    def __init__(self,
                 config_file: Optional[str] = None,
                 lnd_home: Optional[str] = None,
                 lnd_host: Optional[str] = None,
                 regtest=False):
        """
        :param config_file: path to the config file
        :param lnd_home: path to lnd home folder
        :param lnd_host: lnd host of format "127.0.0.1:9735"
        :param regtest: if the node is representing a regtest node
        """
        super().__init__()
        if config_file:
            self.config_file = config_file
            self.config = settings.read_config(self.config_file)
        else:
            self.config_file = None
            self.config = None
        self.lnd_home = lnd_home
        self.lnd_host = lnd_host
        self.regtest = regtest

        self._rpc = None
        self._routerrpc = None
        self.connect_rpcs()

        self.set_info()
        self.network = Network(self)
        self.set_channel_summary()
        self.update_blockheight()
Esempio n. 2
0
    def _add_channel_annotations(self, channels):
        """
        Appends metadata to existing channel dicts from the configuration file.

        :param channels: dict
        :return: dict
        """
        logger.debug("Adding annotations from file %s.", self.node.config_file)
        # mapping between the channel point and channel id
        channel_point_mapping = {
            k: v['channel_point'].split(':')[0]
            for k, v in channels.items()
        }
        # only read annotations if config file is given
        if self.node.config_file:
            config = settings.read_config(self.node.config_file)
            annotations = config['annotations']
        else:
            annotations = {}
        channel_annotations_funding_id = {}
        channel_annotations_channel_id = {}

        for id, annotation in annotations.items():
            if len(id) == 18 and id.isnumeric():
                # valid channel id
                channel_annotations_channel_id[int(id)] = \
                    annotation
            elif len(id) == 64 and id.isalnum():
                # valid funding transaction id
                channel_annotations_funding_id[id] = \
                    annotation
            else:
                raise ValueError(
                    'First part needs to be either a channel id or the '
                    'funding transaction id. \n'
                    'The funding transaction id can be found with '
                    '`lncli listchannels` under the channel point (the '
                    'characters before the colon).')

        for channel_id, channel_values in channels.items():
            # get the annotation by channel id first
            annotation = channel_annotations_channel_id.get(channel_id, None)
            # if no channel annotation, try with funding id
            if annotation is None:
                annotation = channel_annotations_funding_id.get(
                    channel_point_mapping[channel_id], None)

            if annotation is not None:
                channels[channel_id]['annotation'] = annotation
            else:
                channels[channel_id]['annotation'] = ''

        return channels
Esempio n. 3
0
    def __init__(self, lncli_path, config_file):
        self.lncli_path = lncli_path

        config = settings.read_config(config_file)

        cert_file = os.path.expanduser(config['network']['tls_cert_file'])
        macaroon_file = \
            os.path.expanduser(config['network']['admin_macaroon_file'])
        lnd_host = config['network']['lnd_grpc_host']

        # assemble the command for lncli for execution with flags
        self.lncli_command = [
            self.lncli_path, '--rpcserver=' + lnd_host,
            '--macaroonpath=' + macaroon_file, '--tlscertpath=' + cert_file
        ]
Esempio n. 4
0
    def _add_channel_annotations(self, channels: Dict) -> Dict:
        """Appends metadata to existing channel dicts from the configuration file."""
        if self.node.config:
            logger.debug("Adding annotations from file %s.", self.node.config_file)
        # mapping between the channel point and channel id
        channel_point_mapping = {
            k: v["channel_point"].split(":")[0] for k, v in channels.items()
        }
        # only read annotations if config file is given
        if self.node.config_file:
            config = settings.read_config(self.node.config_file)
            annotations = config["annotations"]
        else:
            annotations = {}
        channel_annotations_funding_id = {}
        channel_annotations_channel_id = {}

        for chan_id, annotation in annotations.items():
            if len(chan_id) == 18 and chan_id.isnumeric():
                # valid channel id
                channel_annotations_channel_id[int(chan_id)] = annotation
            elif len(chan_id) == 64 and chan_id.isalnum():
                # valid funding transaction id
                channel_annotations_funding_id[chan_id] = annotation
            else:
                raise ValueError(
                    "First part needs to be either a channel id or the "
                    "funding transaction id. \n"
                    "The funding transaction id can be found with "
                    "`lncli listchannels` under the channel point (the "
                    "characters before the colon)."
                )

        for channel_id, channel_values in channels.items():
            # get the annotation by channel id first
            annotation = channel_annotations_channel_id.get(channel_id, None)
            # if no channel annotation, try with funding id
            if annotation is None:
                annotation = channel_annotations_funding_id.get(
                    channel_point_mapping[channel_id], None
                )

            if annotation is not None:
                channels[channel_id]["annotation"] = annotation
            else:
                channels[channel_id]["annotation"] = ""

        return channels
Esempio n. 5
0
    def connect_rpcs(self):
        """
        Establishes a connection to lnd using the hostname, tls certificate,
        and admin macaroon defined in settings.
        """
        macaroons = True
        os.environ['GRPC_SSL_CIPHER_SUITES'] = \
            'ECDHE-RSA-AES128-GCM-SHA256:' \
            'ECDHE-RSA-AES128-SHA256:' \
            'ECDHE-RSA-AES256-SHA384:' \
            'ECDHE-RSA-AES256-GCM-SHA384:' \
            'ECDHE-ECDSA-AES128-GCM-SHA256:' \
            'ECDHE-ECDSA-AES128-SHA256:' \
            'ECDHE-ECDSA-AES256-SHA384:' \
            'ECDHE-ECDSA-AES256-GCM-SHA384'

        # if no lnd_home is given, then use the paths from the config,
        # else override them with default file paths in lnd_home
        if self.lnd_home is not None:
            cert_file = os.path.join(self.lnd_home, 'tls.cert')
            bitcoin_network = 'regtest' if self.regtest else 'mainnet'
            macaroon_file = os.path.join(self.lnd_home, 'data/chain/bitcoin/',
                                         bitcoin_network, 'admin.macaroon')
            if self.lnd_host is None:
                raise ValueError(
                    'if lnd_home is given, lnd_host must be given also')
            lnd_host = self.lnd_host
        else:
            config = settings.read_config(self.config_file)
            cert_file = os.path.expanduser(config['network']['tls_cert_file'])
            macaroon_file = \
                os.path.expanduser(config['network']['admin_macaroon_file'])
            lnd_host = config['network']['lnd_grpc_host']

        cert = None
        try:
            with open(cert_file, 'rb') as f:
                cert = f.read()
        except FileNotFoundError:
            logger.error("tls.cert not found, please configure %s.",
                         self.config_file)
            exit(1)

        if macaroons:
            try:
                with open(macaroon_file, 'rb') as f:
                    macaroon_bytes = f.read()
                    macaroon = codecs.encode(macaroon_bytes, 'hex')
            except FileNotFoundError:
                logger.error("admin.macaroon not found, please configure %s.",
                             self.config_file)
                exit(1)

            def metadata_callback(context, callback):
                callback([('macaroon', macaroon)], None)

            cert_creds = grpc.ssl_channel_credentials(cert)
            auth_creds = grpc.metadata_call_credentials(metadata_callback)
            creds = grpc.composite_channel_credentials(cert_creds, auth_creds)

        else:
            creds = grpc.ssl_channel_credentials(cert)

        # necessary to circumvent standard size limitation
        channel = grpc.secure_channel(lnd_host,
                                      creds,
                                      options=[
                                          ('grpc.max_receive_message_length',
                                           50 * 1024 * 1024)
                                      ])

        # establish connections to rpc servers
        self._rpc = lndrpc.LightningStub(channel)
        self._routerrpc = lndrouterrpc.RouterStub(channel)