ブロックチェーンの作り方 #8(データ同期処理)

ブロックチェーン
ブロックチェーン
スポンサーリンク

前回の記事 #7 では、各ノード間のデータ同期処理を検証するための検証用ノードを構築しました。最終回となる今回 #8 では各ノード間のデータ同期処理の実装を行います。

記事一覧

ブロックチェーンの作り方 #1(概要と開発環境の準備)
ブロックチェーンの作り方 #2(トランザクションの送受信)
ブロックチェーンの作り方 #3(トランザクションの検証)
ブロックチェーンの作り方 #4(ジェネシスブロックとチェーンの実装)
ブロックチェーンの作り方 #5(マイニング処理)
ブロックチェーンの作り方 #6(各アカウントの残高チェック)
ブロックチェーンの作り方 #7(ノード用サーバーの準備)
【この記事】ブロックチェーンの作り方 #8(データ同期処理)
完成したブロックチェーンのソースコード

検証用ノードの構成

検証用ノードの構成とIPアドレスは次の通りです。各ノードに保存されている「トランザクションプール」と「ブロックチェーン」が全てのノードで常に同期するように処理を追加します。

検証用ノードの構成図

データ同期処理

データ同期処理は、トランザクションもしくはチェーンを受信して保存したノードが、そのトランザクションもしくはチェーンを全てのノードに送信(ブロードキャスト)する形で実装します。

node.py

トランザクションの受信を処理する receiv_tx メソッドと、チェーンの受信を処理する receiv_chain メソッドのそれぞれ引数に broadcast を追加して初期値を on とし、ブロードキャスト処理を追加します。(ブロードキャストの具体的な処理は BlockChain クラスに実装します)

これにより、ノードがトランザクションもしくはチェーンを受信して検証をパスしたら、そのノードはトランザクションもしくはチェーンをブロードキャストしますので、全てのノードのデータが同期されるようになります。

blockchain/node.py
(略)

# トランザクションの受信
@app.post('/tx-pool')
(↓変更↓)
def receiv_tx(tx :Tx):
    ↓
def receiv_tx(tx :Tx, broadcast :str = 'on'):
(↑変更ここまで↑)

    # データモデルインスタンスを辞書(dict)型に変換
    tx_dict = tx.model_dump()

    # トランザクションの検証
    if bc.validate_tx(tx_dict) and bc.validate_duplicate_tx(tx_dict):
        # 受信したトランザクションをトランザクションプールに追加
        bc.add_tx_pool(tx_dict)

        (↓処理の追加↓)
        # トランザクションのブロードキャスト
        if (broadcast == 'on'):
            bc.broadcast_tx(tx_dict)
        (↑処理の追加ここまで↑)

        # 成功のレスポンス
        return 'ok'
    else:
        # 失敗のレスポンス
        return 'error'

(略)

# チェーンの受信
@app.post('/chain')
(↓変更↓)
def receiv_chain(chain :Chain):
    ↓
def receiv_chain(chain :Chain, broadcast :str = 'on'):
(↑変更ここまで↑)

    # データモデルインスタンスを辞書(dict)型に変換
    chain_dict = chain.model_dump()

    # チェーンの検証
    if bc.validate_chain(chain_dict):
        # チェーンの入替え
        bc.replace_chain(chain_dict)

        (↓処理の追加↓)
        # チェーンのブロードキャスト
        if (broadcast == 'on'):
            bc.broadcast_chain(chain_dict)
        (↑処理の追加ここまで↑)

        # 成功のレスポンス
        return 'ok'
    else:
        # 失敗のレスポンス
        return 'error'

BlockChain.py

ブロードキャストの処理を並列して行うため ThreadPoolExecutor モジュールを使いますのでインポートしておいてください。

blockchain/mod/BlockChain.py
(略)
(↓モジュールを追加↓)
from concurrent.futures import ThreadPoolExecutor

class BlockChain:
    def __init__(self):
        # ノードのIPアドレス
        self.node_ips = [
            (↓変更↓)
            '127.0.0.1'
         ↓
            '192.168.56.101',
            '192.168.56.102',
            '192.168.56.103'
            (↑変更ここまで↑)          
        ]

    (略)

    (↓メソッドを追加↓)
    # トランザクションのブロードキャスト
    def broadcast_tx(self, tx):
        # マルチスレッドで処理する
        with ThreadPoolExecutor() as executor:
            for node_ip in self.node_ips:
                # ブロードキャストがループしないように broadcast パラメータを off にする
                url = 'http://' + node_ip + ':8000/tx-pool?broadcast=off'
                executor.submit(requests.post, url, json.dumps(tx))

    (↓メソッドを追加↓)
    # チェーンのブロードキャスト
    def broadcast_chain(self, chain):
        # マルチスレッドで処理する
        with ThreadPoolExecutor() as executor:
            for node_ip in self.node_ips:
                # ブロードキャストがループしないように broadcast パラメータを off にする
                url = 'http://' + node_ip + ':8000/chain?broadcast=off'
                executor.submit(requests.post, url, json.dumps(chain))  

