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)
Beispiel #3
0
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