python区块链实现简版工作量证明

目录
  • 说明
  • 回顾
  • 工作量证明
  • 哈希计算
  • Hashcash
  • 实现

说明

本文根据https://github.com/liuchengxu/blockchain-tutorial的内容,用python实现的,但根据个人的理解进行了一些修改,大量引用了原文的内容。文章末尾有"本节完整源码实现地址"。

回顾

上一节,我们构造了一个非常简单的数据结构 – 区块,它也是整个区块链数据库的核心。目前所完成的区块链原型,已经可以通过链式关系把区块相互关联起来:每个块都与前一个块相关联。

但是,当前实现的区块链有一个巨大的缺陷:向链中加入区块太容易,也太廉价了。而区块链和比特币的其中一个核心就是,要想加入新的区块,必须先完成一些非常困难的工作。在本文,我们将会弥补这个缺陷。

工作量证明

区块链的一个关键点就是,一个人必须经过一系列困难的工作,才能将数据放入到区块链中。正是由于这种困难的工作,才保证了区块链的安全和一致。此外,完成这个工作的人,也会获得相应奖励(这也就是通过挖矿获得币)。

这个机制与生活现象非常类似:一个人必须通过努力工作,才能够获得回报或者奖励,用以支撑他们的生活。在区块链中,是通过网络中的参与者(矿工)不断的工作来支撑起了整个网络。矿工不断地向区块链中加入新块,然后获得相应的奖励。在这种机制的作用下,新生成的区块能够被安全地加入到区块链中,它维护了整个区块链数据库的稳定性。值得注意的是,完成了这个工作的人必须要证明这一点,即他必须要证明他的确完成了这些工作。

整个 “努力工作并进行证明” 的机制,就叫做工作量证明(proof-of-work)。要想完成工作非常地不容易,因为这需要大量的计算能力:即便是高性能计算机,也无法在短时间内快速完成。另外,这个工作的困难度会随着时间不断增长,以保持每 10 分钟出 1 个新块的速度。在比特币中,这个工作就是找到一个块的哈希,同时这个哈希满足了一些必要条件。这个哈希,也就充当了证明的角色。因此,寻求证明(寻找有效哈希),就是矿工实际要做的事情。

哈希计算

获得指定数据的一个哈希值的过程,就叫做哈希计算。一个哈希,就是对所计算数据的一个唯一表示。对于一个哈希函数,输入任意大小的数据,它会输出一个固定大小的哈希值。下面是哈希的几个关键特性:

  • 无法从一个哈希值恢复原始数据。也就是说,哈希并不是加密。
  • 对于特定的数据,只能有一个哈希,并且这个哈希是唯一的。
  • 即使是仅仅改变输入数据中的一个字节,也会导致输出一个完全不同的哈希。

本质上哈希是一个摘要算法。

哈希函数被广泛用于检测数据的一致性。软件提供者常常在除了提供软件包以外,还会发布校验和。当下载完一个文件以后,你可以用哈希函数对下载好的文件计算一个哈希,并与作者提供的哈希进行比较,以此来保证文件下载的完整性。

在区块链中,哈希被用于保证一个块的一致性。哈希算法的输入数据包含了前一个块的哈希,因此使得不太可能(或者,至少很困难)去修改链中的一个块:因为如果一个人想要修改前面一个块的哈希,那么他必须要重新计算这个块以及后面所有块的哈希。

Hashcash

比特币使用 Hashcash ,一个最初用来防止垃圾邮件的工作量证明算法。它可以被分解为以下步骤:

取一些公开的数据(比如,如果是 email 的话,它可以是接收者的邮件地址;在比特币中,它是区块头)

给这个公开数据添加一个计数器。计数器默认从 0 开始

将 data(数据) 和 counter(计数器) 组合到一起,获得一个哈希

检查哈希是否符合一定的条件:

  • 如果符合条件,结束
  • 如果不符合,增加计数器,重复步骤 3-4

因此,这是一个暴力算法:改变计数器,计算新的哈希,检查,增加计数器,计算哈希,检查,如此往复。这也是为什么说它的计算成本很高,因为这一步需要如此反复不断地计算和检查。

