本文作者:咔咔

node.js 区块链

node.js 区块链摘要: Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时,它非常适合用于构建区块链应用,原因如下:异步 I/O:区块链节点需要不断地与网络中的其他节点进...

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时,它非常适合用于构建区块链应用,原因如下:

  1. 异步 I/O:区块链节点需要不断地与网络中的其他节点进行通信(同步数据、广播交易),Node.js 的异步特性非常适合处理这种网络密集型任务。
  2. JavaScript 全栈:你可以使用同一种语言(JavaScript/TypeScript)来开发区块链的底层逻辑、后端 API 以及前端界面,提高了开发效率。
  3. 丰富的 NPM 生态:有大量的库可以帮助你处理加密、网络通信、数据库等任务,让你可以专注于区块链的核心逻辑。
  4. 性能:对于教学、原型开发或中等规模的私有链/Node.js 的性能是完全足够的。

区块链核心概念回顾

在用 Node.js 实现之前,我们先快速回顾一下区块链的几个核心组成部分:

node.js 区块链
(图片来源网络,侵删)
  1. 区块:数据记录的基本单位,每个区块包含:

    • index:区块的索引号。
    • timestamp:区块创建的时间戳。
    • data:区块存储的实际数据(例如交易信息)。
    • previousHash:前一个区块的哈希值,这是形成“链”的关键。
    • hash:当前区块的哈希值,通过对区块内容进行哈希计算得出,确保了数据的完整性。
  2. 哈希:一个将任意长度的输入数据转换为固定长度输出的加密算法,区块链中通常使用 SHA-256,它的特性是:

    • 单向性:无法从哈希值反推出原始数据。
    • 抗碰撞性:找到两个不同输入产生相同哈希值的计算量极大。
    • 雪崩效应:输入的微小变化会导致输出的巨大变化。
  3. :通过每个区块的 previousHash 指向前一个区块,形成了一个不可篡改的、时间上有序的数据链。

  4. 工作量证明:一种共识机制,它要求“矿工”(节点)通过大量的计算(找到一个特定的数字 nonce,使得区块头的哈值满足某个条件,比如前几位是 0)来“证明”他们付出了工作量,这可以防止垃圾数据和恶意攻击。

    node.js 区块链
    (图片来源网络,侵删)
  5. 网络:节点之间通过 P2P 网络进行通信,用于广播新区块、同步链、广播交易等。


使用 Node.js 实现一个简单的区块链

我们将分步构建一个最基础的区块链,包含区块、链、PoW 共识和基本的 P2P 通信。

步骤 1:项目初始化

创建一个新的项目目录并初始化 npm。

mkdir node-blockchain
cd node-blockchain
npm init -y

步骤 2:安装依赖

我们需要几个关键的库:

node.js 区块链
(图片来源网络,侵删)
  • crypto-js:用于计算 SHA-256 哈希值。
  • express:用于创建一个简单的 HTTP 服务器,方便我们通过 API 与区块链交互。
  • body-parser:用于解析 HTTP 请求体。
  • uuid:用于生成唯一的节点 ID。
npm install crypto-js express body-parser uuid

步骤 3:创建 block.js - 区块类

这个文件将定义区块的结构。

// block.js
const CryptoJS = require("crypto-js");
class Block {
    constructor(index, timestamp, data, previousHash = '') {
        this.index = index;
        this.timestamp = timestamp;
        this.data = data;
        this.previousHash = previousHash;
        this.hash = this.calculateHash();
        this.nonce = 0; // 用于工作量证明
    }
    // 计算区块的哈希值
    calculateHash() {
        return CryptoJS.SHA256(
            this.index +
            this.previousHash +
            this.timestamp +
            JSON.stringify(this.data) +
            this.nonce
        ).toString();
    }
    // 工作量证明
    mineBlock(difficulty) {
        const target = Array(difficulty + 1).join("0");
        while (this.hash.substring(0, difficulty) !== target) {
            this.nonce++;
            this.hash = this.calculateHash();
        }
        console.log(`Block Mined: ${this.hash}`);
    }
}
module.exports = Block;

步骤 4:创建 blockchain.js - 区块链类

这个文件将管理整个链,包括添加新区块、验证链的有效性等。

// blockchain.js
const Block = require('./block');
class Blockchain {
    constructor() {
        this.chain = [this.createGenesisBlock()];
        this.difficulty = 2; // 挖矿难度,数字越大越难
    }
    // 创建创世区块
    createGenesisBlock() {
        return new Block(0, new Date().toISOString(), "Genesis Block", "0");
    }
    // 获取最新的区块
    getLatestBlock() {
        return this.chain[this.chain.length - 1];
    }
    // 添加新区块到链中
    addBlock(newBlock) {
        newBlock.previousHash = this.getLatestBlock().hash;
        newBlock.mineBlock(this.difficulty); // 挖矿
        this.chain.push(newBlock);
    }
    // 验证整个链是否有效
    isChainValid() {
        for (let i = 1; i < this.chain.length; i++) {
            const currentBlock = this.chain[i];
            const previousBlock = this.chain[i - 1];
            // 检查当前区块的哈希是否正确
            if (currentBlock.hash !== currentBlock.calculateHash()) {
                console.error(`Invalid hash for block ${currentBlock.index}`);
                return false;
            }
            // 检查当前区块是否正确指向前一个区块
            if (currentBlock.previousHash !== previousBlock.hash) {
                console.error(`Invalid previousHash for block ${currentBlock.index}`);
                return false;
            }
        }
        // 检查创世区块
        return this.chain[0].hash === this.chain[0].calculateHash();
    }
}
module.exports = Blockchain;

