ジェネシスブロック以外のすべてのブロックもマイニングによって生成する.どのブロックにもトランザクションリストの先頭にコインベーストランザクションをつくる.コインの送り先はとりあえずジェネシスブロックと同様にSatoとするが,あとで複数ノードにしたときにノード所有者に置き換える.
block 関数を mine 関数に置き換え,/mine によりマイニングを行いブロックを生成する.
import hashlib, json from time import time from flask import Flask, jsonify, request app = Flask(__name__) class Blockchain(): # ハッシュ値の上4文字が0000となるノンスを見つける difficulty = 4 target = '0000' def __init__(self): # ブロックチェーン本体 self.blockchain = [] # 1ブロックのトランザクションリスト self.tx_list = [] # ジェネシスブロックの"前ブロック"のハッシュ値 self.prev_block_hash = hashlib.sha256("genesis".encode()).hexdigest() # ジェネシスブロック block = { 'index': 0, 'timestamp': time(), # コインベーストランザクション: 'Sato'に1000コインを送付 'tx_list': [{'sender': '0', 'recipent': 'Sato', 'amount': 1000}], 'nonce': 0, 'prev_block_hash': self.prev_block_hash } # ジェネシスブロックのノンスを計算 block['nonce'] = self.proof_of_work(block) # ジェネシスブロックをブロックチェーンに追加 self.blockchain.append(block) # プルーフオブワーク # ターゲットの条件を満たすハッシュ値を与えるノンスを見つける def proof_of_work(self, block): while True: hash = hashlib.sha256(json.dumps(block, sort_keys=True).encode()).hexdigest() if hash[:self.difficulty] == self.target: # 見つけたブロックのハッシュ値(次のブロックに必要) self.prev_block_hash = hash break block['nonce'] += 1 return block['nonce'] # ブロックチェーンインスタンス bc = Blockchain() # マイニングによるブロック生成 @app.route("/mine", methods=["GET"]) def mine(): # マイナー報酬のコインベーストランザクションをトランザクションリスト # の先頭に追加 # 受領者はとりあえず'Sato'にする tx = { 'sender': '0', 'recipient': 'Sato', 'amount': 1000 } bc.tx_list.insert(0, tx) # マイニング block = { 'index': len(bc.blockchain), 'tiemstamp': time(), 'tx_list': bc.tx_list, 'nonce': 0, 'prev_block_hash': bc.prev_block_hash } block['nonce'] = bc.proof_of_work(block) # 次のブロック用に現在のブロックのハッシュを計算しておく bc.prev_block_hash = hashlib.sha256(json.dumps(block, sort_keys=True).encode()).hexdigest() # トランザクションリストをブロックに入れたのでクリア bc.tx_list = [] # ブロックチェーンにブロックを追加 bc.blockchain.append(block) return jsonify(bc.blockchain), 200 # トランザクション生成 @app.route("/tx", methods=['POST']) def tx(): 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: bc.tx_list.append(tx) message ='Tx added' status_code = 201 print(bc.tx_list) responce = {'message': message} return jsonify(responce), status_code if __name__ == "__main__": app.run()
例として,まずトランザクションを1回行い,その後マイニングを行う.
> curl -X POST -H "Content-Type: application/json" -d "{\"sender\": \"Sato\", \"recipient\": \"Yamada\", \"amount\": 123}" "http://localhost:5000/tx" | jq > curl http://localhost:5000/mine | jq
結果
[ { "index": 0, "nonce": 55681, "prev_block_hash": "aeebad4a796fcc2e15dc4c6061b45ed9b373f26adfc798ca7d2d8cc58182718e", "timestamp": 1624885111.6655188, "tx_list": [ { "amount": 1000, "recipent": "Sato", "sender": "0" } ] }, { "index": 1, "nonce": 2540, "prev_block_hash": "00004d7d1c7bf13b448695f131acbacb8d171204ece78da93846b9c70f4d5fd1", "tiemstamp": 1624885122.4209077, "tx_list": [ { "amount": 1000, "recipient": "Sato", "sender": "0" }, { "amount": 123, "recipient": "Yamada", "sender": "Sato" } ] } ]