现在,让我们来仔细看一下一个哈希要满足的必要条件。在原始的 Hashcash 实现中,它的要求是 “一个哈希的前 20 位必须是 0”。在比特币中,这个要求会随着时间而不断变化。因为按照设计,必须保证每 10 分钟生成一个块,而不论计算能力会随着时间增长,或者是会有越来越多的矿工进入网络,所以需要动态调整这个必要条件。

为了阐释这一算法,我从前一个例子(“I like donuts”)中取得数据,并且找到了一个前 3 个字节是全是 0 的哈希。

实现

这里我们实现一个简易的区块链,就不动态调节难度了,使用固定的难度。

class ProofOfWork(object):
    """
    pow
    """
    _N_BITS = 16
    MAX_BITS = 256
    MAX_SIZE = sys.maxsize
    def __init__(self, block, n_bits=_N_BITS):
        self._n_bits = n_bits
        self._target_bits = 1 << (self.MAX_BITS - n_bits)
        self._block = block

这里的_n_bits就是难度值。 在比特币中,当一个块被挖出来以后,“n_bits” 代表了区块头里存储的难度,也就是开头有多少个 0。这里的 16 指的是算出来的哈希前 16 位必须是 0,如果用 16 进制表示,就是前 6 位必须是 0,这一点从最后的输出可以看出来。目前我们并不会实现一个动态调整目标的算法,所以将难度定义为一个全局的常量即可。

16 其实是一个可以任意取的数字,其目的只是为了有一个目标而已,这个目标占据不到 256 位的内存空间。同时,我们想要有足够的差异性,但是又不至于大的过分,因为差异性越大,就越难找到一个合适的哈希。这里的
_target_bits则表示满足要求的最大值,即一个上界,它是由1左移256-n_bits位来的。计算出来的哈希只要满足小于它就满足条件了。

接下来我们要准备用于计算哈希的数据:

    def _prepare_data(self, nonce):
        data_lst = [str(self._block.block_header.prev_block_hash),
                    str(self._block.block_header.hash_merkle_root),
                    str(self._block.block_header.timestamp),
                    str(self._block.block_header.height),
                    str(nonce)]
        return utils.encode(''.join(data_lst))

nonce就是我们要不断尝试要寻找的值,就是上面 Hashcash 所提到的计数器,它是一个密码学术语。其他数据都是区块头的数据。我们需要把这些数据进行合并作为计算哈希的原数据。

寻找nonce的方法:

    def run(self):
        nonce = 0
        found = False
        hash_hex = None
        print('Mining a new block')
        while nonce < self.MAX_SIZE:
            data = self._prepare_data(nonce)
            hash_hex = utils.sum256_hex(data)
            hash_val = int(hash_hex, 16)
            sys.stdout.write("try nonce == %d hash_hex == %s \r" % (nonce, hash_hex))
            if (hash_val < self._target_bits):
                found = True
                break
            nonce += 1
        if found:
            print('Found nonce == %d' % nonce)
        else:
            print('Not Found nonce')
            raise NonceNotFoundError('nonce not found')
        return nonce, hash_hex

为防止溢出,我们要设定一个上线为int64的上限。然后我们不断循环寻找目标值,直到满足难度要求。当然,如果难度设计得过高,有可能寻找不到,所以也需要判断一下。所以我们再循环内做了一下事:

1. 准备数据

2. 用 SHA-256 对数据进行哈希

3. 将哈希转换成一个大整数

4. 将这个大整数与目标进行比较

然后我们还需要很方便的去检验这个块的难度值是否满足我们的要求:

    def validate(self):
        """
        validate the block
        """
        data = self._prepare_data(self._block.block_header.nonce)
        hash_hex = utils.sum256_hex(data)
        hash_val = int(hash_hex, 16)
        return hash_val < self._target_bits

最后运行以前的main.py,结果如下:

