互联网金融

区块链是什么?区块链技术基本原理

关键字:
摘要:

区块链不是一种技术实现,而是一个系统的架构设计,使用一系列的技术组合用于完成去中心化的数据存储。 在此我们来从区块链的区块、链、工作量证明等方面介绍下其基本原理,进

区块链不是一种技术实现,而是一个系统的架构设计,使用一系列的技术组合用于完成去中心化的数据存储。

在此我们来从区块链的区块、链、工作量证明等方面介绍下其基本原理,进而对区块链技术有一个整体的认识。

“区块链”是什么

区块链是一种存储数字数据的方式。数据可以是任何内容。对于比特币,它是事务(在帐户之间转移比特币),它甚至可以是文件;这都无关紧要。数据是以区块形式进行存储的,区块使用哈希值链接在一起。因此得名“区块链”。

区块链的神奇之处是在其中添加和存储此类数据的方式,该方式造就了一些非常理想的特征:

历史记录无法更改

系统无法攻破

数据的持久保存

没有单点故障

我们将在下面的内容中为大家剖析区块链如何实现的这些特征。

区块链整体存储结构

存储事务

这里我们使用比较通用的格式来将数据存储在区块链中:json

{

"author": "作者名",

"content": "交易内容",

"timestamp": "交易发生时间"

}

防止区块被篡改

我们希望检测出对区块内存储的数据的任何篡改。在区块链中,这是使用一个哈希函数来实现的。

哈希函数接受任何大小的数据并生成固定大小的数据,该结果通常用于识别输入。

哈希函数特征

它应该很容易计算。

哪怕只更改数据中的一个位,哈希值也应该完全发生变化。

应该无法根据输出哈希值猜出输入。

生成Hash指纹

public function compute_hash()

{

return sha1(json_encode($this->toArray()));

}

链接区块

我们在设置好区块之后还不够,因为区块链是一个区块集合。我们这里是把所有的区块都放到了数组中。但是为了防止有人故意替换数组中的一个区块,我们必须保证区块及顺序不可更改。

这里我们通过类似链表的形式来实现

从区块链的整体结构可以看到除了创始区块外每个区块都有一个previous_hash用于链接到前一个区块的。

Block类初始结构

class Block

{

public $index;

public $transactions;

public $timestamp;

public $previous_hash;

public $hash;

public $nonce;

public function __construct($index, $transactions, $timestamp, $previous_hash)

{

$this->index = $index;

$this->transactions = $transactions;

$this->timestamp = $timestamp;

$this->previous_hash = $previous_hash;

}

public function compute_hash()

{

return sha1(json_encode($this->toArray()));

}

public function toArray()

{

return [

'index' => $this->index,

'transactions'=> $this->transactions,

'timestamp' => $this->timestamp,

'previous_hash' => $this->previous_hash,

'nonce'=> $this->nonce,

'hash' => $this->hash,

];

}

}

Blockchain类

class BlockChain

{

public static $unconfirmed_transactions = [];

public static $chain = [];

/**

* 初始化第一个节点

*/

public static function init()

{

self::$unconfirmed_transactions = [];

self::$chain = [];

self::create_genesis_block();

}

/**

* 创建创世区块 第一个节点传入--init参数生成

*/

private static function create_genesis_block()

{

$genesis_block = new Block(0, [], time(), "0");

$genesis_block->hash = $genesis_block->compute_hash();

self::$chain[] = $genesis_block;

}

/**

* @return Block

*/

public static function get_last_block()

{

return self::$chain[count(self::$chain) - 1];

}

}

工作量证明

现在存在一个问题就是如果我们修改了前一个区块,也可以非常容易的重新计算后续所有区块的哈希值,并创建不同的有效区块链。为此我们必须让计算哈希值的任务变得困难和随机化

在此我们通过对生成的哈希值添加约束,必须已两个零作为前缀的才可以使用,由于哈希值只会在区块内容发生变更后才会改变,所以我们引入一个新字段:nonce(随机数字)。随机数会不断变化直到获得满足约束条件的哈希值。前缀0的数量决定了工作量证明算法的难度。

