当前位置:   article > 正文

【Python】实现一个简单的区块链系统_python 区块链

python 区块链

本文章利用 Python 实现一个简单的功能较为完善的区块链系统(包括区块链结构、账户、钱包、转账),采用的共识机制是 POW。

一、区块与区块链结构

Block.py

  1. import hashlib
  2. from datetime import datetime
  3. class Block:
  4. """
  5. 区块链结构:
  6. prev_hash: 父区块哈希值
  7. data: 区块内容
  8. timestamp: 区块创建时间
  9. hash: 区块哈希值
  10. """
  11. def __init__(self, data, prev_hash):
  12. # 将传入的父区块哈希值和数据保存到变量中
  13. self.prev_hash = prev_hash
  14. self.data = data
  15. # 获得当前的时间
  16. self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  17. # 计算区块哈希值
  18. # 获取哈希对象
  19. message = hashlib.sha256()
  20. # 先将数据内容转为字符串并进行编码,再将它们哈希
  21. # 注意:update() 方法现在只接受 bytes 类型的数据,不接收 str 类型
  22. message.update(str(self.prev_hash).encode('utf-8'))
  23. message.update(str(self.prev_hash).encode('utf-8'))
  24. message.update(str(self.prev_hash).encode('utf-8'))
  25. # update() 更新 hash 对象,连续的调用该方法相当于连续的追加更新
  26. # 返回字符串类型的消息摘要
  27. self.hash = message.hexdigest()

 BlockChain.py

  1. from Block import Block
  2. class BlockChain:
  3. """
  4. 区块链结构体
  5. blocks: 包含区块的列表
  6. """
  7. def __init__(self):
  8. self.blocks = []
  9. def add_block(self, block):
  10. """
  11. 添加区块
  12. :param block:
  13. :return:
  14. """
  15. self.blocks.append(block)
  16. # 新建区块
  17. genesis_block = Block(data="创世区块", prev_hash="")
  18. new_block1 = Block(data="张三转给李四一个比特币", prev_hash=genesis_block.hash)
  19. new_block2 = Block(data="张三转给王五三个比特币", prev_hash=genesis_block.hash)
  20. # 新建一个区块链对象
  21. blockChain = BlockChain()
  22. # 将刚才新建的区块加入区块链
  23. blockChain.add_block(genesis_block)
  24. blockChain.add_block(new_block1)
  25. blockChain.add_block(new_block2)
  26. # 打印区块链信息
  27. print("区块链包含区块个数为:%d\n" % len(blockChain.blocks))
  28. blockHeight = 0
  29. for block in blockChain.blocks:
  30. print(f"本区块高度为:{blockHeight}")
  31. print(f"父区块哈希:{block.prev_hash}")
  32. print(f"区块内容:{block.data}")
  33. print(f"区块哈希:{block.hash}")
  34. print()
  35. blockHeight += 1

 测试结果 

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

将工作量证明加入到 Block.py 中

  1. import hashlib
  2. from datetime import datetime
  3. from time import time
  4. class Block:
  5. """
  6. 区块链结构:
  7. prev_hash: 父区块哈希值
  8. data: 区块内容
  9. timestamp: 区块创建时间
  10. hash: 区块哈希值
  11. nonce: 随机数
  12. """
  13. def __init__(self, data, prev_hash):
  14. # 将传入的父区块哈希值和数据保存到变量中
  15. self.prev_hash = prev_hash
  16. self.data = data
  17. # 获得当前的时间
  18. self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  19. # 设置随机数、哈希初始值为 None
  20. self.nonce = None
  21. self.hash = None
  22. # 类的 __repr__() 方法定义了实例化对象的输出信息
  23. def __repr__(self):
  24. return f"区块内容:{self.data}\n区块哈希值:{self.hash}"
  25. class ProofOfWork:
  26. """
  27. 工作量证明:
  28. block: 区块
  29. difficulty: 难度值
  30. """
  31. def __init__(self, block, difficult=5):
  32. self.block = block
  33. # 定义出块难度,默认为 5,表示有效哈希值以 5 个零开头
  34. self.difficulty = difficult
  35. def mine(self):
  36. """
  37. 挖矿函数
  38. :return:
  39. """
  40. i = 0
  41. prefix = '0' * self.difficulty
  42. while True:
  43. message = hashlib.sha256()
  44. message.update(str(self.block.prev_hash).encode('utf-8'))
  45. message.update(str(self.block.data).encode('utf-8'))
  46. message.update(str(self.block.timestamp).encode('utf-8'))
  47. message.update(str(i).encode('utf-8'))
  48. # digest() 返回摘要,作为二进制数据字符串值
  49. # hexdigest() 返回摘要,作为十六进制数据字符串值
  50. digest = message.hexdigest()
  51. # str.startswith(prefix) 检测字符串是否是以 prefix(字符串)开头,返回布尔值
  52. if digest.startswith(prefix):
  53. # 幸运数字
  54. self.block.nonce = i
  55. # 区块哈希值为十六进制数据字符串摘要
  56. self.block.hash = digest
  57. return self.block
  58. i += 1
  59. def validate(self):
  60. """
  61. 验证有效性
  62. :return:
  63. """
  64. message = hashlib.sha256()
  65. message.update(str(self.block.prev_hash).encode('utf-8'))
  66. message.update(str(self.block.data).encode('utf-8'))
  67. message.update(str(self.block.timestamp).encode('utf-8'))
  68. message.update(str(self.block.nonce).encode('utf-8'))
  69. digest = message.hexdigest()
  70. prefix = '0' * self.difficulty
  71. return digest.startswith(prefix)
  72. # ++++++++测试++++++++
  73. # 定义一个区块
  74. b = Block(data="测试", prev_hash="")
  75. # 定义一个工作量证明
  76. w = ProofOfWork(b)
  77. # 开始时间
  78. start_time = time()
  79. # 挖矿,并统计函数执行时间
  80. print("+++开始挖矿+++")
  81. valid_block = w.mine()
  82. # 结束时间
  83. end_time = time()
  84. print(f"挖矿花费时间:{end_time - start_time}秒")
  85. # 验证区块
  86. print(f"区块哈希值是否符合规则:{w.validate()}")
  87. print(f"区块哈希值为:{b.hash}")

