自前ノードを使って簡単なライトニングアプリを作ってみる

自前ノードを使って簡単なライトニングアプリを作ってみる

ライトニングノードを立ち上げてから「自分のノードを使ったアプリとか作ってみたいな〜」とずっと思ってたので、この機会に最近プログラミング始めてみました。

パソコン疎いので環境構築からめっちゃ苦戦してます。

というわけで色々ど素人ですが、今回は自前ノードを使った簡単なアプリを作りたいと思います。

準備

以下のものを使用します。

lnd.confの編集, TLS証明書の作成

LNDではノードを操作するためのインターフェースとしてgRPCRESTが提供されています。
LNDへの接続はTLSを使用して暗号化されるため、ノードに接続するときは、クライアントがTLS証明書を使用する必要があります。
デフォルトでは、ローカルホストからの接続のみを許可するように設定されてるので、外部からLNDノードを操作する場合は、これを許可するようにTLS証明書を変更する必要があります。
LNDではlnd.confにtlsextraipを設定することで、ローカルホスト以外のIPアドレスを介してLNDに接続できるようになります。したがって、アプリからLNDノードに接続するために、ノードのグローバルIPアドレスをtlsextraipに設定します。また、RPCインターフェースを外部に公開するように、rpclistenを以下のように設定します。

tlsextraip=ノードのIPアドレス
rpclisten=0.0.0.0:10009

tlsextraiptlsextradomainは複数追加できます。Umbrelの場合はすでに設定されているものを消さないようにしてください。
編集が完了したら古いTLS証明書を削除します。

rm ~/.lnd/tls.*
umbrelの場合は
rm ~/umbrel/lnd/tls.*

削除したらLNDを再起動してください。新しいTLS証明書が作成されます。

プロジェクトの作成

lnd.confの編集が完了したらアプリを作成していきます。まずはVue CLIを使ってプロジェクトを作成します。ターミナルで下記コマンドを実行します。

vue create lapp-sample

vue2のデフォルトのプリセットでいいのでDefault ([Vue 2] babel, eslint)を選択します。
完成したらプロジェクトのディレクトリに移動し、

cd lapp-sample

サーバーを起動します。

npm run serve

サーバーはポート8080番で起動するので、ブラウザでhttp://localhost:8080を開きます。以下のページが表示されていれば正常に起動しています。(ターミナルでCtrl+Cで停止)

サーバーサイドの作成

次にサーバーサイドを作成していきます。
まずは必要なパッケージをインストールします。

npm install express socket.io ln-service

今回はgRPCクライアントにalex bosworth氏ln-serviceを使用します。ln-serviceを使用することで、簡単にgRPCを介してノードに接続することができます。

次にプロジェクトのルートディレクトリにserverを作成し、配下のindex.jsにコードを書いていきます。

mkdir server && touch server/index.js

index.jsは以下のようにしました。createInvoiceでインボイスを作成、subscribeToInvoicesでインボイスの更新を購読、支払いがあったタイミングでインボイスをクライアントに送信します。

const express = require("express");
const app = express();
const server = require("http").createServer(app);
const { Server } = require("socket.io");
const io = new Server(server, {
  path: "/api/ws",
});
const {
  authenticatedLndGrpc,
  createInvoice,
  subscribeToInvoices,
} = require("ln-service");

const PORT = 3000;

const { lnd } = authenticatedLndGrpc({
  cert: "ここを書き換えます",
  macaroon: "ここを書き換えます",
  socket: "ここを書き換えます",
});

app.use(express.json());

app.use(express.static("dist"));

app.post("/api/invoice", async (req, res) => {
  try {
    const { tokens } = req.body;
    const { request } = await createInvoice({ lnd, tokens });
    res.send(request);
  } catch (error) {
    res.status(500).send({ msg: "Failed to create invoice" });
  }
});

const sub = subscribeToInvoices({ lnd });

sub.on("invoice_updated", (invoice) => {
  if (!invoice.is_confirmed) return;

  io.emit("invoice_settled", invoice.request);
});

server.listen(PORT, () => {
  console.log(`listening on port ${PORT}`);
});

各自設定が必要な部分はauthenticatedLndGrpccertmacaroonsocketです。この三つをLNDに合わせた値に設定することで、ノードに接続することができます。これらはあとで環境変数に設定するので、まずはローカルで動作確認するためにコードに直接書いていきます。
一つ目のcertlnd.confの編集で作り直したtls.certをbase64にエンコードしたものです。下記コマンドを実行することで取得できます。

base64 ~/.lnd/tls.cert | tr -d '\n'
umbrelの場合
base64 ~/umbrel/lnd/tls.cert | tr -d '\n'

二つ目のmacaroonはLNDのmacaroonファイルをbase64にエンコードしたものです。macaroonファイルはクライアントがLNDの操作について権限があるかどうかを確認するためのファイルで、権限別にadmin.macarooninvoice.macaroonreadonly.macaroonが存在します。権限は名前の通りです。今回はinvoiceの操作のみ行えれば良いので、invoice.macaroonを使用します。

