暗号通貨のウォレットとgap limitについて


記事の内容とは関係のないちょめじさんの弁当と放水口の現代アート写真

BTCPayServerを使っていて、送金したはずなのにモナコインがウォレットに着金しないということがありました。

そこで、今回はウォレットの仕組みについて調べたので、記事にまとめます。
便宜上、この記事では仮想通貨・暗号通貨のことをモナコインを呼称しています。

さて、様々な種類のウォレットがありますが、どんなウォレットでも、受取用モナコインアドレスが表示されると思います。
このアドレスにモナコインを送ると、ウィレット上には送られた分だけのモナコインが表示されるのですが、「ウォレットで、モナコインを保管している」と思ったら、それは精確ではありません。

実際は、ウォレットとは、秘密鍵を保管し、その秘密鍵から受取用モナコインアドレスを生成し、そのモナコインアドレスにどれほどのモナコインが入っているかをブロックチェーンを参照して表示するソフトウェアです。
秘密鍵 → 公開鍵 → モナコインアドレス

非決定性ウォレット

かつては、秘密鍵一つに一つのモナコインアドレスでした。
このような仕組みのウォレットを非決定性ウォレットと言います。

今でも、このような仕組みをイメージしている人が多いのではないでしょうか。
しかし、一つのアドレスを使い回すと、いつどのような取引をしたのかがバレバレなので、プライバシーの観点から、これは避けるべきという考え方があります。
では、取引ごとに秘密鍵をたくさん作って、管理をすればいいのだということになりますが、秘密鍵が増えていくと、管理が大変です。
秘密鍵はランダムに生成されるため、同じウォレットを再現するためには、同じ秘密鍵を取り込む以外に方法はありません。

そこで新たな決定性ウォレットという仕組みが考えられました。

決定性ウォレット

これは、秘密鍵の上位の概念として、シード(Seed)を用いて管理するウォレットです。
シードからは秘密鍵を生成することができますが、生成のルールが決まっているため、シードを覚えておけば、いつでも、どんな環境でも複数の同じコインアドレスを生成することができます。
シード → マスター鍵 → 秘密鍵1 → 公開鍵1 → コインアドレス1
シード → マスター鍵 → 秘密鍵2 → 公開鍵2 → コインアドレス2
シード → マスター鍵 → 秘密鍵3 → 公開鍵3 → コインアドレス3
*説明のしやすさのために一部を端折っていますので、精確な説明ではありません。

なお、シードは覚えるのが大変なので、Mnemonic Codeという人の認識しやすい単語の組み合わせをウォレットの初期起動時にキーフレーズとしてメモするように出ることが多いです。

シードを盗まれると、そのウォレットの生成する秘密鍵全てを盗まれることを意味します。

これをさらに発展させたものが現在は使われています。

階層的決定性ウォレット

階層的決定性ウォレットは、HDウォレットとも呼ばれています。
BIP32で定義され、階層を決定するパスフレーズを m/ account / change / address_index というような形式で指定します。
この仕様で階層を指定しておくことで、他のウォレットへのインポートも可能になります。
m...マスター鍵
account...アカウント識別
change...受取種別識別(0:受取用 or 1:おつり用)
address_index...k番目の鍵
つまり、mのマスター鍵で作成したとあるアカウントの受取用のk番目に生成した鍵 という意味です。
詳しくはbips/bip-0032.mediawikiをご覧ください。

BIP44では、BIP32のアルゴリズムに基づいて、改良(?)した規格が提案されています。
m / purpose' / coin_type' / account' / change / address_index とこのような階層を構成します。
m...マスター鍵
purpose'...44で固定
coin_type'...通貨識別番号 BITCOINは1で、MONAは22、KOTOは510 その他はこちら
account'...アカウントの識別
change’...受取種別識別(0:受取用 or 1:おつり用)
ddress_index...k番目の鍵
詳しくはbips/bip-0044.mediawikiをご覧ください。

このような階層の仕組みを使うことによって、一つのシードで複数のコインを扱え、また複数のユーザアカウントを作成できるようになります。

gap limit

さて、ようやくgap limitの話ができます。
そもそもこの記事は、gap limitを理解するために書き始めました。
BTCPayServerを使っていて入金が表示されなくなることがあったのですが、原因はgap limitでした。

BTCPayServerとは?という方は、下記をご覧ください。
BTCPAYSERVER(MONACOIN)体験用サンプルページ  
BTCPAYSERVER(MONACOIN)の初期設定から請求まで

BTCPayServerを使ってInvoiceをたくさん生成ということは、アドレスを次々に増やしていくということです。
HDウォレットはgap limitというアドレスを認識する範囲が設定されており、アドレスを確認していった結果、範囲数のアドレスで取引が確認できなかった場合には、その範囲から先は確認しても取引がないものと看做し、その先に実際に取引があったとしても認識しません。
端的にいうと、Invoiceをgap limitである20を超えて作成し、その後21個目のInvoiceで生成したアドレスに入金をしても、あなたのHDウォレットでは入金が表示されません。

なぜなら、gap limitが20であるため、ウォレットが1個目に生成したアドレスから20個目に生成したアドレスまでしか取引の確認をしていないからです。

解決方法は、2個目から20個目のInvoiceで作成したモナコインアドレスにモナコインを送金することです。
認識している範囲内に取引があると、そのアドレスからさらに20個の取引のないアドレスが現れるまで、HDウォレットはアドレスを確認をしてくれます。
  1. derive the first account's node (index = 0)
  2. derive the external chain node of this account
  3. scan addresses of the external chain; respect the gap limit described below
  4. if no transactions are found on the external chain, stop discovery
  5. if there are some transactions, increase the account index and go to step 1
中略

Address gap limit is currently set to 20. If the software hits 20 unused addresses in a row, it expects there are no used addresses beyond this point and stops searching the address chain. We scan just the external chains, because internal chains receive only coins that come from the associated external chains.
Wallet software should warn when the user is trying to exceed the gap limit on an external chain by generating a new address.
出典 bips/bip-0044.mediawiki

Gaplimitが煩わしいという方は、2000に増やすという方法がこちらで紹介されています。
Gaplimitを増やすと、メモリに負荷がかかるとのことがありますが、どの程度の影響があるかは不明です。
Powered by Blogger.