class LightningNode(object):

    displayName = 'lightning'

    def __init__(self,
                 lightning_dir,
                 lightning_port,
                 btc,
                 executor=None,
                 node_id=0):
        self.bitcoin = btc
        self.executor = executor
        self.daemon = LightningD(lightning_dir,
                                 self.bitcoin,
                                 port=lightning_port)
        socket_path = os.path.join(lightning_dir,
                                   "lightning-rpc").format(node_id)
        self.invoice_count = 0
        self.logger = logging.getLogger(
            'lightning-node({})'.format(lightning_port))

        self.rpc = LightningRpc(socket_path, self.executor)

        orig_call = self.rpc._call

        def rpc_call(method, args):
            self.logger.debug("Calling {} with arguments {}".format(
                method, json.dumps(args, indent=4, sort_keys=True)))
            r = orig_call(method, args)
            self.logger.debug("Call returned {}".format(
                json.dumps(r, indent=4, sort_keys=True)))
            return r

        self.rpc._call = rpc_call
        self.myid = None

    def peers(self):
        return [p['id'] for p in self.rpc.listpeers()['peers']]

    def getinfo(self):
        if not self.info:
            self.info = self.rpc.getinfo()
        return self.info

    def id(self):
        if not self.myid:
            self.myid = self.rpc.getinfo()['id']
        return self.myid

    def openchannel(self, node_id, host, port, satoshis):
        # Make sure we have a connection already
        if node_id not in self.peers():
            raise ValueError("Must connect to node before opening a channel")
        return self.rpc.fundchannel(node_id, satoshis)

    def getaddress(self):
        return self.rpc.newaddr()['address']

    def addfunds(self, bitcoind, satoshis):
        addr = self.getaddress()
        txid = bitcoind.rpc.sendtoaddress(addr, float(satoshis) / 10**8)
        bitcoind.rpc.getrawtransaction(txid)
        while len(self.rpc.listfunds()['outputs']) == 0:
            time.sleep(1)
            bitcoind.rpc.generate(1)

    def ping(self):
        """ Simple liveness test to see if the node is up and running

        Returns true if the node is reachable via RPC, false otherwise.
        """
        try:
            self.rpc.help()
            return True
        except:
            return False

    def check_channel(self, remote, require_both=False):
        """Make sure that we have an active channel with remote

        `require_both` must be False unless the other side supports
        sending a `channel_announcement` and `channel_update` on
        `funding_locked`. This doesn't work for eclair for example.

        """
        remote_id = remote.id()
        self_id = self.id()
        peer = None
        for p in self.rpc.listpeers()['peers']:
            if remote.id() == p['id']:
                peer = p
                break
        if not peer:
            self.logger.debug('Peer {} not found in listpeers'.format(remote))
            return False

        if len(peer['channels']) < 1:
            self.logger.debug(
                'Peer {} has no channel open with us'.format(remote))
            return False

        state = p['channels'][0]['state']
        self.logger.debug("Channel {} -> {} state: {}".format(
            self_id, remote_id, state))

        if state != 'CHANNELD_NORMAL' or not p['connected']:
            self.logger.debug(
                'Channel with peer {} is not in state normal ({}) or peer is not connected ({})'
                .format(remote_id, state, p['connected']))
            return False

        # Make sure that gossipd sees a local channel_update for routing
        scid = p['channels'][0]['short_channel_id']

        channels = self.rpc.listchannels(scid)['channels']

        if not require_both and len(channels) >= 1:
            return channels[0]['active']

        if len(channels) != 2:
            self.logger.debug(
                'Waiting for both channel directions to be announced: 2 != {}'.
                format(len(channels)))
            return False

        return channels[0]['active'] and channels[1]['active']

    def getchannels(self):
        result = []
        for c in self.rpc.listchannels()['channels']:
            result.append((c['source'], c['destination']))
        return set(result)

    def getnodes(self):
        return set([n['nodeid'] for n in self.rpc.listnodes()['nodes']])

    def invoice(self, amount):
        invoice = self.rpc.invoice(amount, "invoice%d" % (self.invoice_count),
                                   "description")
        self.invoice_count += 1
        return invoice['bolt11']

    def send(self, req):
        result = self.rpc.pay(req)
        return result['payment_preimage']

    def connect(self, host, port, node_id):
        return self.rpc.connect(node_id, host, port)

    def info(self):
        r = self.rpc.getinfo()
        return {
            'id': r['id'],
            'blockheight': r['blockheight'],
        }

    def block_sync(self, blockhash):
        time.sleep(1)

    def restart(self):
        self.daemon.stop()
        time.sleep(5)
        self.daemon.start()
        time.sleep(1)

    def stop(self):
        self.daemon.stop()

    def start(self):
        self.daemon.start()

    def check_route(self, node_id, amount):
        try:
            r = self.rpc.getroute(node_id, amount, 1.0)
        except ValueError as e:
            if (str(e).find("Could not find a route") > 0):
                return False
            raise
        return True
