ブロックチェーン入門 ─ JavaScriptで学ぶブロックチェーンとBitcoinウォレットの仕組みと実装

本記事ではブロックチェーンのプログラミングを、実践とともに学びます。ブロックチェーンとは、分散環境の新しいデータ構造であり分散合意のアルゴリズムですが、Node.jsでブロックチェーンおよびBitcoinウォレットを実装し、その仕組みを理解していきましょう。

ブロックチェーン入門 ─ JavaScriptで学ぶブロックチェーンとBitcoinウォレットの仕組みと実装

フリーランスでエンジニアとライティングなどをゆるゆる行っているerukitiと申します。
個人のサークル「東京ラビットハウス」から「Modern JavaScript」「簡単JavaScript AST入門」「JavaScriptで覚える暗号通貨入門#1 Bitcoin完全に理解した」といったJavaScript関連の技術同人誌を単著で発行しています。

この記事では、ブロックチェーンの仕組みを解説し、実際にブロックチェーンやBitcoinウォレットを作ってみることをゴールとします。少しでもこれらの技術への理解を深める一助になれば幸いです。

ブロックチェーンとは何か?

ブロックチェーンとは、謎の人物Satoshi Nakamotoが生み出した暗号通貨Bitcoinのコアとなる技術で、分散環境の世界にまったく新しい考え方として導入された、データ構造および分散合意のためのアルゴリズム です。

The network timestamps transactions by hashing them into an ongoing chain of hash-based proof-of-work, forming a record that cannot be changed without redoing the proof-of-work.
出典:Bitcoin: A Peer-to-Peer Electronic Cash System [PDF]

非中央集権型ネットワーク(分散環境、参加するすべてのノードが対等な立場にある)上でP2P電子キャッシュシステムを実現する方法として、時系列に従ったトランザクションをハッシュ計算を用いたProof of Work(計算による証明)なしでは変更不可能なデータを積み重ねるという手法が、2008年11月1日に投稿されました。

1Bitcoin P2P e-cash paper – Satoshi Nakamoto

ブロックチェーンは、信頼のできないノードがつながってる分散環境でどうやってデータ更新をするのかという意味で、とても興味深い技術です。

ブロックチェーンが活用されている分野

ブロックチェーンは、情報の改ざんがされにくく堅牢性が高いことから、暗号通貨の取引だけでなく、さまざまな業界での利活用が進められています。

食品
食品の産地や流通経路を明らかにするため、ブロックチェーンを使いトレーサビリティを確保している。ジビエ肉、ワイン、有機農作物などの各分野で 取引透明化を試みる企業もある
不動産管理システム
積水ハウス株式会社と株式会社bitflyerの共同事業では、賃貸住宅の情報管理システムにブロックチェーンを活用。不動産データや入居者情報、クレーム 情報などを記録し、追跡できるようなシステムを構築している
2bitFlyerが語る近未来、次世代ブロックチェーン「miyabi」の特徴と活用は?【Blockchain for Enterprise 2018】 - INTERNET Watch
電力取引
2018年、関西電力は余剰電力のP2P取引の実証実験を開始。ブロックチェーンを使い、太陽光発電の生産者と消費者が電力を直接取り引きできるプラットフォームの形成を検討中。
3豪州パワーレッジャー社とのブロックチェーン技術を活用した電力直接取引プラットフォーム事業に係る実証研究の開始について|2018|プレスリリース|企業情報|関西電力

これらの事例で本当にブロックチェーンが適しているのか? は、まだわからない手探りの状況です。だからこそ、ブロックチェーンとは何か? 何ができて、何ができないのか? を見極めることが、重要になってきます。

データ構造から見たブロックチェーン

ブロックチェーンは、日本語ではよく「分散型台帳」などと呼ばれます。台帳=ブロックが連なった(チェーンになった)データ構造なので、ブロックチェーンです。

