javascript实现小型区块链功能


Posted in Javascript onApril 03, 2019
区块链概念
狭义:区块链是一种按照时间顺序将数据区块以顺序相连的方式组合成的一种链式数据结构,并以密码方式保证的不可篡改和不可伪造的分布式账本。
一、挖矿(产生新区块)

首先,区块链是由每一个区块联系而形成的,在产生新区块之前必须先有一个最初始的区块,这个区块也叫创世区块。通过这个创世区块,不停地通过变化随机数(nonce)来计算出符合条件的区块。以下是创世区块基本信息:

const initBlock = {
 index: 0,
 data: 'hey,this is a block chain',
 previousHash: '0',
 timestamp: '1551806536961',
 nonce: 80490,
 hash: '0000352fb27dd1141fa7265833190a53e5776b1111e275db0d9a77bf840081e6'
};
  1. index:是指每个区块的序号
  2. data: 这里存放着区块中所有的信息,例如转账,余额等数据
  3. previousHash: 指的是上一个区块的hash值,创世区块没有上一个,显示0即可
  4. timestamp:指的是创建这个区块的时间
  5. nonce:这个是随机数,挖矿就是通过不停变换这个nonce来计算出符合条件的哈希。
  6. hash: 本区块的hash值,通过前面5个字段的信息进行hash运算得出的值。

接着,通过不停的hash运算计算出符合条件的哈希,即挖矿。挖矿也可以调节难度的大小,例如算出的哈希值必须前3位数必须为1或者末3位数必须为1等等,这个可以自行的去定义,只要最后留一个控制的开关,方便控制即可。可以在定义一个变量

哈希的计算:

.createHash('sha256')
 .update(index + data + previousHash + timestamp + nonce)
 .digest('hex')
_that.difficulty = 3 // 即前3位或者末3位数必须为1,数量越多难度越大

生成了符合条件的hash之后,则产生了新的区块,但是还要对这个区块进行校验看看是否有效,因为可能这是一个被篡改的非法的区块,也有可能和这个链没有任何关系的区块而仅仅只是符合上述哈希的规则而已。所以,需要进行一下校验,,前后区块的有效性。

isValidaBlock(newBlock,lastBlock) {
  if (newBlock.index !== lastBlock.index+1) return false
  if (newBlock.previousHash !== lastBlock.hash) return false
  if (newBlock.timestamp <= lastBlock.timestamp) return false
  if (newBlock.hash.slice(1 ,_that.difficulty) !== '1'.repeat(_that.difficulty)) return false
  if (newBlock.hash !== this.computeHashForBlock(newBlock)) return false //确保随机数正确
  // 都满足则返回true
  return true
 }

除了上面的校验之外,还需要使用上面这个函数对整一个chain进行一个每一个块的校验,以保证每一个块的信息是正确的,是没有被篡改过的是合法的。

二、构建P2P网络

区块链的网络是去中心化的,即没有中心服务器的网络,客户端不需要依赖中心服务器来获取或者处理数据。区块链网络中,有这许许多多的节点,每个节点都是一个独立的成员,他们既是客户端也是服务器,节点与节点直接都是点对点进行连接(peer-to-peer),不需要通过某一个中心服务器进行中转,所以,信息安全的角度来说,点对点的连接方式对信息私密性是非常可靠的。

javascript实现小型区块链功能

虽然,区块链是通过点对点的连接方式进行数据传输,但是,在这之前还需要一个东西作为引导,这个就是种子节点。因为,两个节点之间他们可能不是处在同一个域下,他们之间想要联系,必须有一方知道对方的ip和端口,这样才能和对方联系上。节点ip和端口号,在这个节点创建出来之后,种子节点就会发给它在这个区块链中所有节点的ip和端口号同时记录下这个新伙伴的ip和端口号。

那么,新的节点拿到了这一份"通讯录"之后,就会给这个"通讯录"中的所有小伙伴发个消息,告诉他们有一位新的小伙伴加入,之后,其他节点收到了这个信息,也会在自己的"通讯录"中加上新伙伴的ip和端口号,相当于加入了白名单。这样新的节点接下来就可以和任意的的节点进行通信了。

下面用代码演示一下:

(res)=>{
 _that.remotePeerInfo = res.data.data //1
 _that.addPeersList(res.peersList)    //2
 _that.boardCast(_that.remotePeerInfo) //3
 _that.blockChainUpdate(blockChain,blockData)  //4
}

