簡單實現區塊鏈

一、區塊鏈與區塊鏈結構

# Block.py
import hashlib
from datetime import datetime class Block: """ 區塊鏈結構: prev_hash: 父區塊哈希值 data: 區塊內容 timestamp: 區塊創建時間 hash: 區塊哈希值 """ def __init__(self, data, prev_hash): # 將傳入的父區塊哈希值和數據保存到變量中 self.prev_hash = prev_hash self.data = data # 獲得當前的時間 self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") # 計算區塊哈希值 # 獲取哈希對象 message = hashlib.sha256() # 先將數據內容轉爲字符串並進行編碼,再將它們哈希 # 注意:update() 方法現在只接受 bytes 類型的數據,不接收 str 類型 message.update(str(self.prev_hash).encode('utf-8')) message.update(str(self.prev_hash).encode('utf-8')) message.update(str(self.prev_hash).encode('utf-8')) # update() 更新 hash 對象,連續的調用該方法相當於連續的追加更新 # 返回字符串類型的消息摘要 self.hash = message.hexdigest()
# BlockChain.py

from Block import Block
 
 
class BlockChain:
    """
        區塊鏈結構體
            blocks:         包含區塊的列表
    """
 
    def __init__(self):
        self.blocks = []
 
    def add_block(self, block):
        """
        添加區塊
        :param block:
        :return:
        """
        self.blocks.append(block)
 
 
# 新建區塊
genesis_block = Block(data="創世區塊", prev_hash="")
new_block1 = Block(data="張三轉給李四一個比特幣", prev_hash=genesis_block.hash)
new_block2 = Block(data="張三轉給王五三個比特幣", prev_hash=genesis_block.hash)
 
# 新建一個區塊鏈對象
blockChain = BlockChain()
# 將剛纔新建的區塊加入區塊鏈
blockChain.add_block(genesis_block)
blockChain.add_block(new_block1)
blockChain.add_block(new_block2)
 
# 打印區塊鏈信息
print("區塊鏈包含區塊個數爲:%d\n" % len(blockChain.blocks))
blockHeight = 0
for block in blockChain.blocks:
    print(f"本區塊高度爲:{blockHeight}")
    print(f"父區塊哈希:{block.prev_hash}")
    print(f"區塊內容:{block.data}")
    print(f"區塊哈希:{block.hash}")
    print()
    blockHeight += 1

二、加入工作量證明(POW)

# Block.py

import hashlib
from datetime import datetime
from time import time
 
 
class Block:
    """
        區塊鏈結構:
            prev_hash:      父區塊哈希值
            data:           區塊內容
            timestamp:      區塊創建時間
            hash:           區塊哈希值
            nonce:          隨機數
    """
    def __init__(self, data, prev_hash):
        # 將傳入的父區塊哈希值和數據保存到變量中
        self.prev_hash = prev_hash
        self.data = data
 
        # 獲得當前的時間
        self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
 
        # 設置隨機數、哈希初始值爲 None
        self.nonce = None
        self.hash = None
 
    # 類的 __repr__() 方法定義了實例化對象的輸出信息
    def __repr__(self):
        return f"區塊內容:{self.data}\n區塊哈希值:{self.hash}"
 
 
class ProofOfWork:
    """
        工作量證明:
            block:          區塊
            difficulty:     難度值
    """
 
    def __init__(self, block, difficult=5):
        self.block = block
 
        # 定義出塊難度,默認爲 5,表示有效哈希值以 5 個零開頭
        self.difficulty = difficult
 
    def mine(self):
        """
        挖礦函數
        :return:
        """
        i = 0
        prefix = '0' * self.difficulty
 
        while True:
            message = hashlib.sha256()
            message.update(str(self.block.prev_hash).encode('utf-8'))
            message.update(str(self.block.data).encode('utf-8'))
            message.update(str(self.block.timestamp).encode('utf-8'))
            message.update(str(i).encode('utf-8'))
            # digest() 返回摘要,作爲二進制數據字符串值
            # hexdigest() 返回摘要,作爲十六進制數據字符串值
            digest = message.hexdigest()
            # str.startswith(prefix) 檢測字符串是否是以 prefix(字符串)開頭,返回布爾值
            if digest.startswith(prefix):
                # 幸運數字
                self.block.nonce = i
                # 區塊哈希值爲十六進制數據字符串摘要
                self.block.hash = digest
                return self.block
            i += 1
 
    def validate(self):
        """
        驗證有效性
        :return:
        """
        message = hashlib.sha256()
        message.update(str(self.block.prev_hash).encode('utf-8'))
        message.update(str(self.block.data).encode('utf-8'))
        message.update(str(self.block.timestamp).encode('utf-8'))
        message.update(str(self.block.nonce).encode('utf-8'))
        digest = message.hexdigest()
 
        prefix = '0' * self.difficulty
        return digest.startswith(prefix)
 
 
