🤖

Lightning Networkで支払いから認証までやっちゃう激アツ技術LSAT実装「Aperture」について

2022/12/23に公開

はじめに

これはno plan inc.の Advent Calendar 2022の14日目の記事です。

こちら の記事に引き続き、
LSAT実装の「Aperture」について説明しようと思います!

この記事では実際にAperuteを用いて、HTTP status code 402 レスポンスの再現と、satoshi支払いによりコンテンツが見られるようになるところまで手順書こうかと思います。

LSAT とは

lsat(「ルサット」?「エルサット」? 僕は「エルサット」派)って何? もっと知りたい。という方は、
こちら の記事を読んでもらえれば、Lightning Networkの基礎含めて概要が掴めるかと思います。

要は、

有料コンテンツ(有料API,ひみつの画像、アダルトなやつ、ひみつの文章)等々の前に設置される、HTTPの認証入リバースプロキシです。

web上の有料コンテンツへアクセスしたい時に、あるLightning Network ノードが発行する請求書に対して支払いを行ったかどうか判別し、払っていればがコンテンツが見られる、そうでなければHTTPのStatus Code 402が返され、コンテンツへアクセスができません。

Apertureとは

LSATを実装しているOSS projectです。
ドキュメントに書かれてある内容が不十分なところがあるので、この記事ではハマリポイントも押さえながら説明していきます。

Prerequisite(前提条件)

Golang

バージョンは 1.13以上

btcd

ビットコインのノードを立てるのに必要

lnd

ライトニングネットワークのノードを立てるのに必要

初心者の方は下記の記事の通りにインストール・セットアップしました。

事前にこれやっとくことおすすめします。 これやってること前提に進めていきます。

https://dev.lightning.community/guides/installation/

https://dev.lightning.community/tutorial/01-lncli/index.html

etcd

分散型の一貫したキーバリューストア

ネットワーク

ローカルで試すので、simnetを使います。


※面倒なのでdocker でやってみたいという方はこのレポジトリ参考にしてください。(DYOR)

https://github.com/noplan-inc/tokyo-web3-hackathon/tree/main/docker

1. 準備

btcdを起動

ターミナル上で、

btcd
$ btcd --simnet --rpcuser=kek --rpcpass=kek
2022-12-19 06:19:36.891 [INF] BTCD: Version 0.23.1-beta
2022-12-19 06:19:36.891 [INF] BTCD: Loading block database from '/Users/soma/Library/Application Support/Btcd/data/simnet/blocks_ffldb'
2022-12-19 06:19:36.974 [INF] BTCD: Block database loaded
2022-12-19 06:19:36.978 [INF] INDX: Committed filter index is enabled
2022-12-19 06:19:36.979 [INF] CHAN: Loading block index...
2022-12-19 06:19:36.983 [INF] CHAN: Chain state (height 1293, hash 12fe9d93a59d0bb800d0de20ba01485c1f6183747aecb599b23a3b6d722e33aa, totaltx 1299, work 2588)
2022-12-19 06:19:36.990 [INF] RPCS: RPC server listening on [::1]:18556
2022-12-19 06:19:36.990 [INF] RPCS: RPC server listening on 127.0.0.1:18556
2022-12-19 06:19:36.990 [INF] AMGR: Loaded 0 addresses from file '/Users/soma/Library/Application Support/Btcd/data/simnet/peers.json'
2022-12-19 06:19:36.990 [INF] CMGR: Server listening on 0.0.0.0:18555
2022-12-19 06:19:36.990 [INF] CMGR: Server listening on [::]:18555

lnd 起動

別タブを開き、

開発用のディレクトリを作成

terminal
$ mkdir dev
$ cd dev
$ mkdir alice bob

アリスのノード

