hiro99ma blog

ウォレット

目次 (最終更新日:2025/09/26)

HDウォレット

自分の Bitcoinアドレスは自分だけが知っている秘密鍵から作る。
秘密鍵は他の人が推測できないようにランダム値を使う。
Bitcoinではアドレスを使い回すのをよしとしないので、受信アドレスを作ったり、送信でお釣りを受け取るアドレスを作ったり、とにかくアドレスがたくさん必要になる。

素直に毎回ランダム値で秘密鍵を作っていくと、鍵のバックアップを毎回残さないといけないが、これは運用が面倒である。
そこで、鍵を機械的に作りつつ、しかしそれぞれの秘密鍵の関係はわからないとされる方式が考え出された。
それが HDウォレット(Hierarchical Deterministic Wallet)である。 日本語では「階層的決定性ウォレット」などとも呼ばれる。

(ここに階層の図を入れる)

Master Seed と Master Key

まず Master Seed を求める。
Master Seed は 128~512 bits の乱数を求める(推奨は 256 bit)。 できるだけちゃんとした乱数を使用すること。

Master Seed はそのまま使うのでは無く、 Key=”Bitcoin seed”、Data=seed で HMAC-SHA512 計算をした値を I とし、それを長さで半分に分割し $I_L$、$I_R$ とする(左半分と右半分)。
左半分が master secret key、右半分が master chain code でそれぞれ長さは 256 bit である。
$I_L$ が 0 と等しいか n 以上だと NG。
両方ひっくるめて Master Key m と呼び、これから階層を下りながら生成していく extended key の親玉である。

ウォレットを作る際に 12単語や24単語のメモを求められたことがあると思うが、あれは “mnemonic code(ニモニックコード)” と呼ばれる。
方式は BIP-39 に規定されていて、 ニモニックから Seed を生成する(From mnemonic to seed)。
「passphrase(パスフレーズ)」は本来 BIP-38 にあるように秘密鍵を保護するためのものだが、 ニモニックのことをパスフレーズと表現しているウォレットもしばしばある。

Extended Key

鍵導出

Master Key から階層を下りながら鍵を作っていく。

一度に数段下の階層の拡張鍵を作ることはできないので、change の階層で公開用とお釣り用の拡張鍵を作っておき、各アドレスはそれぞれ拡張鍵から派生させるのが効率よいと思われる。

HDウォレットは階層構造になっていて、最上位の m から下に降りていく。
/ はファイル構造のパス区切りと同じものと考えて良い。
各階層は実際には符号無し32bit整数で表される。
' は “hardened”(強化) を意味し、最上位ビットを立てる(h で表すこともある)。

hardened にしていない親拡張公開鍵が他の人に知られていて、ミスなどでその子の秘密鍵の 1つが漏洩してしまうと、親拡張鍵が公開鍵であっても拡張秘密鍵がわかってしまう。 Creating an HD Wallet from the Seed の図にあるように chain code は拡張秘密鍵ではなく拡張公開鍵を使い、Index number も BIP-86 などの階層で推測がつくため計算できてしまうのだ。
hardned にすると chain code の計算に拡張公開鍵を使わないようになるため影響が少ない(Hardened child key derivation)。

拡張公開鍵を他の人に伝えることなどなさそうだが、ショップサイトなどで受信アドレスだけ作ることができれば良い場合や、ウォレットに関するトランザクションの流れを追跡するサービスなど、ときどき拡張公開鍵を求められることがある。 公開する相手には気をつけよう。

Bitcoin の運用としては次の説明にあるように 5階層としているが、計算上はどの階層でも鍵導出できる。 自作するときにこの階層を間違うと他のウォレットで鍵の復元ができなくなるので注意しよう。

階層構造の運用

Bitcoin で BIP-32 の HDウォレットを使う場合は以下の階層構造を用いる(BIP44, 49, 84, 86など)。

m / purpose' / coin_type' / account' / change / address_index

m がルートで、深さを 0 と考えると address_index は深さが 5 になる。

「ウォレットを作る」としたときに 12単語や 24単語のニモニック(場合によってはパスフレーズも)を記録するが、 それだけだとこの階層では最初の m だけしか決まらない。
HDウォレットには階層があり、それぞれの階層の値も同じにしないと同じアドレスは復元できない。

主な使い方

鍵を作る場合、同じ種類(P2WPKH や P2TR)のアドレスを作ることになる。
なので m / purpose' / coin_type' / account' まで鍵導出し、 あとは公開用なのかお釣り用なのかで change を選択肢、最後に address_index を決める。
change ごとの address_index を管理して、最後に作った address_index をインクリメントしていくことになるだろう。

ウォレットに紐付く UTXO を探す場合、address_index をインクリメントさせながら調べていく。 32 bit あるので全部の空間を調べることはできない。 アドレスを作ったけど受信しなかったということもあるので、 デフォルトでは UTXO が見つからないアドレスが 20個続いた場合は探索を打ち切る(gap limit)。
あくまでデフォルトなので、gap_limit を変更可能なウォレットもある。

シリアライズ

name length note
version bytes 4  
depth 1  
fingerprint 4  
child number 4  
chain code 32  
privkey or pubkey 33 privkey は先頭に 00 を付ける

prefix と version bytes

type mainnet private mainnet public testnet private testnet public
P2PKH xprv(0x0488ade4) xpub(0x0488b21e) tprv(0x04358394) tpub(0x043587cf)
P2WPKH-nested-in-P2SH yprv(0x049d7878) ypub(0x049d7cb2) uprv(0x044a4e28) upub(0x044a5262)
P2WPKH zprv(0x04b2430c) zpub(0x04b24746) vprv(0x045f18bc) vpub(0x045f1cf6)
P2TR(single key) xprv(0x0488ade4) xpub(0x0488b21e) tprv(0x04358394) tpub(0x043587cf)

fingerprint

階層として 1つ上の extended public key を HASH160 した先頭 4バイトを fingerprint と呼ぶ。
ただし master key の場合は 00000000 を使用する。

関連ページ

writer: hiro99ma

 < Top page

コメント(Google Formへ飛びます)

GitHub

X/Twitter

Homepage

About me