Bootstrap

Python实现简易区块链的完整交易流程

1 前言

1.1 一些概念

  • 区块链:一种分布式数据库,通过将数据存储在相互连接的区块链中来保证数据的不可更改性和完整性。
  • 区块:区块链的基本单元,包含一系列交易记录。每个区块都链接到前一个区块,形成一条链。
  • 交易:区块链上的基本操作单元,例如加密货币的转移。
  • 交易池:未被加入到区块中的交易组成的集合。
  • 共识机制:区块链网络中的节点使用共识机制来同意数据的真实性和顺序。
  • 挖矿:通过解决复杂的计算问题来创建新区块的过程。成功的挖矿可以获得奖励。

1.2 区块链工作流程

  • 交易发起
    用户发起一个交易,交易包含必要信息,如发送者、接收者、转账金额,以及数字签名。
  • 交易验证
    交易被发送到区块链网络中的节点,网络中的节点验证交易的有效性,例如发送者拥有足够的资金。
  • 交易放入交易池
    经过验证的交易被放入交易池,在那里等待被纳入即将创建的新区块。
  • 区块的挖掘
    挖矿节点从交易池中选择交易并开始构建新的区块,新区块包含了选定的交易,以及前一个区块的哈希值和矿工自己计算出的哈希值。
  • 新区块的验证
    新区块被发送到区块链网络的其他节点。如果大多数节点验证并接受新区块,则该区块被添加到区块链上,并进行资金的转移。
  • 区块链更新
    一旦新区块被加入,所有节点的区块链更新其状态,同时矿工节点会得到奖励,注意资金转移是在这一步进行的,而不是在交易验证通过之后。

2 编码实现

2.1 交易类

交易包括了发送者,接受者,交易金额以及数字签名,为了简化,这里除了交易金额都用下标代替(节点列表中节点的下标),代码如下:

class Transaction:
    def __init__(self, sender_id: int, recipient_id: int, amounts: float, signature: int = -1):
        # 为简化,这里的id均为节点列表的下标, 数字签名也是, 实际数字签名会采用加密算法
        self._sender_id = sender_id
        self._recipient_id = recipient_id
        self._amounts = amounts
        self._signature = signature

    @property
    def sender_id(self):
        return self._sender_id

    @sender_id.setter
    def sender_id(self, value: int):
        self._sender_id = value

    @property
    def recipient_id(self):
        return self._recipient_id

    @recipient_id.setter
    def recipient_id(self, value: int):
        self._recipient_id = value

    @property
    def amounts(self):
        return self._amounts

    @amounts.setter
    def amounts(self, value: float):
        self._amounts = value

    @property
    def signature(self):
        return self._signature

    @signature.setter
    def signature(self, value: int):
        self._signature = value

2.2 交易池类

交易池用于存放交易,并且在创建新区块的时候从中取出部分交易作为新区块中包含的交易,可以使用队列模拟,这里是为了简化,在实际应用中一般会先选交易额大的或采用其他逻辑,而不是先进先出,代码如下:

class TransactionPool:
    def __init__(self):
        self._transaction_queue = queue.Queue()

    def add_transaction(self, new_transaction):
        self._transaction_queue.put(new_transaction)

    def get_transaction(self) -> Union[Transaction, None]:
        if not self._transaction_queue.empty():
            return self._transaction_queue.get()
        else:
            return None

    @property
    def transaction_queue(self):
        return self._transaction_queue

    @transaction_queue.setter
    def transaction_queue(self, value):
        self._transaction_queue = value

2.3 区块类

区块包含了区块在区块链中的下标,上一个区块的哈希值,当前区块的哈希值,以及一系列交易,一般还需要有一个用于计算当前区块哈希值的一个时间戳。在初始情况下,还需要有一个创世区块,代码如下:

class Block:
    def __init__(self,
                 index: int,
                 previous_block_hash: str,
                 transaction_list: List['Transaction'],
                 time_stamp: datetime.datetime = datetime.datetime.now(),
                 block_hash: str = ''
                 ):
        self._index = index
        self._previous_block_hash = previous_block_hash
        self._transaction_list = transaction_list
        self._time_stamp = time_stamp
        self._block_hash = block_hash

    def _set_hash(self, block_hash: str):
        self._block_hash = block_hash

    @property
    def index(self):
        return self._index

    @index.setter
    def index(self, value):
        self._index = value

    @property
    def previous_block_hash(self):
        return self._previous_block_hash

    @previous_block_hash.setter
    def previous_block_hash(self, value):
        self._previous_block_hash = value

    @property
    def block_hash(self):
        return self._block_hash

    @block_hash.setter
    def block_hash(self, value):
        self._block_hash = value

    @property
    def transaction_list(self):
        return self._transaction_list

    @transaction_list.setter
    def transaction_list(self, value):
        self._transaction_list = value

    @property
    def time_stamp(self):
        return self._time_stamp

    @time_stamp.setter
    def time_stamp(self, value):
        self._time_stamp = value

    # 创世区块
    @staticmethod
    def create_genesis_block():
        time_stamp = datetime.datetime.now()
        header_bin = str(time_stamp)
        return Block(0, '0', [], time_stamp, hashlib.sha256(header_bin.encode()).hexdigest())