Esempio n. 2
0
class LightningNode(object):
    def __init__(self,
                 lightning_dir,
                 lightning_port,
                 btc,
                 executor=None,
                 node_id=0):
        self.bitcoin = btc
        self.executor = executor
        self.daemon = LightningD(lightning_dir,
                                 btc.bitcoin_dir,
                                 port=lightning_port)
        socket_path = os.path.join(lightning_dir,
                                   "lightning-rpc").format(node_id)
        self.invoice_count = 0
        self.rpc = LightningRpc(socket_path, self.executor)
        self.logger = logging.getLogger(
            'lightning-node({})'.format(lightning_port))

    def peers(self):
        return [p['peerid'] for p in self.rpc.getpeers()['peers']]

    def id(self):
        return self.rpc.getinfo()['id']

    def openchannel(self, node_id, host, port, satoshis):
        # Make sure we have a connection already
        if node_id not in self.peers():
            raise ValueError("Must connect to node before opening a channel")
        return self.rpc.fundchannel(node_id, satoshis)

    def getaddress(self):
        return self.rpc.newaddr()['address']

    def addfunds(self, bitcoind, satoshis):
        addr = self.getaddress()
        txid = bitcoind.rpc.sendtoaddress(addr, float(satoshis) / 10**8)
        tx = bitcoind.rpc.getrawtransaction(txid)
        self.rpc.addfunds(tx)

    def ping(self):
        """ Simple liveness test to see if the node is up and running

        Returns true if the node is reachable via RPC, false otherwise.
        """
        try:
            self.rpc.help()
            return True
        except:
            return False

    def check_channel(self, remote):
        """ Make sure that we have an active channel with remote
        """
        remote_id = remote.id()
        self_id = self.id()
        for p in self.rpc.getpeers()['peers']:
            if remote.id() == p['peerid']:
                self.logger.debug("Channel {} -> {} state: {}".format(
                    self_id, remote_id, p['state']))
                return p['state'] == 'CHANNELD_NORMAL'

        self.logger.warning("Channel {} -> {} not found".format(
            self_id, remote_id))
        return False

    def getchannels(self):
        result = []
        for c in self.rpc.getchannels()['channels']:
            result.append((c['source'], c['destination']))
        return set(result)

    def getnodes(self):
        return set([n['nodeid'] for n in self.rpc.getnodes()['nodes']])

    def invoice(self, amount):
        invoice = self.rpc.invoice(amount, "invoice%d" % (self.invoice_count),
                                   "description")
        self.invoice_count += 1
        print(invoice)
        return invoice['bolt11']

    def send(self, req):
        result = self.rpc.pay(req)
        return result['preimage']

    def connect(self, host, port, node_id):
        return self.rpc.connect(node_id, "{}:{}".format(host, port))

    def info(self):
        r = self.rpc.getinfo()
        return {
            'id': r['id'],
            'blockheight': r['blockheight'],
        }
Esempio n. 3
0
def get_addr(l: LightningRpc) -> Address:
    return l.newaddr()['address']
Esempio n. 4
0
def lightnings(bitcoin_dir, bitcoind, lightning_dirs):
    procs = []
    for i, dir in enumerate(lightning_dirs):
        proc = TailableProc(
            "{lightningd_bin} --network regtest --bitcoin-cli {bitcoin_cli_bin} --bitcoin-rpcport=10287 --bitcoin-datadir {bitcoin_dir} --bitcoin-rpcuser rpcuser --bitcoin-rpcpassword rpcpassword --lightning-dir {dir} --bind-addr 127.0.0.1:987{i}".format(
                lightningd_bin=lightningd_bin,
                bitcoin_cli_bin=bitcoin_cli_bin,
                bitcoin_dir=bitcoin_dir,
                dir=dir,
                i=i,
            ),
            verbose=False,
            procname="lightningd-{}".format(i),
        )
        proc.start()
        proc.wait_for_log("Server started with public key")
        procs.append(proc)

    # make rpc clients
    rpcs = []
    for dir in lightning_dirs:
        rpc = LightningRpc(os.path.join(dir, "lightning-rpc"))
        rpcs.append(rpc)

    # get nodes funded
    _, bitcoin_rpc = bitcoind
    for rpc in rpcs:
        addr = rpc.newaddr()["address"]
        bitcoin_rpc.sendtoaddress(addr, 15)
        bitcoin_rpc.generate(1)

    for rpc in rpcs:
        wait_for(lambda: len(rpc.listfunds()["outputs"]) == 1, timeout=60)

    # make a channel between the two
    t = rpcs[0]
    f = rpcs[1]
    tinfo = t.getinfo()
    f.connect(tinfo["id"], tinfo["binding"][0]["address"], tinfo["binding"][0]["port"])
    num_tx = len(bitcoin_rpc.getrawmempool())
    f.fundchannel(tinfo["id"], 10000000)
    wait_for(lambda: len(bitcoin_rpc.getrawmempool()) == num_tx + 1)
    bitcoin_rpc.generate(1)

    # wait for channels
    for proc in procs:
        proc.wait_for_log("to CHANNELD_NORMAL", timeout=60)
    for rpc in rpcs:
        wait_for(lambda: len(rpc.listfunds()["channels"]) > 0, timeout=60)

    # send some money just to open space at the channel
    f.pay(t.invoice(1000000000, "open", "nada")["bolt11"])
    t.waitinvoice("open")

    yield procs, rpcs

    # stop nodes
    for proc, rpc in zip(procs, rpcs):
        try:
            rpc.stop()
        except:
            pass

        proc.proc.wait(5)
        proc.stop()