alice
$ cd $GOPATH/dev/alice
$ lnd --rpclisten=localhost:10001 --listen=localhost:10011 --restlisten=localhost:8001 --datadir=data --logdir=log --debuglevel=info --bitcoin.simnet --bitcoin.active --bitcoin.node=btcd --btcd.rpcuser=kek --btcd.rpcpass=kek 
2022-12-19 06:56:42.692 [INF] LTND: Version: 0.15.99-beta commit=tor/v1.1.0-195-g7274cf40d-dirty, build=production, logging=default, debuglevel=info
2022-12-19 06:56:42.693 [INF] LTND: Active chain: Bitcoin (network=simnet)
2022-12-19 06:56:42.694 [INF] RPCS: RPC server listening on 127.0.0.1:10001
2022-12-19 06:56:42.696 [INF] RPCS: gRPC proxy started at 127.0.0.1:8001
2022-12-19 06:56:42.696 [INF] LTND: Opening the main database, this might take a few minutes...
2022-12-19 06:56:42.696 [INF] LTND: Opening bbolt database, sync_freelist=false, auto_compact=false
2022-12-19 06:56:42.702 [INF] LTND: Creating local graph and channel state DB instances
2022-12-19 06:56:42.762 [INF] CHDB: Checking for schema update: latest_version=29, db_version=29
2022-12-19 06:56:42.762 [INF] CHDB: Checking for optional update: prune_revocation_log=false, db_version=empty
2022-12-19 06:56:42.762 [INF] LTND: Database(s) now open (time_to_open=66.812083ms)!
2022-12-19 06:56:42.763 [INF] LTND: We're not running within systemd or the service type is not 'notify'
2022-12-19 06:56:42.763 [INF] LTND: Waiting for wallet encryption password. Use `lncli create` to create a wallet, `lncli unlock` to unlock an existing wallet, or `lncli changepassword` to change the password of an existing wallet and unlock it.
alice
# 別タブにて
$ cd $GOPATH/dev/alice
# macaroonとwalletの作成
$ lncli --rpcserver=localhost:10001 --macaroonpath=data/chain/bitcoin/simnet/admin.macaroon create
$ ls -a data/chain/bitcoin/simnet/
.			..			admin.macaroon		invoice.macaroon	macaroons.db		readonly.macaroon	router.macaroon		wallet.db
# wallet の unlock
$ lncli --rpcserver=localhost:10001 --macaroonpath=data/chain/bitcoin/simnet/admin.macaroon unlock

ボブのノード

bob
$ cd $GOPATH/dev/bob
$ lnd --rpclisten=localhost:10002 --listen=localhost:10012 --restlisten=localhost:8002 --datadir=data --logdir=log --debuglevel=info --bitcoin.simnet --bitcoin.active --bitcoin.node=btcd --btcd.rpcuser=kek --btcd.rpcpass=kek
2022-12-19 06:57:51.361 [INF] LTND: Version: 0.15.99-beta commit=tor/v1.1.0-195-g7274cf40d-dirty, build=production, logging=default, debuglevel=info
2022-12-19 06:57:51.361 [INF] LTND: Active chain: Bitcoin (network=simnet)
2022-12-19 06:57:51.362 [INF] RPCS: RPC server listening on 127.0.0.1:10002
2022-12-19 06:57:51.363 [INF] RPCS: gRPC proxy started at 127.0.0.1:8002
2022-12-19 06:57:51.363 [INF] LTND: Opening the main database, this might take a few minutes...
2022-12-19 06:57:51.363 [INF] LTND: Opening bbolt database, sync_freelist=false, auto_compact=false
2022-12-19 06:57:51.368 [INF] LTND: Creating local graph and channel state DB instances
2022-12-19 06:57:51.431 [INF] CHDB: Checking for schema update: latest_version=29, db_version=29
2022-12-19 06:57:51.431 [INF] CHDB: Checking for optional update: prune_revocation_log=false, db_version=empty
2022-12-19 06:57:51.432 [INF] LTND: Database(s) now open (time_to_open=68.7485ms)!
2022-12-19 06:57:51.432 [INF] LTND: We're not running within systemd or the service type is not 'notify'
2022-12-19 06:57:51.432 [INF] LTND: Waiting for wallet encryption password. Use `lncli create` to create a wallet, `lncli unlock` to unlock an existing wallet, or `lncli changepassword` to change the password of an existing wallet and unlock it.
bob
# 別タブにて
$ cd $GOPATH/dev/bob
# macaroonとwalletの作成
$ lncli --rpcserver=localhost:10002 --macaroonpath=data/chain/bitcoin/simnet/admin.macaroon create
$ ls -a data/chain/bitcoin/simnet/
.			..			admin.macaroon		invoice.macaroon	macaroons.db		readonly.macaroon	router.macaroon		wallet.db
# wallet の unlock
$ lncli --rpcserver=localhost:10002 --macaroonpath=data/chain/bitcoin/simnet/admin.macaroon unlock