Mining a new block
...
...
...
Block(_block_header=BlockHeader(timestamp='1548213145.24', hash_merkle_root='', prev_block_hash='', hash='00008fbcbe3a817641195652d9bad37fa8c974536f152f4bc575b3ead9dc6407', nonce=62489, height=0))Block(_block_header=BlockHeader(timestamp='1548213166.65', hash_merkle_root='', prev_block_hash='00008fbcbe3a817641195652d9bad37fa8c974536f152f4bc575b3ead9dc6407', hash='9e851f78295e7933cd9749f712d1f09f1408dff9bd37cc2f79f1c65d1ab39e2e', nonce=16184, height=1))Block(_block_header=BlockHeader(timestamp='1548213171.15', hash_merkle_root='', prev_block_hash='9e851f78295e7933cd9749f712d1f09f1408dff9bd37cc2f79f1c65d1ab39e2e', hash='f88e7a382dafc50b01c43cbbdbbdfa20ac2bffcf5ddf36b97439ff09203f8c2a', nonce=8286, height=2))

可以看到这次我们产生三个块花费了25秒多,比没有工作量证明之前慢了很多(也就是成本高了很多)。

参考:

[1] proof-of-work

[2] 完整源码

以上就是python区块链实现简版工作量证明的详细内容,更多关于python区块链工作量证明的资料请关注我们其它相关文章!

(0)

