ブロックチェーンをRubyで実装してみた

1. いきさつ

ブロックチェーンの簡単バージョンをRubyで実装してみました。

“Learn Blockchains by Building One”という記事を読み、 Pythonで200行以下の実装するブロックチェーンという事だったので、「よし、Rubyで書き直して見るか」と思い立ちました。

エンジニアにとっては、長い説明を読むよりも実際のコードを見る方が分かりやすい事が多くありますよね。 この元ネタはPythonだったので、Rubyで実装してみることにしました。

2. ブロックチェーンをRubyで実装

Pythonで書かれていたものをRubyで一つずつ実装していきます。

完成版はこちら -> https://github.com/takp/blockchain-ruby

1ファイルに入っているので長く見えますが、全体で200行以下のシンプルなものです。

3. 動かしてみる

さて、動かしてみましょう。

3.1 動かすための準備

git cloneしてローカルにダウンロードした後は、bundle installで必要なgemをインストールすれば大丈夫です。

$ git clone git@github.com:takp/blockchain-ruby.git
$ cd blockchain-ruby/
$ bundle install
$ bundle exec ruby blockchain.rb
[2018-01-09 21:49:25] INFO  WEBrick 1.4.2
[2018-01-09 21:49:25] INFO  ruby 2.5.0 (2017-12-25) [x86_64-darwin16]
== Sinatra (v2.0.0) has taken the stage on 4567 for development with backup from WEBrick
[2018-01-09 21:49:25] INFO  WEBrick::HTTPServer#start: pid=12067 port=4567

これで、Sinatraが起動して、リクエストを受け付ける状態になりました。

3.2 blockChainの状態を確認してみる

GET /chainする事で、blockchainの状態を確認できます。 まだ何もされてない状態なので、最初のジェネシスブロックのみが表示されています。

$ curl http://localhost:4567/chain

{
  "chain":[
    {
      "index":1,
      "timestamp":"2018-01-09 21:59:51 +0700",
      "transactions":[],
      "proof":1,
      "previous_hash":100
    }
  ],
  "length":1
}

3.3 Mining(マイニング)してみる

GET /mineによってマイニングが実行されます。 レスポンスを見ると、ハッシュ値が追加されているのが確認できます。

$ curl http://localhost:4567/mine

{
  "message":"New Block Forged",
  "index":2,
  "transactions":[
    {
      "sender":"0",
      "recipient":"caedf4a87f7841838061dd7dffca2916",
      "amount":1
    }
  ],
  "proof":94813,
  "previous_hash":"84e609b88e68764ac4546cb807d7cf0e"
}

3.4 新規トランザクションの追加

POST /transactions/new によって、新しいトランザクションが追加されます。 -dオプションで送付されているのがトランザクションの内容ですね。

$ curl -X POST -H "Content-Type: application/json" -d '{
 "sender": "d4ee26eee15148ee92c6cd394edd974e",
 "recipient": "someone-other-address",
 "amount": 5
}' "http://localhost:4567/transactions/new"

{
  "message":"Transaction will be added to Block 3"
}

ここでもう1度マイニングを行って、新しいトランザクションをブロックチェーンに追加しましょう。

$ curl http://localhost:4567/mine
{
  "message":"New Block Forged",
  "index":3,
  "transactions":[
    {
      "sender":"d4ee26eee15148ee92c6cd394edd974e",
      "recipient":"someone-other-address",
      "amount":5
    },
    {
      "sender":"0",
      "recipient":"caedf4a87f7841838061dd7dffca2916",
      "amount":1
    }
  ],
  "proof":183390,
  "previous_hash":"91d2d1296158c7faf58a37ddc152593d"
}

3.5 ブロックチェーンの確認

レスポンスがちょっと長いですが、ブロックチェーンの中身をもう1度確認してみましょう。 ブロックが1番から3番までつながっているのが分かると思います。

$ curl http://localhost:4567/chain
{
  "chain":[
    {
      "index":1,
      "timestamp":"2018-01-09 22:04:06 +0700",
      "transactions":[],
      "proof":1,
      "previous_hash":100
    },
    {
      "index":2,
      "timestamp":"2018-01-09 22:04:11 +0700",
      "transactions":[
        {
          "sender":"0",
          "recipient":"caedf4a87f7841838061dd7dffca2916",
          "amount":1
        }
      ],
      "proof":94813,
      "previous_hash":"84e609b88e68764ac4546cb807d7cf0e"
    },
    {
      "index":3,
      "timestamp":"2018-01-09 22:06:40 +0700",
      "transactions":[
        {
          "sender":"d4ee26eee15148ee92c6cd394edd974e",
          "recipient":"someone-other-address",
          "amount":5
        },
        {
          "sender":"0",
          "recipient":"caedf4a87f7841838061dd7dffca2916",
          "amount":1
        }
      ],
      "proof":183390,
      "previous_hash":"91d2d1296158c7faf58a37ddc152593d"
    }
  ],
  "length":3
}

4. 感想

シンプルなものをコードで書くととても分かりやすいですね。 トランザクションを追加して、ハッシュ値を計算する事でそのブロックを確定しているのがよく分かります。

本当はこの後に、コンセンサスの実装もあるのですが、そこは今回は飛ばしてしまいました。 また時間がある時に実装を追加したいなぁと思います。

リポジトリ: https://github.com/takp/blockchain-ruby

@takp