JS 实现区块链同步和共识

news2025/1/18 9:50:52

JS 实现区块链同步和共识

之前实现了区块链和去中心化网络,这里实现区块链的同步和共识,不过本质上来说使用的的方法和 register & broadcast 的方法是一样的。

这个也是目前学习中倒数第二篇笔记了,最后两个部分学完,blockchain 就暂时放一放继续回到前端的学习去了。

同步网络

这里一步会做到的就是当下一个 transaction/block 被添加到网络中某一个 blockchain 上时,其他的 node 也会收到消息,并且同步更新自己 node 上的 blockchain。

同步 transaction

重构创建新的 transaction

要做到这一步首先需要 refactor 一下当前的 blockchain 中,transaction 的实现。这一步主要是将原来的函数拆分成两个,修改前的代码为:

createNewTransaction = (amount: number, sender: string, recipient: string) => {
  const newTransaction = {
    amount,
    sender,
    recipient,
  };

  this.pendingTransactions.push(newTransaction);

  return this.getLastBlock()['index'] + 1;
};

修改后的部分为:

createNewTransaction = (
  amount: number,
  sender: string,
  recipient: string
): Transaction => {
  // interface 也需要同样更新
  const newTransaction: Transaction = {
    amount,
    sender,
    recipient,
    transactionId: crypto.randomUUID().split('-').join(''),
  };

  return newTransaction;
};

addToPendingTransactions = (transaction: Transaction): number => {
  this.pendingTransactions.push(transaction);
  return this.getLastBlock().index + 1;
};

这样当某一个 node 创建了新的 transaction 之后,当前结点就可以将其 broadcast 到整个 network 上实现同步。

实现 broadcast transaction 的功能

就跟将 node 加到 network 中一样的实现步骤,首先从 request 中获取对应的信息,随后调用 createNewTransaction 去获取创建成功的 transaction。随后循环遍历 network 中的所有 nodes,每个 node 都会将最新的 transaction 推到自己 node 中,blockchain 中的 pending transactions 中。

app.post('/transaction/broadcast', (req, res) => {
  const { amount, sender, recipient } = req.body;

  const newTransaction: Transaction = bitcoin.createNewTransaction(
    amount,
    sender,
    recipient
  );

  bitcoin.addToPendingTransactions(newTransaction);

  const reqPromises: RequestPromise<any>[] = bitcoin.networkNodes.map(
    (networkNodeUrl) => {
      const reqOptions = {
        uri: networkNodeUrl + '/transaction',
        method: 'POST',
        body: { newTransaction },
        json: true,
      };

      return rp(reqOptions);
    }
  );

  Promise.all(reqPromises).then(() => {
    res.json({ message: 'Transaction creatd and broadcasted successfully.' });
  });
});

更新 post transaction

这个变动比较小,只需要获取新的 transaction,调用之前写的 append 功能即可。

app.post('/transaction', (req, res) => {
  const { newTransaction } = req.body;

  const blockIdx = bitcoin.addToPendingTransactions(newTransaction);

  res.json({ message: `Transaction will be added in block ${blockIdx}.` });
});

测试新的 transaction

这里依旧用 postman 进行测试,依旧是在 1 个 node 上添加新的 transaction,随后在其他的结点上查看。

首先确定当前 network 上已经有其他的 nodes 进行关联:

在这里插入图片描述

随后 3331 新添一个 transaction:

在这里插入图片描述

3331、3332 和 3333 上都显示出了新的 transaction:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

随后再通过 3332 添加一个 transaction:

在这里插入图片描述

可以发现 3331、3332、3333 上也都出现了最新的 transaction:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

同步 mine

和同步 transaction 的操作差不多,这里也会创建一个新的 block,然后遍历所有的 nodes,将其推到 block 中。

接受新的 block

这里的实现就是从 request 中获取 block 信息,并且做一个简单的验证,如果验证通过得啊,就将当前 block 推到 blockchain 中:

