Esempio n. 1
0
def get_total_balance(n: LightningRpc) -> float:
    """return the total balance of this node in BTC, both in UTXOs and channels"""

    total_sat = (
        sum(map(lambda entry: entry["value"],
                n.listfunds()["outputs"])) +
        sum(map(lambda entry: entry["channel_sat"],
                n.listfunds()["channels"])))
    return total_sat / (10**8)  # convert to BTC
Esempio n. 2
0
class LightningDaemon(object):
    def __init__(self, daemon_rpc):
        self.rpc = LightningRpc(daemon_rpc)

    def invoice_c_lightning(self, msatoshi, label, description):
        result = self.rpc.invoice(msatoshi,
                                  label,
                                  description,
                                  expiry=INVOICE_EXPIRY)
        log(json.dumps(result, indent=1, sort_keys=True))
        return result

    def get_c_lightning_invoices(self):
        result = self.rpc.listinvoices()
        #log(json.dumps(result, indent=1, sort_keys=True))
        return result

    def delete(self, label, state="paid"):
        result = self.rpc.delinvoice(label, state)
        #        log(json.dumps(result, indent=1, sort_keys=True))
        return result

    def getinfo(self):
        return self.rpc.getinfo()

    def listfunds(self):
        return self.rpc.listfunds()

    def listnodes(self):
        return self.rpc.listnodes()
Esempio n. 3
0
def index(request):
    # Bitcoin RPC Credentials (you need to change these)
    rpc_port = "8332"
    rpc_user = "******"
    rpc_password = "******"

    # LIGHTNING NETWORK

    # Lightning Network Socket file (you might need to change this)
    ln = LightningRpc("/home/pi/.lightning/lightning-rpc")

    try:
        l_info = ln.getinfo()
        l = LightningViewData(True)
        l.block_height = l_info["blockheight"]
        l.version = l_info["version"]
        l.version = l.version.replace("v", "")

        l_peers = ln.listpeers()
        l.peer_count = len(l_peers["peers"])

        l_funds = ln.listfunds()
        l.channel_count = len(l_funds["channels"])
    except:
        l = LightningViewData(False)

# BITCOIN

    b = BitcoinViewData(True)

    try:
        rpc_connection = AuthServiceProxy("http://%s:%[email protected]:%s" %
                                          (rpc_user, rpc_password, rpc_port))
        b_conn_count = rpc_connection.getconnectioncount()
        if b_conn_count > 0:
            b.online = True
    except Exception as e:
        b.running = False

    if b.running == True:
        try:
            b.block_height = rpc_connection.getblockcount()
            b_network_info = rpc_connection.getnetworkinfo()
            b.peer_count = b_network_info["connections"]
            b.version = b_network_info["subversion"]
            b.version = b.version.replace("/", "")
            b.version = b.version.replace("Satoshi:", "")
        except Exception as e:
            b.message = str(e)


# RETURN VIEW DATA

    return render(request, 'dashboard/index.html', {
        'lightning': l,
        'bitcoin': b
    })
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. 5
0
def wait_to_funds(n: LightningRpc) -> None:
    """wait until n has a UTXO it controls"""
    while len(n.listfunds()["outputs"]) == 0:
        time.sleep(1)
Esempio n. 6
0
        onion_route = self.__onion_from_channels(amount, channels)
        return onion_route[0]["msatoshi"] - amount
        # print(onion_route)


if __name__ == "__main__":
    pa = PeerAnalyzer()
    exit()
    ln = LightningRpc("/home/rpickhardt/.lightning/lightning-rpc")
    own_channels = None
    try:
        f = open(
            "/Users/rpickhardt/hacken/lightning-helpers/balance-channels/friends20190301.json")
        own_channels = json.load(f)["channels"]
    except:
        own_channels = ln.listfunds()["channels"]

    ego_network = EgoNetwork(own_channels)

    channel_suggester = ChannelSuggester(own_channels, 0.25, 0.5)
    if channel_suggester.is_need_to_balance():
        print("channel balancing is suggested")
        # print("channels with too little outgoing capacity:")
        # for chan in channel_suggester.get_dry_channels():
        #    print(chan)
        print("channels with too little incoming capacity:")
        for chan in channel_suggester.get_liquid_channels():
            print(chan)

    channels = None
    try:
