前言
學(xué)習(xí)區(qū)塊鏈有些時(shí)間了,根據(jù)大佬的文章用Go也實(shí)現(xiàn)了一遍,但作用一名web碼農(nóng),平時(shí)用的最多的還是PHP,所以打算用PHP再搞一搞,當(dāng)是復(fù)習(xí)了。本教程基于LaravelZero(因?yàn)闃?gòu)建命令行省事兒)簡單構(gòu)建一個(gè)屬于你的區(qū)塊鏈,如果你有一些區(qū)塊鏈的前置知識那就最好不過啦。
參考文章:
初始化
- 根據(jù) LaravelZero 文檔,先創(chuàng)建一個(gè)項(xiàng)目,確保能順利跑起來。
- 在app目錄下創(chuàng)建 Services 文件夾,咱們的代碼都寫在這里面。
區(qū)塊鏈?zhǔn)鞘裁?/h1>
簡單來說,區(qū)塊鏈?zhǔn)且粋€(gè)提供了拜占庭容錯、并保證了最終一致性的分布式數(shù)據(jù)庫;從數(shù)據(jù)結(jié)構(gòu)上看,它是基于時(shí)間序列的鏈?zhǔn)綌?shù)據(jù)塊結(jié)構(gòu);從節(jié)點(diǎn)拓?fù)渖峡矗械墓?jié)點(diǎn)互為冗余備份;從操作上看,它提供了基于密碼學(xué)的公私鑰管理體系來管理賬戶。
簡單來說,區(qū)塊鏈?zhǔn)且粋€(gè)提供了拜占庭容錯、并保證了最終一致性的分布式數(shù)據(jù)庫;從數(shù)據(jù)結(jié)構(gòu)上看,它是基于時(shí)間序列的鏈?zhǔn)綌?shù)據(jù)塊結(jié)構(gòu);從節(jié)點(diǎn)拓?fù)渖峡矗械墓?jié)點(diǎn)互為冗余備份;從操作上看,它提供了基于密碼學(xué)的公私鑰管理體系來管理賬戶。
組成區(qū)塊鏈的核心技術(shù)主要有:
- P2P 網(wǎng)絡(luò)協(xié)議
- 分布式一致性算法
- 加密簽名算法
- 賬戶與存儲模型
在本文中,我們將利用數(shù)組來存儲區(qū)塊,來實(shí)現(xiàn)區(qū)塊鏈的基本原型。
區(qū)塊
在區(qū)塊鏈中,真正存儲有效信息的是區(qū)塊(block)。
那就創(chuàng)建一個(gè)區(qū)塊,在 App/Services 目錄新建一個(gè) Block.php。
<?php
namespace App\Services;
use Carbon\Carbon;
class Block
{
/**
* 當(dāng)前時(shí)間戳,也就是區(qū)塊創(chuàng)建的時(shí)間
* @var int $timestamp
*/
public $timestamp;
/**
* 區(qū)塊存儲的信息,也就是交易
* @var string $data
*/
public $data;
/**
* 前一個(gè)塊的哈希,即父哈希
* @var string $prevBlockHash
*/
public $prevBlockHash;
/**
* 當(dāng)前塊的哈希
* @var string $hash
*/
public $hash;
}
在比特幣中,區(qū)塊的結(jié)構(gòu)主要有如下信息:
| Field | Purpose | Size(Bytes) |
|---|---|---|
| Block Size | 區(qū)塊大小 | 4 |
| Block Header | 區(qū)塊頭 | 80 |
| Transaction Counter | 交易計(jì)數(shù)器 | 1~9 |
| Transactions | 交易 | - |
區(qū)塊頭的主要結(jié)構(gòu):
| 字段 | 描述 | 大小 |
|---|---|---|
| Version | Block version number | 4 |
| hashPrevBlock | 256-bit hash of the previous block header | 32 |
| hashMerkleRoot | 256-bit hash based on all of the transactions in the block | 32 |
| Time | Current timestamp as seconds since 1970-01-01T00:00 UTC | 4 |
| Bits | Current target in compact format | 4 |
| Nonce | 32-bit number (starts at 0) | 4 |
其中交易(Transactions)也就是我們創(chuàng)建的 data 字段;而其他字段(prevBlockHash,timestamp)在比特幣中由專門的數(shù)據(jù)結(jié)構(gòu),即區(qū)塊頭存儲;另外為了方便,我們還加上了當(dāng)前塊的 hash 字段,在比特幣中是沒有直接存儲當(dāng)前塊的哈希的。
為了方便,我們就不單獨(dú)實(shí)現(xiàn)區(qū)塊頭了,而是所有數(shù)據(jù)都放在一起,這樣我們就創(chuàng)建了一個(gè)簡單的區(qū)塊。
下面我們來完善區(qū)塊的創(chuàng)建:
Block.php
public function __construct(string $data, string $prevBlockHash)
{
$this->prevBlockHash = $prevBlockHash;
$this->data = $data;
$this->timestamp = time();
$this->hash = $this->setHash();
}
public function setHash(): string
{
return hash('sha256', implode('', [$this->timestamp, $this->prevBlockHash, $this->data]));
}
區(qū)塊鏈
本質(zhì)上,區(qū)塊鏈就是一個(gè)有著特定結(jié)構(gòu)的數(shù)據(jù)庫,是一個(gè)有序,每一個(gè)塊都連接到前一個(gè)塊的鏈表。也就是說,區(qū)塊按照插入的順序進(jìn)行存儲,每個(gè)塊都與前一個(gè)塊相連。這樣的結(jié)構(gòu),能夠讓我們快速地獲取鏈上的最新塊,并且高效地通過哈希來檢索一個(gè)塊。
有了區(qū)塊,下面讓我們來實(shí)現(xiàn)區(qū)塊鏈。在基本原型階段,我們用數(shù)組來存放區(qū)塊結(jié)構(gòu)。新建一個(gè)文件 BlockChain.php
<?php
namespace App\Services;
class BlockChain
{
/**
* @var Block[] $blocks
*/
public $blocks;
public function __construct(Block $block)
{
$this->blocks[] = $block;
}
// 加入一個(gè)塊到區(qū)塊鏈中
public function addBlock(string $data)
{
$prevBlock = $this->blocks[count($this->blocks) - 1];
$newBlock = new Block($data, $prevBlock->hash);
$this->blocks[] = $newBlock;
}
}
addBlock() 方法使我們有了向鏈中添加區(qū)塊的能力,但是還沒完,在添加區(qū)塊以前,區(qū)塊鏈中應(yīng)該存在一個(gè)創(chuàng)世區(qū)塊。
// 初始化創(chuàng)世區(qū)塊
public static function NewGenesisBlock()
{
$block = new Block('Genesis Block', '');
return new BlockChain($block);
}
大功告成,下面讓我們來測試一下!
$bc = BlockChain::NewGenesisBlock();
$bc->addBlock('i am 2 block');
$bc->addBlock('i am 3 block');
dd($bc->blocks);
結(jié)果如下:
array:3 [
0 => App\Services\Block {#155
+timestamp: 1587700797
+data: "Genesis Block"
+prevBlockHash: ""
+hash: "e0ecf169c2ea5143743438fd91778ca6cdff1c870120c784f836650cfaaaff38"
}
1 => App\Services\Block {#153
+timestamp: 1587700797
+data: "i am 2 block"
+prevBlockHash: "e0ecf169c2ea5143743438fd91778ca6cdff1c870120c784f836650cfaaaff38"
+hash: "c33a37e9708a2871c6b03fd2f6468081edb4e96d911c7227ad84271d9c4f8b6d"
}
2 => App\Services\Block {#160
+timestamp: 1587700797
+data: "i am 3 block"
+prevBlockHash: "c33a37e9708a2871c6b03fd2f6468081edb4e96d911c7227ad84271d9c4f8b6d"
+hash: "de300a998179bfeeb3ea60be3f49e205f8552b8ab9d506a76d847f0ba9d93646"
}
]
NICE!