测试结果

更新 BlockChain.py

  1. from Block import Block, ProofOfWork
  2. class BlockChain:
  3. """
  4. 区块链结构体
  5. blocks: 包含区块的列表
  6. """
  7. def __init__(self):
  8. self.blocks = []
  9. def add_block(self, block):
  10. """
  11. 添加区块
  12. :param block:
  13. :return:
  14. """
  15. self.blocks.append(block)
  16. # 新建一个区块链对象
  17. blockChain = BlockChain()
  18. # 新建区块
  19. block1 = Block(data="创世区块", prev_hash="")
  20. w1 = ProofOfWork(block1)
  21. genesis_block = w1.mine()
  22. blockChain.add_block(genesis_block)
  23. block2 = Block(data="张三转给李四一个比特币", prev_hash=genesis_block.hash)
  24. w2 = ProofOfWork(block2)
  25. block = w2.mine()
  26. blockChain.add_block(block)
  27. block3 = Block(data="张三转给王五三个比特币", prev_hash=block.hash)
  28. w3 = ProofOfWork(block3)
  29. block = w3.mine()
  30. blockChain.add_block(block)
  31. # 打印区块链信息
  32. print("区块链包含区块个数为:%d\n" % len(blockChain.blocks))
  33. blockHeight = 0
  34. for block in blockChain.blocks:
  35. print(f"本区块高度为:{blockHeight}")
  36. print(f"父区块哈希:{block.prev_hash}")
  37. print(f"区块内容:{block.data}")
  38. print(f"区块哈希:{block.hash}")
  39. print()
  40. blockHeight += 1

 测试结果

三、实现钱包、账户、交易功能

 实现钱包、账户、交易功能要先安装非对称加密算法库 ecdsa。如果网速慢,引用下面这个网站

-i https://pypi.tuna.tsinghua.edu.cn/simple

添加钱包、账户功能 Wallet.py

  1. import base64
  2. import binascii
  3. from hashlib import sha256
  4. # 导入椭圆曲线算法
  5. from ecdsa import SigningKey, SECP256k1, VerifyingKey
  6. class Wallet:
  7. """
  8. 钱包
  9. """
  10. def __init__(self):
  11. """
  12. 钱包初始化时基于椭圆曲线生成一个唯一的秘钥对,代表区块链上一个唯一的账户
  13. """
  14. # 生成私钥
  15. self._private_key = SigningKey.generate(curve=SECP256k1)
  16. # 基于私钥生成公钥
  17. self._public_key = self._private_key.get_verifying_key()
  18. @property
  19. def address(self):
  20. """
  21. 这里通过公钥生成地址
  22. """
  23. h = sha256(self._public_key.to_pem())
  24. # 地址先由公钥进行哈希算法,再进行 Base64 计算而成
  25. return base64.b64encode(h.digest())
  26. @property
  27. def pubkey(self):
  28. """
  29. 返回公钥字符串
  30. """
  31. return self._public_key.to_pem()
  32. def sign(self, message):
  33. """
  34. 生成数字签名
  35. """
  36. h = sha256(message.encode('utf8'))
  37. # 利用私钥生成签名
  38. # 签名生成的是一串二进制字符串,为了便于查看,这里转换为 ASCII 字符串进行输出
  39. return binascii.hexlify(self._private_key.sign(h.digest()))
  40. def verify_sign(pubkey, message, signature):
  41. """
  42. 验证签名
  43. """
  44. verifier = VerifyingKey.from_pem(pubkey)
  45. h = sha256(message.encode('utf8'))
  46. return verifier.verify(binascii.unhexlify(signature), h.digest())

