見出し画像

ビットコインがどのようにトランザクションを検証するか?Javascriptで遊んでみよう

前回の記事では、一緒にビットコイン・ウォレットを作りました。ウォレットを構成する、2大要素が秘密鍵と公開鍵です。公開鍵は外部に公開してもOKですが、秘密鍵は外部に公開しません。

今回は、その秘密鍵と公開鍵を使って、ビットコイン・トランザクションにどのように署名するか見ていきましょう。署名とは一言でいうと「所有権は誰にあるのか?」デジタルでサインすることです。

一点重要な点として、ブロックチェーンにはビットコイン・ウォレットの情報や、残高を記録しません。秘密鍵と公開鍵は持ちません。ブロックチェーンには、トランザクションのみ記録します。

トランザクションというのは、AさんがBさんが1BTC送ったという記録です。

全てのビットコイン・トランザクションにはインプットとアウトプットがあります。インプットは直前のトランザクション・アウトプットを参照します。つまり、全てインプット(入金)は誰かのアウトプット(出金)です。

画像1

全てのインプットには、ビットコインの所有権が電子署名されています。

問題は、他人から受けたとったアウトプットの所有権を移すには、どうしらよいか?
ビットコインのブロックチェーンは、全世界に公開されています。秘密鍵を公開せずに、どうやってトランザクションに署名するのでしょうか?

ここで楕円曲線暗号が登場します。

楕円曲線暗号は1985年に発明され、さらに2000年代初頭に、NSAによって開発が進められた、公開鍵暗号の一種です。

秘密鍵は256ビットの乱数でしたね。秘密鍵から楕円曲線暗号の計算を使って公開鍵を生成します。 楕円グラフの接点が公開鍵となります。生成には、秘密鍵の乱数と楕円曲線の乗算を使います。 楕円曲線を使う理由は、公開鍵から秘密鍵を逆算するのは不可能だからです。この秘密鍵を使ってトランザクションにデジタル署名します。

楕円曲線の公式は次のとおりです。

y² = x³ + ax + b


早速、デジタル署名の生成と検証をコーディングしてみましょう。

const RIPEMD160 = require('ripemd160');
const EC = require('elliptic').ec;
const sha256 = require('js-sha256');

const ec = new EC('secp256k1'); // トランザクションを確認するためにElliptic Curve Digital Signing Algorithm (ECDSA)を使う。
let message = 'I want to send this money'; // T
let publicKey = '040c1fbe8b870d636823963ff21ba6e5250571848714203a08ae8700e0d97a1d7bb94ae888af72ac9a4fe02d558599d27c8986398902fa9ac0c1654f5839db1af5';
let privateKey = '2ec6d2808dc7b9d11e49516cfc747435feae8e9872d63c92bc5bde68a894199f';

// メッセージをハッシュ -  SHA256 と RIPEMD160でメッセージをハッシュ
function Hash(msg) {
 let result = sha256(msg);
 return new RIPEMD160().update(msg).digest('hex');
}

function createSignature(message, privateKey) {
 const messageHash = Hash(message);
 const privateKeyPair = ec.keyFromPrivate(privateKey);
 const signature = ec.sign(messageHash, privateKeyPair); //ハッシュされたメッセージに対して秘密鍵で電子署名する。
 return signature;
}

function verifySignature(message, publicAddress, signature) {
 const messageHash = Hash(message);
 const publicKeyPair = ec.keyFromPublic(publicAddress, 'hex'); // 公開鍵で電子署名の真正を確認。
 const isVerified = publicKeyPair.verify(messageHash, signature);
 return isVerified;
}

function testVerification(publicKey, privateKey, message) {
 const signature = createSignature(message, privateKey);
 console.log('> Signature: ', signature.toDER('hex'));
 const isVerified = verifySignature(message, publicKey, signature);
 console.log('> Is verified: ', isVerified);
}

testVerification(publicKey, privateKey, message); // true

let wrongPublicKey = '04487bd002b2b61a1bbc89b3c05cebf73039d4722c96877308ee4905c10f155d71f03dca22650a2aea193416dd5071260b3fca82ab5a254163371e5929fb28c0f2';
testVerification(wrongPublicKey, privateKey, message); // false

ローカル環境で実行してみると、正しい秘密鍵と公開鍵で認証されることがわかります。

どう動いているか?

まず、3つのライブラリをインポートします。一番大切なのでは、電子署名を生成するellipticライブラリです。残りの2つはメッセージをハッシュにかけるためのです。(ripemd16とjs-sha256)

次に、楕円曲線のsecp256k1を使用します。 これは、デジタル署名の作成に使用される、特定のパラメータをセットできます。 曲線の開始点を設定し、秘密鍵のサイズの上限を追加します。

メッセージをハッシュ関数にかけます。createdSignatureで最初のハッシュにかけます。秘密鍵で、キーペアを生成してハッシュメーセージに署名します。これはビットコイン・トランザクションで、使われているものと全く同じ署名です。

verifySignutature関数では メッセージボディ、公開鍵、署名を引数にします。最後に、公開鍵でメッセージが真正かどうか確認します。 ここまでで、署名した人の、秘密鍵を公開することなく行えます。

はっきり言って、楕円曲線は難しいです。私も理解できてないです。今回はコードを追って、ビットコインのデジタル署名を、実装できて流れが理解でればOKです。


楕円曲線暗号は出てこないですが、公開鍵暗号の話が超分かりやすいです。こちらも名著中の名著。下手な小説より面白いです。


この記事が気に入ったらサポートをしてみませんか?