ブロックチェーンをpythonで1から書いてみた

ブロックチェーンをpythonで1から書いてみた

最近ふと、ブロックチェーンへの興味がより一層深まってきて、自分でコードを書きたくなりました。

ブロックチェーンは何かを、文章としての説明では理解していたつもりでしたが、やっぱり論理的にコードで理解するのが一番だろうと思いまして、今回助けを借りつつ書いてみました。

ブロックチェーンは分かったつもりになりやすいものだと思うので、概念的ではなくロジックとして理解していこうと思います。

ブロックチェーンはその名の通り、取引データが入った「ブロック」がチェーンのように繋がっていきます。

今日は久しぶりにpythonに触れたので、クラスやインスタンスの部分の理解が本当に曖昧で、すごい苦労しました。

完成品コード

以下がコードです。

import hashlib

class Block:
    def __init__(self, data, previous_hash):
        self.data = data
        self.previous_hash = previous_hash
        self.value = self.calculate_hash()

    def calculate_hash(self):
        content = self.data + self.previous_hash
        block_hash = hashlib.sha256(content.encode()).hexdigest()
        return block_hash
    
    def is_valid(self):
        return self.value == self.calculate_hash()
    

class Blockchain:
    def __init__(self):
        self.chain = []
        genesis_block = self.create_genesis_block()
        self.chain.append(genesis_block)
        second_block = self.add_block("second")
        self.chain.append(second_block)

    def create_genesis_block(self):
        return Block("genesis", "0")
    
    def add_block(self, data):
        new_block = Block(data, self.chain[-1].value)
        return new_block

    def is_chain_valid(self):
        for i in range(1, len(self.chain)):
            current_block = self.chain[i]
            previous_block = self.chain[i-1]
            if current_block.previous_hash == previous_block.value:
                continue
            else:
                return False


bc = Blockchain()

print("initial chain valid?:", bc.is_chain_valid())

本当に簡易的ですが、自分で書いてみるのは、概念を理解する上で、非常におすすめです。

解説

手順を自分なりに解説していきます。

まず、hashlibというライブラリをimportします。
これはハッシュ関数ですね。チェーンとしてつなぐうえで最も重要な一方向性をもちます。

import hashlib

Blockクラス

Blockというクラスを定義します。

これはその名の通りブロックそのもののクラスです。

インスタンス作成時には、インスタンスに引数のdataやprevious_hash、そしてハッシュ後のvalueを保存するようにします。

class Block:
    def __init__(self, data, previous_hash):
        self.data = data
        self.previous_hash = previous_hash
        self.value = self.calculate_hash()

calculate_hashが肝心で、自分自身の現在のデータと、一つ前のブロックのハッシュ値を足し合わせたものをハッシュします。その値を返します。

def calculate_hash(self):
    content = self.data + self.previous_hash
    block_hash = hashlib.sha256(content.encode()).hexdigest()
    return block_hash

最後に、そのブロック自身が正しいかを検証するものとして、is_validという関数を定義します。

def is_valid(self):
    return self.value == self.calculate_hash()

これでブロックそのもののロジックは完了です。

Blockchainクラス

次は実際に繋いで、チェーンにしていきましょう。

まずBlockchainというクラスを定義します。

self.chainに、空のリストをセットします。
これがチェーンのリストです。
この中にはBlockのインスタンスが入っていきます。

class Blockchain:
    def __init__(self):
        self.chain = []
        genesis_block = self.create_genesis_block()
        self.chain.append(genesis_block)

最初のブロック

次に、最初のブロック(ジェネシスブロック)を作成します。これだけは特別なので、create_genesis_blockという関数を定義します。

def create_genesis_block(self):
    return Block("genesis", "0")

これはprevious_valueが0、つまり一つ前のブロックが存在しないという状態で、Blockのインスタンスを作成し、それを返します。valueには適当に”genesis”を入れました。

その値を、genesis_blockという変数に保管し、chainリストに追加します。これで最初のブロックは完成です。

接続

では次は2つ目のブロックを繋いでみましょう。

class Blockchain:
    def __init__(self):
        self.chain = []
        genesis_block = self.create_genesis_block()
        self.chain.append(genesis_block)
        second_block = self.add_block("second")#ここ
        self.chain.append(second_block)#ここ

ここからは前のものに追加する形なので、add_blockという関数を定義すれば、使い続けられます。

def add_block(self, data):
    new_block = Block(data, self.chain[-1].value)
    return new_block

この関数はdataを引数として受け取ります。

new_blockというBlockのインスタンスを作成します。

ここで重要なのが引数で、dataを渡すとともに、chainリストの最後の要素のハッシュ値を渡します

self.chain[-1]はchainリストの最後の要素つまり、 今作成しているものの一つ前のBlockインスタンスですよね。
それのvalueはハッシュ値です。それを引数として渡すのです。

そうしてその材料をもとに完成したブロックを、add_blockが返します。

ここがブロックチェーンの核心ですね。

今回はdataに”second”を入れています。

self.chain.append(second_block)

最後にその作成したブロックをリストに追加します。

これで2つ目のブロックが入ったブロックチェーン完成です!

ブロックチェーンを検証

では今度はブロックチェーン自体が正しいかを検証するis_chain_validを作っていきます。
このような形になります。

def is_chain_valid(self):
    for i in range(1, len(self.chain)):
        current_block = self.chain[i]
        previous_block = self.chain[i-1]
        if current_block.previous_hash == previous_block.value:
            continue
        else:
            return False

forループで、最初のブロックを除いたブロックについて、ひとつひとつ見ていきます。

iには最初1が入りますから、current_blockはself.chain[1]となり、chainリストの2つ目の要素です。

previous_blockは、self.chain[0]なので、current_blockの一つ前のものです。

今見ているブロックの「一つ前のブロックの値」と、一つ前のブロックの値が同じであれば、それは繋がっていることになります。

ですから、もし current_block.previous_hash == previous_block.valueであるならば、次に移る。

そうでないなら、Falseを返す。

という風なことにすれば、正しく検証できるのです。(現時点では正しくてもTrueを返さずNoneになりますが、まあ一旦ここまでにします)

というようなことになっていました!
あとはブロックチェーンを作成して、正しいかの検証結果をプリントするようにしましょう。

bc = Blockchain()
print("initial chain valid?:", bc.is_chain_valid())

これで完了です!

おわりに

今回、超超簡易的ではあるものの、ブロックチェーンをコードとして書いてみて、仕組みの理解がより進んだと感じました。

これから、ブロックをループで作成したり、タイムスタンプやデータの入力を取り入れたり、PoW入れたりしていきたいです!

githubはこちらです!
https://github.com/Ryt890/toy-blockchain1