NodeJS реализует простой блокчейн

Node.js блокчейн

В связи с требованиями курса был реализован простой блокчейн на базе Nodejs. Требования очень простые, структура записывает структуру блока, и, кстати, в цепочку можно вставить новый блок.

Но если вы хотите поддерживать многопользовательское использование, вам необходимо рассмотреть вопрос «достоверности». Затем, в соответствии с требованиями блокчейна, данные в цепочке не могут быть подделаны, если только вычислительная мощность не превышает вычислительную мощность всех машин, кроме самого злоумышленника.

Подумайте об этом, попробуйте.

🔍Посмотреть все руководства / читать оригинал🔍

Технические исследования

Искал в гугле и нашел хороший проект:GitHub.com/Lao HaFlesh хвастается/Это…. Там всего около 200 строк, но десятки строк посвящены построению серверов ws и http, ложка дегтя в том, что пакетная вставка в блокчейн и вычисление достоверности не реализованы.

В сочетании с этим проектом можно в основном определить, что каждый блок будет инкапсулирован в класс (структурированное представление), и блокчейн также будет инкапсулирован в класс, а затем интерфейс будет открыт для внешнего мира.

определение блока

Для удобства представления блок инкапсулирован в виде класса, не имеющего методов:

/**
 * 区块信息的结构化定义
 */
class Block {
  /**
   * 构造函数
   * @param {Number} index 
   * @param {String} previousHash 
   * @param {Number} timestamp 
   * @param {*} data 
   * @param {String} hash 
   */
  constructor(index, previousHash, timestamp, data, hash) {
    this.index = index // 区块的位置
    this.previousHash = previousHash + '' // 前一个区块的hash
    this.timestamp = timestamp // 生成区块时候的时间戳
    this.data = data // 区块本身携带的数据
    this.hash = hash + '' // 区块根据自身信息和规则生成的hash
  }
}

Что касается того, как генерировать хэш, то здесь используются относительно простые правила:

  1. Объедините индекс, previouHash, временную метку и данные, чтобы преобразовать их в строку.
  2. При использовании алгоритма sha256 вычисляемым недостатком является хэш

Для удобства будет введена библиотека шифрования:

const CryptoJS = require('crypto-js')

определение структуры цепи

Многие блоки связаны вместе, чтобы сформировать цепочку. Эта цепочка также представлена ​​классом. И он реализует множество методов:

  1. Сгенерировать хеш в соответствии с правилами шифрования
  2. Вставьте новые блоки и проверьте операции
  3. Массовая вставка блока и проверка операций и расчет достоверности

1. Исходный блок

Исходный блок «жестко закодирован», потому что перед ним нет данных. И оговорено, что его нельзя подделать, то есть нельзя принудительно перезаписать. В конструкторе мы напрямую помещаем сгенерированный блок генезиса в цепочку.

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

  /**
   * 创建区块链起源块, 此块是硬编码
   */
  getGenesisBlock() {
    return new Block(0, '0', 1552801194452, 'genesis block', '810f9e854ade9bb8730d776ea02622b65c02b82ffa163ecfe4cb151a14412ed4')
  }
}

2. Рассчитать следующий блок

Объект BlockChain может автоматически вычислять следующий блок на основе текущей цепочки. И по сравнению с информацией о блоке, отправленной пользователем, если она одинакова, это означает, что она легальна и может быть вставлена; в противном случае блок пользователя является незаконным и не может быть вставлен.

// 方法都是BlockChain对象方法
  /**
   * 根据信息计算hash值
   */
  calcuteHash(index, previousHash, timestamp, data) {
    return CryptoJS.SHA256(index + previousHash + timestamp + data) + ''
  }

  /**
   * 得到区块链中最后一个块节点
   */
  getLatestBlock() {
    return this.blocks[this.blocks.length - 1]
  }

  /**
   * 计算当前链表的下一个区块
   * @param {*} blockData 
   */
  generateNextBlock(blockData) {
    const previousBlock = this.getLatestBlock()
    const nextIndex = previousBlock.index + 1
    const nextTimeStamp = new Date().getTime()
    const nextHash = this.calcuteHash(nextIndex, previousBlock.hash, nextTimeStamp, blockData)
    return new Block(nextIndex, previousBlock.hash, nextTimeStamp, blockData, nextHash)
  }

3. Вставьте блок

При вставке блока необходимо проверить, является ли текущий блок допустимым, если он является допустимым, вставьте его и верните true, в противном случае верните false.

  /**
   * 向区块链添加新节点
   * @param {Block} newBlock 
   */
  addBlock(newBlock) {
    // 合法区块
    if(this.isValidNewBlock(newBlock, this.getLatestBlock())) {
      this.blocks.push(newBlock)
      return true  
    }
    return false
  }