ブロックには、電子署名を施したトランザクションや、前のブロックのハッシュ値が含まれています。トランザクションもブロックもイミュータブル(不変)で、追記オンリーのデータ構造で成り立っているといえます。新しいブロックを発行するためには膨大なハッシュ値の演算(PoW、Proof of Work)が必要であり、そのため、古いブロックであればあるほど改ざんが難しくなります。

それでは、データ構造の側面からブロックチェーンについて見ていきましょう。

トランザクション

仮想通貨として知られているBitcoinを例に挙げて説明していきます。

Bitcoinは、Linux財団のbitcoin-mlで議論が行われ、リファレンス実装でもあるOSSソフトウェア「Bitcoin Core」が使われています。つまり、BitCoinはデータ構造や通信方法を規定したプロトコルであり、そのプロトコルに従ったソフトウェアで運用されているネットワークそのものでもあるといえます。

Bitcoinのトランザクションとは、送金情報をシリアライズして電子署名を施したもので、取引の基本単位となるものです。電子署名を施すことで、発行主、つまりBitcoinの持ち主が送金しているという証明を行います。

シリアライズとは、あるデータ・オブジェクトなどを符号化し、異なるプロセスやマシンなどの間でやりとりしやすいように、文字列かバイナリデータにすることです。文字列による汎用のシリアライズフォーマットとしては、JSONや、YAMLがあります。バイナリであれば、Protocol Buffersや、MessagePackなどが有名です。

const data = { hoge: 'ほげ', fuga: 'ふが' }
const serializedData = JSON.stringify(data)
console.log(serializedData)

プロトコルとしては、曖昧性が残ると計算結果が異なることになります。そのため、ハッシュ値をどうやって取るのか、そのハッシュ値を元に電子署名を施すのか、シリアライズのルールや、そのデータをどの順番でどうやって処理するのかが重要になります。

例えば、Bitcoinの古いトランザクションでは、電子署名の領域にいったんダミーを埋め込んでから電子署名を施していました。去年のアップデートで採用されたSegwit2xではそれらのエリアを分離(segregated witness)しています。

古いトランザクションにあった脆弱性への対応という側面も強いのですが、これによってトランザクションの作成と電子署名を分離できるようになります。分離によって処理がシンプルになり、新しい仕組みを導入できるという利点があります。

Bitcoinのトランザクションを高速化し、手数料を減らせるライトニングネットワーク(マイクロペイメント手法のひとつで、小額取引の実用化が期待できる技術)に欠かせないものです。

ブロック

ブロックは、トランザクションの集合体です。トランザクションが取引の基本単位だとすると、ブロックは記録の基本単位です。トランザクション単体では記録としては認められず、ブロックに取り込まれて初めて取引として成立するのです。

ブロックチェーンでは、前述の通り、ブロックには前のブロックのハッシュ値がポインタとして含まれています。最初のブロックには前のブロックというものが存在しないため、原初となるハッシュ値(genesis hash)が使われます。これはソースコードにハードコーディングされたもので、Bitcoinの本番ネットワークでは次の値がそれに該当します。

000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f

ブロックは、最初から最新まで全部合わせて矛盾がないように作られます。トランザクションが正しいこと、ブロックに矛盾がないこと、これらの条件を満たさないブロックはブロックとして認められず、他のノードからは弾かれてしまいます。

gitはブロックチェーンか?

gitは、一番ルートとなる最初のコミットのハッシュ値を出発点として、イミュータブルなデータが連なる構造です。データ構造として見ればブロックチェーンと類似しています。

ただし、gitとブロックチェーンでは目的の違いから、設計がかなり異なっています。

gitの目的は、ファイルの変更を履歴として残すことです。そのためgitの各ファイル(blob)は、それぞれ単独のファイルとしてgitのリポジトリ内に別々に存在し、複数ブランチを持って枝分かれすることを許容しています。

一方、暗号通貨としてのブロックチェーンでは、枝分かれしていると通貨の取引に支障が出ます。例えば、Bitcoinには「最長のチェーンを正しいチェーンと見なす」というルールがあり、瞬間的に複数のファイルが発生しても1つに収束するように設計されています。最長のチェーン以外は最終的に破棄されるのです。