2.4 节点类

编写节点类前,先完成几个简单的函数,分别是验证哈希是否符合结构、验证区块链结构合法以及交易是否合法,都是完成简单的功能,验证交易是否合法函数会用到节点类,所以先会报错,可以不用管,代码如下:

# 验证哈希值(简化版, 只验证是否符合SHA-256哈希值)
def verify_hash_code(hash_str: str) -> bool:
    if len(hash_str) != 64:
        return False
    pattern = re.compile(r'^[a-f0-9]{64}$')
    return bool(pattern.match(hash_str))

# 验证区块链结构合法(简化版)
def structure_legal(block: Block) -> bool:
    if block.index < 0:
        return False
    if len(block.transaction_list) == 0:
        return False
    if not verify_hash_code(block.block_hash):
        return False
    if not verify_hash_code(block.previous_block_hash):
        return False
    return True

# 验证交易是否合法(简化版, 只看发送人余额是否则足够、数字签名是否合法)
def transaction_legal(transaction: Transaction, node_list: List['Node']) -> bool:
    # 签名是否合法
    if transaction.signature < 0:
        return False
    # 发送人余额是否足够
    if node_list[transaction.sender_id].balance < transaction.amounts:
        return False
    return True

然后再编写节点类,节点包括名称、余额、每个节点维护的区块链以及节点的id,节点的功能包括添加区块、验证新区块以及挖矿三个操作,添加区块只需要往list中append就可以了,验证新区块主要包含验证区块结构、交易以及连续性是否合法,可以利用上面的三个函数完成,而挖矿则是从交易池中挑选交易生成新的区块,共识机制使用简化版的工作量证明(也可以用最简单的最长链规则,认为最长的链就是正确的),代码如下:

class Node:
    def __init__(self, name: str, balance: float = 0., blockchain: List['Block'] = None, node_id: int = -1):
        self._name = name
        self._balance = balance
        if blockchain is None:
            self._blockchain = []
        else:
            self._blockchain = blockchain
        self._node_id = node_id
        self._signature = self._node_id

    def _add_block(self, new_block):
        self._blockchain.append(new_block)

    # 验证新区块(简化版)
    def _verify_block(self, new_block: Block, node_list: List['Node']) -> bool:
        # 验证区块结构合法
        if not structure_legal(new_block):
            print(new_block.block_hash)
            return False
        # 验证交易是否合法
        for transaction in new_block.transaction_list:
            if not transaction_legal(transaction, node_list):
                return False
        # 验证新区块的连续性(与上一个区块的哈希值是否相同)
        if not self.blockchain[-1].block_hash == new_block.previous_block_hash:
            return False
        return True

    # 共识机制使用工作量证明(PoW)
    def _mining(self, transaction_pool: TransactionPool, node_list: List['Node']) -> Union[Block, None]:
        # 从交易池中取出所有交易
        # 一般不会取出所有的交易,矿工可以基于自己的策略和目标选择交易,而且区块大小也有限制,这里做简化,假设每次挖矿都选择交易池中所有的交易
        transaction_list = []
        while not transaction_pool.transaction_queue.empty():
            transaction_list.append(transaction_pool.get_transaction())
        # 计算新区块的哈希值
        # 这里会进行一个非常耗时的计算得到哈希值,这里简化直接对上一个区块的哈希值和交易列表以及时间戳做sha256哈希编码
        time_stamp = datetime.datetime.now()
        header_bin = str(self.blockchain[-1].block_hash) + str(transaction_list) + str(time_stamp)
        new_hash = hashlib.sha256(header_bin.encode()).hexdigest()
        # 创建新的区块
        new_block = Block(len(self.blockchain), self.blockchain[-1].block_hash, transaction_list, time_stamp, new_hash)
        # 广播每个节点进行验证
        # 这里简化只执行一次,实际上每个节点都会验证,当大多数节点验证通过才会通过
        if self._verify_block(new_block, node_list):
            self._balance = self._balance + 10  # 矿工奖励
            return new_block
        else:
            return None

    def add_block(self, new_block):
        self._add_block(new_block)

    def mining(self, transaction_pool: TransactionPool, node_list: List['Node']) -> Union[Block, None]:
        return self._mining(transaction_pool, node_list)

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

    @property
    def balance(self):
        return self._balance

    @balance.setter
    def balance(self, value):
        self._balance = value

    @property
    def blockchain(self):
        return self._blockchain

    @blockchain.setter
    def blockchain(self, value):
        self._blockchain = value

    @property
    def node_id(self):
        return self._node_id

    @node_id.setter
    def node_id(self, value):
        self._node_id = value

    @property
    def signature(self):
        return self._signature

    @signature.setter
    def signature(self, value):
        self._signature = value

2.5 验证

这里为简化就在主函数中写面向过程的函数了,可以改成社区类或者其他类完成整个流程:

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;