簡単なブロックチェーンをつくる(その3 ブロックのハッシュ)

 次にブロックのハッシュを計算する.ブロックチェーンの各ブロックは,その直前のブロックのハッシュ値を保存している.ジェネシスブロックは例外で,「直前の」ブロックは存在しない.そのため直前のブロックのハッシュ値は適当に決めることになる.

 ハッシュは hashlib モジュールの sha256 関数を使うことにする.sha256 は256ビット(32バイト)のハッシュを返す関数である.ジェネシスブロックの直前のブロックのハッシュ値を,ここでは 文字列 genesis をハッシュした

> hashlib.sha256("genesis".encode()).hexdigest()
'aeebad4a796fcc2e15dc4c6061b45ed9b373f26adfc798ca7d2d8cc58182718e'

を使うことにする.encode() は文字列をバイト列に変換する関数,hexdigest() は16進形式文字列にする関数である.ちなみにビットコインジェネシスブロックにおける,対応するハッシュは hashPrevBlock=00000000000000 となっている(Bitcoin Core の src/chainparams.cpp).ジェネシスブロック以外のブロックのハッシュについては,ブロック全体のハッシュを取ることにする.

import hashlib, json

from time import time
from flask import Flask, jsonify, request

app = Flask(__name__)

# ブロックチェーン
blockchain = []
# 1ブロックのトランザクションリスト
tx_list = []
# 直前ブロックのハッシュ値
prev_block_hash = 0

# ブロック生成
@app.route("/block", methods=["GET"])
def block():
    global blockchain, tx_list, prev_block_hash
    nonce = 0

    # ジェネシスブロックの場合
    if len(blockchain) == 0:
        prev_block_hash = hashlib.sha256("genesis".encode()).hexdigest()

    block = {
        'index': len(blockchain),
        'tiemstamp': time(),
        'tx_list': tx_list,
        'nonce': nonce,
        'prev_block_hash': prev_block_hash
    }

    # 次のブロック用に現在のブロックのハッシュを計算しておく
    prev_block_hash = hashlib.sha256(json.dumps(block, sort_keys=True).encode()).hexdigest()

    # トランザクションリストをブロックに入れたのでクリア
    tx_list = []

    # ブロックチェーンにブロックを追加
    blockchain.append(block)

    return jsonify(blockchain), 200

# トランザクション生成
@app.route("/tx", methods=['POST'])
def tx():
    global tx_list
    tx = request.get_json()

    fields = ['sender', 'recipient', 'amount']
    if (not all(k in tx for k in fields)) or (tx['amount'] <= 0):
        message = 'Input Error'
        status_code = 400
    else:
        tx_list.append(tx)
        message ='Tx added'
        status_code = 201

    responce = {'message': message}
    return jsonify(responce), status_code

if __name__ == "__main__":
    app.run()

辞書の block をencodeすることはできないので,json.dumpsでjson形式にしている.現バージョンのpythonでは辞書のキーの順番は固定されているはずだが,念のため sort_keys=True により順番を明示的に固定している.これはブロックの有効性を検証するためにハッシュを再計算した際,ブロックの内容が同じであれば同じハッシュになることを保証するためである.

 ブロック生成のコマンド

> curl http://localhost:5000/block | jq

を3回行ったときの結果を示す.

[
  {
    "index": 0,
    "nonce": 0,
    "prev_block_hash": "aeebad4a796fcc2e15dc4c6061b45ed9b373f26adfc798ca7d2d8cc58182718e",
    "tiemstamp": 1624683426.8969722,
    "tx_list": []
  },
  {
    "index": 1,
    "nonce": 0,
    "prev_block_hash": "0b60c5f9bf90ebcb6093606a6b3b355cc15997d5329b34c4e1c6229be7c7474e",
    "tiemstamp": 1624683430.699087,
    "tx_list": []
  },
  {
    "index": 2,
    "nonce": 0,
    "prev_block_hash": "b5bf0f4f518efaf6db4e2842222f2314e5836ffcc500c4d7d0288264fbd58716",
    "tiemstamp": 1624683463.404587,
    "tx_list": []
  }
]