昨天我們成功實作了區塊鏈的基礎資料結構,但如果你有實際執行昨天的程式碼,會發現 addBlock() 幾乎是在一瞬間就執行完畢並把區塊推入陣列中了。
在真實的分散式網路中,如果任何人都能在一秒內產生幾千幾萬個區塊,整個網路會立刻被垃圾區塊塞爆(這在資安上類似於一種 DoS 攻擊),且各個節點之間也無法有足夠的時間來同步最新的鏈狀態。
因此,我們需要一個機制來「故意拖慢」區塊產生的速度,這就是中本聰在比特幣中引入的工作量證明 (Proof of Work, PoW),俗稱「挖礦」。今天我們就要在程式碼中加入這個機制。
核心概念:Nonce 與難度 (Difficulty)
要怎麼讓電腦「刻意花時間」呢?區塊鏈的做法是設定一個數學難題。
我們要求產生的區塊 Hash 值,開頭必須包含特定數量的 0(例如:0000x...)。
但我們知道,只要區塊內的資料固定,算出來的 Hash 值就永遠是固定的。為了改變 Hash 的結果,我們必須在區塊中加入一個隨機應變的變數,這個變數就叫做 Nonce (Number used once)。礦工必須不斷更改 Nonce 的值,直到算出來的 Hash 剛好符合難度要求為止。
實作:修改 Block 類別
打開昨天的程式碼,我們需要對 Block 類別進行升級:加入 nonce,並實作 mineBlock 方法。
'''
const SHA256 = require('crypto-js/sha256');
class Block {
constructor(index, timestamp, data, previousHash = '') {
this.index = index;
this.timestamp = timestamp;
this.data = data;
this.previousHash = previousHash;
this.nonce = 0; // 新增:初始值設為 0
this.hash = this.calculateHash();
}
calculateHash() {
// 新增:把 nonce 也加入 Hash 計算的字串中
return SHA256(this.index + this.previousHash + this.timestamp + JSON.stringify(this.data) + this.nonce).toString();
}
// 新增:挖礦方法
mineBlock(difficulty) {
// 產生一個長度為 difficulty 的字串,內容全是 '0'
// 例如 difficulty 為 4,target 就是 '0000'
const target = Array(difficulty).join("0");
// 迴圈不斷執行,直到目前的 Hash 前面幾碼符合 target
while (this.hash.substring(0, difficulty) !== target) {
this.nonce++; // 更改 Nonce 值
this.hash = this.calculateHash(); // 重新計算 Hash
}
console.log(`區塊開採成功!Hash: ${this.hash}, 嘗試次數: ${this.nonce}`);
}
}
'''
實作:修改 Blockchain 類別
接著,我們要告訴 Blockchain 目前的「難度」是多少,並在加入區塊前要求先進行挖礦。
'''
class Blockchain {
constructor() {
this.chain = [this.createGenesisBlock()];
this.difficulty = 4; // 新增:設定難度,數字越大越難算
}
// ... (保留原本的 createGenesisBlock, getLatestBlock, isChainValid 方法)
addBlock(newBlock) {
newBlock.previousHash = this.getLatestBlock().hash;
// 改變:不再直接計算 hash,而是呼叫 mineBlock 強制電腦去算數學題
// newBlock.hash = newBlock.calculateHash();
newBlock.mineBlock(this.difficulty);
this.chain.push(newBlock);
}
}
'''
測試 PoW 機制
現在,讓我們再次執行我們的程式碼,看看會發生什麼事:
'''
let myIoTChain = new Blockchain();
console.log("正在開採區塊 1...");
myIoTChain.addBlock(new Block(1, "2026-09-22", { amount: 4 }));
console.log("正在開採區塊 2...");
myIoTChain.addBlock(new Block(2, "2026-09-23", { amount: 8 }));
'''
終端機輸出預期結果:
你會發現,程式不再是瞬間印出結果了。電腦風扇可能會轉一下,過了幾秒鐘後才印出:
'''
正在開採區塊 1...
區塊開採成功!Hash: 00008c2a... , 嘗試次數: 45892
正在開採區塊 2...
區塊開採成功!Hash: 0000b91e... , 嘗試次數: 120531
'''
透過實作 PoW,我們成功地掌握了區塊鏈的產塊節奏。這就是為什麼駭客很難竄改過去的區塊——因為一旦他改了其中一個區塊,他不只要重新計算那個區塊的 Hash,還要「重新挖出」後續所有區塊的工作量證明!在算力不如整個網路的情況下,這是根本做不到的。