長いのでエイリアス登録しておきます

~/.zshrc
alias lncli-alice="lncli --rpcserver=localhost:10001 --macaroonpath=data/chain/bitcoin/simnet/admin.macaroon"
alias lncli-bob="lncli --rpcserver=localhost:10002 --macaroonpath=data/chain/bitcoin/simnet/admin.macaroon"

etcd

etcd
$ etcd
{"level":"info","ts":"2022-12-19T07:51:24.531+0900","caller":"etcdmain/etcd.go:73","msg":"Running: ","args":["etcd"]}
{"level":"info","ts":"2022-12-19T07:51:24.531+0900","caller":"etcdmain/etcd.go:100","msg":"failed to detect default host","error":"default host not supported on darwin_arm64"}
{"level":"warn","ts":"2022-12-19T07:51:24.531+0900","caller":"etcdmain/etcd.go:105","msg":"'data-dir' was empty; using default","data-dir":"default.etcd"}
{"level":"info","ts":"2022-12-19T07:51:24.531+0900","caller":"embed/etcd.go:124","msg":"configuring peer listeners","listen-peer-urls":["http://localhost:2380"]}
{"level":"info","ts":"2022-12-19T07:51:24.532+0900","caller":"embed/etcd.go:132","msg":"configuring client listeners","listen-client-urls":["http://localhost:2379"]}
{"level":"info","ts":"2022-12-19T07:51:24.532+0900","caller":"embed/etcd.go:306","msg":"starting an etcd server","etcd-version":"3.5.5","git-sha":"19002cfc6","go-version":"go1.16.15","go-os":"darwin","go-arch":"arm64","max-cpu-set":8,"max-cpu-available":8,"member-initialized":false,"name":"default","data-dir":"default.etcd","wal-dir":"","wal-dir-dedicated":"","member-dir":"default.etcd/member","force-new-cluster":false,"heartbeat-interval":"100ms","election-timeout":"1s","initial-election-tick-advance":true,"snapshot-count":100000,"snapshot-catchup-entries":5000,"initial-advertise-peer-urls":["http://localhost:2380"],"listen-peer-urls":["http://localhost:2380"],"advertise-client-urls":["http://localhost:2379"],"listen-client-urls":["http://localhost:2379"],"listen-metrics-urls":[],"cors":["*"],"host-whitelist":["*"],"initial-cluster":"default=http://localhost:2380","initial-cluster-state":"new","initial-cluster-token":"etcd-cluster","quota-backend-bytes":2147483648,"max-request-bytes":1572864,"max-concurrent-streams":4294967295,"pre-vote":true,"initial-corrupt-check":false,"corrupt-check-time-interval":"0s","compact-check-time-enabled":false,"compact-check-time-interval":"1m0s","auto-compaction-mode":"periodic","auto-compaction-retention":"0s","auto-compaction-interval":"0s","discovery-url":"","discovery-proxy":"","downgrade-check-interval":"5s"}
{"level":"info","ts":"2022-12-19T07:51:24.557+0900","caller":"etcdserver/backend.go:81","msg":"opened backend db","path":"default.etcd/member/snap......

ウォレットの残高補充

# wallet アドレスの確認
$ lncli-alice newaddress np2wkh
{
    "address": "rZRRSo2f9hHVTMVo2VXUxop4Z6zKWA7tp2"
}
$ lncli-bob newaddress np2wkh
{
    "address": "rXVbXGPiDuZNBtkamddQDH38Vt5h1xb7hJ"
}
btcd
# 一旦btcdノードをストップさせたのち、
# aliceへの補充
$ btcd --simnet --txindex --rpcuser=kek --rpcpass=kek --miningaddr=rZRRSo2f9hHVTMVo2VXUxop4Z6zKWA7tp2
# bobへの補充
$ btcd --simnet --txindex --rpcuser=kek --rpcpass=kek --miningaddr=rXVbXGPiDuZNBtkamddQDH38Vt5h1xb7hJ

チャネルの開設

alice
# aliceノードのアドレスを調べる
$ lncli-alice getinfo
{
    "version": "0.15.99-beta commit=tor/v1.1.0-195-g7274cf40d-dirty",
    "commit_hash": "7274cf40d02ca96a39c1521e79cd7d1277518fee",
    "identity_pubkey": "03195ae574334bd5c673372cd7a7e416c901a3bc3709c20403df0a3a5a5d3d32f5",
    "alias": "03195ae574334bd5c673",
    .....
bob
# aliceノードとP2P接続
$ lncli-bob connect 03195ae574334bd5c673372cd7a7e416c901a3bc3709c20403df0a3a5a5d3d32f5@localhost:10011
{

}

# aliceノードとチャネル開設
$ lncli-bob openchannel --node_key=03d329c0c19c78e7c5507e4c1a17e9f5b4df3f38584ff25227f0a2ab043b6ba29e --local_amt=1000000
{
	"funding_txid": "485df6d619d30a5b92e588d39ff4b9226416ff943e77f4f96224c8adbdf7a6cb"
}

チャネル開設のtxをブロックにブロードキャストするため、btcチェーンを3ブロック進める

btcd
$ btcctl --simnet --rpcuser=kek --rpcpass=kek generate 3
[
  "586d1cf7e803256bb38c229448e157837e00ab56fd31424f1df6d48d684897ed",
  "622651f8881651df54e9608e94e7b12ed0dfc123840d904cae66d9a04e41b46f",
  "6bbfcd329c91ce8347e6f0cff48fb74182baf3792f57d03de71d1ab37d7b9b37"
]

2. HTTP 402レスポンスを体験する

apertureをクローン

terminal
$ git clone git@github.com:lightninglabs/aperture.git
$ cd aperture

apertureの設定

READMEに従うと、、

aperture
$ make build && make install
go build -v github.com/lightninglabs/aperture/cmd/aperture
golang.org/x/net/html/atom
golang.org/x/text/encoding/internal/identifier
golang.org/x/crypto/salsa20
golang.org/x/text/internal/utf8internal
github.com/beorn7/perks/quantile...........

サンプルのyamlファイルを複製

aperture
$ cp sample-confg.yaml config.yaml

yamlファイルの中身は以下のように設定します。なるべくサンプルから変えないようにしました。

config.yaml
# The address which the proxy can be reached at.
listenaddr: "localhost:8081"

# The root path of static content to serve upon receiving a request the proxy
# cannot handle.
staticroot: "./static"

# Should the static file server be enabled that serves files from the directory
# specified in `staticroot`?
servestatic: false

# The log level that should be used for the proxy.
#
# Valid options include: trace, debug, info, warn, error, critical, off.
debuglevel: "debug"

# Whether the proxy should create a valid certificate through Let's Encrypt for
# the fully qualifying domain name.
autocert: false
servername: aperture.example.com

# The port on which the pprof profile will be served. If no port is provided,
# the profile will not be served.
profile: 9999

# Settings for the lnd node used to generate payment requests. All of these
# options are required.
authenticator:
  # The host:port which lnd's RPC can be reached at.
  lndhost: "127.0.0.1:10001"

  # The path to lnd's TLS certificate.
  tlspath: "~/Library/Application\ Support/Lnd/tls.cert"

  # The path to lnd's macaroon directory.
  macdir: "/Users/soma/gocode/dev/alice/data/chain/bitcoin/simnet"

  # The chain network the lnd is active on.
  network: "simnet"

# Settings for the etcd instance which the proxy will use to reliably store and
# retrieve token information.
etcd:
  # The client host:port which the etcd instance can be reached at.
  host: "localhost:2379"

  # If authentication is enabled, the user and password required to access the
  # etcd instance.
  # user: "user"
  # password: "password"

# List of services that should be reachable behind the proxy.  Requests will be
# matched to the services in order, picking the first that satisfies hostregexp
# and (if set) pathregexp. So order is important!
#
# Use single quotes for regular expressions with special characters in them to
# avoid YAML parsing errors!
services:
    # The identifying name of the service. This will also be used to identify
    # which capabilities caveat (if any) corresponds to the service.
  - name: "service1"

    # The regular expression used to match the service host.
    hostregexp: '^service1.com:3000$'

    # The regular expression used to match the path of the URL.
    pathregexp: '^/.*$'

    # The host:port which the service can be reached at.
    address: "127.0.0.1:3000"

    # The HTTP protocol that should be used to connect to the service. Valid
    # options include: http, https.
    protocol: http

    # If required, a path to the service's TLS certificate to successfully
    # establish a secure connection.
    # tlscertpath: "path-to-optional-tls-cert/tls.cert"

    # A comma-delimited list of capabilities that will be granted for tokens of
    # the service at the base tier.
    # capabilities: "add,subtract"

    # The set of constraints that are applied to tokens of the service at the
    # base tier.
    constraints:
        "valid_until": "2023-01-01"

    # The LSAT value in satoshis for the service. It is ignored if
    # dynamicprice.enabled is set to true.
    price: 10

    # Options to use for connection to the price serving gRPC server.
    # dynamicprice:
      # Whether or not a gRPC server is available to query price data from. If
      # this option is set to true then the 'price' option is ignored.
      # enabled: true

      # The address of the gRPC pricer server.
      # grpcaddress: "127.0.0.1:10010"

      # Whether or not TLS encryption should be used for communications with the
      # gRPC server.
      # insecure: false

      # The path to the pricer server's tls.cert. If the 'insecure' option is
      # set to true then this path must be set.
      # tlscertpath: "path-to-pricer-server-tls-cert/tls.cert"

  # - name: "service2"
  #   hostregexp: "service2.com:8083"
  #   pathregexp: '^/.*$'
  #   address: "123.456.789:8082"
  #   protocol: https
  #   constraints:
  #       "valid_until": "2020-01-01"
  #   price: 1

  # - name: "service3"
  #   hostregexp: "service3.com:8083"
  #   pathregexp: '^/.*$'
  #   address: "123.456.789:8082"
  #   protocol: https
  #   constraints:
  #       "valid_until": "2020-01-01"
  #   dynamicprice:
  #     enbled: true
  #     grpcaddress: 123.456.789:8083
  #     insecure: false
  #     tlscertpath: "path-to-pricer-server-tls-cert/tls.cert"

# Settings for a Tor instance to allow requests over Tor as onion services.
# Configuring Tor is optional.
# tor:
  # The host:port which Tor's control can be reached at.
  # control: "localhost:9051"

  # The internal port we should listen on for client requests over Tor. Note
  # that this port should not be exposed to the outside world, it is only
  # intended to be reached by clients through the onion service.
  # listenport: 8082

  # The port through which the onion services to be created can be reached at.
  # virtualport: 8082

  # Whether a v2 onion service should be created to handle requests.
  # v2: false

  # Whether a v3 onion service should be created to handle requests.
  # v3: false

# Enable the Lightning Node Connect hashmail server, allowing up to 1k messages
# per burst and a new message every 20 milliseconds.
# hashmail:
#   enabled: true
#   messagerate: 20ms
#   messageburstallowance: 1000

# Enable the prometheus metrics exporter so that a prometheus server can scrape
# the metrics.
# prometheus:
#   enabled: true
#   listenaddr: "localhost:9000"

上の設定をざっくり要約すると、

  • Aperture自身のサーバーはlocalhost:8081
  • 認証がないと見られないコンテンツのホストは、service1.com:3000
  • 支払いの有無を検証したり、invoice(請求書)を発行したりするlndのアドレスは、127.0.0.1:10001(aliceノード)
  • コンテンツを閲覧するには10satoshi払う必要がある

これに合わせてホスト名を追記します

terminal
$ sudo vim /etc/hosts
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1       localhost    service1.com <----これを追記
255.255.255.255 broadcasthost
::1             localhost
# Added by Docker Desktop
# To allow the same kube context to work on the host and the container:
127.0.0.1 kubernetes.docker.internal
# End of section

コンテンツ用のローカルサーバーをstatic下で立てます

aperture
$ cd static
$ python3 -m http.server 3000

apertureの起動

aperture
$ ./aperture --configfile=config.yaml

rpcserver.go -> addinvoices.go -> invoices.go

ヘッダー内にアクセスしたいコンテンツのホストを指定して、apertureにリクエストを送ると、'payment required'とLSATが返ってくる!!

terminal
$ curl -ki -H 'Host:service1.com:3000' https://localhost:8081
HTTP/2 402
accept: */*
access-control-allow-headers: Authorization, Grpc-Metadata-macaroon, WWW-Authenticate
access-control-allow-methods: GET, POST, OPTIONS
access-control-allow-origin: *
access-control-expose-headers: WWW-Authenticate
content-type: text/plain; charset=utf-8
user-agent: curl/7.79.1
www-authenticate: LSAT macaroon="AgEEbHNhdAJCAACRU66KpxK3tevMpkzdRypkgyPn2KfV92NPD99+M1JUyn+hVF59TItFv8g7O+R4mHCWoLQmi/erLUPmN5tmtQv2AAITc2VydmljZXM9c2VydmljZTE6MAACFnNlcnZpY2UxX2NhcGFiaWxpdGllcz0AAhZ2YWxpZF91bnRpbD0yMDIzLTAxLTAxAAAGIB0hCEA+mbtIaB0N9bz8B00Fpd6aALXCQpktXjeH4vlt", invoice="lnsb100n1p36ghdrpp5j9f6az48z2mmt67v5exd63e2vjpj8e7c5l2lwc60pl0huv6j2n9qdq8f3f5z4qcqzpgxqyz5vqsp5g7ueyychjnwdew48cr8ufcr2996cju0gccy0yqghc3tyh8pkuhlq9qyyssq3m2qx8cmd6rkk6ef5mv2n58ukun54qg7vuvlkggak2g04v3yvtjrxrlk3yllxwzzvd4xh56ednnqztfs0ylpnzkpjywg9cl6wffvhzcp8g8c8u"
x-content-type-options: nosniff
content-length: 17
date: Tue, 13 Dec 2022 13:37:39 GMT

payment required

これは、Apertureに対して、「service1.com:3000のコンテンツを見せてくれ」とリクエストして、「お前はお金(10 satoshi)払ってないから無理、この請求書(invoice)に支払いしてくれ」と言われているということです。

3.LNで支払いをしてみる

invoiceの支払いを行う

bob
$ lncli-bob sendpayment --pay_req=lnsb100n1p36ghdrpp5j9f6az48z2mmt67v5exd63e2vjpj8e7c5l2lwc60pl0huv6j2n9qdq8f3f5z4qcqzpgxqyz5vqsp5g7ueyychjnwdew48cr8ufcr2996cju0gccy0yqghc3tyh8pkuhlq9qyyssq3m2qx8cmd6rkk6ef5mv2n58ukun54qg7vuvlkggak2g04v3yvtjrxrlk3yllxwzzvd4xh56ednnqztfs0ylpnzkpjywg9cl6wffvhzcp8g8c8u
Payment hash: 9153ae8aa712b7b5ebcca64cdd472a648323e7d8a7d5f7634f0fdf7e335254ca
Description: LSAT
Amount (in satoshis): 10
Fee limit (in satoshis): 10
Destination: 03195ae574334bd5c673372cd7a7e416c901a3bc3709c20403df0a3a5a5d3d32f5
Confirm payment (yes/no): yes
+------------+--------------+--------------+--------------+-----+----------+------------------+-------+
| HTLC_STATE | ATTEMPT_TIME | RESOLVE_TIME | RECEIVER_AMT | FEE | TIMELOCK | CHAN_OUT         | ROUTE |
+------------+--------------+--------------+--------------+-----+----------+------------------+-------+
| SUCCEEDED  |        0.089 |        0.915 | 10           | 0   |     2158 | 2323268069556224 |       |
+------------+--------------+--------------+--------------+-----+----------+------------------+-------+
Amount + fee:   10 + 0 sat
Payment hash:   9153ae8aa712b7b5ebcca64cdd472a648323e7d8a7d5f7634f0fdf7e335254ca
Payment status: SUCCEEDED, preimage: f25b3af74803787c6ff364341829ecacf0c7e55a35738c403f08ad19d2e7576a <--この値をコピー

invoiceの支払いが成功すると、preimageというレシートのような支払いの証明書がレスポンスで返ってくる

4.コンテンツが見られるようになる

macaroonとpreimageをリクエストヘッダAuthorizationに設定する

terminal
$ curl -ki -H 'Host:service1.com:3000' -H "Authorization: LSAT AgEEbHNhdAJCAACRU66KpxK3tevMpkzdRypkgyPn2KfV92NPD99+M1JUyn+hVF59TItFv8g7O+R4mHCWoLQmi/erLUPmN5tmtQv2AAITc2VydmljZXM9c2VydmljZTE6MAACFnNlcnZpY2UxX2NhcGFiaWxpdGllcz0AAhZ2YWxpZF91bnRpbD0yMDIzLTAxLTAxAAAGIB0hCEA+mbtIaB0N9bz8B00Fpd6aALXCQpktXjeH4vlt:f25b3af74803787c6ff364341829ecacf0c7e55a35738c403f08ad19d2e7576a" https://localhost:8081
HTTP/2 200
access-control-allow-headers: Authorization, Grpc-Metadata-macaroon, WWW-Authenticate
access-control-allow-methods: GET, POST, OPTIONS
access-control-allow-origin: *
access-control-expose-headers: WWW-Authenticate
content-type: text/html
date: Thu, 22 Dec 2022 14:54:39 GMT
last-modified: Sun, 18 Dec 2022 22:08:56 GMT
server: SimpleHTTP/0.6 Python/3.10.8
content-length: 3560

<html>
<head>
    <title>LSAT proxy demo page</title>
    <style>
        .row:after {
            content: "";
            display: table;
            clear: both;
        }
	......

コンテンツが見られるようになりました。

ハマりポイント

1. apertureからのレスポンスが一向に返ってこない

以下のことができていないと、aperture内の処理が終わらずレスポンスが返ってこないことがあります。注意しましょう。

Apertureを起動する前に、必要なlndやetcdを起動しているか

apertureの起動時に設定したノードとも接続するので起動の順番は大事。btcd->lnd(2つ)->etcd->apertureの順に起動させれば問題ないはず。

リクエストを送る際は、ヘッダーに正しいホスト名を記述しているか

ヘッダー情報を記述しないと、404 Page not found が返ってきたりします。

2. walletに資金補充したはずが、FAILURE_REASON_INSUFFICIENT_BALANCEが出る

bob
$ lncli-bob sendpayment --pay_req=lnsb100n1p36ghdrpp5j9f6az48z2mmt67v5exd63e2vjpj8e7c5l2lwc60pl0huv6j2n9qdq8f3f5z4qcqzpgxqyz5vqsp5g7ueyychjnwdew48cr8ufcr2996cju0gccy0yqghc3tyh8pkuhlq9qyyssq3m2qx8cmd6rkk6ef5mv2n58ukun54qg7vuvlkggak2g04v3yvtjrxrlk3yllxwzzvd4xh56ednnqztfs0ylpnzkpjywg9cl6wffvhzcp8g8c8u
Payment hash: 9153ae8aa712b7b5ebcca64cdd472a648323e7d8a7d5f7634f0fdf7e335254ca
Description: LSAT
Amount (in satoshis): 10
Fee limit (in satoshis): 10
Destination: 03195ae574334bd5c673372cd7a7e416c901a3bc3709c20403df0a3a5a5d3d32f5
Confirm payment (yes/no): yes
+------------+--------------+--------------+--------------+-----+----------+----------+-------+
| HTLC_STATE | ATTEMPT_TIME | RESOLVE_TIME | RECEIVER_AMT | FEE | TIMELOCK | CHAN_OUT | ROUTE |
+------------+--------------+--------------+--------------+-----+----------+----------+-------+
+------------+--------------+--------------+--------------+-----+----------+----------+-------+
Amount + fee:   0 + 0 sat
Payment hash:   9153ae8aa712b7b5ebcca64cdd472a648323e7d8a7d5f7634f0fdf7e335254ca
Payment status: FAILED, reason: FAILURE_REASON_INSUFFICIENT_BALANCE
[lncli] FAILED

こうなった際に試してみることが、

  • もう一度balance足りてるか確認する
  • btcdのブロックを数ブロック分進める

3. 支払いが終わり、preimageも手に入れたが期待したレスポンスが返ってこない

原因として考えられるのは、

ヘッダーのAuthenticationに正しく情報が記述されていない

Header内のAuthenticationに支払い情報を記述したい場合は、
Authentication: LSAT <402レスポンスが返ってきた時についてきたmacaroon>:<支払い成功時レスポンスされてきたpreimage>
のように、

  • LSAT という文字列
  • 正しいmacaroon
  • 正しいpreimage
    が正しい形で書かれている必要があります。ご注意を。

Apertureの課題

  • 準備に必要な工程が多く面倒(btcd,lnd,etcd,チャネル開設etc)
  • エコシステムがまだまだ未熟
  • そもそもBTCは手放したくない

おわりに

LSATという認証と支払いの管理を行なってくれるプロトコルをすでに実装に組み込める形にしてある、Apertureは、今後必ずマイクロペイメントとwebコンテンツが絡んだアプリケーションでは使われたり、応用した技術が出てくると思います。

今回は、かなりオーソドックスな設定でHelloworldしただけですが、もっと工夫していけば、

  • 1ビュー単位で料金を徴収することができるブログ
  • プラン変更の際に手動作業が不要でリクエストごとに請求額を変えられるAPI
  • 3分の動画のうち、指定した1分の部分のみ購入して視聴できるような動画
    等を提供できるサービスが作れるのではと思いました。

lightning networkのエコシステムには既存のウェブサービスやペイメント体験を変えるような面白いプロトコルがまだまだあるので、今後も引き続き追っていきたいと思います。

引き続きBitcoin周りの記事を見たい方はいいねとTwitterのフォロー、記事の拡散をお願いします!

no plan株式会社について

外部リンク・参考文献

Discussion