Esempio n. 7
0
class CLightningWallet(Wallet):
    def __init__(self):
        if LightningRpc is None:  # pragma: nocover
            raise ImportError(
                "The `pylightning` library must be installed to use `CLightningWallet`."
            )

        self.rpc = getenv("CLIGHTNING_RPC")
        self.ln = LightningRpc(self.rpc)

        # check description_hash support (could be provided by a plugin)
        self.supports_description_hash = False
        try:
            answer = self.ln.help("invoicewithdescriptionhash")
            if answer["help"][0]["command"].startswith(
                    "invoicewithdescriptionhash msatoshi label description_hash",
            ):
                self.supports_description_hash = True
        except:
            pass

        # check last payindex so we can listen from that point on
        self.last_pay_index = 0
        invoices = self.ln.listinvoices()
        for inv in invoices["invoices"][::-1]:
            if "pay_index" in inv:
                self.last_pay_index = inv["pay_index"]
                break

    async def status(self) -> StatusResponse:
        try:
            funds = self.ln.listfunds()
            return StatusResponse(
                None,
                sum([ch["channel_sat"] * 1000 for ch in funds["channels"]]),
            )
        except RpcError as exc:
            error_message = f"lightningd '{exc.method}' failed with '{exc.error}'."
            return StatusResponse(error_message, 0)

    async def create_invoice(
        self,
        amount: int,
        memo: Optional[str] = None,
        description_hash: Optional[bytes] = None,
    ) -> InvoiceResponse:
        label = "lbl{}".format(random.random())
        msat = amount * 1000

        try:
            if description_hash:
                if not self.supports_description_hash:
                    raise Unsupported("description_hash")

                params = [msat, label, description_hash.hex()]
                r = self.ln.call("invoicewithdescriptionhash", params)
                return InvoiceResponse(True, label, r["bolt11"], "")
            else:
                r = self.ln.invoice(msat,
                                    label,
                                    memo,
                                    exposeprivatechannels=True)
                return InvoiceResponse(True, label, r["bolt11"], "")
        except RpcError as exc:
            error_message = f"lightningd '{exc.method}' failed with '{exc.error}'."
            return InvoiceResponse(False, label, None, error_message)

    # WARNING: correct handling of fee_limit_msat is required to avoid security vulnerabilities!
    # The backend MUST NOT spend satoshis above invoice amount + fee_limit_msat.
    async def pay_invoice(self, bolt11: str,
                          fee_limit_msat: int) -> PaymentResponse:
        invoice = lnbits_bolt11.decode(bolt11)
        fee_limit_percent = fee_limit_msat / invoice.amount_msat * 100

        payload = {
            "bolt11": bolt11,
            "maxfeepercent": "{:.11}".format(fee_limit_percent),
            "exemptfee":
            0  # so fee_limit_percent is applied even on payments with fee under 5000 millisatoshi (which is default value of exemptfee)
        }

        try:
            r = self.ln.call("pay", payload)
        except RpcError as exc:
            return PaymentResponse(False, None, 0, None, str(exc))

        fee_msat = r["msatoshi_sent"] - r["msatoshi"]
        preimage = r["payment_preimage"]
        return PaymentResponse(True, r["payment_hash"], fee_msat, preimage,
                               None)

    async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
        r = self.ln.listinvoices(checking_id)
        if not r["invoices"]:
            return PaymentStatus(False)
        if r["invoices"][0]["label"] == checking_id:
            return PaymentStatus(r["invoices"][0]["status"] == "paid")
        raise KeyError("supplied an invalid checking_id")

    async def get_payment_status(self, checking_id: str) -> PaymentStatus:
        r = self.ln.call("listpays", {"payment_hash": checking_id})
        if not r["pays"]:
            return PaymentStatus(False)
        if r["pays"][0]["payment_hash"] == checking_id:
            status = r["pays"][0]["status"]
            if status == "complete":
                return PaymentStatus(True)
            elif status == "failed":
                return PaymentStatus(False)
            return PaymentStatus(None)
        raise KeyError("supplied an invalid checking_id")

    async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
        stream = await trio.open_unix_socket(self.rpc)

        i = 0
        while True:
            call = json.dumps({
                "method": "waitanyinvoice",
                "id": 0,
                "params": [self.last_pay_index],
            })

            await stream.send_all(call.encode("utf-8"))

            data = await stream.receive_some()
            paid = json.loads(data.decode("ascii"))

            paid = self.ln.waitanyinvoice(self.last_pay_index)
            self.last_pay_index = paid["pay_index"]
            yield paid["label"]

            i += 1
Esempio n. 8
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()