app.post('/recieve-new-block', (req, res) => {
  const newBlock: Block = req.body.newBlock;
  const lastBlock = bitcoin.getLastBlock();
  const correctHash = lastBlock.hash === newBlock.previousBlockHash,
    correctIndex = lastBlock.index === newBlock.index - 1;

  if (correctHash && correctIndex) {
    bitcoin.chain.push(newBlock);
    bitcoin.pendingTransactions = [];
    res.json({ message: 'New block recieved and accepted.', newBlock });
  } else {
    res.json({ message: 'New block rejected.', newBlock });
  }
});

修改 mine

mine 这部分主要时添加了一个遍历调用上面创建的 endpoint 的部分,以及现在奖励矿工的部分放到了 pending transaction 中,而不是直接添加到当前 block 里:

app.get('/mine', (req, res) => {
  const lastBlock = bitcoin.getLastBlock();
  const prevBlockHash = lastBlock.hash;
  const currBlockData = {
    transactions: bitcoin.pendingTransactions,
    index: lastBlock.index + 1,
  };
  const nonce = bitcoin.proofOfWork(prevBlockHash, currBlockData);
  const blockHash = bitcoin.hashBlock(prevBlockHash, currBlockData, nonce);

  bitcoin.createNewTransaction(12.5, '00', nodeAddress);

  const newBlock = bitcoin.createNewBlock(nonce, prevBlockHash, blockHash);

  const reqPromises = bitcoin.networkNodes.map((networkNodeUrl) => {
    const reqOptions = {
      uri: networkNodeUrl + '/recieve-new-block',
      method: 'POST',
      body: { newBlock },
      json: true,
    };

    return rp(reqOptions);
  });

  Promise.all(reqPromises)
    .then(() => {
      const reqOptions = {
        uri: bitcoin.currentNodeUrl + '/transaction/broadcast',
        method: 'POST',
        body: { amount: 12.5, sender: '00', recipient: nodeAddress },
        json: true,
      };

      return rp(reqOptions);
    })
    .then(() => {
      res.json({ message: 'New block mined successfully', block: newBlock });
    });
});

测试 mine

这里依旧用 3331 去挖一个新的 block:

在这里插入图片描述

随后查看 3331、3332 和 3333 是否都同步了:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

共识 consensus

consensus 是每个 node 都需要承认当前的 transaction/block 是 valid 的过程,简单的说就是认证。

新增验证功能

新增到 blockchain 中的函数如下:

validateChain = (blockchain: Block[]): boolean => {
  for (let i = 1; i < blockchain.length; i++) {
    const currBlock = blockchain[i],
      prevBlock = blockchain[i - 1];

    if (currBlock.previousBlockHash !== prevBlock.hash) return false;

    const blockHash = this.hashBlock(
      prevBlock.hash,
      {
        transactions: currBlock.transactions,
        index: currBlock.index,
      },
      currBlock.nonce
    );

    if (blockHash.substring(0, 4) !== '0000') return false;
  }

  const genesisBlock = blockchain[0];
  const { nonce, hash, previousBlockHash, transactions } = genesisBlock;

  return (
    nonce === 100 &&
    previousBlockHash === '0' &&
    hash === '0' &&
    transactions.length === 0
  );
};

这个函数主要实现了这么几个功能:

  1. 验证从第 2 个 block 开始,所有存在于 blockchain 中的 block 是否合法

    合法的 block 必须要满足:

    1. 当前 block 中存储的 prevBlockHash 需要与上一个 block 存储的 hash 值一致
    2. 当前 block 重新计算出来的 hash 值是一个合法的 blockchain hash,即前四个数字为 0000
  2. 验证 genesis block 是否合法

    这个部分就基于当前实现 hardcode 了

这个感兴趣的可以自己生成一些数据进行操作修改,这里提供一下我当时用来做测试的简单数据:

