配景
对于年夜 多半 开发者,区块链行业不容易在第一时间形成初步的思维印象。本文旨在在最短时间内,授与 刚入门的开发者一个最简的区块链Demo。
最终效果
最终效果:每隔1s模拟一个区块的产生
正文
1. 每个区块拥有区块头和区块体两部分 ;
2. 区块头有如下几个症结 字段:height - 高度,previousHash - 上一个区块的哈希值,timestamp - 当前时间戳, Nonce - 一个随机数,MerkelRoot - 梅克尔树根的哈希值,hash - 当前区块的哈希;
3. 区块体data中包含 了:在当前这一段时间内产生 的,并且 被加入到该区块的交易;
class Block { /** * 结构 函数 * @param {Number} height * @param {String} previousHash * @param {Number} timestamp * @param {*} data * @param {String} hash */ constructor(height, previousHash, timestamp, data, hash) { this.height = height this.previousHash = previousHash + '' this.timestamp = timestamp this.data = data this.hash = hash + '' } static generateBlock(blockData, previousBlock) { const nextHeight = previousBlock.height + 1; const nextTimeStamp = new Date().getTime(); //暂时忽略MerkelRoot和Nonce const nextHash = CryptoJS.SHA256(nextHeight + previousBlock.hash + nextTimeStamp + blockData) + ''; return new Block(nextHeight, previousBlock.hash, nextTimeStamp, blockData, nextHash); }}
区块链类拥有的功能 :
1. 创建 “创世区块”;
2. 依据 当前区块的各个字段和区块体的交易数据,计算对应的哈希值;
3. 获得 区块链中最后一个块节点;
4. 计算当前链表的下一个区块;
5. 判断新加入的块是否正当 ;
6. 判断height是否连续且自增;
7. 判断前后区块的哈希是否相连;
8. 向区块链新增节点;
9. 插入新的短链表时判断是否正当 ,如何插入;
10. 附加功能 :保存 /展示区块链的数据;
class BlockChain { /** * 如果指定在历史BlockChain上继续增加区块,则从本地 存储中取出;不然 默认创建 新的区块链 * @param { string } historyChain */ constructor(historyChain) { this.blocks = [this.getGenesisBlock()] } // constructor(historyChain) { // if( historyChain) { // let blocks = this.getHistoryChain(historyChain); // this.blocks = blocks ? [blocks] : [this.getGenesisBlock()]; // } else { // this.blocks = [this.getGenesisBlock()] // } // } /** * 将区块链异步保存 到文件中 */ async saveHistoryChain(file, block) { await fs.appendFile(file, JSON.stringify(block), err => { if (err) throw err; console.log(`Height: ${block.height} is saved.`); }); } /** * 从文件中同步读出历史区块数据 */ // getHistoryChain() { // return fs.readFileSync('historyChain.txt', 'utf-8'); // } /** * 创建 区块链起源块, 此块是硬编码(取比特币高度:642022, 对应的时间是2020-08-03 17:00) */ getGenesisBlock() { return new Block(0, '0', 1595490064640, 'GenesisBlock', '0000000000000000000d87bedef9550a014af9a3af74b791d84d049cc3ca85f4') } /** * 依据 信息计算hash值 */ calcuteHash(height, previousHash, timestamp, data) { return CryptoJS.SHA256(height + previousHash + timestamp + data) + '' } /** * 获得 区块链中最后一个块节点 */ getLatestBlock() { return this.blocks[this.blocks.length - 1] } /** * 计算当前链表的下一个区块 * @param {*} blockData */ generateNextBlock(blockData) { const previousBlock = this.getLatestBlock() const nextIndex = previousBlock.height + 1 const nextTimeStamp = new Date().getTime() const nextHash = this.calcuteHash(nextIndex, previousBlock.hash, nextTimeStamp, blockData) return new Block(nextIndex, previousBlock.hash, nextTimeStamp, blockData, nextHash) } /** * 判断新加入的块是否正当 * @param {Block} newBlock * @param {Block} previousBlock */ isValidNewBlock(newBlock, previousBlock) { if( !(newBlock instanceof Block) || !(previousBlock instanceof Block) ) { return false } // 判断height if(newBlock.height !== previousBlock.height + 1) { return false } // 判断hash值 if(newBlock.previousHash !== previousBlock.hash) { return false } // 计算新块的hash值是否相符 规矩 if(this.calcuteHash(newBlock.height, newBlock.previousHash, newBlock.timestamp, newBlock.data) !== newBlock.hash) { return false } return true } /** * 向区块链添加新节点 * @param {Block} newBlock */ addBlock(newBlock) { if(this.isValidNewBlock(newBlock, this.getLatestBlock())) { this.blocks.push(newBlock) return true } return false } /** * 判断新插入的区块链是否正当 并且 可以笼罩 原来的节点 * @param {Array} newChain */ isValidNewChain(newChain) { if(Array.isArray(newChain) === false || newChain.length === 0) { return false } let newChainLength = newChain.length, firstBlock = newChain[0] // 硬编码的起源块不克不及 转变 if(firstBlock.height === 0) { return false } // 移植新的链的长度 <= 现有链的长度 // 新的链弗成 信 if(newChainLength + firstBlock.height <= this.blocks.length) { return false } // 下面检查新的链能否移植 // 以及新的链的每个节点是否相符 规矩 if(!this.isValidNewBlock(firstBlock, this.blocks[firstBlock.height - 1])) { return false } for(let i = 1; i < newChainLength; ++i) { if(!this.isValidNewBlock(newChain, newChain[i - 1])) { return false } } return true } /** * 插入新链表 * @param {Array} newChain */ addChain(newChain) { if(this.isValidNewChain(newChain)) { const height = newChain[0].height this.blocks.splice(height) this.blocks = this.blocks.concat(newChain) return true } return false } //打印该区块链的所有区块 printBlockChain() { console.table(this.blocks); } //打印该区块链的最新区块 printLastBlock() { console.table(this.blocks[this.blocks.length - 1]); } }
代码功能 有:
1. 模拟生成区块体中的交易数据;
2. 新建一个区块链条;
3. 准时 (每隔2秒)模拟生成一个区块,并添加到区块链中;
4. 同时打印该区块链
//生成模拟的区块体交易数据function generateBlockData() { const dataList = ['Zhangjie is cool', 'Pengxiaohua is cool', 'ChenZiqiang is cool', 'Fangguojun is cool', 'Lulina is beautiful', 'Maqicheng is cool', 'Wangchuanshuo is cool', 'Linshaoyuan is beautiful', 'Lulina is beautiful']; return dataList[Math.random() * dataList.length >> 0];}function mockBlocks() { //实例化一个区块链 const blockChain = new BlockChain('testNet'); blockChain.printLastBlock(); let newBlock; setInterval(() => { newBlock = Block.generateBlock(generateBlockData(), blockChain.getLatestBlock()); blockChain.addBlock(newBlock); blockChain.printBlockChain(); }, 2000);}//开启模拟区块mockBlocks();完整源码
基于NodeJS的完整可运行的最简区块链Demo
运行方法
git clone https://github.com/stevekeol/YunDang-Chain
cd yundang-chain/demo && npm install
npm start作者简介
【stevekeol】卒业 于三墩职业技术学院的一名loser。求索于区块链行业四五载,拥有近乎信仰级其余 热爱。近期已提上日程的计划是打造一款 基于NodeJS的移动端全节点公有链: YunDang-Chain。如需交流,参考 https://github.com/stevekeol/YunDang-Chain |