PHP也能實現區塊鏈?

引言

什麼是區塊鏈?官方的解釋是:區塊鏈是一個分佈式記帳系統,是藉用密碼學串接並保護其內容的串連交易記錄(又稱區塊)。每個區塊包含了前一個區塊的加密散列、對應的時間戳記以及交易數據(一般用默克爾樹算法計算的散列值表示),這樣的設計使得區塊內容具備難以被篡改的特性。用區塊鏈所串接的分佈式帳本能讓兩方有效率地紀錄交易,且此交易可永久被查驗。php


但這個解釋對於初學者來講太抽象了,因此接下來咱們將會使用PHP來實現一個簡易的區塊鏈來加深對區塊鏈的理解。html


區塊

你們應該玩過成語接龍,規則是這樣:我先說一個成語「人山人海」,下一個玩家須要使用我說的成語的最後一個字做爲下一個成語的開頭,就是說須要使用「海」這個字做爲新成語的開頭,這時就能夠接一個「海闊天空」。算法


而區塊鏈的形式有點像成語接龍,就是下一個區塊必須使用上一個區塊的Hash值做爲憑據來生成下一個區塊。以下圖:數據庫


這樣作的好處是:從任意一個區塊開始均可以經過前一個區塊的Hash值能夠不斷的追溯整條區塊鏈,直到創世區塊(也就是區塊鏈的第一個區塊)。若是有人惡意攻擊,也必須更改整條區塊鏈的數據。可是計算Hash值是一個耗時的操做,因此要更改整條區塊鏈的數據基本是不可能達到,這就保證了區塊鏈的安全性。數組

下面咱們使用PHP代碼來定義區塊:安全

class Block {
    public $prevHash;
    public $hash;
    public $timeStamp;
    public $data;
}
 
複製代碼
  • prevHash:前一個區塊的Hash值
  • 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");
}
複製代碼

咱們來運行一下測試代碼,運行結果以下:

很好,結果符合咱們的預期。


總結

本文只是實現了一個最簡易的區塊鏈,離完整的區塊鏈還有很是遠的距離。在咱們如今的實現中存在不少不足,如:添加一個區塊的成本很低,沒有實現分佈式,不能保存到本地磁盤(重啓機器數據就會丟失)。

閱讀原文

相關文章
相關標籤/搜索