const bc1 = {
  chain: [
    {
      index: 1,
      timestamp: 1683430683516,
      transactions: [],
      nonce: 100,
      hash: '0',
      previousBlockHash: '0',
    },
    {
      index: 2,
      timestamp: 1683430713676,
      transactions: [
        {
          amount: 10,
          sender: 'sender a',
          recipient: 'miner a',
          transactionId: 'b60d9820fa5b41678a2ddd7e362ce677',
        },
      ],
      nonce: 57094,
      hash: '00008d14fdea3462ba42d5877404d0e79031b37efe0f836c4ed498521fbe6718',
      previousBlockHash: '0',
    },
    {
      index: 3,
      timestamp: 1683430741200,
      transactions: [
        {
          amount: 12.5,
          sender: '00',
          recipient: '0a574140dc034e0aa95d2f94202be1c9',
          transactionId: 'a950c175607545f393b2818c7dc8783e',
        },
        {
          amount: 10,
          sender: 'sender b',
          recipient: 'miner b',
          transactionId: '153b8d642e974c7a8141582f432b1333',
        },
      ],
      nonce: 23143,
      hash: '0000508c0ecea10829d6c757aa8d29d6198561cac666496add65dffbcdc9899d',
      previousBlockHash:
        '00008d14fdea3462ba42d5877404d0e79031b37efe0f836c4ed498521fbe6718',
    },
  ],
  pendingTransactions: [
    {
      amount: 12.5,
      sender: '00',
      recipient: '0a574140dc034e0aa95d2f94202be1c9',
      transactionId: 'a6da7fea6cba47fa996bdd001f56b11a',
    },
  ],
  currentNodeUrl: 'http://localhost:3331',
  networkNodes: [],
};

创建 consensus endpoint

这里要做的步骤是:

  1. 获取当前所有 network 上所有 nodes 的 blockchains
  2. 遍历获取的 blockchains,以最长的那条 blockchain 作为 single source of truth
app.get('/consensus', (req, res) => {
  const reqPromises = bitcoin.networkNodes.map((newtorkNodeUrl) => {
    const reqOptions = {
      uri: newtorkNodeUrl + '/blockchain',
      method: 'GET',
      json: true,
    };

    return rp(reqOptions);
  });

  Promise.all(reqPromises).then((blockchains) => {
    const currChainLen = bitcoin.chain.length;
    let maxChainLen = currChainLen,
      newLongestChain = null,
      newPendingTransactions = null;
    blockchains.forEach((blockchain: Blockchain) => {
      if (blockchain.chain.length > maxChainLen) {
        maxChainLen = blockchain.chain.length;
        newLongestChain = blockchain.chain;
        newPendingTransactions = blockchain.pendingTransactions;
      }
    });

    const isValidateChain = newLongestChain
      ? bitcoin.validateChain(newLongestChain)
      : false;

    if (!newLongestChain || (newLongestChain && !isValidateChain)) {
      res.json({
        message: 'Current chain has not been replaced.',
        chain: bitcoin.chain,
      });

      return;
    }

    if (newLongestChain && isValidateChain) {
      bitcoin.chain = newLongestChain;
      res.json({
        message: 'Current chain has been replaced.',
        chain: bitcoin.chain,
      });
    }
  });
});

测试 consensus 部分:

⚠️:刚开始错误使用了 post 去实现,应该是 get,代码部分改了,postman 测试这里暂时不改了

3331、3332、3333 这三个 nodes 运行 consensus 都不会有任何的区别,因为 3 条 blockchain 都是一致的:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

但是这个时候如果有新的 node 加到了 network 中,那么这个 node 上所留有的就是一个空的 blockchain:

在这里插入图片描述

再运行 consensus 就会使得当前 node 上的 blockchain 被当前网络中最长的 blockchain 所取代:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/500877.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器视觉之线缆字符检测

