Email me: jollen # jollen.org

more: Jollen 的 Embedded Linux 教育訓練

« June 2016 | (回到Blog入口) | February 2017 »

December 2016 歸檔

December 2, 2016

Blockchain Developer - 認識 Genesis Block

Merkle tree 是一種 hash tree,用來表示 hash 值的資料結構。Merkle tree 的發明人是 Ralph Merkle,當然這就是這個資料結構的名稱由來...

本文章採用 Markdown 語法撰寫,若無法完整閱讀全文,請點擊這裡。

Genesis Block

Merkle tree 是一種 hash tree,用來表示 hash 值的資料結構。Merkle tree 的發明人是 Ralph Merkle,當然這就是這個資料結構的名稱由來。

Merkle tree 的基本結構是 binary tree(二元樹),每一個 non-leaf 的節點(node),都被標示一個 hash 值。

圖 1:就是一個 binary Merkle Tree 的結構。其中,Top hash 的部份,就是 Merkle Root。

圖 1 Merkle Tree(圖片來源:https://commons.wikimedia.org/wiki/File%3AHash_tree.png,By Davidgothberg at English Wikipedia,遵循 Public Domain 授權)

圖 1:Merkle Tree(圖片來源:https://commons.wikimedia.org/wiki/File%3AHash_tree.png,By Davidgothberg at English Wikipedia,遵循 Public Domain 授權

學習 Merkle tree 資料結構,可以說是「Blockchain 系統開發者」的第 1 堂課。為什麼這麼說呢?

以圖 2 來看,分散(Distributed)在世界各地的所有 Block 之間,以一個鏈(Chain)的關係串連在一起,這就是 Blockchain(區塊鏈)的概念與名稱由來。

圖 2 Block 與 Chain

圖 2:Block 與 Chain

Block #0

這些分散在世界各地的 Block,都會有一個編號,如圖 3。這個編號就是區塊產生的「順序」。

圖 3 Block #0

圖 3:Block #0

這其中,就一定會有編號為 0 的第一個區塊,這個區塊就稱之為 Genesis Block(創世區塊),學習如何建立 Genesis Block 就是 Blockchain 系統開發者的第 2 堂課。

而區塊的產生「方式」,則可以由 Blockchain 的系統開發者來設計。以 Nakamoto Blockchain 來說(Bitcoin 的 Blockchain 系統),區塊的產生過程,就稱為「挖礦」。

Blockchain 與 Merkle Tree

那 Merkle Tree 與 Blockchain 的關係倒底是什麼呢?將 Blockchain、Genesis block 與 Merkle tree 放在一起討論時,它們的關係就是圖 4。

圖 4 Blockchain、Genesis block 與 Merkle tree

圖 4:Blockchain、Genesis block 與 Merkle tree

如果我發展一個叫做 Jollen's Blockchain 系統時,一個粗略的起步應該就是:

  • 建立 Genesis block,genesis block 會有自已的一個 hash 值,這個值是經由 hash 演算法產生,因此也叫做 hash ID

  • 利用演算法,經過一段困難的演算法運算,產生 Block #1

  • Block #1 也會有自已的 hash ID,同時, Block #1 要使用 PreviousHash 欄位,串接到前一個 Block,它的前一個 Block 就是 Genesis block(Block #0)

  • 同理,產生 Block #2 與更多 Blocks,這些 block 之間都用 PreviousHash 欄位串接,這條鏈就是 Block chain

到這邊,還是很好奇 Merkle tree 的用途啊?回顧圖 4 發現,每個 Block 裡面都會有 Merkle Root 欄位,這個欄位就是一顆 Merkle tree。

區塊倒底能做什麼呢?每一個區塊,都能用來「記帳」,整個 Blockchain 串接起來就是一本完整的帳冊,所以說,Blockchain 也叫做 distributed ledger(分散式帳冊)。

更深入技術來看,區塊裡的 Merkle tree 就是負責記帳的欄位。

其它

December 3, 2016

Blockchain Developer - 開始建立 Genesis Block

使用 Node.js 發展區塊鏈的下一個動作,就是建立 Genesis Block...

本文章採用 Markdown 語法撰寫,若無法完整閱讀全文,請點擊這裡。

Blockchain Developer - 開始建立 Genesis Block

使用 Node.js 發展區塊鏈的下一個動作,就是建立 Genesis Block。

Step 1:定義區塊資料結構

根據 [Blockchain Developer - 認識 Genesis Block] 的說明,區塊的資料結構包含 4 個欄位如下:

  • hash:區塊的 hash ID
  • previousHash:紀錄前一個區塊的 hash ID
  • timestamp:區塊建立的時間
  • merkleRoot:區塊的 merkle tree

以 Node.js 來實作此資料結構,方式是以 function 關鍵字來定義 Block 類別(Class):

function Block(block) {
	this.hash = block.hash || '';
	this.previousHash = block.previousHash || '';
	this.timestamp = block.timestamp || new Date();
	this.merkleRoot = block.merkleRoot || {};
}

Step 2:生成 Hash ID

每個 Block 都有一個獨一無二(uniquely)的編號,這個編號是使用 SHA256 演算法產生,稱之為 Block Hash(即 Block Hash ID)。

Genesis block 的 hash ID 要如何生成呢?原則上是使用 SHA256 演算法來產生,當然開發者也能自行定義 Block Hash 的生成方式。在這篇教學裡,筆者打算根據 Merkle tree 的演算法來生成 Hash ID。

Merkle tree 同樣是使用 SHA256 演算法來產生 Hash ID,標準的 Merkle tree 會使用二次的 SHA256 來計算出 hash ID,這樣的做法也稱為 double SHA256。本文的 Block Hash 就以 double SHA256 來產生。

Node.js 內建的 crypto 模組,就提供了 SHA256 演算法函數。先引入 crypto 模組:

var crypto = require('crypto');

使用 createHmac 函數,計算出第 1 個 hash 值,用法如下:

  • 第 1 個參數,填寫 sha256
  • 第 2 個參數,填寫 secret:任意一段句子即可

執行後,createHmac 會建立 Hmac 的實例化(instance),再呼叫 Hmac 物件的 update 函數,並傳入一段本文來進行 sha256 編碼運算。完成後,呼叫 digest 將結果轉為 hex 格式。

完整範例:

var secret = 'blockchain developer';

var hash1 = crypto.createHmac('sha256', secret)
                   .update('created by jollen')
                   .digest('hex');

得到第 1 個的 hash 值。接著,使用這個 hash 值做為新的 secret,進行第 2 次的 hash 運算:

var hash2 = crypto.createHmac('sha256', hash1)
                   .update('powered by flowchain')
                   .digest('hex');

console.log(hash2);

輸出結果:

dd0e2b79d79be0dfca96b4ad9ac85600097506f06f52bb74f769e02fcc66dec6

這就是 genesis block 的 hash ID 了。

Step 3:定義 Genesis Block

建立 genesis block 最簡單的方式,就是直接「定義」它。建立一個名為 config.js 的檔案,並且直接定義好 genesis block 的欄位資訊:

// Filename: config.js
'use strict';                                                                                              

exports.genesis = {
    hash: 'dd0e2b79d79be0dfca96b4ad9ac85600097506f06f52bb74f769e02fcc66dec6',

    prevHash: '0000000000000000000000000000000000000000000000000000000000000000',

    timestamp: new Date(),
    
    merkleRoot: {}
};

Step 4:建立 Genesis Block

終於來到歷史性的一刻了。先引入事先準備好的 genesis block 定義:

var config = require('../config.js');

接著,再實例化 Block,得到的物件,就是 Genesis Block 了。

// Filename: index.js
var genesis = new Block(config.genesis);

後續可以將 genesis 物件,儲存在 NoSQL 資料庫裡。

小結

創建出 Genesis Block 後,下一個步驟就是幫它加入一個空的 merkle tree。

其它

December 4, 2016

Blockchain Developer - 建立 Merkle Tree

Merkle tree 用來存放交易資訊(transactions),為了要討論更詳細的 Merkle tree 生成過程,假設現在有 2 筆交易正在等候「處理」...

本文章採用 Markdown 語法撰寫,若無法完整閱讀全文,請點擊這裡。

Blockchain Developer - 建立 Merkle Tree

Merkle Tree 的生成過程

Merkle tree 用來存放交易資訊(transactions),為了要討論更詳細的 Merkle tree 生成過程,假設現在有 2 筆交易正在等候「處理」。這 2 筆交易資訊,分別以 Tx0 與 Tx1 來表示。

圖 1 生成 Merkle tree

圖 1 生成 Merkle tree

Merkle tree 節點存放的是 double SHA-256 運算結果。如何將這 2 筆交易資訊,以 Merkle tree 來表示呢?以下是這一顆 Merkle tree 的生成過程。

將 Tx0 的本文(content)以 double SHA-256 進行雜湊運算,並將結果儲存在 HA,表示方法如下:

HA = SHA256( SHA256(Tx0) )

同理,再將 Tx1 進行 double SHA-256 運算,結果儲存於 HB:

HB = SAH256( SHA256(Tx1) )

SHA-256 的運算結果,是一個 64 bytes 的 HEX(十六進位)字串。在得到 HA 與 HB 後,就將這二個字串連接(concat)在一起,成為一個 64*2=128 bytes 的字串,這裡以 HA + HB 來表示。

再將 HA + HB 進行 double SHA-256 運算,結果儲存於 HAB:

HAB = SAH256( SHA256( HA + HB ) )

得到結果 HAB 就是 HA 與 HB 的父節點。這是一個 3 個節點的 binary Merkle tree。

更多交易

如果現在有 Tx0、Tx1、Tx2 與 Tx3 共 4 筆交易呢?完整的 double SHA-256 運算過程就是:

HA = SHA256( SHA256(Tx0) )
HB = SAH256( SHA256(Tx1) )
HC = SAH256( SHA256(Tx2) )
HD = SAH256( SHA256(Tx3) )

HAB = SAH256( SHA256( HA + HB ) )
HCD = SAH256( SHA256( HC + HD ) )

HABCD = SAH256( SHA256( HAB + HCD ) )

最後得到的 binary Merkle tree 就是圖 2。

使用 Node.js 打造 Merkle Tree

Node.js 開發者不一定要自行實作 Merkle tree 演算法,網路上能找到開放源碼的實作。在 GitHub 上可以找到 Merkle 模組,這是 JavaScript 的 Merkle tree 實作,並且支援 SHA-256 在內的多種 hash algorithm。

Step 1:安裝 Merkle 模組

先安裝 Merkle 模組:

$ npm install merkle --save

接著引入 merkle 模組:

var merkle = require('merkle');

建立 root node,並指定使用 SHA-256 演算法:

var merkleRoot = merkle('sha256');

Step 2:準備交易資訊

宣告幾筆交易資訊,例如:

// 建立一筆新的交易紀錄
var tx = ['Created by Jollen'];

交易的內容,現階段可任意填寫。例如,如果有 4 筆交易資訊:

// 建立 4 筆新的交易紀錄
var tx = ['a', 'b', 'c', 'd'];

現在只是練習 Merkle tree 的生成,暫時還沒有定義交易的資料結構,所以填寫任意內容即可。

Step 3:建立完整 Merkle Tree

呼叫 async 函數,傳入所有交易資訊來建立 Merkle tree:

merkleRoot.async(tx, function(err, tree){
});

透過 Callback 函數來取得 Merkle tree。根據 Merkle 官方文件的說明,可以呼叫 tree 物件的 root 函數,來取得 Merkle root 的 Hash 值。以下是完整的範例列表:

var merkle = require('merkle');
var merkleRoot = merkle('sha256');

// 建立一筆新的交易紀錄
var tx = ['a', 'b', 'c', 'd'];

merkleRoot.async(tx, function(err, tree){
    console.log( tree.root() );
});

結出結果:

AB4587D9F4AD6990E0BF4A1C5A836C78CCE881C2B7C4287C0A7DA15B47B8CF1F

更多 Merkle Tree 資訊

如圖 2 所示,Merkle tree 是 binary tree(二元樹),以 4 筆交易量來看,總計會有 6 個節點(nodes),並且「高度」為 3。這個高度稱為 level。

圖 2 Merkle Tree 的 depth 為 2

圖 2 Merkle Tree 的 depth 為 2

這是一個 levels 為 3 的 Merkle tree,排除 leaf nodes 後的高度稱為 depth。所以:

  • HA 與 HB 稱為 leaf nodes
  • 這個 Merkle tree 的 depth 為 2
  • 這個 Merkle tree 的 level 為 3

延續上述範例,取得該 Merkle tree 的 depth 與 levels:

merkleRoot.async(tx, function(err, tree){
    console.log( tree.root() );
    console.log( tree.depth() );
    console.log( tree.levels() );    
});

結出結果:

AB4587D9F4AD6990E0BF4A1C5A836C78CCE881C2B7C4287C0A7DA15B47B8CF1F
2
3

此外,呼叫 level 函數,可以取得指定 level 的所有節點,例如:

merkleRoot.async(tx, function(err, tree){
    console.log( tree.level(1) );
});

輸出結果:

[ '6A20F2EE7789E6BB7F404CC2DD729FF308B724D904F6A455B74D4851ADE5AECB',
  'A99E82F486656840A790C0EF6024D2C02359DE7674A587562FEB81C8970F24DD' ]

如圖 2 所示:

  • level 0 是根節點(root)
  • level 1 有 2 個節點

如果要顯示所有的節點,要怎麼修改程式碼呢?答案如下:

merkleRoot.async(tx, function(err, tree){
    // 印出所有節點
    for (i = 0; i < tree.levels(); i++) {
        console.log( tree.level(i) );
    }
});

視覺化 Merkle Tree

實際撰寫程式,來觀察 4 筆交易資訊的 Merkle tree:

var merkle = require('merkle');
var merkleRoot = merkle('sha256');

// 4 筆交易資訊
var tx = ['a', 'b', 'c', 'd'];

merkleRoot.async(tx, function(err, tree){
    // 印出所有節點
    for (i = 0; i < tree.levels(); i++) {
        console.log( tree.level(i) );
    }
});

輸出結果:

[ 'AB4587D9F4AD6990E0BF4A1C5A836C78CCE881C2B7C4287C0A7DA15B47B8CF1F' ]
[ '6A20F2EE7789E6BB7F404CC2DD729FF308B724D904F6A455B74D4851ADE5AECB',
  'A99E82F486656840A790C0EF6024D2C02359DE7674A587562FEB81C8970F24DD' ]
[ 'CA978112CA1BBDCAFAC231B39A23DC4DA786EFF8147C4E72B9807785AFEE48BB',
  '3E23E8160039594A33894F6564E1B1348BBD7A0088D42C4ACB73EEAED59C009D',
  '2E7D2C03A9507AE265ECF5B5356885A53393A2029D241394997265A1A25AEFC6',
  '18AC3E7343F016890C510E93F935261169D9E3F565436429830FAF0934F4F8E4' ]

為了幫助學習,圖 3 以視覺化的方式,來呈現這個範例的結果。

圖 3 視覺化 Merkle Tree

圖 3 視覺化 Merkle Tree

小結

現在,你學會了如何使用 Node.js 來生成 Merkle tree,並且也更進一步了解 Merkle tree 的結構。

其它

December 5, 2016

Blockchain Developer - 為什麼要挖礦?

交易(transaction)確認後的資訊以 Merkle tree 來做紀錄,所以就要有 Block 來儲存這個 Merkle tree...

本文章採用 Markdown 語法撰寫,若無法完整閱讀全文,請點擊這裡。

為什麼要 Mining?

交易(transaction)確認後的資訊以 Merkle tree 來做紀錄,所以就要有 Block 來儲存這個 Merkle tree。這個時候就需要有新的區塊。

在 Bitcoin 的生態中,mining(挖礦)的主要目的就是「產生新的區塊」,當區塊產生時,就會產生另一個「副作用」:新 Bitcoin 被產生出來。

簡單說,產生新的 Bitcoin 並不是挖礦的主要目的,這只是挖礦的副作用。挖礦的主要目的,是生產區塊來確認並紀錄新的交易資訊。本章的目標,在學習挖礦的基本知識,內容以簡單易懂為原則,並不是介紹如何重新實作 Bitcoin 的挖礦技術。但教學內容會以 Bitcoin 做為實例,輔助說明 mining 技術。

Difficulty

眾所皆知,Bitcoin 的挖礦難度是非常高的。這個意思是:產生新的 Block 是一件非常困難的事情。Bitcoin 將挖礦設計的非常困難,其實是有一個很重要的原因:避免有人任意產生區塊。

要產生新的區塊,就會有所謂的 difficulty(難度),這個 difficulty 的作用是什麼呢?主要目的是:決定新的 hash 值產生條件。

要產生新的區塊前,必須先計算出這個區塊的 Block Hash,區塊的 hash 值如何決定呢?這點後續再談。因為,這裡有一個更重要的問題:如何決定這個 hash 值是否可用?

例如,根據新的交易與其它資訊,運算出一個 double SHA-256 的 hash 值如下:

18AC3E7343F016890C510E93F935261169D9E3F565436429830FAF0934F4F8E4

新的區塊是否就能直接使用這個 hash 值呢?如果可以,表示新的 hash 值已成功(success)建立,如果不行,表示新的 hash 值產生失敗。系統必須不斷進行運算,「直到成功得到新的 hash 值」。

不如用一個簡單的方法,來「定義什麼是 success」:當產生的 hash 值前面「有足夠的零」時,就是 success。例如,「前面至少要有 2 個零」,上述的 hash 值就是失敗的運算。以下的這個 hash 值,則是 success:

0018AC3E7343F016890C510E93F935261169D9E3F565436429830FAF0934F4F8

這個條件就是 difficulty 會儲存在最後一個區塊上,所以修改 22.1 節的範例,加入 difficulty 欄位,並且在 Genesis Block 裡,設定最原始的 difficulty 為「前面至少有 2 個零」。

function Block(block) {
	this.hash = block.hash || '';
	this.previousHash = block.previousHash || '';
	this.timestamp = block.timestamp || new Date();
	this.merkleRoot = block.merkleRoot || {};
	this.difficulty = block.difficulty || '00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
}

以 JavaScript 來實作時,只要以字串比對方式,就可以知道 hash 值是否為 success 了。例如:

if ('CD18AC3E7343F016890C510E93F935261169D9E3F565436429830FAF0934F4' <'00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF') {
	// success
} else {
	// failed
}

越來越難

新的區塊產生後,會「重新調整」這個 difficulty。例如,將 difficulty 調整為「前面至少有 3 個零」,這時,可以將新區塊的 difficulty 欄位設定為:

000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

Difficulty 的條件設定,由每一個區塊鏈系統的設計者所制定。但是有一個基本原則就是:難度必須越來越高,以上述例子來說,要產生「前面 3 個零」的 hash 值,其難度大於「前面 2 個零」的 hash 值。

小結

認識為什麼要 mining 以及什麼是 difficulty 後,就可以開始設計 mining 的演算法了。

其它

December 6, 2016

Blockchain Developer - 簡單易懂的 Mining 演算法設計

假設表 1 是「最後一個 Block」內容,根據先前教學的介紹,要如何挖出新區塊呢...

本文章採用 Markdown 語法撰寫,若無法完整閱讀全文,請點擊這裡。

簡單易懂的 Mining 演算法設計

Mining 演算法初體驗

表 1 是截至目前為止,範例所設計的 Block 資料結構。假設表 1 是「最後一個 Block」內容,根據先前教學的介紹,要如何挖出新區塊呢?

欄位 範例 用途說明
hash dd0e2b79d79be0dfca96b4ad9ac85600097506f06f52bb74f769e02fcc66dec6 Block Hash
previousHash 0000000000000000000000000000000000000000000000000000000000000000 前一個 Block 的 Hash 值
timestamp Tue Dec 06 2016 15:14:58 GMT+0800 (CST) 區塊建立的時間
merkleRoot 851AE7D7390A76384ACA2D7CC29BE820918CA900071FC22F41F5C399BE065558 區塊的 Merkle Root
difficulty 00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 挖礦的困難度

表 1 最後一個 Block 內容

表 1 的內容,將做為「挖礦」的依據:透過最後一個 Block 的資訊,計算出新區塊的 Hash 值。

一個簡單的挖礦演算法實作步驟如下。

Step 1:建立新的 Merkle Tree

假設現在有一筆交易資訊,正等著被紀錄在區塊裡,這筆交易的狀態目前就是「待確認」。挖礦機就要先取得這筆「待確認」的交易資訊,再建立這筆交易的 Merkle tree。

多筆待確認交易的做法也相同:挖礦機先取得這些待確認的交易資訊,並建立它們的 Merkle tree。

以 Bitcoin 的網路來說,Bitcoin network 裡一個稱為「unverified pool」的地方,就是存放這些「待確認」的交易。因此,unverified pool 的設計與實作,是區塊鏈開發者的另一個課程,本教學暫不涉及 unverified pool 的介紹。

延續先前的教學,為一筆交易建立 Merkle tree 的程式碼實作如下:

// 一筆待確認的交易
var tx = [‘Created by Jollen’];

// Merkle root hash
var hashMerkleRoot;

merkleRoot.async(tx, function(err, tree){
    // 取得 Merkle Root 的 Hash
    hashMerkleRoot = tree.level(0)[0];
});

Step 2:定義本文

這裡所講的「本文」,就是用來進行 SHA-256 計算的資料內容。一個簡單的本文定義,需要 3 個項資訊:

  • merkleRoot:由前一個步驟產生
  • previousHash:最後一個區塊的 block hash,未來產生的新區塊,要往前「鏈接」到這個區塊
  • nonce:number once 的簡寫,在加密學裡,nonce 指的是只能使用一次的任意數

為簡化演算法的設計,可以將 nonce 定義為一個「流水號」。因為 nonce 只能使用一次,所以流水號只能「持續遞增」,不能歸零重算。

本文所需的資訊都收集齊全了,接著以 JavaScript 的物件語法,來定義本文如下:

var nonce = 0;

var header = {
	nonce: nonce,
	previousHash: ‘dd0e2b79d79be0dfca96b4ad9ac85600097506f06f52bb74f769e02fcc66dec6’,
	merkleRoot: hashMerkleRoot
};

本文的定義由區塊鏈開發者自行決定,例如:把 timestamp 也加入到本文裡。

Step 3:Double SHA-256 運算

將 header 物件 stringify(轉換為文件)後,使用這個「文件」做為本文,來進行 SHA-256 雜湊運算:

// Secret
var secret = ‘Dummy Blockchain’;

var hash1 = crypto.createHmac(‘sha256’, secret)
					.update( JSON.stringify(header) )
					.digest(‘hex’);

再將得到的 hash 值,做為新的 secret,進行第 2 次運算:

var hash2 = crypto.createHmac(‘sha256’, hash1)
					.update(‘powered by flowchain’)
					.digest(‘hex’);

現在,hash2 存放的就是 Block Hash 的「候選人」。如果 hash2 的值,確認為「success」的話,表示「挖礦成功」了:一個新的區塊被計算出來了。

Step 4:Difficulty 運算

候選人的意思是:它還不一定是成功的 hash 值。必須比對 difficulty 的條件設定,才能決定這個 hash 值是否能使用。

延續先前教學的介紹,假設困難度是「有足夠的零」時,就要進行困難度的確認:

if (hash2 < ‘00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF’) {
	console.log(‘success: ‘ + id);
}

當 hash2 不滿足目前的 difficulty 條件時,就要重新計算,直到成功為止。

以上述的範例來說,當 hash 值不滿足 difficulty 條件時,就變更 nonce 值後,再重新運算。本文範例,使用流水號的方式來產生 nonce 值。

Step 5:完整範例

根據前個的步驟,實作一段簡單的 mining 演算法如下:

var crypto = require(‘crypto’);
var merkle = require(‘merkle’);
var merkleRoot = merkle(‘sha256’);

// Secret
var secret = ‘Dummy Blockchain’;

// Unverified pool
var tx = [‘Created by Jollen’];

merkleRoot.async(tx, function(err, tree){
    // Merkle Root 的 Hash
    var hashMerkleRoot = tree.level(0)[0];
    var nonce = 0;

    var hash = function(nonce) {
	    var header = {
			nonce: nonce,
			previousHash: ‘dd0e2b79d79be0dfca96b4ad9ac85600097506f06f52bb74f769e02fcc66dec6’,
			merkleRoot: hashMerkleRoot
	    };

		var hash1 = crypto.createHmac(‘sha256’, secret)
							.update( JSON.stringify(header) )
							.digest(‘hex’);

		var hash2 = crypto.createHmac(‘sha256’, hash1)
                   			.update(‘powered by flowchain’)
							.digest(‘hex’);

		return hash2;
    };

    while (1) {
    	var id = hash(nonce++);
    	console.log(nonce + ‘: ‘ + id);
		if (id < ‘0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF’) {
			console.log(‘success: ‘ + id);
			break;
		}
    }
});

輸出結果:

…
7590: 9208c185a5d218dcd1a9ce63b4609a21c9ac90e0cad65d3355ce436522ded234
7591: 766ccefa06fd97cf8b1472809e03499321fde6ba1e7341e74bd7bbcdc0a7ce01
7592: f3cb6f4f6ae187556a3ec8218453d3073958eed430155cd73d9a8d2976d30e1f
7593: 74ff8bf0695100c6cce400fde5fcbfbb0574efb79664c229a8044df0525c39ca
7594: 0002db2b239b29f52711a2629e98face0151c2020f48c94a12459a43b24a3f85
success: 0002db2b239b29f52711a2629e98face0151c2020f48c94a12459a43b24a3f85

由這個結果發現,總計 mining 了 7594 次才得到成功的 hash 值。當 difficulty 提升時,mining 所花的時間也會更多。

例如,當困難度為「前面至少 4 個零」時,mining 的次數就增加到 118432 次。挖礦的困難度在於,產生的 hash 值有一定程度的「隨機」性,通常是不太可預期的。

Step 6:難度調整

難度調整是 mining 的重要技術。本文暫不涉及這個部份,現階段,可以採用「前面有足夠的零」做為難度設定條件,並使用上述的範例進行練習。

調整後的 difficulty,以及 nonce 值,都必須儲存在新產生的區塊裡,以做為後續「挖礦」的依據。

更多 Mining 觀念

本節所實作的 mining 演算法,僅只是用來測試的粗淺程式(dirty code)。但透過這 30 行的程式碼,還能很快了解「如何開始設計 mining 的演算法」。

還有更多 mining 的觀念,正等待區塊鏈開發者學習:

  1. 前面有足夠的零:這意味著 difficutly 會到一個極限,也就是當前面的零夠多時,表示這個數字可能是最小了,再也無法算出更小的數值了,這表示區塊的數量是有限的,總有一天會挖完所有的礦
  2. 挖礦機:執行這段挖礦演算法的電腦(正式說法為節點:node),稱為挖礦機
  3. Proof-of-Work:這是來自 Bitcoin 的觀念,大略的意思就是,「大家都同意你真的挖到礦了」,此外還有很多工作要做,像是挖礦機如何彼此間更新並同步資料庫等

Proof-of-work 是一個複雜的系統,除了上述提及的功能外,它還涉及 Peer-to-Peer 的網路技術,這個部份,是區塊鏈開發者的真正挑戰「之一」。

小結

下一個階段是加入資料庫功能,並且將目前為止的區塊鏈系統實作成伺服器。

其它

December 9, 2016

Blockchain Developer - 簡單易懂的 Memory-Hard Function

Bitcoin mining 演算法,就是使用傳統的 SHA-256 函數,而 SHA-256 的優點,也好就是它的一個缺點...

本文章採用 Markdown 語法撰寫,若無法完整閱讀全文,請點擊這裡。

Blockchain Developer - 簡單易懂的 Memory-Hard Function

SHA-256 函數是傳統的 hash 演算法,但是應用在區塊鏈系統時,有一個缺點。Bitcoin mining 演算法,就是使用傳統的 SHA-256 函數,而 SHA-256 的優點,也好就是它的一個缺點。

SHA-256 的問題

為了提升 SHA-256 的計算速度,工程師會利用行平行處理(parallelism)的技術。利用平行運算,大幅提升 SHA-256 的運算速度,這樣做不是很好嗎?

然而,這就是一個問題了。簡單來說,一個能平行化的演算法,就能使用硬體來做加速,例如:使用 GPU、FPGA 或是 ASIC。這裡就是「弊端」所在了。從 Proof-of-Work 的觀點來看,「大家必須公平地做計算」,意思是說,因為 hash 值的運算是「decentralization」的架構,所以「大家的硬體最好一樣」。

Decentralization of Trust

Proof-of-work 的主要工作是 mining。此外,驗證交易的「可信度」,也是 proof-of-work 的一環。Proof-of-work 的工作不此於此,例如:miner 間的資料庫同步,也是包含在內。總之,proof-of-work 很忙。

這些 proof-of-work 的工作,是 miners 一起進行的,而不是由一個中央伺服器(centralized)來統一運算,這就是 decentralization of trust 的觀念。

更簡單來看,這就是所謂的 decentralization(去中心化)架構。以 mining 來說,每個人都可以參與 hash 值的運算(大家都可以挖礦),所以,想「快一點」的人,就會使用 ASIC 挖礦機。可是,有些人是用自已的電腦來挖礦。

到這裡,問題就很清楚了:大家的硬體如果等級不同,挖礦就會「不公平」。當大家的運算速度都差不多,沒有人可以「大幅加速」時,理論上就公平了。

Memory-Hard Function

為了解決這個問題,科學家就想出了一個方法。這個方法非常簡單,首先,你不可能強制每個人都要買一樣的電腦才能挖礦,所以解決方式就要回歸演算法的本質:parallelism。

於是,科學家提出一種稱為 memory-hard function 的 hash 演算法觀念:一種不能或難以平行化的 hash 演算法。

這讓我想過幾年前的一個有趣故事。過去,多核心處理器開始後,開始有 Android 手機的製造廠,以「多核心手機」做為市場宣傳口號。這當然很好啊,「多核就是快」。但是,學軟體的人都知道一個道理,就是「軟體必須支援多核心」。如果你的軟體設計,本身就不是多核心架構,那就會像這支手機一樣:明明是 4 核心,但是開機後,其實只用了 1 個核心,另外 3 個核心被關閉(省電考量)了。

有了 memory-hard function 後,「挖礦」就理論上公平了,並且也能消除弊端。因為,就算有頂極的挖礦機,也會像這支多核心手機一樣:再強的硬體也很難加速軟體運算。

Memory Intensive

Memory-hard function 是怎麼做到這點的呢?上述的「一種不能或難以平行化的 hash 演算法」,其實是利用這個原理:降低平行處理的優勢。讓平行處理難有發揮的空間,這樣就能降低 GFP、FPGA 或 ASIC 挖礦機的優勢了。

要消除平行處理的優勢,只要讓軟體是 memory intensive 即可。Memory intensive 是每天都會看到的現象:記憶體不足時、電腦變慢。

Memory-hard function 的原理就是這樣,在 hash 運算時,可以透過「亂塞一堆資料到記憶體」的方式,降低硬體的運算優勢。這樣的 hash 演算法,就稱為 memory-hard function。這堆要塞到記憶體的資料,稱為 data array(array of data)。

Argon2

Argon2 是屬於 memory-hard function 的一種演算法,Blockchain 開發者會知道它,是因為 Argon2 在 2015 年,從 24 個參賽者中,拿下 PHC 競賽的優勝。

小結

Argon2 可以取代傳統的 SHA-256 函數。如果想要知道原因的話,一般的說法就是:Argon2 沒辦法用 ASIC 挖礦機進行挖礦。

接下來,可以試著 fork 一份 block0 專案,導入 Argon2 演算法,並試著比較 Argon2 與 SHA-256 的挖礦困難度。

其它

關於 December 2016

此頁面包含了在December 2016發表於Jollen's Blog的所有日記,它們從老到新列出。

前一個存檔 June 2016。

後一個存檔 February 2017。

更多信息可在 主索引 頁和 歸檔 頁看到。

Top | 授權條款 | Jollen's Forum: Blog 評論、討論與搜尋
Copyright(c) 2006 www.jollen.org