amount = Millisatoshi("0.0001btc") wait_to_route(src=n1, dest=n3, msatoshi=amount.millisatoshis) # send many payments to Charlie, which would result in unresolved HTLCs (assuming charlie is evil) make_many_payments( sender=n1, receiver=n3, num_payments=480, msatoshi_per_payment=amount.millisatoshis, ) # see the number of HTLCs that node 3 have with each peer for i, peer in enumerate(n3.listpeers()["peers"]): print(f"htlcs with peers {i}: {len(peer['channels'][0]['htlcs'])}") # Alice is not responsive. Bob can't remove HTLCs gracefully n1.stop() # node 3 closes all channels gracefully for peer in n3.listpeers()["peers"]: n3.close(peer_id=peer["id"]) mine(1) # Bob now has to publish the commitment tx of him and Alice # slowly mine blocks for _ in range(20): mine(1) time.sleep(5)
def main(): # Create RPC connection to bitcoin daemon bt_cli = bitcoin.rpc.Proxy() # Create two instances of the LightningRpc object using two different local c-lightning daemons client_node = LightningRpc("/tmp/l1-regtest/regtest/lightning-rpc") merchant_node = LightningRpc("/tmp/l2-regtest/regtest/lightning-rpc") # start Merchant plugin merchant_plugin_path = abspath("Merchant.py") response = merchant_node.plugin("start", merchant_plugin_path) for plugin in response["plugins"]: if plugin["name"] == merchant_plugin_path: if plugin["active"] == True: break else: print("Merchant plugin is not loaded") print("Merchant plugin initialized") # start Client plugin client_plugin_path = abspath("Client.py") response = client_node.plugin("start", client_plugin_path) for plugin in response["plugins"]: if plugin["name"] == client_plugin_path: if plugin["active"] == True: break else: print("Client plugin is not loaded") print("Client plugin initialized") # Connect client with merchant node # get merchant id, address and port using plugin's RPC method merchant_info = merchant_node.connectinfo() for i in merchant_info: print("Merchant's {} is {}".format(i, merchant_info[i])) client_connect_reply = client_node.link(merchant_info["node_id"], merchant_info["address"], merchant_info["port"]) if client_connect_reply["code"] == 0: print("Client node is connected to Merchant") else: print("Client node couldn't connect to Merchant") # Fund channel with Merchant, 0.1BTC = 10.000.000 satoshi client_channel_reply = client_node.createchannel(merchant_info["node_id"], "0.1btc") print("Channel was funded") print("Current blockchain height is ", bt_cli.getblockcount()) # Include funding transaction in blockchain address = bt_cli.getnewaddress() bt_cli.generatetoaddress(6, address) print("Current blockchain height is ", bt_cli.getblockcount()) # Waiting for lightningd synchronize with bitcoind route = False while not route: try: route = client_node.getroute(merchant_info["node_id"], 100, 1) except Exception as e: continue print("Route was found") # Create invoice invoice_label = "invoice#1" merchant_invoice = merchant_node.createinvoice(1000000000, invoice_label, "test payment to merchant") # Pay by invoice client_pay_reply = client_node.payinvoice(merchant_invoice["bolt11"]) # Wait for payment status to become complete client_node.waitsendpay(merchant_invoice["payment_hash"]) print("Client's payment complete") # Wait for invoice status to become paid merchant_node.waitinvoice(invoice_label) print("Merchant's invoice is paid") # Close payment channel client_node.close(client_channel_reply["channel_id"]) # Include final transaction in blockchain bt_cli.generatetoaddress(6, address)
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