在生活当中&#xff0c;随处可见与印刷字符有关的产品&#xff0c;比如&#xff1a;线缆上字符&#xff0c;键盘上的字符&#xff0c;衣物上的标签字符&#xff0c;电器上的字符等等。 而这些产品的外观由于字符在印刷时产生的一些瑕疵&#xff0c;如字符拉丝、移位、多墨、缺失…

身为管理层总是被下属怼,自己毫无威严,如何改变这样的现状?

身为一名管理层&#xff0c;被下属怼的感觉无疑是相当不爽的。毕竟&#xff0c;作为领导者&#xff0c;我们希望能够得到下属的尊重和信任&#xff0c;而不是被他们视为“摆设”。如果你也有类似的经历&#xff0c;那么不妨试试以下几种方法&#xff0c;来改变这种局面。 首先…

C++ Qt5.9学习笔记-事件1.5W字总结

⭐️我叫忆_恒心&#xff0c;一名喜欢书写博客的在读研究生&#x1f468;‍&#x1f393;。 如果觉得本文能帮到您&#xff0c;麻烦点个赞&#x1f44d;呗&#xff01; 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧&#xff0c;喜欢的小伙伴给个三…

物业管理系统【纯控制台】(Java课设)

系统类型 纯控制台类型&#xff08;没有用到数据库&#xff09; 使用范围 适合作为Java课设&#xff01;&#xff01;&#xff01; 部署环境 jdk1.8Idea或eclipse 运行效果 本系统源码地址&#xff1a;https://download.csdn.net/download/qq_50954361/87753361 更多系统…

YOLOv5结合BiFPN:BiFPN网络结构调整,BiFPN训练模型训练技巧

目录 一、BiFPN网络结构调整1、堆叠BiFPN2、调整网络深度3、调整BiFPN的参数 二、训练技巧和注意事项1、数据增强2、学习率调度3、优化器选择4、权重初始化5、模型选择6、Batch size的选择7、模型保存和加载8、注意过拟合和欠拟合问题 三、实验结果和分析1、数据集和评估指标2、…

开发、部署应用程序APP的【12要素原则】你顺便了解一下?

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 开发、部署应用程序APP的【12要素原则】你顺便了解一下&#xff1f; ☘️摘要☘️介绍☘️背景☘️谁应该阅读这份文件?☘️十二要素原则&#x1f33f;I. 代码库 Codebase&#x1f…

2.进程与线程

2.进程与线程 2.1 进程与线程 进程&#xff1a; 程序由指令和数据组成&#xff0c;指令要执行&#xff0c;数据要读写&#xff0c;就需要将指令加载到cpu&#xff0c;数据加载到内存&#xff0c;进程就是用来加载指令、管理IO、管理内存的当一个程序被执行&#xff0c;从磁盘…

大数据环境准备(二) - VMware 虚拟机系统设置

VMware 虚拟机系统设置 1.对三台虚拟机完成主机名、固定IP、SSH免密登录等系统设置 1&#xff09;配置固定IP地址 开启node1,修改主机名为node1 #切换root用户 su - #修改主机名 hostnamectl set-hostname node1关闭node1终端&#xff0c;重新打开&#xff1b; 同理开启nod…

Java页面布局

Java页面常用的布局主要有五种&#xff1a;FlowLayout、BorderLayout、GridLayout、CardLayout和NULL 1、FlowLayout 称为“流布局”&#xff0c;将组件按从左到右顺序、流动的安排到容器中&#xff0c;直到占满上方的空间时、则向下移动一行&#xff0c;Flow Layout是面板的…

13.多线程

1.实现多线程 1.1简单了解多线程【理解】 是指从软件或者硬件上实现多个线程并发执行的技术。 具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程&#xff0c;提升性能。 1.2并发和并行【理解】 并行&#xff1a;在同一时刻&#xff0c;有多个指令在多个CPU上…

Packet Tracer - 配置 IPv6 ACL