【コードの要点】
self.node_ips リスト
ノードのIPアドレスを検証用ノードのIPアドレスに変更します。リスト形式で全てのノードのIPアドレスを設定してください。

broadcast_tx メソッド、broadcast_chain メソッド
self.node_ips リストに設定されたIPアドレス宛にブロードキャストを行います。ブロードキャストのループを防ぐため broadcast パラメータに off を指定して、ブロードキャストを受信したノードが再度ブロードキャストしないようにします。
また、自分のノードにもブロードキャストしてしまいますが、検証処理でトランザクションの重複や保持しているチェーンと同じ長さのため弾かれますので問題ありません。

動作確認

全てのノードで「トランザクションプール」と「ブロックチェーン」の内容が同期していることを確認してください。

また、各ノードのログを表示しながら、トランザクションの送信やマイニングを実行すると同期処理が実行されていることを確認できると思います。ログの出力が処理の順番と異なるため少し分かりづらいのですが、ノードbc02 が受信したトランザクションがブロードキャストされていること、ノードbc03 が受信したチェーンがブロードキャストされていることが確認できます。

bcdmin@bc01:~$ journalctl -u blockchain -f

Mar 22 01:12:54 bc01 python[1181]: INFO:     192.168.56.102:57886 - "POST /tx-pool?broadcast=off HTTP/1.1" 200 OK
Mar 22 01:13:14 bc01 python[1181]: INFO:     192.168.56.1:61474 - "GET /tx-pool HTTP/1.1" 200 OK
Mar 22 01:13:14 bc01 python[1181]: INFO:     192.168.56.103:55964 - "POST /chain?broadcast=off HTTP/1.1" 200 OK
bcdmin@bc02:~$ journalctl -u blockchain -f

Mar 22 01:12:54 bc02 python[1153]: トランザクションプールに同じトランザクションがある
Mar 22 01:12:54 bc02 python[1153]: INFO:     192.168.56.102:54000 - "POST /tx-pool?broadcast=off HTTP/1.1" 200 OK
Mar 22 01:12:54 bc02 python[1153]: INFO:     192.168.56.1:61469 - "POST /tx-pool HTTP/1.1" 200 OK
Mar 22 01:13:14 bc02 python[1153]: INFO:     192.168.56.1:61473 - "GET /chain HTTP/1.1" 200 OK
Mar 22 01:13:14 bc02 python[1153]: INFO:     192.168.56.103:37890 - "POST /chain?broadcast=off HTTP/1.1" 200 OK
bcdmin@bc03:~$ journalctl -u blockchain -f

Mar 22 01:12:54 bc03 python[1211]: INFO:     192.168.56.102:39500 - "POST /tx-pool?broadcast=off HTTP/1.1" 200 OK
Mar 22 01:13:14 bc03 python[1211]: 保持しているチェーンより短い
Mar 22 01:13:14 bc03 python[1211]: INFO:     192.168.56.103:59336 - "POST /chain?broadcast=off HTTP/1.1" 200 OK
Mar 22 01:13:14 bc03 python[1211]: INFO:     192.168.56.1:61475 - "POST /chain HTTP/1.1" 200 OK

おわりに

以上でブロックチェーンは完成です。おつかれさまでした!!

実際にブロックチェーンとして運用するには、報酬やマイニング難易度の調整などの課題がありますが、ブロックチェーンの仕組みは理解できたのではないでしょうか?

暗号資産だけでなく様々な組織や活動においてブロックチェーンが利用され、分散型の社会が実現されることを心から祈ります。

参考にさせて頂いた書籍

最後に、ブロックチェーンの作り方(全8回)の記事を書くために参考にさせて頂いた書籍を2冊ご紹介します。

マイニングやNFTを無料で本格運用できるブロックチェーンを作る!

書籍名の通りブロックチェーンを作ってみよう!と思ったきっかけがこの書籍です。ブロックチェーンの仕組みから実装までとても分かりやすく解説してくれていますので、ブロックチェーンについてほとんど知識のない方でも、この1冊があれば実際に動くブロックチェーンを作れると思います。

ブロックチェーン実践入門

ブロックチェーンの仕組みについてさらに深く学びたい方にオススメの書籍です。ブロックチェーンの技術的要素だけでなく、非中央集権型システムの考え方や課題、ゲーム理論、ナッシュ均衡、ピザンチン将軍問題など、ブロックチェーンを構成する要素を幅広く解説されています。また、代表的なパブリックブロックチェーンとなるビットコインとイーサリアムの仕組みについても詳しく解説されていますので、本格的なレイヤー1ブロックチェーンを構築する際にも参考になると思います。

コメント

タイトルとURLをコピーしました