源码地址:PHP从零实现区块链(二)工作量证明 - 简书
注:本例只是从网页版实现一下原理,源码非本人所写,只是将原帖的源码更改了一下,变成网页版
因为运行环境问题,本例暂时从windows转到ubuntu下,因为后面例子使用了gmp库的gmp_pow和gmp_cmp函数,而php在windows下暂时没有找到使用gmp的解决方案。
所以直接用ubuntu系统来运行本例,支持的比较友好。(后面看情况是否转回windows)
关于怎么在ubuntu下搭建php运行环境,可以参考我这篇文章:
ubuntu下安装php运行环境-CSDN博客
好了,接下来都是在ubuntu下操作。
因为要使用gmp_pow和gmp_cmp函数,我们需要安装gmp库。
打开终端输入如下命令安装:
udo apt-get install php-gmp
然后将gmp和php关联一下,如下命令:
sudo phpenmod gmp
但是显示文件模块不在php 8.1下。我一看是下了8.2相关的。
所以我们得加上版本号(你们看情况选择自己的版本号 php -v可查看你当前的php版本)
1. sudo apt-get install php8.1-gmp
OK,然后运行
2. sudo phpenmod gmp
接着再重启一下apache
3. sudo /etc/init.d/apache2 restart
好,正常的话,上面三步就能搞定了。
接着我们来个例子调用一下gmp_pow函数测试一下,求2的10次方,如下:
<?php
$pow1 = gmp_pow("2", 10);
echo $pow1;
?>
结果:
OK,接下来我们正式来研究这个例子.
首先我们新增ProofOfWork.php文件,代码如下:
<?php
define('targetBits',24);
class ProofOfWork
{
/**
* @var Block $block
*/
public $block;
/**
* 目标值(计算结果要小于这个目标值才有效)
* @var GMP $target
*/
public $target;
public function __construct(Block $block)
{
// $targetBits = config('blockchain.targetBits');
$this->target = gmp_pow('2', (256 - targetBits));
$this->block = $block;
}
public function prepareData(int $nonce): string
{
return implode('', [
$this->block->timestamp,
$this->block->prevBlockHash,
$this->block->data,
$nonce
]);
}
public function run(): array
{
$nonce = 0;
$hash = '';
while (true) {
$data = $this->prepareData($nonce);
$hash = hash('sha256', $data);
if (gmp_cmp('0x' . $hash, $this->target) == -1) {
break;
}
$nonce++;
}
return [$nonce, $hash];
}
}
在里面,我做了一些更改,(config()因为要安装laravel框架来使用,这里我暂时先用常量代替。在下一章中再介绍安装。)
用下面两句代替config():
define('targetBits',24);
$this->target = gmp_pow('2', (256 - targetBits));
ProofOfWork 类解释:
block在得到前三个变量数据后,就把block通过ProofOfWork的构造函数传进去。
然后在ProofOfWork里的run()函数产生一个变动的数字nonce。再调用prepareData函数,把这个nonce和block的三个数据合在一起得到data返回去。
run()得到data后,就对data进行哈希运算,然后把哈希值和提前设定的哈希值比较,
如果小于设定的哈希值,那么就符合要求了。如果不符合,则nonce+1,把新数字放进去再进行哈希运算,如此循环,直到得到符合要求的哈希值,然后就把符合要求的nonce和哈希值传回去。
接下来就是block.php,我们要更改block类的构造函数。换proofOfwork来得到哈希值。
完整代码如下:
<?php
require_once 'ProofOfWork.php';
class Block
{
/**
* 当前时间戳,也就是区块创建的时间
* @var int $timestamp
*/
public $timestamp;
/**
* 区块存储的信息,也就是交易
* @var string $data
*/
public $data;
/**
* 前一个块的哈希,即父哈希
* @var string $prevBlockHash
*/
public $prevBlockHash;
/**
* 当前块的哈希
* @var string $hash
*/
public $hash;
public $nonce;
public function __construct(string $data, string $prevBlockHash)
{
$this->prevBlockHash = $prevBlockHash;
$this->data = $data;
$this->timestamp = time();
$pow = new ProofOfWork($this);
list($nonce, $hash) = $pow->run();
$this->nonce = $nonce;
$this->hash = $hash;
}
public function setHash(): string
{
return hash('sha256', implode('', [$this->timestamp, $this->prevBlockHash, $this->data]));
}
}
开头引用一下require_once 'ProofOfWork.php';
解释:这里添加了block的三个数据后,跟原先相比,要添加哈希值,不是采用setHash直接添加了。而是调用$pow->run找到符合要求的哈希值后,才进行添加。
OK,接着修改一下app.php如下:
<?php
require_once 'BlockChain.php';
$time1 = time();
$bc = BlockChain::NewGenesisBlock();
$bc->addBlock('i am 2 block');
$bc->addBlock('i am 3 block');
$time2 = time();
$spend = $time2 - $time1;
foreach ($bc->blocks as $block){
print_r($block);
echo('<hr>');
}
echo('花费时间(s):'.$spend);
接下来运行一下,但是很久没响应,然后报这个错 :
原是程序执行超时了,因为一直在进行哈希运算寻找符合要求的哈希值,然后一直没找到。
这个我们可以去把超时时间设置的大一点即可。
不过我们也可以把难度调小一些, 就是将define('targetBits',24);改为define('targetBits',16);
意思,只是找前4位为0的哈希值就行了,不用前6位为0。
更改后OK,运行如下:
可以看到哈希值前4位都为0,说明程序运行正常。
接着我们改一下超时时间,将难度重新调为24,找一 下前6位为0的哈希值。
将php.ini下两句都改成300,最大时间5分钟。
max_execution_time=300
max_input_time=300
改完后重启一下apache。接着再次打开app.php,找到了需要的哈希值:
总算找到了,花费3分多钟,这个有点看运气,我测试了几次,有时候20秒也找到了。
PS:再讲一下这个$this->target = gmp_pow('2', (256 - targetBits));难度控制逻辑吧。
这里写的有点乱,凑合看一下吧。
首先这算的是一个求2的平方,我们来看看,2的二进制,是怎么表示的:
0010
好,这样一个二进制数字,它的平方是什么?两个0010相加,就是0100。
然后又是乘2平方,1000。
什么意思呢,就是2的n次方,他的二进制就是正好有一个1,然后其它全是0。
不是1011,也不是0101。每多一次就是把1往前进一位。
那么可以想到,2的256次方肯定是100000000...这样开始的。
那么2的248,肯定也是跟2的256相比,它的前八位肯定是0。
二进制前八位是0,我们知道八位一个字节,一个字节的范围0到256
一个字节的16进制表示,就是“00”到“FF”。
那么二进制前八位是00,那么换成十六进制两位肯定也是00了。
那么难度24,设定的目标值,就是2的232次方,他的二进制前三个字节是全是0,
那么意思就是16进制的前6位都是0。
而哈希值我们知道是16进制64个字符的,那么两个字符一个字节,就是32个字节。
八位一个字节,就是32*8,256位二进制。
而2的256次方,正好也要256位二进制表示。就是10000000000.....开头。
那么得到的哈希值,如果要小于2的256次方,是不是有很多?太容易找到了。
因为2的256次方差不多已经是最大哈希值了,所以我设定减24,那么符合2的232次方的范围变小了。这个值也难找一些。
所以targetBits难度值每加1,那么二进制里的1就会右移一位,变小。文章来源:https://www.toymoban.com/news/detail-777717.html
大概就是这样。文章来源地址https://www.toymoban.com/news/detail-777717.html
到了这里,关于PHP从零实现区块链(网页版二)工作量证明的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!