未加星标

区块链扫盲篇之使用 PHP 实现区块链(二):工作量证明

字体大小 | |
[开发(php) 所属分类 开发(php) | 发布者 店小二03 | 时间 2018 | 作者 红领巾 ] 0人收藏点击收藏

1

前言

上一篇文章我们介绍了区块链的最基本数据结构-区块,而且还构建了一个最原始的区块链。但是现在我们很容易就可以向区块链中添加区块,这样有可能导致大量的区块在同时添加到区块链中,从而导致广播风暴。而且在分布式环境中,如果并发量太大会导致很多问题,例如拜占庭问题。

为了解决这个问题,比特币使用了工作量证明机制。

2

工作量证明

由于现在添加一个区块的成本很低,所以必须找到一个增加添加区块成本的方法,而在比特币中使用的是工作量证明,我们也效仿比特币使用工作量证明。

工作量证明(POW,Proof-of-Work)是一个用于阻止拒绝服务攻击和类似垃圾邮件等服务错误问题的协议,它在 1993 年被 Cynthia Dwork 和 Moni Naor 提出。

那么什么是工作量证明呢?其实很简单,就是找到一个符合某一规定的Hash值。例如我们规定区块的Hash值的前 20 个位必须为 0,要符合这样的区块才能添加到区块链中,那么工作量证明就是要找到符合这样规定的Hash值。

由于找到这样的Hash值是非常困难的,所以会给予找到合适Hash值的人相应奖励(比特币),找到符合规定Hash值的过程被称为“挖矿”,而挖矿的机器被称为“矿工”。

3

Hash值计算

如前面所说,我们需要找到一个符合规定的Hash值才能将区块添加到区块链中,而在我们的前一篇文章中计算区块Hash值的方法是直接序列化区块,然后使用SHA-256来计算其Hash值。那么有个问题是,区块的内容是不变的,所以计算出来的Hash值也是固定的,那么有什么办法来改变区块的Hash值呢?这里我们使用Hashcash 算法,步骤如下:

1. 取一些公开的数据(比如区块头)。 2. 给这个公开数据添加一个计数器。计数器默认从 0 开始。 3. 将数据和计数器组合到一起,获得一个Hash值。 4. 检查Hash值是否符合规定的条件: 1) 如果符合条件,结束 2) 如果不符合,增加计数器,重复步骤 3-4 可以看出这个是一个暴力计算的过程:改变计数器,计算新的Hash值,检查是否符合条件,直到找到符合条件的Hash值。

在 Hashcash 算法中,它的要求是“一个Hash值的前 20 位必须是 0”。条件越苛刻,找到符合条件的Hash值就越难。下图详细说明了这个过程:


区块链扫盲篇之使用 PHP 实现区块链(二):工作量证明

在上图中,129022是计数器的值,而符合条件的Hash值是前 16 个位为 0 (上图中表现为Hash值的前4个字符为0)。

4

实现

在上一篇文章中的Block对象的实现中,添加一个findBlockHash()的方法用于找到符合条件的Hash值:

<php class Block { ... public $nonce; private function prepareData($nonce) { return json_encode([ $this->prevHash, $this->timeStamp, $this->data, $nonce, ]); } public function findBlockHash() { $found = false; $condition = '0000'; // Hash值前N个字符必须等于$condition $condlength = strlen($condition); printf("Mining the block containing \"%s\"\n", $this->data); for ($nonce = 0; $nonce < PHP_INT_MAX; $nonce++) { $data = $this->prepareData($nonce); $hash = hash('sha256', $data); printf("\r%d: %s", $nonce, $hash); if (substr($hash, 0, $condlength) === $condition) { $found = true; break; } } print("\n\n"); if ($found) { $this->nonce = $nonce; $this->hash = $hash; } return $found; } }

在上面的代码中,我们为Block类添加了一个nonce的成员变量,用于记录计数器。而在findBlockHash()函数中,我们不断增加计数器的值,计算出Hash值,然后比较Hash值是否符合条件(前N个字符是否等于变量$condition)。如果找到合适的Hash值就退出循环,否则增加计数器的值计算下一个Hash值。而 prepareData()方法用于序列化要计算Hash值的数据。

最后,我们在Block类的构造函数中调用findBlockHash()方法:

<?php class Block { ... public function __construct($prevHash, $data) { $this->prevHash = $prevHash; $this->timeStamp = time(); $this->data = $data; $this->findBlockHash(); } ... }

现在我们来运行一下代码看看效果:


区块链扫盲篇之使用 PHP 实现区块链(二):工作量证明

从运行结果可以看到,算出来的Hash值都符合我们规定的条件。当然,你可以修改更苛刻的条件来增加挖矿的难度。

最后还有一件事需要做,就是验证区块是否合法:

<?php class Block { ... public function validate() { $condition = '0000'; $condlength = strlen($condition); $data = $this->prepareData($this->nonce); return substr(hash('sha256', $data), 0, $condlength) === $condition; } }

验证的方法很简单,就是计算出区块的Hash值,然后比较Hash值是否符合条件。然后我们在打印区块链的时候验证区块是否合法:

<?php include('blockchain.php'); $bc = new Blockchain(); $bc->addBlock('This is block1'); $bc->addBlock('This is block2'); foreach ($bc->blocks as $block) { ... printf("PoW: %s\n", $block->validate() ? 'true' : 'false'); ... }

运行代码查看结果:


区块链扫盲篇之使用 PHP 实现区块链(二):工作量证明

结果符合我们的预期,代码在:https://github.com/liexusong/blockchain-php/tree/v2.0

本文开发(php)相关术语:php代码审计工具 php开发工程师 移动开发者大会 移动互联网开发 web开发工程师 软件开发流程 软件开发工程师

tags: Hash,区块,gt,nonce,data,condition,符合,hash,计数器,Block,工作量,添加,lt,findBlockHash
分页:12
转载请注明
本文标题:区块链扫盲篇之使用 PHP 实现区块链(二):工作量证明
本站链接:https://www.codesec.net/view/579584.html


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 开发(php) | 评论(0) | 阅读(37)