zoukankan      html  css  js  c++  java
  • 最简区块链Demo | 基于NodeJS实现移动端区块链公有链

    前言

    注:文末有源码地址

    "凡是可以用 JavaScript 来写的应用,最终都会用 JavaScript 来写。" ——Atwood定律

    【本文重心】: 如何以最简代码打造一款区块链基于NodeJS如何开发一款区块链公有链

    本文将以解读源码的形式,让你有如下收获:

    • 本文最终完成的效果是怎样的?
    • 区块链中的区块是什么意思?
    • 区块是如何构成区块链的?
    • 最终实现上述功能的代码有多简洁?
    • 如何基于NodeJS开发区块链?

    最终效果

    1. 如果你想立即看到效果,请按照文末的实践步骤(仅需三步),你将基于NodeJS开发你的第一款区块链可视化的应用。

    即: 每隔2秒,一个新的区块被挖出,并缀连到前一个区块的尾部,如此形成区块链,同时打印出来。

     
    每隔2秒,一个区块被挖出来
    1. 如果你想了解如何从零开始,下面将带你手把手,一步一步实现上述效果

    正文 - 源码详解

    一. 区块链中的区块什么意思,如何创建一个区块?
    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();
        const nextHash = CryptoJS.SHA256(nextHeight + previousBlock.hash + nextTimeStamp + blockData) + ''; 
        return new Block(nextHeight, previousBlock.hash, nextTimeStamp, blockData, nextHash);    
      }
    }
    

    如上所见,区块的数据结构主要由以下几个字段:

    • height : 当前这个区块的高度(所谓的高度,就是这个区块被挖出来的顺序,第一个,第二个...)
    • previousHash: 前一个区块的hash(你可以简单认为hash就是一个类似身份证号的唯一识别码)
    • timestamp: 即当前时间戳(用来指明当前高度的区块被挖出的时间)
    • data: 区块除了上述区块头中的字段之外,在区块体中需要存放这一小段时间内该链上的事件/交易,这些事件/交易放在data字段中,当该区块被添加到区块链上之后,这些事件、交易就跟着区块被写入了区块链,从此具有了可追溯不可篡改等特性
    • hash:即当前区块将所有的区块头区块体的数据打包后,计算出的一个唯一识别码。
    三. 上述生成的区块如何构成区块链的?

    其实根据上述区块间缀连的关系,已经知道了区块通过hash前后相连,逐步形成了区块链

    下面通过源码解读,区块间如何通过hash相连,又有哪些需要注意的地方?

    这就是一个区块链最简最朴实无华且枯燥的样子:

    class BlockChain {
      constructor() {
        this.blocks = [];
      }
    }
    

    现在我希望将一个有一个的区块逐步缀连到前一个区块上,即用previousHash字段指明前一区块是谁!那么问题来了,第一个区块的previousHash是什么?也就是第一个区块该跟在谁后面?此刻什么都没有,难道跟着上帝么?

    没错!就是跟着上帝!即区块链中的上帝,也就是固化在源码中,所谓的创世区块

      getGenesisBlock() {
        return new Block(0, '0', 1595490064640, 'GenesisBlock', '0000000000000000000d87bedef9550a014af9a3af74b791d84d049cc3ca85f4')
      }
    

    如上所见,创世区块做了这么几件事:

    • height: 将高度设定为0
    • previousHash: 将创世区块的前一hash置为0
    • timestamp: 取当前时间戳:1595490064640(即2020-08-03 18:00))
    • data: 区块的数据体中,目前就是朴实无华的一句话,你写什么都OK!
    • hash: 根据上述字段计算出来的一个唯一的识别码

    现在有了创世区块了,那么接下来的第一个区块怎么生成呢?源码在此:

      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)
      }
    

    如上可见,后继区块使用到了前一区块的高度height和哈希值hash

    当区块链发现有一个新的区块被挖出,且需要添加到该链的尾部时,必须得验证该块是否合乎要求:

      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
      }
    

    由上可见,判断一个新区块能否被添加到该区块链的依据有如下四点:

    • 当前挖出的新区块newBlock和取出的上一区块previousBlock是否是合法的区块?
    • newBlock的高度,是否比previousBlock的高度刚好大1?
    • newblock的前一哈希: previousHash,是否刚好是previousBlock哈希: hash
    • newBlockhash是否是根据该区块的所有信息按固定规范加密生成的?

    只有当上述几点全部满足,该新区块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[i], 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
      }
    

    获取完整源码: Github

    快速上手/如何运行

    • 下载源码: git clone https://github.com/stevekeol/YunDang-Chain
    • 切换至对应目录,并安装依赖项: cd yundang-chain/demo && npm install
    • 运行: npm start

    总结

    本文仅以近乎于Demo的形式,最简的展示了:

    • 何为代码层面区块链
    • 如何以最简代码打造区块链

    作者简介

    stevekeol

    近期已提上日程的计划是打造一款 基于NodeJS的移动端全节点公有链: YunDang-Chain



    作者:stevekeol
    链接:https://www.jianshu.com/p/b83c1e50c309
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    【win10】浏览器Chrome 和edge 体验对比与使用心得
    【Java】 VM 环境配置过程要点( win10,64位)
    office2013 激活方法
    产品激活 比如Windows激活 , office激活 等激活的原理是什么? KMS等激活工具安全吗?
    回顾外滩踩踏事件,吸取的教训
    【win7】Ubuntu安装使用中的一些注意事项
    黑屏
    mock测试(一)
    Django模型类(一)
    获取本机局域网ip和出口ip
  • 原文地址:https://www.cnblogs.com/zzsdream/p/13488100.html
Copyright © 2011-2022 走看看