# ++++++++測試++++++++
# 定義一個區塊
b = Block(data="測試", prev_hash="")
 
# 定義一個工作量證明
w = ProofOfWork(b)
 
# 開始時間
start_time = time()
# 挖礦,並統計函數執行時間
print("+++開始挖礦+++")
valid_block = w.mine()
# 結束時間
end_time = time()
print(f"挖礦花費時間:{end_time - start_time}秒")
 
# 驗證區塊
print(f"區塊哈希值是否符合規則:{w.validate()}")
print(f"區塊哈希值爲:{b.hash}")
# 更新BlockChain.py

from Block import Block, ProofOfWork
 
 
class BlockChain:
    """
        區塊鏈結構體
            blocks:         包含區塊的列表
    """
 
    def __init__(self):
        self.blocks = []
 
    def add_block(self, block):
        """
        添加區塊
        :param block:
        :return:
        """
        self.blocks.append(block)
 
 
# 新建一個區塊鏈對象
blockChain = BlockChain()
 
# 新建區塊
block1 = Block(data="創世區塊", prev_hash="")
w1 = ProofOfWork(block1)
genesis_block = w1.mine()
blockChain.add_block(genesis_block)
 
block2 = Block(data="張三轉給李四一個比特幣", prev_hash=genesis_block.hash)
w2 = ProofOfWork(block2)
block = w2.mine()
blockChain.add_block(block)
 
block3 = Block(data="張三轉給王五三個比特幣", prev_hash=block.hash)
w3 = ProofOfWork(block3)
block = w3.mine()
blockChain.add_block(block)
 
# 打印區塊鏈信息
print("區塊鏈包含區塊個數爲:%d\n" % len(blockChain.blocks))
blockHeight = 0
for block in blockChain.blocks:
    print(f"本區塊高度爲:{blockHeight}")
    print(f"父區塊哈希:{block.prev_hash}")
    print(f"區塊內容:{block.data}")
    print(f"區塊哈希:{block.hash}")
    print()
    blockHeight += 1

三、實現錢包、賬戶、交易

# Wallet.py

import base64
import binascii
from hashlib import sha256
# 導入橢圓曲線算法
from ecdsa import SigningKey, SECP256k1, VerifyingKey
 
 
class Wallet:
    """
        錢包
    """
 
    def __init__(self):
        """
            錢包初始化時基於橢圓曲線生成一個唯一的祕鑰對,代表區塊鏈上一個唯一的賬戶
        """
        # 生成私鑰
        self._private_key = SigningKey.generate(curve=SECP256k1)
        # 基於私鑰生成公鑰
        self._public_key = self._private_key.get_verifying_key()
 
    @property
    def address(self):
        """
            這裏通過公鑰生成地址
        """
        h = sha256(self._public_key.to_pem())
        # 地址先由公鑰進行哈希算法,再進行 Base64 計算而成
        return base64.b64encode(h.digest())
 
    @property
    def pubkey(self):
        """
            返回公鑰字符串
        """
        return self._public_key.to_pem()
 
    def sign(self, message):
        """
            生成數字簽名
        """
        h = sha256(message.encode('utf8'))
        # 利用私鑰生成簽名
        # 簽名生成的是一串二進制字符串,爲了便於查看,這裏轉換爲 ASCII 字符串進行輸出
        return binascii.hexlify(self._private_key.sign(h.digest()))
 
 
def verify_sign(pubkey, message, signature):
    """
        驗證簽名
    """
    verifier = VerifyingKey.from_pem(pubkey)
    h = sha256(message.encode('utf8'))
    return verifier.verify(binascii.unhexlify(signature), h.digest())

 

