一、區塊鏈與區塊鏈結構
# 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()
結束!