ウォレットの紹介記事が一通り終わりましたので、今回はBitcoinの鍵とウォレットについての簡単な説明をします。
鍵
Bitcoinではいくつかの鍵を扱いますが、まずは秘密鍵です。
秘密鍵があれば、Bitcoinを受け取るアドレスを作ることもできますし、そのアドレスから送金することもできます。
というわけで、秘密鍵からアドレスを作るまでの過程を眺めていきましょう。
1. 秘密鍵を作る
秘密鍵は、ランダムな値です。
1~115792089237316195423570985008687907852837564279074904382605163141518161494336(10進数で72桁!)の中から1つ選ぶだけでよいです。
日本の数字単位で一番大きいのは「無量大数(wikipedia)」で1068のようなので、1158無量大数、くらいでしょうか。
2. 公開鍵を計算する
秘密鍵はランダムな値ですが、それ以降は全部計算で求めます。
まず、秘密鍵から公開鍵を計算して求めます。
Bitcoinでは「楕円曲線」という曲線を使います。
秘密鍵は単なる値ですが、それを計算して、楕円曲線上の点に置き換えます。
この点の座標が、公開鍵です。
図1 Bitcoinで使用している楕円曲線
https://en.bitcoin.it/wiki/Secp256k1
「秘密鍵から計算して公開鍵が得られるなら、逆算できるのでは?」という気もしますが、今のところそれは計算上無理ということになっています。
「いうことに」という微妙な表現になってますが、今のところ計算方法が見つかっていないという意味です。
もちろん、総当たりで秘密鍵を順番に公開鍵計算していけば見つけることはできますが、それは逆算ではないですね。。。
3. アドレスを計算する(P2PKH)
では、アドレスを求めてみましょう。
ここではP2PKHという、mainnnetであれば「1」で始まるアドレス方式です(testnetならば「m」か「n」)。
まず、公開鍵を別の形式に変換します。
計算(「HASH160」と呼んでます)を行って、20バイトの値(「PubKeyHash」と呼んでます)にします。
この計算は楕円曲線とは違う方式なのですが、これもまた逆算ができないことになっています。
この20バイトの値に、mainnet/testnetの区別や、間違っているときにチェックできる値を付け加えて、ごにょごにょと計算すると、P2PKHアドレスのできあがりです。
ここまでの流れを図にしてみました。
図2 秘密鍵からBitcoinアドレスを作る
逆方向に戻せるのは最後のところだけで、それ以外は逆算ができません。
秘密鍵があれば、アドレスを含め全部計算することができます。
公開鍵だけでも、アドレスは求めることができます。
送金
さて、このBitcoinアドレスに送金してもらったとして、使う場合にはどうするでしょうか?
Bitcoinを使うというのは、別の人に送金するということを意味します。
送金するというのは、トランザクションの持ち主であることを証明しつつ、使用する権利を別のBitcoinアドレスに移すということです。
なんとなく難しくなってしまいましたが、トランザクションに秘密鍵で署名できる=送金、なのです。
署名は、秘密鍵以外ではできません(公開鍵は、その署名が正当かどうかをチェックするのに使います)。
秘密鍵が重要だというのは、署名ができるから、といってもよいでしょう。
鍵とアドレス
Bitcoinで送金先に指定できるアドレスは、以下になります(2018/11現在)。
- P2PKH
- P2SH
- MultiSig
- P2WPKHのP2SH表現
- P2WSHのP2SH表現
- P2WPKHのnative表現
- P2WSHのnative表現
ウォレットによって指定できないアドレスもあるので、ウォレットを選ぶ際に気にするのもよいでしょう。
P2PKHやP2WPKHは1つの秘密鍵からアドレスを作るので、署名も1つです。
しかし、MultiSigは複数の秘密鍵からアドレスを作り、署名は複数必要になる場合があります。
秘密鍵が1つだと、それが漏れてしまうと署名して送金できてしまいますが、MultiSigで複数の署名が必要なアドレスにしておくと、署名が必要な秘密鍵が複数漏れないと送金できないので、安心度は高まるでしょう(同じ場所に秘密鍵を複数管理していると意味がないですが)。
P2SHやP2WSHのように、末尾に「SH」がついているアドレスの方式は、BitcoinスクリプトというBitcoin上でのプログラムを解かないと送金できません。
通常のウォレットではそういうことはできないので、専用でアプリケーションを作ることになるでしょう。
ウォレット
では、ウォレットは何かというと、秘密鍵の集まり、でよいかと思います。
あるいは、秘密鍵を管理する機能、でしょうか。
銀行の口座と違い、Bitcoinではしばしばアドレスを作ります。
例えば送金するときも、全額を相手に送るのでなければ、お釣りが発生します。
よって、お釣りの送金先として、自分のBitcoinアドレスがいります。
以前と同じBitcoinアドレスに送金してもよいのですが、セキュリティの関係などで、だいたいは別のBitcoinアドレスを新しく作って、そこに送金します。
図3 ウォレットが秘密鍵を管理する
そうやると、送金のたびにお釣りのBitcoinアドレスを新しく作ることになり、秘密鍵を管理してやらないといけません。
秘密鍵がなくなってしまうと、署名することができず、すなわちそのBitcoinを使うことができなくなるからです。
そこで、ウォレットが登場します。
Bitcoinでは物理的なものがなく、あるのはトランザクションの持ち主であることを表す秘密鍵だけです。
トランザクションデータはBlockchain上に全部残っているので、またダウンロードすれば済みます。
よって、秘密鍵だけをきっちり管理すれば、あとはなんとかなります。
HDウォレット
しかし、アドレスの分だけ秘密鍵があるので、それを管理するのも大変です。
バックアップを取るとよいのですが、最後にバックアップした地点までにしか戻ることができません。
大金を送金してもらった後、そのバックアップを取り忘れてPCが壊れてしまい、送金前の状況に戻ってしまったら、泣いてしまうのではないでしょうか。
もちろん、送金された大金はブロックチェーン上では取引が完了しているので、送った相手は再送することができません。
つまり、どうしようもありません。。。
そこで、HDウォレットというものが出てきます。
どういうものかというと、秘密鍵を生成するためのデータをウォレットで1つだけ持ち、秘密鍵はそのデータから計算して生成させるという方式になったウォレットです。
これならば、その1つのデータさえ覚えておけば、ウォレットが壊れても今まで生成した秘密鍵すべてを計算で導き出せます。
最初に1回だけバックアップして残しておけば、それ以降はバックアップ不要、ということです(バックアップを紛失してしまえばおしまいですが)。
秘密鍵の元となるデータ(生成する元になるという意味で「シード(種の意味、seed)」や「マスターキー」などと呼ぶことが多い)を用意し、それに連番を使って計算することで秘密鍵を作っていきます。
図4 シードから秘密鍵を作る
ウォレットアプリで、最初に何十個かの単語をメモに取るように言われると思いますが、あれを使ってシードを作ります。
もし秘密鍵が1つ漏れてしまったとしても、そこから他の秘密鍵を生成することはできないようになっていますが、シードが漏れてしまうとどうしようもありません。
Bitcoinには管理者がいないため、誰も助けることができません。
スイープ
ちょっと話が変わりますが、ウォレットに「スイープ」という機能が載っていることがあります。
あれは、「シード」ではなく「秘密鍵」を吸い取る方法です。
何十個かの単語ではなく、WIF形式というアドレスのような文字列です。
ペーパーウォレットなどは、このWIF形式の文字列をQRコードで表しているでしょう。
秘密鍵を吸い取るといっても、HDウォレットになっているウォレットであれば、その秘密鍵は自分のシードから生まれていない秘密鍵になります。
他のシードから生まれているかもしれませんし、単なる乱数で作った秘密鍵かもしれないので、ともかく自分のシードからはその秘密鍵を作ることができないのです。
図5 スイープすると秘密鍵がシードと結びつかない
それをスイープした場合は、アプリは以下のどちらかの振る舞いをすることになると思います。
- スイープした秘密鍵をHDウォレットで作ったアドレスに送金する
- 送金が確定するまで使用できない可能性がある
- 送金手数料が引かれる
- スイープした秘密鍵は無効になる(トランザクションが使用済みになる)
- HDウォレットの管理下になるため、ウォレット復元しても影響がない
- スイープした秘密鍵をHDウォレットとは別管理にする
- 何十個かの単語とは別に管理されるので、あの単語たちからは復元できない
- 送金はしないので、送金手数料は引かれない
- 送金しないので、即座に使うことができる(confirmationがいらない)
- トランザクションはブロックチェーンに展開されていないので、他に秘密鍵を知っている人がいればその人が送金してしまう可能性がある
どちらがよいとか悪いとかではないのですが、スイープした結果がどうなるか調べてから使うべきでしょう。
Lightning Networkのウォレット
Lightning Networkの場合は、この話とは違います。
ブロックチェーンを使わないで送金するので、取引情報はチャネルを作っているマシンの中にしかありません(チャネルを閉じるときにブロックチェーンに戻します)。
よって、取引しているデータが壊れたりすると、どうしようもありません。
通常は、チャネル接続して相手から古いと判断されたら、相手は強制的にチャネルを閉じようとするはずなので、それに任せるのが無難です。
自分で強制的に閉じようとすると、「古いデータで閉じようとしている=裏切り行為だ!」と判断されて、すべてを失う可能性があるためです。