实现转账功能 Transaction.py

  1. import json
  2. class Transaction:
  3. """
  4. 交易的结构
  5. """
  6. def __init__(self, sender, recipient, amount):
  7. """
  8. 初始化交易,设置交易的发送方、接收方和交易数量
  9. """
  10. # 交易发送者的公钥
  11. self.pubkey = None
  12. # 交易的数字签名
  13. self.signature = None
  14. if isinstance(sender, bytes):
  15. sender = sender.decode('utf-8')
  16. self.sender = sender # 发送方
  17. if isinstance(recipient, bytes):
  18. recipient = recipient.decode('utf-8')
  19. self.recipient = recipient # 接收方
  20. self.amount = amount # 交易数量
  21. def set_sign(self, signature, pubkey):
  22. """
  23. 为了便于验证这个交易的可靠性,需要发送方输入他的公钥和签名
  24. """
  25. self.signature = signature # 签名
  26. self.pubkey = pubkey # 发送方公钥
  27. def __repr__(self):
  28. """
  29. 交易大致可分为两种,一是挖矿所得,而是转账交易
  30. 挖矿所得无发送方,以此进行区分显示不同内容
  31. """
  32. if self.sender:
  33. s = f"从{self.sender}转自{self.recipient}{self.amount}个加密货币"
  34. elif self.recipient:
  35. s = f"{self.recipient}挖矿所得{self.amount}个加密货币"
  36. else:
  37. s = "error"
  38. return s
  39. class TransactionEncoder(json.JSONEncoder):
  40. """
  41. 定义Json的编码类,用来序列化Transaction
  42. """
  43. def default(self, obj):
  44. if isinstance(obj, Transaction):
  45. return obj.__dict__
  46. else:
  47. return json.JSONEncoder.default(self, obj)
  48. # return super(TransactionEncoder, self).default(obj)

更新 Block.py

  1. import hashlib
  2. import json
  3. from datetime import datetime
  4. from Transaction import Transaction, TransactionEncoder
  5. class Block:
  6. """
  7. 区块结构
  8. prev_hash: 父区块哈希值
  9. transactions: 交易对
  10. timestamp: 区块创建时间
  11. hash: 区块哈希值
  12. Nonce: 随机数
  13. """
  14. def __init__(self, transactions, prev_hash):
  15. # 将传入的父哈希值和数据保存到类变量中
  16. self.prev_hash = prev_hash
  17. # 交易列表
  18. self.transactions = transactions
  19. # 获取当前时间
  20. self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  21. # 设置Nonce和哈希的初始值为None
  22. self.nonce = None
  23. self.hash = None
  24. # 类的 __repr__() 方法定义了实例化对象的输出信息
  25. def __repr__(self):
  26. return f"区块内容:{self.transactions}\n区块哈希值:{self.hash}"
  27. class ProofOfWork:
  28. """
  29. 工作量证明
  30. block: 区块
  31. difficulty: 难度值
  32. """
  33. def __init__(self, block, miner, difficult=5):
  34. self.block = block
  35. self.miner = miner
  36. # 定义工作量难度,默认为5,表示有效的哈希值以5个“0”开头
  37. self.difficulty = difficult
  38. # 添加挖矿奖励
  39. self.reward_amount = 1
  40. def mine(self):
  41. """
  42. 挖矿函数
  43. """
  44. i = 0
  45. prefix = '0' * self.difficulty
  46. # 设置挖矿自动生成交易信息,添加挖矿奖励
  47. t = Transaction(
  48. sender="",
  49. recipient=self.miner.address,
  50. amount=self.reward_amount,
  51. )
  52. sig = self.miner.sign(json.dumps(t, cls=TransactionEncoder))
  53. t.set_sign(sig, self.miner.pubkey)
  54. self.block.transactions.append(t)
  55. while True:
  56. message = hashlib.sha256()
  57. message.update(str(self.block.prev_hash).encode('utf-8'))
  58. # 更新区块中的交易数据
  59. # message.update(str(self.block.data).encode('utf-8'))
  60. message.update(str(self.block.transactions).encode('utf-8'))
  61. message.update(str(self.block.timestamp).encode('utf-8'))
  62. message.update(str(i).encode("utf-8"))
  63. digest = message.hexdigest()
  64. if digest.startswith(prefix):
  65. self.block.nonce = i
  66. self.block.hash = digest
  67. return self.block
  68. i += 1
  69. def validate(self):
  70. """
  71. 验证有效性
  72. """
  73. message = hashlib.sha256()
  74. message.update(str(self.block.prev_hash).encode('utf-8'))
  75. # 更新区块中的交易数据
  76. # message.update(str(self.block.data).encode('utf-8'))
  77. message.update(json.dumps(self.block.transactions).encode('utf-8'))
  78. message.update(str(self.block.timestamp).encode('utf-8'))
  79. message.update(str(self.block.nonce).encode('utf-8'))
  80. digest = message.hexdigest()
  81. prefix = '0' * self.difficulty
  82. return digest.startswith(prefix)

