之前,由于课程需要,基于Nodejs做了一个简单的区块链实现。要求很简单,structure记录了区块结构,顺便可以往链上插入新的区块。但如果要支持多人使用,就需要考虑“可信度”的问题。那么根据区块链的要求,除非算力超过除攻击者自身以外的所有机器的算力,否则链上数据是不可篡改的。想了想,还是试试吧。一系列专注于前端和算法的干货分享,欢迎关注(???):《微信公众号:新坛博客》|新坛网|GitHub技术研究谷歌搜索,发现一个不错的项目:https://github.com/lhartikk/naivechain。大概只有200行,但是有几十行是关于搭建ws和http服务器的。美中不足的是,它没有实现批量插入区块链和计算可信度。结合这个项目,基本可以确定每个区块都会被封装成一个类(结构化表示),区块链也会被封装成一个类,然后对外暴露接口。为了方便表示块,块定义将其封装为类,类没有方法:/***块信息的结构定义*/classBlock{/***构造函数*@param{Number}index*@param{String}previousHash*@param{Number}timestamp*@param{*}data*@param{String}hash*/constructor(index,previousHash,timestamp,data,hash){this.index=index//位置oftheblockthis.previousHash=previousHash+''//前一个块的hashthis.timestamp=timestamp//块生成时的时间戳this.data=data//块本身携带的数据this.hash=hash+''//区块根据自己的信息和规则生成的hash}}至于如何生成hash,这里采用的规则比较简单:拼接index、previousHash、timestamp和data,串起来使用sha256算法,计算的缺点是hash。为了方便起见,引入一个加密库:constCryptoJS=require('crypto-js')链式结构定义了很多块链接在一起形成一条链。该链也由类表示。并且里面实现了很多方法:根据加密规则生成hash,插入新块和校验操作,批量插入块和校验操作,计算可信度1.起源块起源块是“硬编码”的,因为没有前面的数据。并且规定了不可篡改,即不可强行覆盖。在构造函数中,我们直接将生成的创世块放入链中。classBlockChain{constructor(){this.blocks=[this.getGenesisBlock()]}/***创建区块链创世块,这个块是硬编码的*/getGenesisBlock(){returnnewBlock(0,'0',1552801194452,'创世块','810f9e854ade9bb8730d776ea02622b65c02b82ffa163ecfe4cb151a14412ed4')}}2.计算下一个区块BlockChain对象可以根据当前链自动计算下一个区块。并与用户发送的区块信息进行对比,如果相同,则说明合法,可以插入;否则,用户的块是非法的,不允许插入。//方法是区块链对象方法/***根据信息计算哈希值*/calculateHash(index,previousHash,timestamp,data){returnCryptoJS.SHA256(index+previousHash+timestamp+data)+''}/***获取区块链的最后一个区块节点*/getLatestBlock(){returnthis.blocks[this.blocks.length-1]}/***计算当前链表的下一个区块*@param{*}blockData*/generateNextBlock(blockData){constpreviousBlock=this.getLatestBlock()constnextIndex=previousBlock.index+1constnextTimeStamp=newDate().getTime()constnextHash=this.calcuteHash(nextIndex,previousBlock.hash,nextTimeStamp,blockData)返回新块(nextIndex,previousBlock.hash,nextTimeStamp,blockData,nextHash)}3。插入块插入块时,需要检查当前块是否合法。如果合法,则插入并返回true;否则,返回假。/***向区块链添加一个新节点*@param{Block}newBlock*/addBlock(newBlock){//有效块if(this.isValidNewBlock(newBlock,this.getLatestBlock())){this.blocks.push(newBlock)returntrue}returnfalse}校验逻辑放在isValidNewBlock方法中,主要完成三件事:判断新块的索引是否自增判断previousHash是否等于上一个块的hash根据约束规则生成新区块的hash/***判断新增区块是否合法*@param{Block}newBlock*@param{Block}previousBlock*/isValidNewBlock(newBlock,previousBlock){if(!(newBlockinstanceofBlock)||!(previousBlockinstanceofBlock)){returnfalse}//判断索引if(newBlock.index!==previousBlock.index+1){returnfalse}//判断哈希值if(newBlock.previousHash!==previousBlock.hash){returnfalse}//计算新区块的哈希值是否符合规则if(this.calcuteHash(newBlock.index,newBlock.previousHash,newBlock.timestamp,newBlock.data)!==newBlock.hash){returnfalse}returntrue}4.batchinsertbatchinsert逻辑比较复杂的,比如当前链上有4个下标:0->1->2->3除了原始区块0不能被覆盖,当插入新的下标为“1->2->3->4",可以替换原来的块。最后的结果是:0->1->2->3->4。关于下标索引的处理,假设上述情况,如果入链的下标从大于4的整数开始,显然不可能拼接原区块链的下标直接扔掉。但是如何保证可信度呢?即当新链(B链)取代原链(A链)时,会产生一条新链(C链)。如果length(C)>length(A),则可以覆盖要替换的部分。这确保了只有当算力超过所有算力的50%时,才能对链进行篡改。插入新链的方法如下:/***插入新链表*@param{Array}newChain*/addChain(newChain){if(this.isValidNewChain(newChain)){constindex=newChain[0].indexthis.blocks.splice(index)this.blocks=this.blocks.concat(newChain)returntrue}returnfalse}实现上述逻辑的方法如下:/***判断是否新插入的区块链是合法的,可以覆盖原来的一个Node*@param{Array}newChain*/isValidNewChain(newChain){if(Array.isArray(newChain)===false||newChain.length===0){returnfalse}letnewChainLength=newChain.length,firstBlock=newChain[0]//硬编码的原始区块不能改变if(firstBlock.index===0){returnfalse}//移植新链长度<=现有链长度//新chaincannotbetrustedif(newChainLength+firstBlock.index<=this.blocks.length){returnfalse}//接下来检查新链是否可以移植//以及新链的每个节点是否符合规则if(!this.isValidNewBlock(firstBlock,this.blocks[firstBlock.index-1])){returnfalse}for(leti=1;我<新链长;++i){如果(!这.isValidNewBlock(newChain[i],newChain[i-1])){returnfalse}}returntrue}5.为什么需要批量插入?我当时很惊讶,为什么我需要“批量插入”的方法然后我想通了(希望我没有看错)。假设一台服务器S,有两个用户A和B,A和B同时从已知链上拉取数据,然后分别生成。A的网速较快,但算力较低,所以生成一个区块放入S中。注意:此时S上的区块已经更新。但是B更惨,它在本地生成了2个区块,但是受限于网速,只能等待网速恢复进来的区块。这时候按照规则是可以覆盖的(算力高)。所以在这种情况下,服务器S收到B的2个区块,更新后的链长为3(算上原始区块),A的区块已经被覆盖。效果测试虽然没有写服务器,但是还是模拟了上面介绍的第五种情况。代码在test.js文件中,直接运行即可。看一下效果截图:红线上方是先计算出来的,红线下方是被更高算力的客户端篡改过的区块链。具体的模拟过程可以看代码,这里不再赘述。所有代码都在这里:https://github.com/dongyuanxin/node-blockchain更多系列文章专注于前端和算法系列的干货分享,欢迎关注(???)
