Greenlightをセルフホストしてリモート署名をする⚡
GreenlightはBlockstream社が開発しているLNノードとその秘密鍵を分離して、リモート署名を可能にするサービスです。言い換えると、秘密鍵は自己管理して、LNノードをマネージドサービスとして利用することができます。LNノードと秘密鍵を分離することのメリットは以下が上げられます。
- ノードの保守管理が不要となる
- 秘密鍵を自己管理できる(ハッキングによる資金流出リスクを低減)
- ウォレット開発コストが低減する
- Lappsの連携が容易になる
既存のモバイル用LNウォレットはLNノード自体が搭載されているものや、LDKのようなライブラリを組み合わせてLNノードをモバイル向けに実装するものが主流ですが、これはウォレットのパフォーマンスが低下したり、開発コストが高くなります。これはペイメントチャネル特有の状態管理やオニオンルーティングの経路選択など様々な処理をしなければならないからです。一方、オンチェーン専用のビットコインウォレットやメタマスク等はこの状態管理は不要で、主に送金時の署名をするだけでよいので、LNウォレットと比べると開発コストはそこまで高くありません。
Greenlightでは、ノード(ペイメントチャネルの状態管理)と秘密鍵(署名)を分離することで、開発者は署名に関する箇所だけを実装すればよく、LNノード自体を気にする必要がありません。また、ユーザーとしても秘密鍵のみ管理すればよく、LNノードの保守管理は不要になります。
基本的に秘密鍵さえ管理していれば、あとはマネージドサービスを利用するだけで、LNノードでの送受信ができますが、今回はGreenlightをマネージドからセルフホストする方法について試したことを以下に記載します。
---
1.セットアップ
Greenlightのレポジトリからソースコードをクローンして、ビルドします。ビルドにはDockerが必要です。詳細はこちらのチュートリアルを参照ください。
$ git clone git@github.com:Blockstream/greenlight.git gl-testing-tutorial
$ cd gl-testing-tutorial
$ make docker-image
$ make build-self
一度、チュートリアルのサンプルにある my-first-test.pyを実行してテストします。
その際、以下の環境変数がコンソールへ表示されるので、その値を使って環境変数を設定します。
export PATH=/opt/cln/v24.02gl1/usr/local/bin/lightningd:/opt/cln/v24.02gl1/usr/local/libexec/c-lightning:$PATH
export GL_NODE_BIND=127.0.0.1:39355
export GL_PLUGIN_CLIENTCA_PATH=/tmp/gltesting/tmp/test1/node-0/certs/ca.pem
export GL_CERT_PATH=/tmp/gltesting/tmp/test1/node-0/certs/
export GL_NODE_NETWORK=regtest
export GL_NODE_ID=02058e8b6c2ad363ec59aa136429256d745164c2bdc87f98f0a68690ec2c5c9b0b
export GL_NODE_INIT=00720000000500060000001c0000008e0000009300000095000000200000002202058e8b6c2ad363ec59aa136429256d745164c2bdc87f98f0a68690ec2c5c9b0b043587cf02af562dfb0000000077e8a0b57210b61746f6ccfe7ae983f2ae86c1786846b0caa8f38e7fef3c9dd403a2551256f0b0b1545ef15c40af45a592654fb4c31b7508426e6424f67330c1bd03f7c33aec8fe6b15bd9424313cc1660418c56c36d32e45ec1ad67fcc4c0adf3df
2.Schedulerを無効化
Signerへ署名のリクエストをする際、Schedulerへの接続も必要で、Schedulerをlightningdとは別に起動しておかないとエラーになります。このSchedulerの扱い方がまだ理解できていないので、以下のソースコードからSchedulerとの通信を遮断させ、lightningdとの通信のみにできるよう変更し、ビルドしなおします。
- https://github.com/Blockstream/greenlight/blob/62eec15f25d0f77dce2aec1fb092d7144a435272/libs/gl-client/src/signer/mod.rs#L746
- https://github.com/Blockstream/greenlight/blob/62eec15f25d0f77dce2aec1fb092d7144a435272/libs/gl-client/src/signer/mod.rs#L796
※ただ、Schedulerがない場合、デバイス証明書を無くすとlightningdへのアクセスができなくなるので、この辺りの対応が今後の課題になります。
3.lightningd, Greenlightの起動(サーバー)
gl-plugin
と gl-signerproxy
をパラメータにlightningdを起動します。この際、必要な証明書の指定をしないとエラーとなるので、適宜設定します。
/opt/cln/v24.02gl1/usr/local/bin/lightningd
--subdaemon=hsmd:/tmp/gltesting/target/target/debug/gl-signerproxy
--important-plugin=/tmp/gltesting/target/target/debug/gl-plugin
--lightning-dir=/tmp/gltesting/tmp/test1/node-0
--network=regtest --log-level=debug
--bitcoin-rpcuser=rpcuser
--bitcoin-rpcpassword=rpcpass
--bitcoin-rpcconnect=127.0.0.1:18443
--disable-plugin=commando
--rescan=1
--log-timestamps=false
--cltv-final=6
--addr=127.0.0.1:33876
--dev-bitcoind-poll=5
--dev-fast-gossip
--offline
--experimental-anchors
--disable-plugin=cln-grpc
--developer
4.Greenlightの起動(クライアント)
別のコンソールから同Dockerコンテナへ入り、Greenlightのクライアントライブラリからインボイスの生成(署名)をしてみます。プログラムの実行の前にGL_SCHEDULER_GRPC_URIを設定する必要があります。
$ docker exec -it {container-id} bash
$ python signer-test.py
signer-test.pyの内容は以下のとおりです。
from glclient import TlsConfig, Scheduler, Signer, clnpb, nodepb, Credentials, Node
import datetime
path = "/tmp/work/clients/client-1/greenlight.auth"
print(Credentials.from_path(path).to_bytes())
signer = Signer(b"\x00" * 32, network="regtest", creds=Credentials.from_path(path))
print(signer.version())
signer.run_in_thread()
node_id = signer.node_id()
grpc_uri="https://localhost:39355"
node_id=[]
print(node_id)
node = Node(node_id, grpc_uri, Credentials.from_path(path))
invoice =node.invoice(
amount_msat=clnpb.AmountOrAny(any=True),
label=str(datetime.datetime.now().timestamp()),
description="testing",
)
print(invoice)
クライアント側のNode
が指定されたgrpc_uri先のlightningdへインボイス生成の要求をだし、それを受け取ったgl-plugin
がgl-signerproxy
へ要求を中継し、gl-signerproxyがクライアント側のSinger
へ署名要求を返します。その要求にSignerが署名をすることでインボイスの生成ができます。
以上がGreenlightをセルフホストしてリモート署名をする手順になります。記事内ではいくつか手順を割愛している部分もありますが、大まかなリモート署名の流れを確認できれば幸いです。
上記で無効にしたスケジューラーは、サーバー/クライアントの認証に不可欠なmTLSの生成も担っており、本来はかなり重要なコンポーネントだとは思いますが、TLS周りのソースコードが理解できなかったので、今回は割愛しています。