區塊鏈:通過演示Demo理解區塊鏈執行原理
BlockChain Demo
開啟比特幣Demo演示網頁區塊鏈Demo演示地址,我們可以看到如下頁面。
點選開始演示
接下來是BlockChain Demo 2.0的新功能介紹
關於Demo功能區的介紹
顯示每個區塊儲存的資訊
介紹區塊鏈中區塊的索引
介紹建立區塊時候的時間戳
介紹區塊中hash雜湊加密
介紹區塊hash雜湊中前導零
也就是難度
的介紹
// cosnt Block = reuqire("./Block.js");
// class Blockchain {
// constructor() {
// this.blockchain = [Block.genesis()];
this.difficulty = 3 ;
// }
// get() { ... }
// get latestBlock() { ... }
isValidHashDifficulty(hash) {
for (var i = 0; i < hash.length; i++) {
if (hash[i] !== "0") {
break;
};
}
return i >= this.difficulty;
}
// };
// module.exports = Blockchain;
介紹Hash雜湊的生成規則
(索引+上一個雜湊+時間戳+資料+隨機數) =雜湊
(0 +“0”+ 1508270000000 +“歡迎使用Blockchain Demo 2.0!”+ 604)= 000dc75a315c77a1f9c98fb6247d03dd18ac52632d7dc6a9920261d8109b37cf
介紹關於上一個區塊中的Hash值
關於區塊中的資料
,這也是我們關注的
關於資料的變化
,資料的微小變化都可以給Hash值帶來巨大的變化
。Hash值的是否有效
,是我們設定的前導零難度決定的
,符合前導零難度的規則才算是有效的Hash
假如一個區塊鏈中的某一個區塊中Hash值失效
,那麼它後面的區塊都會失效
生成有效Hash雜湊的唯二變數就是資料和隨機數
,當我們需要的資料固定時
匹配的前導零難度的隨機數
才能尋找到新的區塊
// const Block = require("./Block.js");
// const crypto = require("crypto");
// class Blockchain {
// constructor() { ... }
// get() { ... }
// get latestBlock() { ... }
// isValidHashDifficulty(hash) { ... }
// calculateHashForBlock(block) { ... }
// calculateHash(...) { ... }
mine(data) {
const newBlock = this.generateNextBlock(data);
try {
this.addBlock(newBlock);
} catch (err) {
throw err;
};
}
// };
// module.exports = Blockchain;
匹配查詢有效Hash的數字,從0開始找,直到找到。隨著難度的不斷增加,有效雜湊的數量減少,因此匹配的計算力也會逐漸增大
。
新增新的區塊
// const Block = require("./Block.js");
// const crypto = require("crypto");
// class Blockchain {
// constructor() { ... }
// get() { ... }
// get latestBlock() { ... }
// isValidHashDifficulty(hash) { ... }
// calculateHashForBlock(block) { ... }
// calculateHash(...) { ... }
// mine(data) { ... }
// generateNextBlock(data) { ... }
addBlock(newBlock) {
if (this.isValidNewBlock(newBlock, this.latestBlock)) {
this.blockchain.push(newBlock);
} else {
throw "Error: Invalid block";
}
}
// };
// module.exports = Blockchain;
新增區塊需要滿足的要求
- 上一個區塊的索引+1等於新的索引
- 上一個區塊的hash要等於下一個區塊的前導hash
- hash要滿足前導零難度要求
- 正確的hash值
// const Block = require("./Block.js");
// const crypto = require("crypto");
// class Blockchain {
// constructor() { ... }
// get() { ... }
// get latestBlock() { ... }
// isValidHashDifficulty(hash) { ... }
// calculateHashForBlock(block) { ... }
// calculateHash(...) { ... }
// mine(data) { ... }
// generateNextBlock(data) { ... }
// addBlock(newBlock) { ... }
isValidNextBlock(nextBlock, previousBlock) {
const nextBlockHash = this.calculateHashForBlock(nextBlock);
if (previousBlock.index + 1 !== nextBlock.index) {
return false;
} else if (previousBlock.hash !== nextBlock.previousHash) {
return false;
} else if (nextBlockHash !== nextBlock.hash) {
return false;
} else if (!this.isValidHashDifficulty(nextBlockHash)) {
return false;
} else {
return true;
}
}
// };
// module.exports = Blockchain;
全球的計算機網路共同維護,因此要確保區塊鏈的安全性
,正確性
及一致性
。
P2p.js
const wrtc = require('wrtc');
const Exchange = require('peer-exchange');
const p2p = new Exchange("Blockchain Demo 2.0", { wrtc: wrtc });
const net = require("net");
class PeerToPeer {
constructor(blockchain) {
this.peers = [];
this.blockchain = blockchain;
}
startServer(port) {
const server = net
.createServer(socket =>
p2p.accept(socket, (err, conn) => {
if (err) {
throw err;
} else {
this.initConnection.call(this, conn);
}
})
)
.listen(port);
}
}
module.exports = PeerToPeer;
同伴訊息:連結對等方新增到網路
// const wrtc = require('wrtc');
// const Exchange = require('peer-exchange');
// const p2p = new Exchange(...);
// const net = require("net");
// class PeerToPeer {
// constructor(blockchain) { ... }
// startServer(port) { ... }
discoverPeers() {
p2p.getNewPeer((err, conn) => {
if (err) {
throw err;
} else {
this.initConnection.call(this, conn);
}
});
}
// }
// module.exports = PeerToPeer;
同伴訊息:Demo的三種同伴訊息的狀態
- 藍色:當前有效的
- 綠色:連線的
- 紅色:斷開連線的
改變:直接點選頭像
連結:在紅色對等方單擊加號的圖示
檢視:在綠色的對等體單擊簡訊圖示
斷開:點選綠色對等方上面的加號圖示
P2p.js
// const wrtc = require('wrtc');
// const Exchange = require('peer-exchange');
// const p2p = new Exchange(...);
// const net = require("net");
// class PeerToPeer {
// constructor(blockchain) { ... }
// startServer(port) { ... }
// discoverPeers() { ... }
connectToPeer(host, port) {
const socket = net.connect(port, host, () =>
p2p.connect(socket, (err, conn) => {
if (err) {
throw err;
} else {
this.initConnection.call(this, conn);
}
})
);
}
closeConnection() {
p2p.close(err => {
throw err;
})
}
// }
// module.exports = PeerToPeer;
同伴訊息:確定誰擁有最新的區塊鏈
Blockchain.js
// const wrtc = require('wrtc');
// const Exchange = require('peer-exchange');
// const p2p = new Exchange(...);
// const net = require("net");
const messageType = {
REQUEST_LATEST_BLOCK: 0,
RECEIVE_LATEST_BLOCK: 1,
REQUEST_BLOCKCHAIN: 2,
RECEIVE_BLOCKCHAIN: 3,
};
const {
REQUEST_LATEST_BLOCK,
RECEIVE_LATEST_BLOCK,
REQUEST_BLOCKCHAIN,
RECEIVE_BLOCKCHAIN,
REQUEST_TRANSACTIONS,
RECEIVE_TRANSACTIONS
} = messageType;
// class PeerToPeer { ... }
// module.exports = PeerToPeer;
class Messages {
static getLatestBlock() {
return {
type: REQUEST_LATEST_BLOCK
};
}
static sendLatestBlock(block) {
return {
type: RECEIVE_LATEST_BLOCK,
data: block
};
}
static getBlockchain() {
return {
type: REQUEST_BLOCKCHAIN
};
}
static sendBlockchain(blockchain) {
return {
type: RECEIVE_BLOCKCHAIN,
data: blockchain
};
}
}
邏輯圖
P2p.js
// const wrtc = require('wrtc');
// const Exchange = require('peer-exchange');
// const p2p = new Exchange(...);
// const net = require("net");
// const messageType = { ... };
// const { ... } = messageType;
// class PeerToPeer {
// constructor(blockchain) { ... }
// startServer(port) { ... }
// discoverPeers() { ... }
// connectToPeer(host, port) { ... }
// closeConnection() { ... }
broadcastLatest() {
this.broadcast(Messages.sendLatestBlock(this.blockchain.latestBlock));
}
broadcast(message) {
this.peers.forEach(peer => this.write(peer, message));
}
write(peer, message) {
peer.write(JSON.stringify(message));
}
initConnection(connection) {
this.peers.push(connection);
this.initMessageHandler(connection);
this.initErrorHandler(connection);
this.write(connection, Messages.getLatestBlock());
}
initMessageHandler(connection) {
connection.on("data", data => {
const message = JSON.parse(data.toString("utf8"));
this.handleMessage(connection, message);
});
}
initErrorHandler(connection) {
connection.on("error", err => {
throw err;
});
}
handleMessage(peer, message) {
switch (message.type) {
case REQUEST_LATEST_BLOCK:
this.write(peer, Messages.sendLatestBlock(this.blockchain.latestBlock));
break;
case REQUEST_BLOCKCHAIN:
this.write(peer, Messages.sendBlockchain(this.blockchain.get()));
break;
case RECEIVE_LATEST_BLOCK:
this.handleReceivedLatestBlock(message, peer);
break;
case RECEIVE_BLOCKCHAIN:
this.handleReceivedBlockchain(message);
break;
default:
throw "Received invalid message.";
}
}
// }
// module.exports = PeerToPeer;
// class Messages { ... }
P2p.js
// const wrtc = require('wrtc');
// const Exchange = require('peer-exchange');
// const p2p = new Exchange(...);
// const net = require("net");
// const messageType = { ... };
// const { ... } = messageType;
// class PeerToPeer {
// constructor(blockchain) { ... }
// startServer(port) { ... }
// discoverPeers() { ... }
// connectToPeer(host, port) { ... }
// closeConnection() { ... }
// broadcastLatest() { ... }
// broadcast(message) { ... }
// write(peer, message) { ... }
// initConnection(connection) { ... }
// initMessageHandler(connection) { ... }
// initErrorHandler(connection) { ... }
// handleMessage(peer, message) { ... }
handleReceivedLatestBlock(message, peer) {
const receivedBlock = message.data;
const latestBlock = this.blockchain.latestBlock;
if (latestBlock.hash === receivedBlock.previousHash) {
try {
this.blockchain.addBlock(receivedBlock);
} catch(err) {
throw err;
}
} else if (receivedBlock.index > latestBlock.index) {
this.write(peer, Messages.getBlockchain());
} else {
// Do nothing.
}
}
// }
// module.exports = PeerToPeer;
// class Messages { ... }
P2p.js
handleReceivedLatestBlock(message, peer) {
// if (latestBlock.hash === receivedBlock.previousHash) {
// ...
} else if (receivedBlock.index > latestBlock.index) {
this.write(peer, Messages.getBlockchain());
} else {
// Do nothing.
}
}
handleReceivedBlockchain(message) {
const receivedChain = message.data;
try {
this.blockchain.replaceChain(receivedChain);
} catch(err) {
throw err;
}
}
isValidChain(chain) {
if (JSON.stringify(chain[0]) !== JSON.stringify(Block.genesis)) {
return false;
}
const tempChain = [chain[0]];
for (let i = 1; i < chain.length; i = i + 1) {
if (this.isValidNextBlock(chain[i], tempChain[i - 1])) {
tempChain.push(chain[i]);
} else {
return false;
}
}
return true;
}
isChainLonger(chain) {
return chain.length > this.blockchain.length;
}
replaceChain(newChain) {
if (this.isValidChain(newChain) && this.isChainLonger(newChain)) {
this.blockchain = JSON.parse(JSON.stringify(newChain));
} else {
throw "Error: invalid chain";
}
}
終端演示
安裝命令列工具
npm install blockchain-cli -g
blockchain
在blockchian
->後面輸入blockchain
或者bc
檢視創始區塊結構。
創世區塊
Index (Block #):
第幾個區塊? (創世區塊鏈的索引為0)Hash:
當前區塊的hash值Previous Hash:
上一個區塊的hash值Timestamp:
當前區塊建立時的時間戳Data:
儲存在當前區塊上的交易資訊Nonce:
在找到有效區塊之前,我們經歷的迭代次數
mine data資料
建立好了第一個區塊
Hash是怎麼計算的?
Hash值
是一個十六進位制
固定長度為64位
的唯一的標識。
hash
值是由index
, previous block hash
, timestamp
, block data
, 和 nonce
作為輸入資料計算而得。
CryptoJS.SHA256(index + previousHash + timestamp + data + nonce)
四個前導0是有效雜湊的最低要求。 所需的前導0的數量稱為難度
。
下面的方法驗證hash難度是否有效。
function isValidHashDifficulty(hash, difficulty) {
for (var i = 0, b = hash.length; i < b; i ++) {
if (hash[i] !== '0') {
break;
}
}
return i >= difficulty;
}
nonce
是一個用來找到滿足條件的hash值的數字。
let nonce = 0;
let hash;
let input;
while(!isValidHashDifficulty(hash)) {
nonce = nonce + 1;
input = index + previousHash + timestamp + data + nonce;
hash = CryptoJS.SHA256(input)
}
nonce
值一直迭代,直到hash
值有效為止。在我們案例中一個有效的hash值是最少有4個前導0
。找到nonce值以滿足合適條件的hash值的過程就叫做挖礦
。
隨著難度的增加,可能的有效雜湊數減少。 使用較少可能的有效雜湊,需要更多的處理能力才能找到有效的雜湊。