addPeersList(peers) {
 peers.forEach(peer => {
  if (!_that.peers.find(v => _that.isEqualPeer(peer, v))) {
   _that.peers.push(peer)
  }
 })
}

boardCast(remotePeerInfo) {
 this.peers.forEach(v => {
  this.send(action, v.port, v.address)
 })
}

blockChainUpdate(blockChain,blockData){
 if(newChain.length === 1 ){
 return
 }

 if(_that.isValidaChain(newChain) && newChain.length>_that.blockchain.length){
 _that.blockchain = Object.assign({}, newChain)
 }else{
 console.log('error')
 return
 }

 if (trans.every(v => _that.isValidTransfer(v))) {
 _that.data = trans
 }
}

1.保存种子节点传来的此新节点的信息包括ip和端口号,因为,新节点的ip和端口号是会有改变的情况。

2.接受种子节点传来的节点列表,将列表的节点遍历检查一下,没有相同的就写进列表中。

3.将新节点的信息广播到所有的节点上,同时接受到信息的节点更新一下节点列表

4.将区块链上信息同步一份都本地,同时对种子节点传来的blockchain进行每个区块的信息

三、转账交易

BTC的交易模型是使用的是UTXO

javascript实现小型区块链功能

而这个小型区块链的交易模型使用的是最简单的方法。

区块链中"现金”,它是一个虚拟的东西就是一个字符串,来源于挖矿。每次挖矿成功都会有一定的奖励,得到的这些“钱”就可以在区块链网络中自由的转账交易。

在区块链中,进行记录转账交易的时候是需要一个加密的算法,把所有的信息进行加密之后再push到新区块中的data中,从而完成一笔新交易的记录。以BTC为例,BTC的加密算法是使用elliptic这个加密算法,elliptic是一个非对称性的加密算法,非对称的加密算法的特点就是,私钥是惟一的,只有拥有者才可以和他私钥对应的公钥进行校验 。 nodejs也有对应的库在github上搜索elliptic即可。

{
 "privateKey": "34a425df3eb1f22fb6cb74b0e7298b16ffd7f3fb",
 "publicKey": "ac208623a38d2906b090dbcf3a09378dfe79b77bf39c2b753ef98ea94fe08dc3995a1bd05c917"
}

上面是一个生成好的密钥对格式,仅作为展示,我删减了一部分长度。

使用银行卡进行转账交易的时候,会有一个转出的账号和一个转入的账号,在区块链中的记账也会有这个账号,这个账号就是上面使用生成的密钥对中的公钥,公钥就是地址,或者说公钥代表的就是自己的钱包。

校验的方法,首先使用字段“from”,“to”,“amount”的参数进行sign签名,然后在每次挖矿(记账)的时候,则使用verify(),通过前面的三个参数,和sig进行校验

verify(type,data){
 swtich(type){
  case 'sign':
   const bufferMsg = Buffer.from(`${data.from}-${data.to}-${data.amount}`)
   let signature = Buffer.from(keypair.sign(bufferMsg).toDER()).toString('hex')
    this.signature = signature
  break;
  case 'verify':
    const keypairTemp = ec.keyFromPublic(pub, 'hex')
    const bufferMsg = Buffer.from(`${data.from}-${data.to}-${data.amount}`)
    this.keypair = keypairTemp.verify(bufferMsg, sig)
  break;
  default;
 }
}

转帐的时候需要3步,分别是校验转出账户是否有足够的金额,转出账户就是本地公钥。如有则进行记账并且使用两个地址、金额、时间,还有签名加密打包,之后进行全节点广播。其他节点收到这个信息之后第一件事也是对新区块的有效性做一个校验,通过校验之后就会写入data中。

transfer(data) {
 const timestamp = new Date().getTime()
 const sig = rsa.sign({data.from, data.to, data.amount , timestamp})
 const sigTrans = {data.from, data.to, data.amount ,timestamp, sig }

  // 非创世区块
 if (trans.from !== '0') {
   // 检验余额
  if (!(_that.blance < amount)) { //_that.blance 当前账户余额
   //全节点广播
   _that.send('trans', sigTrans)
  }else{
   console.log('not enough blance')
   return
  }
 }
 this.data.push(sigTrans)
 return sigTrans
}

其他节点收到消息之后,先进行去重校验,然后再更新数据。

四、查询余额

这个链的查询方法比较简单,就是将区块中的每一条交易的信息进行校验和匹配,满足条件的就进行增减,同时忽略精度上的问题。