Логика проверки размещена вisValidNewBlockметод, он в основном выполняет 3 вещи:

  1. Определить, увеличивается ли индекс нового блока
  2. Определить, равен ли previousHash хешу предыдущего блока
  3. Определить, генерируется ли хэш нового блока в соответствии с ограниченными правилами
  /**
   * 判断新加入的块是否合法
   * @param {Block} newBlock 
   * @param {Block} previousBlock 
   */
  isValidNewBlock(newBlock, previousBlock) {
    if(
      !(newBlock instanceof Block) ||
      !(previousBlock instanceof Block)
    ) {
      return false
    }

    // 判断index
    if(newBlock.index !== previousBlock.index + 1) { 
      return false
    }

    // 判断hash值
    if(newBlock.previousHash !== previousBlock.hash) { 
      return false
    }

    // 计算新块的hash值是否符合规则
    if(this.calcuteHash(newBlock.index, newBlock.previousHash, newBlock.timestamp, newBlock.data) !== newBlock.hash) { 
      return false
    }

    return true
  }

4. Объемная вставка

Логика пакетной вставки более сложная, например, индексы 4-х блоков в текущей цепочке: 0->1->2->3. За исключением того, что исходный блок 0 не может быть перезаписан, когда вставляется новая цепочка с нижним индексом «1->2->3->4», исходный блок может быть заменен. Конечный результат: 0->1->2->3->4.

При обработке индекса нижнего индекса, предполагая вышеописанную ситуацию, если индекс входящей цепочки начинается с целого числа больше 4, очевидно, невозможно склеить индекс исходного блокчейна и отбросить его напрямую.

Но как обеспечить доверие? То есть, когда новая цепочка (цепочка B) заменяет исходную цепочку (цепь A), создается новая цепочка (цепочка C). Если длина (C) > длина (A), то заменяемая часть может быть перезаписана.Это гарантирует, что цепочка может быть подделана только тогда, когда вычислительная мощность превышает 50% всей вычислительной мощности..

Вот как вставить новую цепочку:

  /**
   * 插入新链表
   * @param {Array} newChain 
   */
  addChain(newChain) {
    if(this.isValidNewChain(newChain)) {
      const index = newChain[0].index
      this.blocks.splice(index)
      this.blocks = this.blocks.concat(newChain)
      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.index === 0) {
      return false
    }

    // 移植新的链的长度 <= 现有链的长度
    // 新的链不可信
    if(newChainLength + firstBlock.index <= this.blocks.length) {
      return false
    }

    // 下面检查新的链能否移植
    // 以及新的链的每个节点是否符合规则
    if(!this.isValidNewBlock(firstBlock, this.blocks[firstBlock.index - 1])) {
      return false
    }

    for(let i = 1; i < newChainLength; ++i) {
      if(!this.isValidNewBlock(newChain[i], newChain[i - 1])) {
        return false
      }
    }

    return true
  }

5. Зачем вам пакетные вставки?

Мне было интересно, зачем нужен метод "массовой вставки". Я понял это позже (надеюсь). Предположим, сервер S и два пользователя A и B.

A и B одновременно извлекают данные из известной цепочки, а затем генерируют их по отдельности. Скорость сети A высокая, но вычислительная мощность низкая, поэтому 1 блок генерируется и размещается на S. Примечание. В настоящее время блок на S обновлен.

А B хуже, он генерирует 2 блока локально, но ограниченный скоростью сети, он может только ждать скорости сети, чтобы восстановить входящий блок. В это время по правилам его можно накрыть (вычислительная мощность высокая). Таким образом, в этом случае сервер S получает 2 блока B, обновленная длина цепочки равна 3 (включая исходный блок), а блок A был перезаписан.

тест на эффект

Хотя сервер не записывается, моделируется пятая ситуация, описанная выше. код находится вtest.jsВ файле вы можете запустить его напрямую. Взгляните на скриншот эффекта:

image.png

Над красной линией находится первый расчет, а под красной линией — блок-цепочка, измененная клиентом с более высокой вычислительной мощностью. Конкретный процесс симуляции можно увидеть в коде, который здесь уже не лишний.

Весь код находится в:GitHub.com/Дунъюаньсинь…

Еще цикл статей

⭐ Добавить в избранное/подписаться на GitHub ⭐

«Фронтальная система знаний»

Справочник по шаблонам проектирования

«Прогрессивное руководство по Webpack4»

⭐ Добавить в избранное/подписаться на GitHub ⭐