# Transaction.py

import json
 
 
class Transaction:
    """
        交易的結構
    """
 
    def __init__(self, sender, recipient, amount):
        """
            初始化交易,設置交易的發送方、接收方和交易數量
        """
        # 交易發送者的公鑰
        self.pubkey = None
        # 交易的數字簽名
        self.signature = None
 
        if isinstance(sender, bytes):
            sender = sender.decode('utf-8')
        self.sender = sender        # 發送方
        if isinstance(recipient, bytes):
            recipient = recipient.decode('utf-8')
        self.recipient = recipient  # 接收方
        self.amount = amount        # 交易數量
 
    def set_sign(self, signature, pubkey):
        """
            爲了便於驗證這個交易的可靠性,需要發送方輸入他的公鑰和簽名
        """
        self.signature = signature  # 簽名
        self.pubkey = pubkey  # 發送方公鑰
 
    def __repr__(self):
        """
            交易大致可分爲兩種,一是挖礦所得,而是轉賬交易
            挖礦所得無發送方,以此進行區分顯示不同內容
        """
        if self.sender:
            s = f"從{self.sender}轉自{self.recipient}{self.amount}個加密貨幣"
        elif self.recipient:
            s = f"{self.recipient}挖礦所得{self.amount}個加密貨幣"
        else:
            s = "error"
        return s
 
 
class TransactionEncoder(json.JSONEncoder):
    """
        定義Json的編碼類,用來序列化Transaction
    """
    def default(self, obj):
        if isinstance(obj, Transaction):
            return obj.__dict__
        else:
            return json.JSONEncoder.default(self, obj)
            # return super(TransactionEncoder, self).default(obj)
# 更新Block.py

import hashlib
import json
from datetime import datetime
from Transaction import Transaction, TransactionEncoder
 
 
class Block:
    """
        區塊結構
            prev_hash:      父區塊哈希值
            transactions:   交易對
            timestamp:      區塊創建時間
            hash:           區塊哈希值
            Nonce:          隨機數
    """
 
    def __init__(self, transactions, prev_hash):
        # 將傳入的父哈希值和數據保存到類變量中
        self.prev_hash = prev_hash
        # 交易列表
        self.transactions = transactions
        # 獲取當前時間
        self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
 
        # 設置Nonce和哈希的初始值爲None
        self.nonce = None
        self.hash = None
 
    # 類的 __repr__() 方法定義了實例化對象的輸出信息
    def __repr__(self):
        return f"區塊內容:{self.transactions}\n區塊哈希值:{self.hash}"
 
 
class ProofOfWork:
    """
        工作量證明
            block:          區塊
            difficulty:     難度值
    """
 
    def __init__(self, block, miner, difficult=5):
        self.block = block
        self.miner = miner
 
        # 定義工作量難度,默認爲5,表示有效的哈希值以5個“0”開頭
        self.difficulty = difficult
 
        # 添加挖礦獎勵
        self.reward_amount = 1
 
    def mine(self):
        """
            挖礦函數
        """
        i = 0
        prefix = '0' * self.difficulty
 
        # 設置挖礦自動生成交易信息,添加挖礦獎勵
        t = Transaction(
            sender="",
            recipient=self.miner.address,
            amount=self.reward_amount,
        )
        sig = self.miner.sign(json.dumps(t, cls=TransactionEncoder))
        t.set_sign(sig, self.miner.pubkey)
        self.block.transactions.append(t)
 
        while True:
            message = hashlib.sha256()
            message.update(str(self.block.prev_hash).encode('utf-8'))
            # 更新區塊中的交易數據
            # message.update(str(self.block.data).encode('utf-8'))
            message.update(str(self.block.transactions).encode('utf-8'))
            message.update(str(self.block.timestamp).encode('utf-8'))
            message.update(str(i).encode("utf-8"))
            digest = message.hexdigest()
            if digest.startswith(prefix):
                self.block.nonce = i
                self.block.hash = digest
                return self.block
            i += 1
 
    def validate(self):
        """
            驗證有效性
        """
        message = hashlib.sha256()
        message.update(str(self.block.prev_hash).encode('utf-8'))
        # 更新區塊中的交易數據
        # message.update(str(self.block.data).encode('utf-8'))
        message.update(json.dumps(self.block.transactions).encode('utf-8'))
        message.update(str(self.block.timestamp).encode('utf-8'))
        message.update(str(self.block.nonce).encode('utf-8'))
        digest = message.hexdigest()
 
        prefix = '0' * self.difficulty
        return digest.startswith(prefix)