Packet Tracer - 配置 IPv6 ACL 拓扑图 地址分配表 设备 接口 IPv6 地址/前缀 默认网关 服务器 3 NIC 2001:DB8:1:30::30/64 FE80::30 目标 第 1 部分&#xff1a;配置、应用并验证一个 IPv6 ACL 第 2 部分&#xff1a;配置、应用并验证第二个 IPv6 ACL 第 1 部分…

node.js+vue鲜花销售网站

后台模块设计&#xff1a; ①用户管理功能。管理员在后台首页点击用户管理就会进入用户列表页面&#xff0c;系统会将数据库中的用户信息以列表的形式显示出来&#xff0c;管理员可以在这个页面进行用户的更新和删除操作&#xff0c;系统可以将最新更新的信息重新写入用户表中并…

Chrome浏览器更新字体看不清的最终解决方案

阿酷TONY / 2023-5-6 / 长沙 / 原创 / 实测解决 Chrome更新至版本Chrome 109.0.5414.120 字体看不清 浏览器症状&#xff1a;Chrome更新至版本Chrome 109.0.5414.120 字体看不清&#xff1b;会很细&#xff0c;在设置中选择自定义的字体&#xff0c;仍无法解决&#xff1b;…

当因果推理遇上时间序列,会碰撞出怎样的火花?

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 近年来因果推理和时间序列已经成为了数据科学领域备受瞩目的研究方向。因果推理可以帮助我们识别变量之间的因果关系&#xff0c;时间序列分析则可以便于我们理解变量随时间变化的规律。这两个方向都可以为我们…

javaScript---设计模式-封装与对象

目录 1、封装对象时的设计模式 2、基本结构与应用示例 2.1 工厂模式 2.2 建造者模式 2.3 单例模式 封装的目的&#xff1a;①定义变量不会污染外部&#xff1b;②能作为一个模块调用&#xff1b;③遵循开闭原则。 好的封装&#xff08;不可见、留接口&#xff09;&#xff1a;①…

Cacti 前台命令注入漏洞

文章目录 文档说明漏洞描述影响版本漏洞原理命令执行简单分析客户端ip伪造分析 漏洞复现下载vulhub启动环境配置攻击 复现总结修复方案原创申明 文档说明 本文作者:SwBack 创作时间:2023/4/8 0:12 知乎:https://www.zhihu.com/people/back-88-87 CSDN:https://blog.csdn.net/q…

solidity--语言基础

solidity源文件结构 // SPDX-License-Identifier: MIT pragma solidity ^0.5.2; pragma abicoder v1; import "filename"; 注释 智能合约组成 状态变量 // SPDX-License-Identifier: GPL-3.0 pragma solidity >0.4.0 <0.9.0;contract SimpleStorage {uint stor…

SD卡打不开是怎么回事?SD卡打不开里面数据怎样恢复

SD卡已经成为了移动设备和数码相机中受欢迎的存储选项之一。但是使用过程中难免会遇到一些问题&#xff0c;例如SD卡突然打不开了&#xff0c;并且无法访问其中的数据。这种情况常常让人感到烦恼和无助。但是不要紧张&#xff0c;下面我们将介绍SD卡打不开里面数据怎样恢复的方…

Yuzuki Lizard V851S开发板 –编译 OPENCV 4.5.4

1.主要参考教程地址&#xff0c;实际操作结合多个教程。 https://blog.csdn.net/Flag_ing/article/details/109508374 2.放从OPENCV RELEASE 下载的解压出来的文件&#xff0c;里面还要放对应版本的contribute 解压文件 /root/opencv-4.5.4/root/opencv-4.5.4/build6在这里要…

深入理解Java虚拟机——内存分配与回收策略

1.前言 在读这篇博客之前&#xff0c;你需要了解分代收集理论中&#xff0c;收集器应该将Java堆划分出不同的区域**&#xff0c;**然后将回收对象依据其年龄&#xff08;年龄即对象熬过垃圾收集过程的次数&#xff09;分配到不同的区域之中存储。 例如appel式回收&#xff0c…