base64 ~/.lnd/data/chain/bitcoin/mainnet/invoice.macaroon | tr -d '\n'
umbrelの場合
base64 ~/umbrel/lnd/data/chain/bitcoin/mainnet/invoice.macaroon | tr -d '\n'

三つ目のsocketにはノードのIPアドレスを設定します。環境によってはルーターでポート転送をする必要があるかもしれません。socketで設定したポートからの通信をノードのポート10009に転送するように設定してください。

以上、三つが確認出来たらauthenticatedLndGrpcの部分を書き換えてください(これらの値を第三者に教えたり、公開したりしないでください!)。
こんな感じになるとおもいます。

const { lnd } = authenticatedLndGrpc({
  cert: "LS0tLSL..........0tLS0tDg==",
  macaroon: "AgEKbC..........n4cUct",
  socket: 1.2.3.4:56789,
});

フロントエンドの作成

サーバーサイドの作成が終わったので、次はフロントエンドを作成していきます。まずは必要なパッケージをインストールします。

npm install axios socket.io-client

プロキシを設定します。プロジェクトのルートディレクトリにvue.config.jsを作成し

touch vue.config.js

以下のようにします。

module.exports = {
  devServer: {
    proxy: {
      "/api": {
        target: "http://localhost:3000",
        ws: true
      },
    },
  },
};

インストールが完了したらsrc/App.vueを編集していきます。

<template>
  <div id="app">
    <template v-if="!settled">
      <img alt="Vue logo" src="./assets/logo.png" />
      <form @submit.prevent="createInvoice">
        <input type="number" v-model="amount" />
        <button type="submit">Pay</button>
      </form>
      <p style="word-break: break-all;">{{ request }}</p>
    </template>
    <template v-else>
      <h1>Payment successful!</h1>
      <button @click.prevent="done">Done</button>
    </template>
  </div>
</template>

<script>
import axios from "axios";
import io from "socket.io-client";
const socket = io({ path: "/api/ws" });

export default {
  name: "App",
  data: () => ({
    amount: null,
    request: null,
    settled: false,
  }),
  methods: {
    async createInvoice() {
      try {
        const { data } = await axios.post("/api/invoice", {
          tokens: this.amount,
        });
        this.request = data;
      } catch (error) {
        alert(error.response.data.msg);
      }
    },
    done() {
      this.amount = null;
      this.request = null;
      this.settled = false;
    },
    ws() {
      socket.on("invoice_settled", (request) => {
        if (this.request === request) {
          this.settled = true;
        }
      });
    },
  },
  created() {
    this.ws();
  },
};
</script>

styleタグは省略していますがそのままにしてます。ロゴもかっこいいので残しました。Payment successful!の部分は支払いが完了した際に表示するメッセージです。自由に書き換えてください。

動作確認

準備が整ったので動作確認してみます。サーバーを起動します。

npm run serve
node server/index.js

サーバーが起動したらブラウザでhttp://localhost:8080にアクセスします。フォームに金額を入力しPayボタンを押してください。うまくいけばインボイスが届きます。届いたらウォレットでインボイスの支払いを行ってください。支払いが完了したタイミングで設定したメッセージが表示されれば正常に動作しています。

デプロイ

せっかくなのでherokuにデプロイしてみます。まずserver/index.jsの一部を書き換えます。

const PORT = process.env.PORT;

const { lnd } = authenticatedLndGrpc({
  macaroon: process.env.MACAROON,
  cert: process.env.CERT,
  socket: process.env.SOCKET,
});

次にpackage.jsonを編集します。

  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "start": "node ./server/index.js" この行を追加してください
  },

アプリを作成します。ターミナルで下記コマンドを実行します。

heroku create

出力されるhttps://xxx-xxx-xxx.herokuapp.comがアプリのURLです。

次に環境変数を設定します。サーバーサイドの作成で確認した三つの値をそれぞれ設定してください。

heroku config:set CERT="ここに設定します"
heroku config:set MACAROON="ここに設定します"
heroku config:set SOCKET="ここに設定します"

設定できたらデプロイします。

git add . && git commit -m "init" && git push heroku master

https://xxx-xxx-xxx.herokuapp.com/ deployed to Herokuと出力されていればデプロイ完了です。アプリのURLにアクセスしてみてください。こんな感じに動けば成功です。

お疲れ様でした!

[追記]IPアドレスの取得

グローバルIPアドレスの取得は下記コマンドを実行してください。

curl inet-ip.info
この続き : 0字 / 画像 0枚
100

会員登録 / ログインして続きを読む

関連記事

記事を書いた人

SNSにシェア

このクリエイターの人気記事

Bitcoin Core の基本操作

394

チャネルバックアップファイルを自動でクラウドに保存してみる

178

使わなくなったAndroidスマホをBTC(LN)ノードに変えてみる

145