什么是区块链?官方的解释是:区块链是一个分布式记帐系统,是藉用密码学串接并保护其内容的串连交易记录(又称区块)。每个区块包含了前一个区块的加密散列、对应的时间戳记以及交易数据(一般用默克尔树算法计算的散列值表示),这样的设计使得区块内容具备难以被篡改的特性。用区块链所串接的分布式帐本能让两方有效率地纪录交易,且此交易可永久被查验。php
但这个解释对于初学者来讲太抽象了,因此接下来咱们将会使用PHP来实现一个简易的区块链来加深对区块链的理解。html
你们应该玩过成语接龙,规则是这样:我先说一个成语“人山人海”,下一个玩家须要使用我说的成语的最后一个字做为下一个成语的开头,就是说须要使用“海”这个字做为新成语的开头,这时就能够接一个“海阔天空”。算法
而区块链的形式有点像成语接龙,就是下一个区块必须使用上一个区块的Hash值做为凭据来生成下一个区块。以下图:数据库
这样作的好处是:从任意一个区块开始均可以经过前一个区块的Hash值能够不断的追溯整条区块链,直到创世区块(也就是区块链的第一个区块)。若是有人恶意攻击,也必须更改整条区块链的数据。可是计算Hash值是一个耗时的操做,因此要更改整条区块链的数据基本是不可能达到,这就保证了区块链的安全性。数组
下面咱们使用PHP代码来定义区块:安全
class Block {
public $prevHash;
public $hash;
public $timeStamp;
public $data;
}
复制代码
hash:当前区块的Hash值bash
timeStamp:区块生成的时间戳markdown
data:区块保存的数据分布式
prevHash、hash和timeStamp这几个字段在区块链中被称为区块头,区块的Hash值使用SHA-256算法计算。计算方法以下:函数
<php class Block { ... public function setBlockHash() { $data = serialize($this); $this->hash = hash('sha256', $data); } } 复制代码
首先咱们使用serialize()函数把整个区块序列化,而后使用hash()函数计算区块的Hash值,并赋值给hash字段。
<php class Block { ... public function __construct($prevHash, $data) { $this->prevHash = $prevHash; $this->timeStamp = time(); $this->data = $data; $this->setBlockHash(); } } 复制代码
另外咱们提供一个获取区块Hash值的方法:
<?php class Block { ... public function getBlockHash() { return $this->hash; } } 复制代码
前面说了,区块链就是按照必定的规则链接起来的区块,链接的规则就是下一个区块的区块头中必须包含前一个区块的Hash值。咱们编写一个区块链对象来保存整条区块链,代码以下:
<?php include('block.php'); class Blockchain { public $blocks = []; } 复制代码
区块链对象内部使用了一个数组来保存全部的区块,现阶段咱们尚未使用到数据库来保存区块链,因此如今只须要把区块链保存在内存便可。
向区块链添加一个新的区块代码以下:
<?php include('block.php'); class Blockchain { ... public function addBlock($data) { $prevBlock = $this->blocks[count($this->blocks)-1]; $this->blocks[] = new Block($prevBlock->getBlockHash(), $data); } } 复制代码
由于生成新区块必须包含前一个区块的Hash值,因此在添加新区块时须要获取区块链中最后一个区块做为新区块的前一个区块,而后把前一个区块的Hash包含到新区块的区块头中。
可能聪明的读者会发现,在区块链刚建立时并无任何区块,那么添加新区块时拿哪一个区块做为前一个区块呢?答案就是创世区块。创世区块不用包含前一个区块的Hash值,并且随着区块链的建立被建立,代码以下:
<?php include('block.php'); class Blockchain { ... public function __construct() { $this->blocks[] = new Block('', 'Genesis Block'); } } 复制代码
创世区块并不须要包含前一个区块的Hash值,因此在建立创世区块时把前一个区块的Hash值设置为空。
OK!咱们的简易区块链已经完成了,如今来测试一下咱们的代码吧:
<?php include('blockchain.php'); $bc = new Blockchain(); $bc->addBlock('This is block1'); $bc->addBlock('This is block2'); foreach ($bc->blocks as $block) { printf("PrevHash: %s\n", $block->prevHash); printf("Hash: %s\n", $block->hash); printf("Data: %s\n", $block->data); printf("\n"); } 复制代码
咱们来运行一下测试代码,运行结果以下:
很好,结果符合咱们的预期。
本文只是实现了一个最简易的区块链,离完整的区块链还有很是远的距离。在咱们如今的实现中存在不少不足,如:添加一个区块的成本很低,没有实现分布式,不能保存到本地磁盘(重启机器数据就会丢失)。