步骤 5:创建 server.js - API 服务器

这是我们的核心文件,它将使用 express 来暴露 API,让我们可以与区块链交互。

// server.js
const express = require('express');
const bodyParser = require('body-parser');
const Blockchain = require('./blockchain');
const uuid = require('uuid');
const nodeAddress = uuid.v1().split('-').join('');
const app = express();
const port = 3000;
const myCoin = new Blockchain();
// 中间件
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// 获取整个区块链
app.get('/blockchain', (req, res) => {
    res.send(myCoin);
});
// 创建一个新的区块
app.post('/block', (req, res) => {
    const newBlockData = {
        amount: req.body.amount,
        sender: req.body.sender,
        recipient: req.body.recipient,
    };
    myCoin.addBlock(new Block(myCoin.chain.length, new Date().toISOString(), newBlockData));
    res.send(myCoin);
});
// 挖矿
app.get('/mine', (req, res) => {
    const lastBlock = myCoin.getLatestBlock();
    const newBlock = new Block(
        lastBlock.index + 1,
        new Date().toISOString(),
        { reward: 10 }, // 挖矿奖励
        lastBlock.hash
    );
    myCoin.addBlock(newBlock);
    res.send(myCoin);
});
app.listen(port, () => {
    console.log(`Blockchain server running on http://localhost:${port}`);
});

步骤 6:运行和测试

你可以启动服务器了:

node server.js

服务器启动后,你可以使用 Postman 或 curl 来测试 API。

  1. 查看区块链

    curl http://localhost:3000/blockchain

    你会看到一个只包含创世区块的链。

  2. 创建并添加一个新区块(一笔交易)

    curl -X POST -H "Content-Type: application/json" -d '{"amount": 100, "sender": "Alice", "recipient": "Bob"}' http://localhost:3000/block

    你会看到服务器在“挖矿”,因为 difficulty 是 2,需要计算一段时间才能找到满足条件的 nonce,完成后,链上会多一个新区块。

  3. 再次查看区块链

    curl http://localhost:3000/blockchain

    你现在可以看到包含两个区块的完整链。

  4. 验证链的有效性: 在 server.js 中添加一个路由:

    app.get('/validate', (req, res) => {
        res.send(myCoin.isChainValid());
    });

    然后访问 http://localhost:3000/validate,会返回 true

  5. 尝试篡改数据: 修改 blockchain.js 中的 isChainValid 方法,在验证前先篡改一个区块的数据:

    npm install crypto-js express body-parser uuid0

    重启服务器,再次访问 http://localhost:3000/validate,你会得到 false,并且控制台会打印出错误信息,证明你的篡改被检测到了。


进阶:实现 P2P 网络

上面的例子是一个中心化的服务器,一个真正的区块链是分布式的,我们需要实现节点之间的通信。

目标

  • 每个节点都运行一个自己的区块链实例。
  • 节点可以知道网络中其他所有节点的地址。
  • 节点可以广播自己的新区块给其他节点。
  • 节点可以请求并同步整个最长的链。

修改 server.js 来支持 P2P

我们将添加新的 API 端点来处理节点注册和同步。

npm install crypto-js express body-parser uuid1

如何测试 P2P 网络

  1. 启动第一个节点(端口 3000)。
  2. 启动第二个节点(端口 3001),你需要修改 server.js 中的 port 变量。
  3. 在第二个节点上,向第一个节点发送一个 POST 请求,注册自己:
    npm install crypto-js express body-parser uuid2
  4. 在第一个节点上,你可以访问 http://localhost:3000/blockchain,然后尝试在第二个节点上挖矿(/mine),再回到第一个节点查看区块链,你会发现两个节点的链已经同步了。

真实世界的 Node.js 区块链项目

这个简单的例子展示了原理,但真实的区块链项目要复杂得多:

  • 以太坊 (Ethereum):虽然以太坊客户端(如 Geth)主要用 Go 和 C++ 编写,但它的智能合约是使用 Solidity(类 JavaScript 语言)编写的,并且有许多 JavaScript/TypeScript 的开发工具库(如 web3.jsethers.js)用于与以太坊网络交互。
  • NEAR Protocol:一个高性能的区块链平台,其核心智能合约和许多工具链都是用 Rust 和 TypeScript/JavaScript 构建的。
  • Substrate (by Parity):一个用 Rust 构建区块链框架,但它允许你使用 "链上运行时"(WebAssembly)来定义业务逻辑,你可以用 AssemblyScript(TypeScript 的超集)来编写这些逻辑。

使用 Node.js 构建区块链是一个绝佳的学习途径,通过亲手实现一个简单的区块链,你能够深刻理解哈希、区块、链、共识机制和 P2P 网络等核心概念,虽然它不能与比特币或以太坊这样的生产级系统相提并论,但它为你进入更广阔的区块链世界打下了坚实的基础。

文章版权及转载声明

作者:咔咔本文地址:https://jits.cn/content/22788.html发布于 12-26
文章转载或复制请以超链接形式并注明出处杰思科技・AI 股讯

阅读
分享

发表评论

快捷回复:

评论列表 (暂无评论,1人围观)参与讨论

还没有评论,来说两句吧...