Lappへ決済しました。
L2のデザインパターン
Bitcoinコントリビュータとしてビットコイン開発を推進してきたうちの1人であるPeter Todd氏による現状のL2概要についての記事が素晴らしかったので、簡単に紹介します。 https://petertodd.org/2024/covenant-dependent-layer-2-review --- L2のデザインパターンには大きく分けて2種類ある
Greenlightとチャネルの強制閉鎖
Greenlight, BreezSDKを使ったライトニング搭載ビットコインウォレットを開発中に、テスターから強制チャネル閉鎖になると報告がいくつかありました。そこでテスターからログをいただき、解析してみた結果、ビットコイン、ライトニング特有な原因であることがわかりました。以下はそのログの原因箇所を抜粋したものです。 19:08:57 : DEBUG 02c...70d-channeld-chan#1: Received commit_sig with 0 htlc sigs 19:08:59 : DEBUG 02c...70d-channeld-chan#1: sending_revoke_and_ack: HTLC REMOTE 15 = SENT_ADD_REVOCATION/RCVD_ADD_REVOCATION 19:08:59 : DEBUG 02c...70d-channeld-chan#1: revoke_and_ack made pending: commit timer 19:08:59 : DEBUG 02c...70d-channeld-chan#1: Sending master 1021 19:08:59 : UNUSUAL 02c...70d-chan#1: Deferring incoming commit until we sync ライトニングの送金は、送信者と受信者が互いにその取引に署名をします。まずは、送信者がコミットメントに対して署名をします。受信者はそれを受け取り、内容に問題がなければ、古いコミットメントを失効させます。この際、受信者は古いコミットメントがオンチェーンへブロードキャストされていないか確認する必要があります。そのためには、まずはビットコインの最新のブロック高まで同期して確認します。もしブロック高の同期が未完であれば、相手からのコミットメントを受け取ってもその処理を中断して、ブロック高の同期を待ちます。もし、この状態でアプリを閉じてしまうとどうなるか...送信者はコミットメントに署名をして受信者へ渡した状態で、これは送金が宙に浮いている状態です。この場合ライトニングでは、ある一定期間が経過すると、その中途半端な取引をオンチェーンへ展開して資金を回収するプロトコルになっています。これが所謂、チャネルの強制閉鎖です。 ウォレットを開き、支払いを受け取ろうとする。しかしブロック高の同期が完了していない場合、その取引は中途半端になる。その状態でアプリを閉じることで、一定期間経過後にチャネルが閉鎖されてしまう。これがテスターから報告のあったチャネル強制閉鎖の原因でした。 対応策は、ブロック同期が完了するまでインボイスの生成や受け取りができないようにすれば良いはずです。 GreenlightやBreezSDKといったライトニングウォレットの開発が楽になるツールがでてきても、ブロック高の同期など、ビットコインの基本的な処理が必須で、これが「
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 一度、チュートリアルのサ
なぜCLNの送金は不安定なのか
LNDの経路探索(pathfinding)の肝はMission Controlである。これによりLNDの送金は比較的安定しており、個人的にも満足している。Mission Controlには主に2つの探索手法がある。 Apriori estimator:これは過去の送金データを元に成功確率の高い経路を探索する。未試行なホップの成功率を60%として、これをアプリオリな値としている Bimodal estimator:大半のチャネルは流量性が偏っているという前提で、成功率はチャネルサイズに比例(大きなチャネルほど成功率が高い)する 一方、CLNの送金は不安定であり、送金時間も比較的長いという感覚がある。なぜCLNの送金は不安定なのか。CLNはチャネルサイズを元に経路を探索する(LNDでいうBimodal estimator)。それに加えて、経路のランダム化や金額を小さく分割して送金するMPPなどを活用して、プライバシー重視な経路探索をする。 CLNがLNDと比べて送金が不安定な要因は、過去の送金データを保存していなことである。過去に送金が失敗した経路を区別することができないため、再度同じ経路で送金をしてしまったり、成功した経路も記録されていないため、同じ経路で送金しようとすらしない。 このため、CLNはLNDなどと比べて送金が不安定であったり、送金時間がかかったりする。しかし、現在、CLNはChannel Hintsを使った過去データからの経路探索の実装が進んでいるので、次期リリースが待ち遠しい。 https://github.com/ElementsProject/lightning/pull/7487
OP_CAT😺も不要!?ESDSA署名の長さを署名するランポート署名の提案
ビットコインの署名方式にはESDSA署名とSchnorr署名の二種類があり、pre-Taprootでは前者を、post-Taprootでは後者の方式を使いトランザクションを署名します。Schnorr署名はソフトフォークによって新たにビットコインコンセンサスに追加された新しい署名です。これらの署名にはそれぞれの特徴があり、Schnorr署名は署名の加算やバッチ検証ができます。ただし、どちらの署名も量子耐性を持っていません。 ランポート署名は量子耐性のある署名で、これまでもビットコイン界隈ではその活用方法が議論されてきました。しかし、ランポート署名を有効にするには、新しいOPコードの追加など、ソフトフォークが必要だと考えられていました。 今月Bitcoin-Devメーリングリストに、ソフトフォークなしでもランポート署名を使えるようにする提案がされました。 ランポート署名は、まず2k個のペアのランダム値を秘密鍵、そのハッシュ値を公開鍵として生成します。署名は、kビットのメッセージに対して、そのビットに対応する秘密鍵を選択します。検証は、公開鍵、メッセージ、署名を使い、署名(実際は秘密鍵)をハッシュしてメッセージのビット列に対応する公開鍵と等しいかを検証します。 今回提案されたランポート署名は、ESDSA署名の長さをランポート署名します。ESDSA署名の長さは可変で(変化する確率は1/256とかなり小さい)、OP_SIZEでこの値を取得します。この値が可変なのでOP_IFで条件分岐させるることができ、その分岐ごとにランポート署名の検証をさせます。以下はメーリスで提案された擬似コードの一部抜粋です。 PUSH ecdsaPubkey0 OP_CHECKSIG (ecdsaSig0) // Verify lamport signature on ecdsaSig0 PUSH x00, x01 if OP_SIZE (ecdsaSig0) == 59: if OP_HASH(y00) != x00: OP_RETURN else if OP_SIZE (ecdsaSig0) < 59: if OP_HASH(y01) != x01: OP_RETURN 正当なユーザーはランポート署名に使ったペアのどちらか一方の秘密鍵を公開するだけで済みます。上記の例で、ecdsaSig0のサイズが58バイトであれば、y01の秘密鍵を公開します。 一方、量子コンピュータのある世界で攻撃者はESDSA署名を偽造できたとしても、その長さが59バイトであれば、y00を公開しなければならず、この値は未知です。そのため、攻撃者は既知のy01を使うためにESDSA署名の
OpenTimestamps:タイムスタンプサーバーの仕組み
OpenTimestampsは任意のファイルがある時点で存在していたことを証明する仕組みです。基本的な仕組みは、ファイルのダイジェストからマークル木のルートを計算して、その値をビットコインのトランザクションに書き込み、トランザクションがブロックに含まれるのを待ちます。これで、そのファイルは、トランザクションが含まれたブロックのヘッダーに書き込まれているタイムスタンプ時点で存在していたことを証明できます。証明するために必要なのは以下となります。 ファイルそのものとそのダイジェスト(ハッシュ) ルートから対象のファイルダイジェストがあるノードへのパスともう片方のハッシュ そのパスに含まれないトップノードのハッシュ 以下の図で、ファイルダイジェストがHBだとすると、2はHA、3はHCDとなります。 出展元はこちら OpenTimestampsでは各人がファイルダイジェストをサーバーへ送信し、サーバーはそれらのダイジェストからマークル木を構成し、そのルート値をビットコインのトランザクションへ書き込んでいます。しかし、ビットコインのブロックが生成されるまでに10分程度かかるため、使い勝手がよくありません。そこでカレンダーというサーバーを介在させることで、ユーザーはタイムスタンプを数秒で完了させることができます。このカレンダーサーバーはユーザーからのファイルダイジェストを預かり、ブロック承認された時点で構成したマークル木のデータを更新してくれます。ユーザーは後でカレンダーへアクセスして証明に必要なデータを取得することができます。 このタイムスタンプサービスは誰でも無料で使うことができます。ただし、ビットコインのブロックチェーンにデータを書き込んでいるので、その都度手数料が発生しています。こちらのサイトで確認できますが、だいたい1トランザクションで200円(2000sat)くらいみたいです。 参考OpenTimestamps: Scalable, Tr
サトシの受け取りはバケツで
ウォレットをバケツにたとえた説明は分かりやすい。もし受け取る水がバケツから溢れる場合、バケツを大きなものと取り換える。この際、オンチェーンへの書き込みが発生する。以前までは、バケツから溢れる場合、新たにバケツを用意していたが、スプライシングを使うことでバケツを大きなものと取り換え、常に1つのバケツを保有しているだけでよい。 ※ウォレットをバケツにたとえているが正確にはチャネル <iframe id="twitter-widget-0" scrolling="no" frameborder="0" allowtransparency="true" allowfullscreen="allowfullscreen" class="" style="position: static; visibility: visible; width: 550px; height: 417px; display: block; flex-grow: 1;" title="Twitter Tweet" src="https://platform.twitter.com/embed/Tweet.html?dnt=true&embedId=twitter-widget-0&features=eyJ0ZndfdGltZWxpbmVfbGlzdCI6eyJidWNrZXQiOltdLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X2ZvbGxvd2VyX2NvdW50X3N1bnNldCI6eyJidWNrZXQiOnRydWUsInZlcnNpb24iOm51bGx9LCJ0ZndfdHdlZXRfZWRpdF9iYWNrZW5kIjp7ImJ1Y2tldCI6Im9uIiwidmVyc2lvbiI6bnVsbH0sInRmd19yZWZzcmNfc2Vzc2lvbiI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9LCJ0ZndfZm9zbnJfc29mdF9pbnRlcnZlbnRpb25zX2VuYWJsZWQiOnsiYnVja2V0Ijoib24iLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X21peGVkX21lZGlhXzE1ODk3Ijp7ImJ1Y2tldCI6InRyZWF0bWVudCIsInZlcnNpb24iOm51bGx9LCJ0ZndfZXhwZXJpbWVudHNfY29va2llX2V4cGlyYXRpb24iOnsiYnVja2V0IjoxMjA5NjAwLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X3Nob3dfYmlyZHdhdGNoX3Bpdm90c19lbmFibGVkIjp7ImJ1Y2tldCI6Im9uIiwidmVyc2lvb
GreenlightとコミットメントTX
Greenlightは、クライアント側に秘密鍵を保有しつつLNのノード管理をクラウド上で行えるBlockstream社のサービスです。LNでの2者間の残高は、署名済みコミットメントTXとしてお互いがオフライン(ローカル)で管理しています。もし相手が音信不通になればこのコミットメントTXをブロードキャストすることで資金をオンチェーン上で回収できます。 ここで疑問となるのは、Greenlightのクラウド上ではこのコミットメントTXが管理されているので、運営側はユーザーの同意なしにコミットメントTXをブロードキャストできるのでしょうか? 答えは、できないです。コミットメントTXは署名済みではありますが、この署名済みとは、相手側の署名のみです。そのため、Greenlight上で管理されているコミットメントTXをブロードキャストするにはクライアント側で署名をする必要があります。GreenlightのFAQページにもその旨が記載されています。
Greenlightのgl-clientとバージョン互換
Greenlightのgl-clientでハマった点の備忘録。Greenlightの使い方はこちらの記事を参照してみてください。 BreezSDKでGreenlightのノード登録をすると、v23.08としてCLNのインスタンスが生成される。しかし、PyPIに公開されているgl-clientはv23.05なので、BreezSDKで登録したGreenlightのノードをgl-clientで操作しようとするとバージョンの違いによってRPCがハングする。v23.08用のgl-clientを使うにはgreenlightのレポジトリをローカルに複製してビルド、インストールする必要がある。 # 複製したGreenlightのレポジトリ配下でビルド $ git clone git@github.com:Blockstream/greenlight.git $ cd greenlight $ make build-py # ビルドされたフォルダへ移動してgl-clientをインストール $ cd ./libs/gl-client-py/dist $ pip install gl_client-0.1.9-cp37-abi3-manylinux_2_27_x86_64.whl
ビットコインがチューリング完全に対応!?任意のプログラムを実行検証するBitVMとは
BitVMはビットコイン上で任意のプログラムを実行したかを検証するプロトコル。現状のビットコインは限られたプログラムしか実行できないが、BitVMを使うことで任意のプログラムを実行できる。ただし、このプログラムが実行されるのはビットコイン上ではなく、オフチェーン上で実行される。その実行結果が正しいかの検証をビットコイン上で行う。もし実行結果が間違っている場合、Fraud Proofを使って資金を没収することができる、というのがBitVMの仕組み。 BitVMの提案書はページ数も少なく内容も難しくないのでこちらの原文を読むのがおすすめ。日本語での解説記事はこちら。 論理回路コミットメントという発明 BitVMではBit Value Commitmentという0または1を出力するためのプリイメジハッシュをそれぞれ構成する。これをOP_BITCOMMITMENTと呼ぶ。 #Bit Value Commitment OP_IF OP_HASH160 <hash1> OP_EUALVERIFY <1> OP_ELSE OP_HASH160 <hash0> OP_EUALVERIFY <0> OP_ENDIF 次に、NAND回路を構成するが、これは既存のビットコインスクリプトであるOP_BOOLAND, OP_NOTを使うことで簡単に構成できる。これをOP_NANDと呼ぶ。 #OP_NAND OP_BOOLAND OP_NOT OP_BITCOMMITMENTとOP_NANDを使ってLogic Gate Commitment(論理回路コミットメント)を構成する。これもプリイメジハッシュをセットすることで論理回路にコミットする。0または1に対応するプリイメジを公開することで論理回路の出力を制御することができる。 //Cのビット値をALTSTACKへ退避 OPBITCOMMITMENT OPTOALTSTACK //Bのビット値をALTSTACKへ退避 OPBITCOMMITMENT OPTOALTSTACK //Aのビット値をSTACKへセット OP_BITCOMMITMENT //Bのビット値をSTACKへセット OP_FRO
LoopのMusig2によるプライバシー向上と手数料削減
Loopのこちらのリリース(2023年4月)から、デフォルトのスワップにMusig2が使われることになっていました。Musig2を使うことでよりプライバシーの向上や手数料の削減になります。以下、その概要です。 ユーザーとLoopはMusig2を使い両者の鍵を集約する。Tapleafにはプリイメジハッシュをセットする。 LoopはHodlインボイスを生成し、ユーザーが支払う。 Loopは1.で作ったP2TRアドレスへオンチェーン送金する。 ユーザーはプリイメジを公開する。 Loopはそのプリイメジを使い2.のLN送金を決済させる。 ユーザーは自身とLoopの両者が署名をしてオンチェーン資金を回収する。 もしLoopが署名をしない場合、プリイメジを使ってTapleafからオンチェーン資金を回収する。 6.はトラストポイントになりますが、最悪7.でユーザーは非協調的、アトミックにスワップを実行できます。Loopとしては協調的に署名をしなくても問題ないですが、ユーザー側の手数料削減やオンチェーンフットプリントの低減に貢献することで、サービス満足度を向上できるので、ビジネス的にやらない意味はないでしょう。
Elements/Liquidでコベナンツを作ってみた
ElementsとはLiquidなどのサイドチェーンを運用するためのソフトウェアです。Bitcoinを拡張した実装となっています。 Elementsに追加された新OPコード Elementsでは以前からビットコインにはないOPコードがいくつかあります。OP_CATとOP_CHECKSIGFROMSTACKを使うことでコベナンツを実現できますが、この方法は計算コストがかかるというデメリットがあります。そこでインプットやアウトプットを直接調べるためのOPコードが追加されました(詳細はこちら)。これらのOPコードはSegwit V1(別名P2TRアドレス)として使うことができます。例えば、OP_INSPECTOUTPUTVALUEはアウトプットの金額を参照することができ、以下のようなスクリプトを構成することで送金金額を制限できます。こちらのツールを使うことで、スクリプトの実行経過を確認することができます。 //インデックス0のアウトプットの金額を取得 OP0 OPINSPECTOUTPUTVALUE OP1 OPEQUALVERIFY //1000のリトルエンディアンを指定 <0xe8
BitcoinがUTXOを採用した理由
UTXOモデルは主に"スマコン"ではない通貨用途を目的としたブロックチェーンに採用されることが多いです。スマコンの場合はアカウントベースと呼ばれる、残高およびデータが直接アカウントと結びつくデータ形式を取ることが多いです。しかし、そのアカウントベースの方がなんとなく実装しやすそうではないですか?なぜBitcoinはその直接的な実装ではなく、UTXOモデルを考案・採用したのでしょう?UTXOには様々な利点があります。堅牢で個別的な検証可能性、mem pool内の他のtxの実行に影響されてtxの再計算をする必要がない、アカウントとの所有権の紐付けがないのでプライバシーが高い。とりわけアドレスのリユースをしなければプライバシーが高くなるのは間違いないです。以前はここら辺がUTXO採用の理由かなと考えていました。しかしビットコインのオペコードの廃止と導入の仕様を思い出すと、新しい仮説を考えることができます。つまり、もう一つ考えられる仮説は、ソフトフォークによるアップデート可能性を維持するためではないかということです。ソフトフォークでは以前のバージョンで回しているノードがアップデートをしなくてもそのまま回し続けてプロトコルに従うことができ、ノードにアップデートを強制しなくてもフォークが起きません。多くのチェーンでは機能追加改良をする時にハードフォークを辞さないケースが多いですが、Bitcoinでは初期から伝統的にソフトフォークによるアップデートで後方互換性を維持しています。多くの場合でそれは条件をきつくするのがソフトフォーク、ゆるくするのがハードフォークです。この説明が少しミスリーディングになるケースが限定的ですが少々あります。なぜBitcoinに新しい署名など新しい機能を入れてもノードがチェーンを検証できるとされているのでしょうか?実際には古いバージョンのノードは新しい機能のオペコード実行を検証せずにスキップしています。つまり、特定のオペコード実行されたUTXOが検証されていません。新しい仕様のオペコード実行は、まだ使われていないオペコード番号を利用するか、入っているけど誰にも使われていないオペコードが廃止されたのちそれを使うことができたります。そして、昔のバージョンのノードはオペコードをnopとしてスキップするため、新仕様のオペコードを実行したUTXOの所有権について追う必要がないのです。これはアカウントベースでは格段に難しく、UTXOがより向いていると言えるでしょう。サトシ(か昔のcore dev)はソフトフォークを維持し、開発への権力集中を避けながら、機能追加をするためにUTXOを考案した?考えすぎでしょうか?しかし、もしそうだとしたら、異様に考え抜かれた設計です。