この点は、forkやブランチを前提にしているgitと大きく異なるといえるでしょう。

分散合意アルゴリズムとしてのブロックチェーン

ブロックチェーンはデータ構造が特徴的ではありますが、より重要なのはアルゴリズム面です。

ブロックチェーンに使われている分散合意アルゴリズムとは、分散された複数のノードで正しくデータを更新するために合意を取るためのものです。もし合意を取らずに好き勝手にデータが更新できてしまうのであれば、それはただのでたらめなネットワークにすぎないからです。

分散合意アルゴリズムは、分散ファイルシステム、分散データベース、システムオーケストレーションツールなどさまざまな分散システムで使われています。

let counter = 0
console.log(counter) // --> 0
counter++            // データ更新
console.log(counter) // --> 1

このシンプルなJavaScriptのコードでは、counterという変数には初期値0が入っていて、counter++というコードによって、データが書き換えられ、1という値になります。このコードは、シングルスレッドで、同時に一カ所からしか参照されないからこそ成立します。

分散環境では、分散された複数台のマシンで1つの共有されたcounterを安全に更新するために、分散合意アルゴリズムなどを利用します。「安全」とは、以下のようなことを示します。

  • データが変な値にならない
  • データをロストしない
  • 更新に失敗したら、ちゃんと把握できる

データの積み重ねで更新を表現する

Bitcoinのようなブロックチェーンではcounterを更新しません。データを書き換えずに、トランザクションの積み重ねで通貨の移動を表現します。データベースやファイルシステムの世界でログやジャーナルと呼ばれる構造に近いです。

const counterLogs = []

const printCounter = () => {
  console.log(counterLogs.reduce((acc, n) => acc + n, 0))
}

printCounter()       // --> 0
counterLogs.push(1)  // データ更新
printCounter()       // --> 1

counterLogsは、counterの増減をログとして保存するための配列です。表示するときには、配列のすべての数値を合計します。

printCounterで使われているreduceメソッドでは、配列の全要素を足し合わせて、その合計値を求めています。

counterLogsに何も入っていない状態では、初期値の0になります。counterLogs.push(1)で配列に1という数値を追加すると、counterは1になります。例えば、さらにpush(2)するとcounterは3になります。

データ更新を分散環境で合意する

多くの分散合意アルゴリズムでは、データ更新を一手に引き受けるリーダーを選出し、リーダーがメンバーたちの更新要求を受け取って、データ更新を行ってからその変更情報をブロードキャストするような仕組みになっています。興味のある方は、PaxosRaftを調べるといいでしょう。特にRaftは扱いやすく、理解もしやすいでしょう。

しかし、暗号通貨のように、嘘をつくことで得をする可能性があるようなノードがいる環境では、Raftなど既存のアルゴリズムでは対応しきれません。そのため、ブロックチェーンでは、データ記録の基本単位であるブロックの発行手順が、分散合意アルゴリズムそのものになっています。

トランザクションが電子署名によって当人のものであることが証明されているとして、なぜブロックで合意を取らなければならないのでしょうか? それは個々のトランザクションが正しくても、時系列で並べたときに矛盾が生じる可能性があるからです。

例1. 10BTCを持っている人が10BTCを支払うトランザクションを発行するのは正しいでしょう(いったん手数料は考えません)。このとき10BTCを支払うトランザクションが1つだけなら問題はありませんが、複数の宛先に対して10BTCを支払うトランザクションが生じた場合、矛盾することになります。
例2. 例えば、ネットワークが分断されていて、あるネットワークではAliceがBobに10BTCを支払い、別のネットワークではAliceがCarolに10BTCを支払ったとします。これらは別々のネットワークでなら矛盾なく成立しますが、ネットワークの分断が解決されたとき、どちらのトランザクションが認められるべきでしょうか?

Bitcoinでは、次に述べるPoWを用いて、ブロックの発行について合意を取ります。

