区块链太复杂,那咱们就讲点简单的。用JS来构建你本身的区块链系统,寥寥几行代码就能够说明区块链的底层数据结构,POW挖矿思想和交易过程等。固然了,真实的场景远远远比这复杂。本文的目的仅限于让你们初步了解,初步认识区块链。node
文章内容主要参考视频:使用Javascript创建区块链(https://www.youtube.com/playlist?list=PLzvRQMJ9HDiTqZmbtFisdXFxul5k0F-Q4)数组
感谢原做者,本文在原视频基础上作了修改补充,并加入了我的理解。数据结构
区块链顾名思义是由区块链接而成的链,所以最基本的数据结构是块。每一个块都含有时间戳,数据,散列,previousHash等信息。其中数据用来存储数据,previousHash是前一个区块的哈希值示意以下:区块链
哈希是对区块信息的摘要存储,哈希的好处是任意长度的信息通过哈希均可以映射成固定长度的字符串,如可用SHA256:this
calculateHash() { return SHA256(this.previousHash + this.timestamp + JSON.stringify(this.data)).toString(); }
块的最基本数据结构以下:spa
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,显然可用用数组或链表来表示,如:code
class BlockChain { constructor() { this.chain = []; } }
正所谓万物始于一,区块链的第一个区块老是须要人为来手动建立,这个区块的previousHash为空,如:视频
createGenesisBlock() { return new Block("2018-11-11 00:00:00", "Genesis block of simple chain", ""); }
区块链的构造方法也应改成:ip
class BlockChain { constructor() { this.chain = [this.createGenesisBlock()]; } }
每新加一个区块,必须保证与原有区块链链接起来,即:rem
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
本文为云栖社区原创内容,未经容许不得转载。