更新 BlockChain.py

  1. from Block import Block, ProofOfWork
  2. from Transaction import Transaction
  3. from Wallet import Wallet, verify_sign
  4. class BlockChain:
  5. """
  6. 区块链结构体
  7. blocks: 包含的区块列表
  8. """
  9. def __init__(self):
  10. self.blocks = []
  11. def add_block(self, block):
  12. """
  13. 添加区块
  14. """
  15. self.blocks.append(block)
  16. def print_list(self):
  17. print(f"区块链包含个数为:{len(self.blocks)}")
  18. for block in self.blocks:
  19. height = 0
  20. print(f"区块链高度为:{height}")
  21. print(f"父区块为:{block.prev_hash}")
  22. print(f"区块内容为:{block.transactions}")
  23. print(f"区块哈希值为:{block.hash}")
  24. height += 1
  25. print()

为了方便我们对区块链进行操作,我们可以在 BlockChain.py 中补充一些方法

  1. # 传入用户和区块链,返回用户的“余额”
  2. def get_balance(user, blockchain):
  3. balance = 0
  4. for block in blockchain.blocks:
  5. for t in block.transactions:
  6. if t.sender == user.address.decode():
  7. balance -= t.amount
  8. elif t.recipient == user.address.decode():
  9. balance += t.amount
  10. return balance
  11. # user生成创世区块(新建区块链),并添加到区块链中
  12. def generate_genesis_block(user):
  13. blockchain = BlockChain()
  14. new_block = Block(transactions=[], prev_hash="")
  15. w = ProofOfWork(new_block, user)
  16. genesis_block = w.mine()
  17. blockchain.add_block(genesis_block)
  18. # 返回创世区块
  19. return blockchain
  20. # 用户之间进行交易并记入交易列表
  21. def add_transaction(sender, recipient, amount):
  22. # 新建交易
  23. new_transaction = Transaction(
  24. sender=sender.address,
  25. recipient=recipient.address,
  26. amount=amount
  27. )
  28. # 生成数字签名
  29. sig = sender.sign(str(new_transaction))
  30. # 传入付款方的公钥和签名
  31. new_transaction.set_sign(sig, sender.pubkey)
  32. return new_transaction
  33. # 验证交易,若验证成功则加入交易列表
  34. def verify_new_transaction(new_transaction, transactions):
  35. if verify_sign(new_transaction.pubkey,
  36. str(new_transaction),
  37. new_transaction.signature
  38. ):
  39. # 验证交易签名没问题,加入交易列表
  40. print("交易验证成功")
  41. transactions.append(new_transaction)
  42. else:
  43. print("交易验证失败")
  44. # 矿工将全部验证成功的交易列表打包出块
  45. def generate_block(miner, transactions, blockchain):
  46. new_block = Block(transactions=transactions,
  47. prev_hash=blockchain.blocks[len(blockchain.blocks) - 1].hash)
  48. print("生成新的区块...")
  49. # 挖矿
  50. w = ProofOfWork(new_block, miner)
  51. block = w.mine()
  52. print("将新区块添加到区块链中")
  53. blockchain.add_block(block)

进行测试

  1. # 新建交易列表
  2. transactions = []
  3. # 创建 3 个用户
  4. alice = Wallet()
  5. tom = Wallet()
  6. bob = Wallet()
  7. print("alice创建创世区块...")
  8. blockchain = generate_genesis_block(alice)
  9. print()
  10. print(f"alice 的余额为{get_balance(alice, blockchain)}个比特币")
  11. print(f"tom 的余额为{get_balance(tom, blockchain)}个比特币")
  12. print(f"bob 的余额为{get_balance(bob, blockchain)}个比特币")
  13. print()
  14. # 打印区块链信息
  15. blockchain.print_list()
  16. print("新增交易:alice 转账 0.5 比特币给 tom")
  17. nt = add_transaction(alice, tom, 0.5)
  18. print()
  19. verify_new_transaction(nt, transactions)
  20. print(f"矿工 bob 将全部验证成功的交易列表打包出块...")
  21. generate_block(bob, transactions, blockchain)
  22. print("添加完成\n")
  23. # 打印区块链信息
  24. blockchain.print_list()

测试结果

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/blog/article/detail/36977
推荐阅读
相关标签
  

闽ICP备14008679号