this.blance = blance(address)
 blance(address) {
  let blance = 0;
  this.blockchain.forEach(block => {
   block.data.forEach(trans => {
    if (address == trans.from) {
     blance -= trans.amount
    }

    if (address == trans.to) {
     blance += trans.amount
    }

   })

  });
  return blance
 }

至此,区块链的最简单的功能就实现完毕。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
JS保存、读取、换行、转Json报错处理方法
Jun 14 Javascript
jquery.validate的使用说明介绍
Nov 12 Javascript
jquery实现的一个文章自定义分段显示功能
May 23 Javascript
jquery实现表单验证并阻止非法提交
Jul 09 Javascript
JS延时提示框实现方法详解
Nov 26 Javascript
jQuery实现简单的DIV拖动效果
Feb 19 Javascript
jQuery中的基本选择器用法学习教程
Apr 14 Javascript
利用Node.js制作爬取大众点评的爬虫
Sep 22 Javascript
基于jquery实现二级联动效果
Mar 30 jQuery
JS实现的按钮点击颜色切换功能示例
Oct 19 Javascript
JSON字符串操作移除空串更改key/value的介绍
Jan 05 Javascript
js实现图片放大并跟随鼠标移动特效
Jan 18 Javascript
vue插槽slot的理解和使用方法
Apr 03 #Javascript
react写一个select组件的实现代码
Apr 03 #Javascript
vue框架下部署上线后刷新报404问题的解决方案(推荐)
Apr 03 #Javascript
JavaScript变速动画函数封装添加任意多个属性
Apr 03 #Javascript
JS中注入eval, Function等系统函数截获动态代码
Apr 03 #Javascript
性能优化篇之Webpack构建速度优化的建议
Apr 03 #Javascript
elementUI多选框反选的实现代码
Apr 03 #Javascript
You might like
php.ini-dist 和 php.ini-recommended 的区别介绍(方便开发与安全的朋友)
2012/07/01 PHP
php中将字符串转为HTML的实体引用的一个类
2013/02/03 PHP
ThinkPHP中U方法的使用浅析
2014/06/13 PHP
php通过session防url攻击方法
2014/12/10 PHP
使用PHP生成二维码的方法汇总
2015/07/22 PHP
可缩放Reloaded-一个针对可缩放元素的复用组件
2007/03/10 Javascript
JavaScript flash复制库类 Zero Clipboard
2011/01/17 Javascript
jQuery操作select的实例代码
2012/06/14 Javascript
replace()方法查找字符使用示例
2013/10/28 Javascript
修复jQuery tablesorter无法正确排序的bug(加千分位数字后)
2016/03/30 Javascript
jquery自定义表单验证插件
2016/10/12 Javascript
完全深入学习Bootstrap表单
2016/11/28 Javascript
BootStrap便签页的简单应用
2017/01/06 Javascript
Vue关于数据绑定出错解决办法
2017/05/15 Javascript
Angular2 之 路由与导航详细介绍
2017/05/26 Javascript
详解node字体压缩插件font-spider的用法
2018/09/28 Javascript
微信小程序中遇到的iOS兼容性问题小结
2018/11/14 Javascript
vue搜索和vue模糊搜索代码实例
2019/05/07 Javascript
解决layui的使用以及针对select、radio等表单组件不显示的问题
2019/09/05 Javascript
Vue组件模板及组件互相引用代码实例
2020/03/11 Javascript
Python将xml和xsl转换为html的方法
2015/03/10 Python
python与sqlite3实现解密chrome cookie实例代码
2018/01/20 Python
python绘制规则网络图形实例
2019/12/09 Python
python:批量统计xml中各类目标的数量案例
2020/03/10 Python
Python判断变量是否是None写法代码实例
2020/10/09 Python
从一次项目重构说起CSS3自定义变量在项目的使用方法
2021/03/01 HTML / CSS
Carmen Sol官网:购买果冻鞋、手袋和配件
2021/01/01 全球购物
总账会计岗位职责
2014/03/13 职场文书
租房协议书
2014/04/10 职场文书
关于保护环境的建议书
2014/08/26 职场文书
委托书的写法
2014/09/16 职场文书
党的作风建设心得体会
2014/10/22 职场文书
2014年施工员工作总结
2014/11/18 职场文书
人事局接收函
2015/01/31 职场文书
解析Redis Cluster原理
2021/06/21 Redis
Oracle查看表空间使用率以及爆满解决方案详解
2022/07/23 Oracle