PoWという発明

Proof of Workは、Bitcoinで採用された分散合意技術です。これは確率論と、ゲーム理論的なインセンティブの考え方に裏打ちされています。

Bitcoinでは、トランザクションをとりまとめてブロックを発行するとマイニング報酬(一番最初の頃で50BTC、2018年現在は12.5BTC)を得ることができます。例えば、1BTCが60万円だとすれば、12.5BTCは7500万円です。ブロックを発行できればこのような大金が手に入るため、世界中の人がこぞってブロック発行できる権利を奪い合っています。

しかし、ブロックを発行するのは決して容易ではありません。256bitのハッシュを計算して、しかもその結果が、指定された数値(難易度)未満でないといけないという、特殊な制約があります。

これを満たすために、ダミーデータを付与してハッシュ値を調整する必要があります。SHA256では狙ったハッシュ値を算出するような方法はまだ見つかっていないため、総当たりで計算しなければなりません。

難易度は、おおよそ1週間(正確には2016ブロック)ごとに、Bitcoinネットワークにあるすべての計算力を費やして、平均的に10分で1つのハッシュ値が見つかる確率に設定されます。

  • 1つ前のブロックのハッシュ値をブロックの中に入れる
  • ブロックの中にあるすべてのトランザクションは、過去のブロックすべてのトランザクションと矛盾がないようにする
  • ほか、決められたプロトコルに従っている
  • ダミーデータを挿入してハッシュ値を調整する

これらの条件を満たせばブロックを発行できますが、これだけでは10BTCだけしか持っていないAliceが、BobとCarolの双方に10BTCを支払おうとしたときの問題(二重支払い問題)を解決できません。

正当なハッシュ値を持ち、複数のブロックがある、つまり枝分かれ状態をどう扱うかというルールが必要だからです。

そこで、ブロックチェーンの長さ(Bitcoinではheightと呼ぶ)が一番長いブロックが正しいとされるというルールがあります。

世界中のノードがこぞってこの計算を行っていますが、ブロックを1つ発行できたとしても、自分のブロックのあとにチェーンが続いてくれなければ、その計算は無意味になります。

自分の発行したブロックに別のブロックが続くことを、暗号通貨の世界では承認を得ると呼びます。ブロックに含まれるトランザクションが正しいこと、ハッシュ値として参照するブロックに不正がないことを積極的に確認し、かつ自分が参照するブロックがその時点で最長でないとおそらく計算が無駄になるため、ブロックが続けばもとのブロックが承認されていると見なせるからです。

このように、インセンティブを得たいという欲望にまみれた全世界の計算量の投入により、不正が生まれる確率を極端に減らしているのです。

AliceがBobとCarolのどちらに送金したことになるのかは、そのトランザクションが含まれたブロックに多くブロックが積み重なれば、それが覆されることはまずなくなります。

例えば、1024ブロック目でAliceがBobに10BTCを送金したトランザクションが含まれているなら、1025ブロック目くらいではまだ覆る可能性があります。ただし、1026、1027と続いていくと覆せる確率は減っていき、歴史は収束することになります。

Bitcoinの取引には時間が掛かるという問題があります。オルトコインと呼ばれるBitcoin以外の暗号通貨では難易度を下げて、もっと短いスパンでブロックが発行されるようにしていますが、それは不正ができる確率が上がることを意味していて、実際にMonacoinではそのような不正により取引所が被害を受けています。

既存の分散合意技術では、こんな莫大な計算量が必要になるようなアルゴリズムを採用しません。ですが、暗号通貨では、ビザンチン将軍問題と呼ばれる、他のノードをだまして金をかすめ取ろうとする悪意のあるノードに対応するため、このようなアルゴリズムが必要になるのです。

ブロックチェーンを作ってみよう

エンジニアHubに会員登録すると
続きをお読みいただけます(無料)。
登録のメリット
  • すべての過去記事を読める
  • 過去のウェビナー動画を
    視聴できる
  • 企業やエージェントから
    スカウトが届く