初識區塊鏈 - 用JS構建你自己的區塊鏈
前言
區塊鏈太複雜,那我們就講點簡單的。用JS來構建你自己的區塊鏈系統,寥寥幾行程式碼就可以說明區塊鏈的底層資料結構,POW挖礦思想和交易過程等。當然了,真實的場景遠遠遠比這複雜。本文的目的僅限於讓大家初步瞭解,初步認識區塊鏈。
文章內容主要參考視訊:使用Javascript建立區塊鏈(https://www.youtube.com/playlist?list=PLzvRQMJ9HDiTqZmbtFisdXFxul5k0F-Q4)
感謝原作者,本文在原視訊基礎上做了修改補充,並加入了個人理解。
認識區塊鏈
區塊鏈顧名思義是由區塊連線而成的鏈,因此最基本的資料結構是塊。每個塊都含有時間戳,資料,雜湊,previousHash等資訊。其中資料用來儲存資料,previousHash是前一個區塊的雜湊值示意如下:
雜湊是對區塊資訊的摘要儲存,雜湊的好處是任意長度的資訊經過雜湊都可以對映成固定長度的字串,如可用SHA256:
calculateHash() {
return SHA256(this.previousHash + this.timestamp + JSON.stringify(this.data)).toString();
}
塊的資料結構
塊的最基本資料結構如下:
class Block {
constructor(timestamp, data, previousHash = '') {
this.timestamp = timestamp;
this.data = data;
this.previousHash = previousHash;
//對hash的計算必須放在最後,保證所有資料賦值正確後再計算
this.hash = this.calculateHash();
}
calculateHash() {
return SHA256(this.previousHash + this.timestamp + JSON.stringify(this.data)).toString();
}
}
BlockChain的資料結構
多個區塊連結而成BlockChain,顯然可用用陣列或連結串列來表示,如:
class BlockChain {
constructor() {
this.chain = [];
}
}
創世區塊
正所謂萬物始於一,區塊鏈的第一個區塊總是需要人為來手動建立,這個區塊的previousHash為空,如:
createGenesisBlock() {
return new Block("2018-11-11 00:00:00", "Genesis block of simple chain", "");
}
區塊鏈的構造方法也應改為:
class BlockChain {
constructor() {
this.chain = [this.createGenesisBlock()];
}
}
新增區塊
每新加一個區塊,必須保證與原有區塊鏈連線起來,即:
class BlockChain {
getLatestBlock() {
return this.chain[this.chain.length - 1];
}
addBlock(newBlock) {
//新區塊的前一個hash值是現有區塊鏈的最後一個區塊的hash值;
newBlock.previousHash = this.getLatestBlock().hash;
//重新計算新區塊的hash值(因為指定了previousHash);
newBlock.hash = newBlock.calculateHash();
//把新區塊加入到鏈中;
this.chain.push(newBlock);
}
...
}
校驗區塊鏈
區塊鏈資料結構的核心是保證前後連結,無法篡改,但是如果有人真的篡改了某個區塊,我們該如何校驗發現呢?最笨也是最自然是想法就是遍歷所有情況,逐一校驗,如:
isChainValid() {
//遍歷所有區塊
for (let i = 1; i < this.chain.length; i++) {
const currentBlock = this.chain[i];
const previousBlock = this.chain[i - 1];
//重新計算當前區塊的hash值,若發現hash值對不上,說明該區塊有資料被篡改,hash值未重新計算
if (currentBlock.hash !== currentBlock.calculateHash()) {
console.error("hash not equal: " + JSON.stringify(currentBlock));
return false;
}
//判斷當前區塊的previousHash是否真的等於前一個區塊的hash,若不等,說明前一個區塊被篡改,雖然hash值被重新計算正確,但是後續區塊的hash值並未重新計算,導致整個鏈斷裂
if (currentBlock.previousHash !== previousBlock.calculateHash) {
console.error("previous hash not right: " + JSON.stringify(currentBlock));
return false;
}
}
return true;
}
跑吧
跑起來看看,即:
let simpleChain = new BlockChain();
simpleChain.addBlock(new Block("2018-11-11 00:00:01", {amount: 10}));
simpleChain.addBlock(new Block("2018-11-11 00:00:02", {amount: 20}));
console.log(JSON.stringify(simpleChain, null, 4));
console.log("is the chain valid? " + simpleChain.isChainValid());
結果如下:
ali-186590cc4a7f:simple-chain shanyao$ node main_1.js
{
"chain": [
{
"timestamp": "2018-11-11 00:00:00",
"data": "Genesis block of simple chain",
"previousHash": "",
"hash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89"
},
{
"timestamp": "2018-11-11 00:00:01",
"data": {
"amount": 10
},
"previousHash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89",
"hash": "150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529"
},
{
"timestamp": "2018-11-11 00:00:02",
"data": {
"amount": 20
},
"previousHash": "150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529",
"hash": "274a7a13ed20118e8cb745654934a7e24a4d59333ba17dfbf5d4cfe0fa8a6e34"
}
]
}
is the chain valid? true
注意看其中的previousHash與雜湊,確實是當前區塊的previousHash指向前一個區塊的雜湊值。
篡改下試試
都說區塊鏈不可篡改,是真的嗎讓我們篡改第2個區塊試試,如?
let simpleChain = new BlockChain();
simpleChain.addBlock(new Block("2018-11-11 00:00:01", {amount: 10}));
simpleChain.addBlock(new Block("2018-11-11 00:00:02", {amount: 20}));
console.log("is the chain valid? " + simpleChain.isChainValid());
//將第2個區塊的資料,由10改為15
simpleChain.chain[1].data = {amount: 15};
console.log("is the chain still valid? " + simpleChain.isChainValid());
console.log(JSON.stringify(simpleChain, null, 4));
結果如下:
ali-186590cc4a7f:simple-chain shanyao$ node main_1.js
is the chain valid? true
hash not equal: {"timestamp":"2018-11-11 00:00:01","data":{"amount":15},"previousHash":"fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89","hash":"150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529"}
is the chain still valid? false
{
"chain": [
{
"timestamp": "2018-11-11 00:00:00",
"data": "Genesis block of simple chain",
"previousHash": "",
"hash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89"
},
{
"timestamp": "2018-11-11 00:00:01",
"data": {
"amount": 15
},
"previousHash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89",
"hash": "150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529"
},
{
"timestamp": "2018-11-11 00:00:02",
"data": {
"amount": 20
},
"previousHash": "150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529",
"hash": "274a7a13ed20118e8cb745654934a7e24a4d59333ba17dfbf5d4cfe0fa8a6e34"
}
]
}
顯然,篡改了資料之後,雜湊值並未重新計算,導致該區塊的雜湊值對不上。
再篡改下試試
那麼,如果我們聰明點,篡改後把雜湊值也重新計算會如何?
let simpleChain = new BlockChain();
simpleChain.addBlock(new Block("2018-11-11 00:00:01", {amount: 10}));
simpleChain.addBlock(new Block("2018-11-11 00:00:02", {amount: 20}));
console.log("is the chain valid? " + simpleChain.isChainValid());
//篡改後重新計算hash值
simpleChain.chain[1].data = {amount: 15};
simpleChain.chain[1].hash = simpleChain.chain[1].calculateHash();
console.log("is the chain still valid? " + simpleChain.isChainValid());
console.log(JSON.stringify(simpleChain, null, 4));
結果如下:
ali-186590cc4a7f:simple-chain shanyao$ node main_1.js
is the chain valid? true
previous hash not right: {"timestamp":"2018-11-11 00:00:02","data":{"amount":20},"previousHash":"150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529","hash":"274a7a13ed20118e8cb745654934a7e24a4d59333ba17dfbf5d4cfe0fa8a6e34"}
is the chain still valid? false
{
"chain": [
{
"timestamp": "2018-11-11 00:00:00",
"data": "Genesis block of simple chain",
"previousHash": "",
"hash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89"
},
{
"timestamp": "2018-11-11 00:00:01",
"data": {
"amount": 15
},
"previousHash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89",
"hash": "74d139274fb692495b7c805dd5822faa0c5b5e6058b6beef96e87e18ab83a6b1"
},
{
"timestamp": "2018-11-11 00:00:02",
"data": {
"amount": 20
},
"previousHash": "150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529",
"hash": "274a7a13ed20118e8cb745654934a7e24a4d59333ba17dfbf5d4cfe0fa8a6e34"
}
]
}
顯然,第3個區塊的previousHash並未指向第2個區塊的雜湊值。
是真的無法篡改嗎
其實並不是,如果我們再聰明一點,把後續區塊的雜湊值也重新計算一下,不就OK了嗎?確實如此,如:
let simpleChain = new BlockChain();
simpleChain.addBlock(new Block("2018-11-11 00:00:01", {amount: 10}));
simpleChain.addBlock(new Block("2018-11-11 00:00:02", {amount: 20}));
console.log("is the chain valid? " + simpleChain.isChainValid());
//篡改第2個區塊
simpleChain.chain[1].data = {amount: 15};
simpleChain.chain[1].hash = simpleChain.chain[1].calculateHash();
//並把第3個區塊也重新計算
simpleChain.chain[2].previousHash = simpleChain.chain[1].hash;
simpleChain.chain[2].hash = simpleChain.chain[2].calculateHash();
console.log("is the chain still valid? " + simpleChain.isChainValid());
console.log(JSON.stringify(simpleChain, null, 4
本文作者:扁鵲他大哥
本文為雲棲社群原創內容,未經允許不得轉載。
相關文章
- 1.3 初識區塊鏈:區塊鏈分類區塊鏈
- 區塊鏈初識區塊鏈
- 谷歌要構建自己的區塊鏈技術谷歌區塊鏈
- 區塊鏈知識,區塊鏈簡史區塊鏈
- 區塊鏈教程、區塊鏈指南、區塊鏈中文手冊、區塊鏈原理區塊鏈
- 區塊鏈100講: 區塊鏈共識的確定性區塊鏈
- 區塊鏈初學者指南區塊鏈
- 區塊鏈2.0架構:以太坊區塊鏈的介紹區塊鏈架構
- 區塊鏈技術開發主鏈區塊鏈的應用分析區塊鏈
- 用Go構建區塊鏈——6.交易2Go區塊鏈
- 區塊鏈100講:區塊鏈為什麼叫“區塊”“鏈”?區塊鏈
- 區塊鏈學習筆記01--區塊鏈常識區塊鏈筆記
- 區塊鏈特徵與區塊鏈技術應用落地區塊鏈特徵
- 區塊鏈公司談區塊鏈技術最新應用區塊鏈
- 構建 EOS 區塊鏈瀏覽器區塊鏈瀏覽器
- 區塊鏈系列1-區塊鏈概述區塊鏈
- 深知區塊鏈,可你知道區塊鏈的專業術語嗎?區塊鏈
- 雲+區塊鏈 實現區塊鏈技術的普惠應用區塊鏈
- 區塊鏈開發公司談區塊鏈的應用場景區塊鏈
- 區塊鏈區塊鏈
- “區塊”和“鏈”的火花,區塊鏈到底為何物區塊鏈
- 用 Python 構建一個極小的區塊鏈Python區塊鏈
- 區塊鏈的發展,時代轉變下的區塊鏈應用區塊鏈
- 區塊鏈的架構模型區塊鏈架構模型
- 區塊鏈溯源落地應用,區塊鏈在商品溯源中的應用區塊鏈
- 區塊鏈開發_建立區塊鏈公鏈,聯盟鏈,私有鏈區塊鏈
- 區塊鏈 2.0:房地產區塊鏈(四)區塊鏈
- 區塊鏈入門 -- 02 區塊鏈介紹區塊鏈
- 區塊鏈以及區塊鏈技術總結區塊鏈
- 區塊鏈應用場景有哪些?區塊鏈應用開發區塊鏈
- 區塊鏈技術公司 區塊鏈的應用場景思路剖析區塊鏈
- 區塊鏈發展歷程,區塊鏈技術的落地應用區塊鏈
- 構建 EOS 區塊鏈瀏覽器 API區塊鏈瀏覽器API
- 區塊鏈技術開發主鏈 區塊鏈的企業級應用剖析區塊鏈
- 區塊鏈不好落地?騰訊雲用區塊鏈做了這些事區塊鏈
- 區塊鏈溯源解決方案_區塊鏈溯源應用場景區塊鏈
- 區塊鏈共識的確定性區塊鏈
- 區塊鏈共識機制區塊鏈