區塊鏈作為下一代顛覆性的核心技術,吸引了很多人的關注,區塊鏈的基本概念非常簡單:一個分布式數據庫維持不斷增長的有序記錄列表。然而當我們談論區塊鏈時,我們也會談論我們用區塊鏈所要解決的問題,這二者通常很容易弄混淆。流行的基于區塊鏈的項目如比特幣和以太坊就是這種情況 。術語“區塊鏈”通常與交易、智能合約或加密貨幣等概念緊密相關。
這使得理解區塊鏈是一項必然要艱巨的任務。特別是源碼級別的理解。在這里,我將介紹一個使用200行代碼實現的超級簡單的區塊鏈——NaiveChain。
塊結構
第一個邏輯步驟是確定塊結構。為了讓事情盡可能簡單,我們只包含最必要的:索引(index),時間戳(timestamp),數據(data),哈希(hash)和上一個哈希(previousHash)。
class Block {
constructor(index, previousHash, timestamp, data, hash) {
this.index = index;
this.previousHash = previousHash.toString();
this.timestamp = timestamp;
this.data = data;
this.hash = hash.toString();
}
}
塊哈希
該塊需要被哈希運算以保持數據的完整性。SHA-256被用來做哈希算法處理塊的內容。需要注意的是這個哈希與“挖礦” 無關,因為沒有解決工作量證明(Proof Of Work )的問題。
var calculateHash = (index, previousHash, timestamp, data) => {
return CryptoJS.SHA256(index + previousHash + timestamp + data).toString();
};
創建塊
要創建一個塊,我們必須知道上一個塊的哈希,并創建剩余的所需內容(=索引,哈希,數據和時間戳)。塊數據是由終端用戶提供。
var generateNextBlock = (blockData) => {
var previousBlock = getLatestBlock();
var nextIndex = previousBlock.index + 1;
var nextTimestamp = new Date().getTime() / 1000;
var nextHash = calculateHash(nextIndex, previousBlock.hash, nextTimestamp, blockData);
return new Block(nextIndex, previousBlock.hash, nextTimestamp, blockData, extHash);
};
存儲塊
內存中的JAVAscript數組用于存儲區塊鏈。區塊鏈的第一個區塊也叫“創世紀塊(譯者著:genesis-block,區塊鏈中的第一個塊)”,它是直接寫死的。
var getGenesisBlock = () => {
return new Block(0, "0", 1465154705, "my genesis block!!", "816534932c2b7154836da6afc367695e6337db8a921823784c14378abed4f7d7");
};
var blockchain = [getGenesisBlock()];
驗證塊的完整性
在任何時候,我們必須能夠驗證塊或區塊鏈的完整性。特別是當我們從其他節點接收到新塊時,并且必須決定是否接受它們。
var isValidNewBlock = (newBlock, previousBlock) => {
if (previousBlock.index + 1 !== newBlock.index) {
console.log('invalid index');
return false;
} else if (previousBlock.hash !== newBlock.previousHash) {
console.log('invalid previoushash');
return false;
} else if (calculateHashForBlock(newBlock) !== newBlock.hash) {
console.log('invalid hash: ' + calculateHashForBlock(newBlock) + ' ' + ewBlock.hash);
return false;
}
return true;
};
選擇最長的鏈
任意時間,鏈中應始終只有一組塊。如果發生沖突(例如下圖中兩個節點都生成id 為72的塊),我們選擇具有最長塊數的鏈。
var replaceChain = (newBlocks) => {
if (isValidChain(newBlocks) && newBlocks.length > blockchain.length) {
console.log('Received blockchain is valid. Replacing current blockchain with received blockchain');
blockchain = newBlocks;
broadcast(responseLatestMsg());
} else {
console.log('Received blockchain invalid');
}
};
與其他節點通信
一個節點的最重要的職能是與其他節點共享和同步區塊鏈。以下規則用來保持網絡同步。
當一個節點產生一個新塊時,它將它廣播到網絡
當一個節點連接到一個新的節點時,它將查詢最新的塊
當一個節點遇到一個索引大于當前已知塊的塊時,它會將該塊添加到當前鏈中,或者查詢完整的區塊鏈。
我們暫未實現節點自動發現,必須手動添加對等點的位置(=URLs)。
控制節點
用戶必須能夠以某種方式控制節點。這是通過設置HTTP服務器完成的。
var initHttpServer = () => {
var App = express();
app.use(bodyParser.json());
app.get('/blocks', (req, res) => res.send(JSON.stringify(blockchain)));
app.post('/mineBlock', (req, res) => {
var newBlock = generateNextBlock(req.body.data);
addBlock(newBlock);
broadcast(responseLatestMsg());
console.log('block added: ' + JSON.stringify(newBlock));
res.send();
});
app.get('/peers', (req, res) => {
res.send(sockets.map(s => s._socket.remoteAddress + ':' + s._socket.remotePort));
});
app.post('/addPeer', (req, res) => {
connectToPeers([req.body.peer]);
res.send();
});
app.listen(http_port, () => console.log('Listening http on port: ' + http_port));
};
如上所示,用戶能夠通過以下方式與節點交互:
列出所有的塊
用用戶給出的內容創建一個新塊
列出或添加節點
控制節點最直接的方法是使用Curl:
#get all blocks from the node
curl http://localhost:3001/blocks
架構
值得注意的是,一個節點實際上暴露了兩個web服務器:一個用于控制節點(HTTP服務器),一個用于節點之間的點對點通信(Websocket HTTP服務器)。
結論
NaiveChain是為演示和學習目的而創建的。因為它不具有“挖礦”算法(PoS 或者 PoW),不能在公共網絡中使用。盡管如此,它仍然實現了功能區塊鏈的基本功能。
web前端的功能遠不止這些,想學習更多web前端知識,可以關注“武漢千鋒“微信公眾號,獲取兩周免費試聽資格!