相关推荐

  • Python区块链范围结论及Genesis Block的添加教程

    目录 Genesis Block添加 结论 Genesis Block添加 将块添加到区块链包括将创建的块附加到我们的 TPCoins 列表. TPCoins.append (block0) 请注意,与系统中的其余块不同,genesis块只包含一个由TPCoins系统的发起者启动的事务.现在,您将通过调用我们的全局函数转储区块链的内容 dump_blockchain : dump_blockchain(TPCoins) 执行此功能时,您将看到以下输出 : Number of blocks in 

  • python区块链简易版交易完善挖矿奖励示例

    目录 说明 引言 奖励 UTXO 集 Merkle 树 P2PKH 总结 说明 本文根据https://github.com/liuchengxu/blockchain-tutorial的内容,用python实现的,但根据个人的理解进行了一些修改,大量引用了原文的内容.文章末尾有"本节完整源码实现地址". 引言 在这个系列文章的一开始,我们就提到了,区块链是一个分布式数据库.不过在之前的文章中,我们选择性地跳过了“分布式”这个部分,而是将注意力都放到了“数据库”部分.到目前为止,我们几

  • Python区块链块的添加教程

    目录 添加第一个区块 添加更多块 转储整个区块链 每个矿工将从先前创建的交易池中获取交易.要跟踪已挖掘的消息数量,我们必须创建一个全局变量 : last_transaction_index = 0 我们现在让我们的第一个矿工在区块链中添加一个区块. 添加第一个区块 到添加一个新块,我们首先创建一个 Block 类的实例 block = Block() 我们从队列中选取前3个交易并减去; for i in range(3):    temp_transaction = transactions[l

  • python区块链简易版交易实现示例

    目录 说明 引言 比特币交易 交易输出 发送币 余额查看 总结 说明 本文根据https://github.com/liuchengxu/blockchain-tutorial的内容,用python实现的,但根据个人的理解进行了一些修改,大量引用了原文的内容.文章末尾有"本节完整源码实现地址". 引言 交易(transaction)是比特币的核心所在,而区块链唯一的目的,也正是为了能够安全可靠地存储交易.在区块链中,交易一旦被创建,就没有任何人能够再去修改或是删除它.今天,我们将会开始

  • Python区块链创世块创建教程

    区块链包含彼此链接的区块列表.要存储整个列表,我们将创建一个名为TPCoins : 的列表变量; TPCoins = [] 我们还将编写一个名为 dump_blockchain 的实用程序方法来转储整个区块链的内容.我们首先打印区块链的长度,以便我们知道区块链中当前存在多少个区块 def dump_blockchain (self):    print ("Number of blocks in the chain: " + str(len (self))) 请注意,随着时间的推移,数

  • python区块链基本原型简版实现示例

    目录 说明 引言 区块 区块头 区块链 总结 说明 本文根据https://github.com/liuchengxu/blockchain-tutorial的内容,用python实现的,但根据个人的理解进行了一些修改,大量引用了原文的内容.文章末尾有"本节完整源码实现地址". 引言 区块链是 21 世纪最具革命性的技术之一,它仍然处于不断成长的阶段,而且还有很多潜力尚未显现. 本质上,区块链只是一个分布式数据库而已. 不过,使它独一无二的是,区块链是一个公开的数据库,而不是一个私人数

  • python区块链实现简版工作量证明

    目录 说明 回顾 工作量证明 哈希计算 Hashcash 实现 说明 本文根据https://github.com/liuchengxu/blockchain-tutorial的内容,用python实现的,但根据个人的理解进行了一些修改,大量引用了原文的内容.文章末尾有"本节完整源码实现地址". 回顾 在上一节,我们构造了一个非常简单的数据结构 – 区块,它也是整个区块链数据库的核心.目前所完成的区块链原型,已经可以通过链式关系把区块相互关联起来:每个块都与前一个块相关联. 但是,当前

  • python区块链实现简版网络

    目录 说明 引言 区块链网络 kademlia发现协议 简化协议 消息 TCP服务端 TCP客户端 P2P服务器 连接节点 RPC 测试 区块同步方式 问题 总结 说明 本文根据https://github.com/liuchengxu/blockchain-tutorial的内容,用python实现的,但根据个人的理解进行了一些修改,大量引用了原文的内容.文章末尾有"本节完整源码实现地址". 引言 到目前为止,我们所构建的原型已经具备了区块链所有的关键特性:匿名,安全,随机生成的地址

  • python区块链持久化和命令行接口实现简版

    目录 说明 引言 选择数据库 couchdb couchdb的安装 数据库结构 序列化 持久化 区块链迭代器 CLI 测试一下 说明 本文根据https://github.com/liuchengxu/blockchain-tutorial的内容,用python实现的,但根据个人的理解进行了一些修改,大量引用了原文的内容.文章末尾有"本节完整源码实现地址". 引言 到目前为止,我们已经构建了一个有工作量证明机制的区块链.有了工作量证明,挖矿也就有了着落.虽然目前距离一个有着完整功能的区

  • python区块链地址的简版实现

    说明 本文根据https://github.com/liuchengxu/blockchain-tutorial 的内容,用python实现的,但根据个人的理解进行了一些修改,大量引用了原文的内容.文章末尾有"本节完整源码实现地址". 引言 在上一篇文章中,我们已经初步实现了交易.相信你应该了解了交易中的一些天然属性,这些属性没有丝毫“个人”色彩的存在:在比特币中,没有用户账户,不需要也不会在任何地方存储个人数据(比如姓名,护照号码或者 SSN).但是,我们总要有某种途径识别出你是交易

  • Python区块链Creating Miners教程

    目录 消息摘要函数 挖掘函数 第1步 第2步 第3步 测试挖掘函数 为了实现挖掘,我们需要开发一个挖掘功能.挖掘功能需要在给定的消息字符串上生成摘要并提供工作证明.让我们在本章讨论这个. 消息摘要函数 我们将编写一个名为 sha256 的实用程序函数来创建给定消息的摘要 : def sha256(message): return hashlib.sha256(message.encode('ascii')).hexdigest() sha256 函数将消息作为参数进行编码它为ASCII,生成十六

  • python区块链创建多个交易教程

    目录 创建多个交易 显示事务 交易队列 创建多个客户端 创建第一个事务 添加更多交易 转储交易 创建多个交易 各个客户进行的交易在系统中排队;矿工从这个队列中获取交易并将其添加到块中.然后他们将挖掘区块,获胜的矿工将有权将区块添加到区块链中,从而为自己赚取一些钱. 我们将在稍后讨论这个挖掘过程区块链的创建.在我们为多个事务编写代码之前,让我们添加一个小实用程序函数来打印给定事务的内容. 显示事务 display_transaction 函数接受事务类型的单个参数.接收到的事务中的字典对象被复制到

  • Python区块链客户端类开发教程

    目录 开发客户端 客户端类 客户端 测试客户端 开发客户端 客户是持有TPCoins并从网络上的其他供应商处交换商品/服务的客户,包括他自己的.我们应该为此目的定义 Client 类.要为客户端创建全局唯一标识,我们使用PKI(公钥基础结构).在本章中,让我们详细讨论一下. 客户应该能够将钱包从另一个已知的人那里汇款.同样,客户应该能够接受来自第三方的钱.对于花钱,客户将创建一个指定发件人姓名和支付金额的交易.为了收款,客户将向第三方提供他的身份 : 本质上是钱的发送者.我们不存储客户持有的钱包

随机推荐