# 更新BlockChain.py

from Block import Block, ProofOfWork
from Transaction import Transaction
from Wallet import Wallet, verify_sign
 
 
class BlockChain:
    """
        區塊鏈結構體
            blocks:        包含的區塊列表
    """
 
    def __init__(self):
        self.blocks = []
 
    def add_block(self, block):
        """
            添加區塊
        """
        self.blocks.append(block)
 
    def print_list(self):
        print(f"區塊鏈包含個數爲:{len(self.blocks)}")
        for block in self.blocks:
            height = 0
            print(f"區塊鏈高度爲:{height}")
            print(f"父區塊爲:{block.prev_hash}")
            print(f"區塊內容爲:{block.transactions}")
            print(f"區塊哈希值爲:{block.hash}")
            height += 1
            print()
# 追加到BlockChain.py

# 傳入用戶和區塊鏈,返回用戶的“餘額”
def get_balance(user, blockchain):
    balance = 0
    for block in blockchain.blocks:
        for t in block.transactions:
            if t.sender == user.address.decode():
                balance -= t.amount
            elif t.recipient == user.address.decode():
                balance += t.amount
    return balance
 
 
# user生成創世區塊(新建區塊鏈),並添加到區塊鏈中
def generate_genesis_block(user):
    blockchain = BlockChain()
    new_block = Block(transactions=[], prev_hash="")
    w = ProofOfWork(new_block, user)
    genesis_block = w.mine()
    blockchain.add_block(genesis_block)
    # 返回創世區塊
    return blockchain
 
 
# 用戶之間進行交易並記入交易列表
def add_transaction(sender, recipient, amount):
    # 新建交易
    new_transaction = Transaction(
        sender=sender.address,
        recipient=recipient.address,
        amount=amount
    )
    # 生成數字簽名
    sig = sender.sign(str(new_transaction))
    # 傳入付款方的公鑰和簽名
    new_transaction.set_sign(sig, sender.pubkey)
    return new_transaction
 
 
# 驗證交易,若驗證成功則加入交易列表
def verify_new_transaction(new_transaction, transactions):
    if verify_sign(new_transaction.pubkey,
                   str(new_transaction),
                   new_transaction.signature
                   ):
        # 驗證交易簽名沒問題,加入交易列表
        print("交易驗證成功")
        transactions.append(new_transaction)
    else:
        print("交易驗證失敗")
 
 
# 礦工將全部驗證成功的交易列表打包出塊
def generate_block(miner, transactions, blockchain):
    new_block = Block(transactions=transactions,
                      prev_hash=blockchain.blocks[len(blockchain.blocks) - 1].hash)
    print("生成新的區塊...")
    # 挖礦
    w = ProofOfWork(new_block, miner)
    block = w.mine()
    print("將新區塊添加到區塊鏈中")
    blockchain.add_block(block)
# 測試

# 新建交易列表
transactions = []
 
# 創建 3 個用戶
alice = Wallet()
tom = Wallet()
bob = Wallet()
 
print("alice創建創世區塊...")
blockchain = generate_genesis_block(alice)
print()
 
print(f"alice 的餘額爲{get_balance(alice, blockchain)}個比特幣")
print(f"tom 的餘額爲{get_balance(tom, blockchain)}個比特幣")
print(f"bob 的餘額爲{get_balance(bob, blockchain)}個比特幣")
print()
 
# 打印區塊鏈信息
blockchain.print_list()
 
print("新增交易:alice 轉賬 0.5 比特幣給 tom")
nt = add_transaction(alice, tom, 0.5)
print()
verify_new_transaction(nt, transactions)
print(f"礦工 bob 將全部驗證成功的交易列表打包出塊...")
generate_block(bob, transactions, blockchain)
print("添加完成\n")
 
# 打印區塊鏈信息
blockchain.print_list()

結束!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章