def init(options, configuration, plugin): global rpc, our_nodeid plugin.log("feereport init") path = join(configuration["lightning-dir"], configuration["rpc-file"]) rpc = LightningRpc(path) info = rpc.getinfo() our_nodeid = info["id"]
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()
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
class LightningOperator: rpc_interface = None __network = None def __init__(self, network): self.__network = network if self.__network == "mainnet": self.rpc_interface = LightningRpc(expanduser("~")+"/.mainnet/lightning-rpc") self.rpc_interface.connect("02f6725f9c1c40333b67faea92fd211c183050f28df32cac3f9d69685fe9665432@104.198.32.198:9735") #self.rpc_interface.connect("030fe6f75d41d7112afee0f6f0e230095d9036abf19c7f88f416cc7b9ab9e9ef3e@203.206.164.188:9735") #self.rpc_interface.connect("03abf6f44c355dec0d5aa155bdbdd6e0c8fefe318eff402de65c6eb2e1be55dc3e@52.15.79.245:9735") #self.rpc_interface.connect("03cb7983dc247f9f81a0fa2dfa3ce1c255365f7279c8dd143e086ca333df10e278@46.28.204.21:9735") #self.rpc_interface.connect("03e50492eab4107a773141bb419e107bda3de3d55652e6e1a41225f06a0bbf2d56@35.172.33.197:9735") else: self.rpc_interface = LightningRpc(expanduser("~")+"/.lightning/lightning-rpc") self.rpc_interface.connect("03236a685d30096b26692dce0cf0fa7c8528bdf61dbf5363a3ef6d5c92733a3016@50.116.3.223:9734") #self.rpc_interface.connect("03782bb858e1ec9c0a4a5ac665ed658c97ced02e723eafc3c56137ab4e2e3caebf@52.8.119.71:9736") #self.rpc_interface.connect("02ec66fb12dd5d4943d63e7a1a35d063aec015e1c8b89cee6c9f2db3faf0f6687f@3.93.159.131:19735") #self.rpc_interface.connect("0218b92d19de937ef740154d20d3a6f37c44e6381ca72b95789295073664cfdd36@5.95.80.47:9735") print(type(self.rpc_interface.getinfo())) print(self.rpc_interface.listpeers()) def isMainnet(self): return __network == "mainnet" def populate_graph(self, graph): print("Attempt RPC-call to download nodes and channels from the lightning network") nodes = [] print(self.rpc_interface) try: while len(nodes) == 0: peers = self.rpc_interface.listpeers()["peers"] if len(peers) < 1: time.sleep(2) nodes = self.rpc_interface.listnodes()["nodes"] except ValueError as e: print("Cannot download nodes from the network, are you connected to a peer?") print("RPC error: " + str(e)) return False print("Number of nodes found: {}".format(len(nodes))) for node in nodes: graph.add_node(node["nodeid"], object=node) """ Grab the channels """ if len(graph.nodes) == 0: print("Cannot download channels if nodes do not exist. ") return False try: channels = self.rpc_interface.listchannels()["channels"] print("Number of retrieved channels: {}".format(len(channels))) except ValueError as e: print("Cannot download channels from the network, are you connected to a peer?") print("RPC error: " + str(e)) return False i = 0 j = 0 for channel in channels: if (j % 1000) == 0: print(j) j += 1 if channel["source"] not in graph.nodes: print(channel["source"], "not in list") i += 1 if channel["destination"] not in graph.nodes: print(channel["destination"], "not in list") i += 1 graph.add_edge(channel["source"], channel["destination"], **channel) print("NOT IN: ", i) return True
def connect_nodes(n1: LightningRpc, n2: LightningRpc) -> None: n1_id = get_id(n1) n1_host: str = n1.getinfo()['binding'][0]['address'] n1_port: int = n1.getinfo()['binding'][0]['port'] n2.connect(peer_id=n1_id, host=n1_host, port=n1_port)
def get_id(l: LightningRpc) -> str: return l.getinfo()['id']
rpc1 = LightningRpc("/tmp/l1-regtest/regtest/lightning-rpc") rpc2 = LightningRpc("/tmp/l2-regtest/regtest/lightning-rpc") rpc3 = LightningRpc("/tmp/l3-regtest/regtest/lightning-rpc") # rpc3 adds an invoice and returns the decoded invoice try: inv = rpc3.decodepay(rpc3.invoice(10000, uuid4().hex, uuid4().hex)["bolt11"]) except RpcError: raise # get rpc2 node_id rpc2_node_id = rpc2.getinfo()["id"] # rpc1 gets a route to rpc2 # we add 10 satoshi to amount (10 hops max x 1 satoshi fee each) # we add 60 to cltv (10 hops max, CLTV of 6 each) amt_msat = inv["msatoshi"] + 10 cltv = 9 + 60 try: route = rpc1.getroute( node_id=rpc2_node_id, msatoshi=amt_msat, riskfactor=10, cltv=cltv )["route"] except RpcError: raise
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'], }
class CLightning(LnNode): def __init__(self): super().__init__() self.lnrpc = '' self.rpc_file = '/tmp/lightningrpc' def setup(self, ipaddr='127.0.0.1', port=9735, argv=None): self.ipaddr = ipaddr self.port = port if argv is not None: self.rpc_file = argv self.lnrpc = LightningRpc(self.rpc_file) def get_name(self): return 'c-lightning' ''' enum channel_state { /* In channeld, still waiting for lockin. */ CHANNELD_AWAITING_LOCKIN = 2, /* Normal operating state. */ CHANNELD_NORMAL, /* We are closing, pending HTLC resolution. */ CHANNELD_SHUTTING_DOWN, /* Exchanging signatures on closing tx. */ CLOSINGD_SIGEXCHANGE, /* Waiting for onchain event. */ CLOSINGD_COMPLETE, /* Waiting for unilateral close to hit blockchain. */ AWAITING_UNILATERAL, /* We've seen the funding spent, we're waiting for onchaind. */ FUNDING_SPEND_SEEN, /* On chain */ ONCHAIN }; ''' def get_status(self, peer): channel_sat = 0 try: result = self.lnrpc.listpeers() if ('peers' not in result) or (len(result['peers']) == 0): print('(status=none)') return LnNode.Status.NONE, 0 peer_status = '' current_ch = None for p in result['peers']: if p['id'] == peer: for ch in p['channels']: if ch['state'] != 'ONCHAIN': # onchainなものは「済」と判断して無視する current_ch = ch peer_status = ch['state'] break print('(status=', peer_status + ')') if peer_status == 'CHANNELD_NORMAL': status = LnNode.Status.NORMAL channel_sat = current_ch['msatoshi_to_us'] elif peer_status == 'CHANNELD_AWAITING_LOCKIN': status = LnNode.Status.FUNDING elif peer_status == 'CHANNELD_SHUTTING_DOWN' or\ peer_status == 'CLOSINGD_SIGEXCHANGE' or\ peer_status == 'CLOSINGD_COMPLETE' or\ peer_status == 'AWAITING_UNILATERAL' or\ peer_status == 'FUNDING_SPEND_SEEN': status = LnNode.Status.CLOSING else: status = LnNode.Status.NONE except: print('traceback.format_exc():\n%s' % traceback.format_exc()) os.kill(os.getpid(), signal.SIGKILL) return status, channel_sat def get_nodeid(self): try: info = self.lnrpc.getinfo() return info['id'] except: print('traceback.format_exc():\n%s' % traceback.format_exc()) os.kill(os.getpid(), signal.SIGKILL) # result[1] = "OK" or "NG" def connect(self, node_id, ipaddr, port): try: res = self.lnrpc.connect(node_id, ipaddr, port) print('connect=', res) res = '{"result": ["connect","OK","' + node_id + '"]}' except RpcError as e: print('traceback.format_exc():\n%s' % traceback.format_exc()) print('fail connect') res = '{"result": ["connect","NG","' + node_id + '","' + e.error[ 'message'] + '"]}' except: print('traceback.format_exc():\n%s' % traceback.format_exc()) print('fail connect') res = '{"result": ["connect","NG","' + node_id + '"]}' return res # result[1] = "OK" or "NG" def disconnect(self, node_id): print('disconnect: ' + node_id) try: res = self.lnrpc.disconnect(node_id, force=True) print('disconnect=', res) res = '{"result": ["disconnect","OK","' + node_id + '"]}' except RpcError as e: print('traceback.format_exc():\n%s' % traceback.format_exc()) print('fail disconnect') res = '{"result": ["disconnect","NG","' + node_id + '","' + e.error[ 'message'] + '"]}' except: print('traceback.format_exc():\n%s' % traceback.format_exc()) print('fail disconnect') res = '{"result": ["disconnect","NG","' + node_id + '"]}' return res # result[1] = "OK" or "NG" def open_channel(self, node_id, amount): try: res = self.lnrpc.fundchannel(node_id, amount) print('open_channel=', res) res = '{"result": ["openchannel","OK","' + node_id + '"]}' except RpcError as e: print('traceback.format_exc():\n%s' % traceback.format_exc()) print('fail openchannel') res = '{"result": ["openchannel","NG","' + node_id + '","' + e.error[ 'message'] + '"]}' except: print('traceback.format_exc():\n%s' % traceback.format_exc()) print('fail open_channel') res = '{"result": ["openchannel","NG","' + node_id + '"]}' return res # result[1] = BOLT11 or "NG" def get_invoice(self, amount_msat, label=''): try: res = self.lnrpc.invoice(amount_msat, "lbl{}".format(random.random()), "testpayment") print('invoice=', res) res = '{"result": ["invoice","' + res[ 'bolt11'] + '","' + label + '"]}' except RpcError as e: print('traceback.format_exc():\n%s' % traceback.format_exc()) print('fail invoice') res = '{"result": ["invoice","NG","' + label + '","' + e.error[ 'message'] + '"]}' except: print('traceback.format_exc():\n%s' % traceback.format_exc()) print('fail invoice') res = '{"result": ["invoice","NG","' + label + '"]}' return res # result[1] = "OK" or "NG" def pay(self, invoice): try: res = self.lnrpc.pay(invoice, riskfactor=100) print('pay=', res) res = '{"result": ["pay","OK","' + invoice + '"]}' except RpcError as e: print('traceback.format_exc():\n%s' % traceback.format_exc()) print('fail pay: ' + invoice) res = '{"result": ["pay","NG","' + invoice + '","' + e.error[ 'message'] + '"]}' except: print('traceback.format_exc():\n%s' % traceback.format_exc()) print('fail pay: ' + invoice) res = '{"result": ["pay","NG","' + invoice + '"]}' return res # result[1] = "OK" or "NG" def close_mutual(self, node_id): print('close_mutual') return self._close(node_id, False) # result[1] = "OK" or "NG" def close_force(self, node_id): print('close_force') self.disconnect(node_id) return self._close(node_id, True) # result[1] = "OK" or "NG" def _close(self, node_id, force): str_force = 'force' if force else 'mutual' try: res = self.lnrpc.close(node_id, force=force) print('close=', res) res = '{"result": ["closechannel","OK","' + node_id + '","' + str_force + '"]}' except RpcError as e: print('traceback.format_exc():\n%s' % traceback.format_exc()) print('fail closechannel') res = '{"result": ["closechannel","NG","' + node_id + '","' + e.error[ 'message'] + '"]}' except: print('traceback.format_exc():\n%s' % traceback.format_exc()) print('fail closechannel') res = '{"result": ["closechannel","NG","' + node_id + '","' + str_force + '"]}' return res