工作量证明很难计算,但是验证却很容易,只需要运行一次哈希函数即可。

工作量证明算法

class BlockChain

{

/*......*/

public static function proof_of_work(Block $block)

{

$block->nonce = 0;

$hash = $block->compute_hash();

$prefix = self::getPrefix();

while (strpos($hash, $prefix) !== 0) {

$block->nonce += 1;

$hash = $block->compute_hash();

}

return $hash;

}

/**

* 获取前缀,前缀长度决定了工作量证明算法难度

*/

private static function getPrefix()

{

return '00';

}

}

此处的随机数无法快速确定,只能暴力破解

添加区块到链中

添加区块到链中,首先要验证所提供的工作量证明是否正确,以及所添加区块的previous_hash字段是否指向链中最新区块的哈希值。

一下为添加区块到链中的代码:

添加区块到链中

class BlockChain

{

/*......*/

public static function add_block(Block $block, $proof, $broadcast = true)

{

$last_block = self::get_last_block();

$previous_hash = $last_block->hash;

if ($previous_hash != $block->previous_hash) {

return false;

}

if (!self::is_valid_proof($block, $proof)) {

return false;

}

$block->hash = $proof;

self::$chain[] = $block;

return true;

}

private static function is_valid_proof(Block $block, $hash)

{

return strpos($hash, self::getPrefix()) === 0 && $hash == $block->compute_hash();

}

挖矿

事务最初存储在一个未确认事务池中。将未确认事务放入区块中并计算工作量证明的过程被成为区块挖矿。一旦找到满足我们的约束条件的随机数,我们就可以说挖到了一个区块,这个区块就会放入区块链中。

在大多数加密货币(包括比特币)中,作为对耗费算力来计算工作量证明的奖励,矿工可以获得一些加密货币。以下是我们的挖矿函数的格式:

挖矿

class BlockChain

{

/*......*/

public static function add_new_transaction($transaction)

{

self::$unconfirmed_transactions[] = $transaction;

}

public static function mine()

{

if (!self::$unconfirmed_transactions) {

return false;

}

$last_block = self::get_last_block();

$new_block = new Block($last_block->index+1, self::$unconfirmed_transactions, time(), $last_block->hash);

$proof = self::proof_of_work($new_block);

self::add_block($new_block, $proof);

self::$unconfirmed_transactions = [];

return $new_block->index;

}

}

创建接口

为了方便与其它对等节点进行交互,我们在项目中使用fastroute来创建路由及对应的控制层。

控制层基类

class BaseController

{

private $_request;

private $_response;

public function setRequest($request)

{

$this->_request = $request;

}

/**

* @return mixed

*/

public function getRequest()

{

return $this->_request;

}

/**

* @param mixed $response

*/

public function setResponse($response)

{

$this->_response = $response;

}

public function response(string $data, $status = 200, $headers = ['Content-Type' =>'application/json'])

{

$this->_response->status = $status;

foreach ($headers as $key => $header) {

$this->_response->header($key, $header);

}

$this->_response->end($data);

return true;

}

protected function get_json()

{

if (!$this->getRequest()->rawContent()) {

return null;

}

$ret = json_decode($this->getRequest()->rawContent(), true);

return $ret;

}

}

提交新事务

use AppBlock;

use AppBlockChain;

use AppPeer;

class IndexController extends BaseController

{

public function new_transaction()

{

$data = $this->get_json();

$required = ['author', 'content'];

foreach ($required as $field) {

if (!array_key_exists($field, $data)) {

$this->response("Invlaid transaction data", 404);

}

}

$data['timestamp'] = time();

BlockChain::add_new_transaction($data);

$this->response('Success');

}

}

获取节点链副本列表。应用程序中可用来查询要显示的所有帖子

public function get_chain()

{

$data = [

"length" => count(BlockChain::$chain),

"chain" => BlockChain::$chain,

];

$this->response(json_encode($data));

}

请求节点挖掘未确认事务接口。可用来发起挖矿命令

挖矿

public function mine_unconfirmed_transactions()

{

$result = BlockChain::mine();

if (!$result) {

$this->response("No Transactions to mine");

} else {

$this->response("Block #{$result} is mined.");

}

}

添加路由

路由

/**

* @return FastRouteDispatcher

*/

function getRouter()

{

$dispatcher = FastRoutesimpleDispatcher(function (FastRouteRouteCollector $route) {

$route->addRoute('POST', '/new_transaction', 'ControllerIndexController@new_transaction');

$route->addRoute('GET', '/chain', 'ControllerIndexController@get_chain');

$route->addRoute('GET', '/mine', 'ControllerIndexController@mine_unconfirmed_transactions');

$route->addRoute('GET', '/pending_tx', 'ControllerIndexController@get_pending_tx');

$route->addRoute('POST', '/add_nodes', 'ControllerIndexController@add_nodes');

$route->addRoute('POST', '/add_block', 'ControllerIndexController@add_block');

});

return $dispatcher;

}

建立共识和去中心化

当前的代码只能在单个计算机上运行。及时通过哈希值链接了区块,仍然不能信任单个实体。我们需要多个节点来维护,所以我们创建一个接口来让一个节点可获取网络中其它对等节点信息。

Peer类

class Peer

{

public static $peers = [];

public static function add($node)

{

echo "add Node $noden";

$peer_num = count(self::$peers);

$hash = self::hash_peer($node);

if (!array_key_exists($hash, self::$peers)) {

self::$peers[$hash] = $node;

}

if ($peer_num === 0 && count(BlockChain::$chain) === 0) {

self::consensus();

}

}

public static function hash_peer($node)

{

return md5($node);

}

}

添加节点

public function add_nodes()

{

if (!($nodes = $this->get_json())) {

return $this->response("Invalid data" , 400);

}

foreach ($nodes as $node) {

Peer::add($node);

}

$this->response("Success", 200);

}

由于多节点可能存在一节点副本不一致的问题,这种情况下我们需要商定采用链的某个版本来维持整个系统的完整性。即我们需要达成共识。

这里我们采用一种简单的方式,当网络中不同节点出现不一致时,商定采用最长的有效链(最长链是对已完成的最多工作量的有效估算):

共识机制

/**

* 节点之间达成共识,已最长的链为准

* @return bool

*/

public static function consensus()

{

echo "同步最长节点...n";

$longest_chain = null;

$current_length = count(BlockChain::$chain);

$request = new Request();

foreach (self::$peers as $peer) {

$chain = $request->chain($peer);

if ($chain && count($chain['chain']) > $current_length) {

$longest_chain = $chain['chain'];

$current_length = count($chain['chain']);

}

}

if ($longest_chain) {

BlockChain::$chain = array_map(function($chain) {

$block = new Block($chain['index'], $chain['transactions'], $chain['timestamp'],$chain['previous_hash']);

$block->nonce = $chain['nonce'];

$block->hash = $chain['hash'];

return $block;

},$longest_chain);

echo sprintf("最长链: %d %sn", $current_length, json_encode($longest_chain));

return true;

}

return false;

}

最后需要通过一个接口,可以让任何节点宣布它已经挖到一个区块,以便每个人都能更新他们的区块链并继续挖掘其他事务。其它节点可以很容易的验证工作量证明,并将区块添加到各自的链中。

宣布挖矿结果

public function add_block()

{

$block_data = $this->get_json();

echo sprintf("POST add_block %sn", json_encode($block_data));

$block = new Block($block_data['index'], $block_data['transactions'],

$block_data['timestamp'], $block_data['previous_hash']);

$block->nonce = $block_data['nonce'];

$proof = $block_data['hash'];

$added = BlockChain::add_block($block, $proof, false);

if (!$added) {

$this->response("The Block was discarded by the node", 400);

}else {

$this->response("Block added to the chain", 201);

}

}

【免责声明】:怡馨金融网所载文章、数据等内容仅代表作者个人观点,与怡馨金融网无关,转载目的在于传递更多信息,其陈述、观点仅供参考。如若不允许转载,请联系我们,我们将及时撤除。怡馨金融网友情提示:市场有风险,投资需谨慎!

说点什么吧
  • 全部评论(0
    还没有评论,快来抢沙发吧!