用 Node.js 与 Crypto-JS 30 分钟打造极简区块链

·

关键词:区块链入门、Node.js 区块链、Crypto-JS 教程、去中心化账本、哈希算法、工作量证明、区块链测试、JavaScript 挖矿、区块结构

项目总览

要想真正理解区块链,亲手撸一个是最快的方法。本文带你用不到 200 行代码,实现一个具备链式结构、哈希校验以及区块追加功能的「玩具级」区块链,适合用作教学演示或技术入门原型。

👉 看完本文,再领一份可直接跑在浏览器里的合约演示代码!


技术背景

核心概念速记

原理速览

  1. 用户发起交易。
  2. 节点收集交易,组成新区块。
  3. 哈希指针 指向上一区块,保证时间轴不可回滚。
  4. 网络通过共识算法确认区块合法性,分布式同步账本。

新手易踩坑


环境与工具

必要项版本建议
Node.js≥ 14
Crypto-JS最新版
编辑器VS Code( ESLint + Prettier )

安装命令

mkdir toy-chain && cd toy-chain
npm init -y
npm i crypto-js

动手实现

1. 定义 Block 类

新建 src/block.js
职责:计算哈希、验证数据完整性。

const crypto = 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 crypto
      .SHA256(
        this.index +
        this.previousHash +
        this.timestamp +
        JSON.stringify(this.data) +
        this.nonce
      )
      .toString();
  }

  // 简易工作量证明:哈希前四位为 0
  mineBlock(difficulty = 4) {
    const target = '0'.repeat(difficulty);
    console.log(`⛏️  开始挖矿(目标:${target}...)`);
    while (this.hash.substring(0, difficulty) !== target) {
      this.nonce++;
      this.hash = this.calculateHash();
    }
    console.log(`🎯 挖到啦!Nonce=${this.nonce}, Hash=${this.hash}`);
  }
}

module.exports = Block;

2. 定义 Blockchain 类

新建 src/blockchain.js
职责:维护链式结构、提供校验与追加逻辑。

const Block = require('./block');

class Blockchain {
  constructor() {
    this.chain = [this.createGenesisBlock()];
    this.difficulty = 4;
  }

  createGenesisBlock() {
    return new Block(0, Date.now(), '创世区块', '0');
  }

  getLatestBlock() {
    return this.chain[this.chain.length - 1];
  }

  addBlock(newBlock) {
    newBlock.previousHash = this.getLatestBlock().hash;
    newBlock.mineBlock(this.difficulty);
    this.chain.push(newBlock);
  }

  // 校验链完整性
  isValid() {
    for (let i = 1; i < this.chain.length; i++) {
      const cur  = this.chain[i];
      const prev = this.chain[i - 1];

      if (cur.hash !== cur.calculateHash()) return false;
      if (cur.previousHash !== prev.hash)   return false;
    }
    return true;
  }

  // 打印链的 JSON
  toJSON() {
    return JSON.stringify(this.chain, null, 2);
  }
}

module.exports = Blockchain;

3. 启动脚本

新建 src/main.js

const Blockchain = require('./blockchain');
const Block      = require('./block');

const ledger = new Blockchain();
ledger.addBlock(new Block(1, Date.now(), { amount: 4 }));
ledger.addBlock(new Block(2, Date.now(), { amount: 8 }));

console.log('🧾 账本内容', ledger.toJSON());
console.log('✅ 有效性检查:', ledger.isValid());

运行效果

node src/main.js
⛏️  开始挖矿(目标:0000...)
🎯 挖到啦!Nonce=38765, Hash=0000abc...
...
✅ 有效性检查:true

FAQ | 常见问题

Q1:为什么仅仅四位的 0 前缀就能模拟挖矿?
答:真实比特币主网用的是 16 进制后立即转成 256 位哈希,前缀零数 ≈ 全网算力动态调整,这里简化为常数仅为教学。

Q2:交易列表/data 可以是数组吗?
答:可以。只需把 data 换成对象,例如 { transactions: [...] },再去 calculateHash 里序列化即可。

Q3:Nonce 重复或溢出会被区块链拒绝吗?
答:当前演示代码无此逻辑。生产网会通过最大 Nonce 保护及难度调整共同保障。

Q4:如何让多个节点共享同一份账本?
答:引入 P2P 网络层(如 libp2p),广播新区块并进行共识(PoW/PoS)投票,再替换本地最长链。

Q5:客户端怎么防止双花?
答:只有在最长累计工作量链上的交易才被承认。矿工实时监听网络,若发现冲突交易,仅纳入手续费更高的那笔。


示例扩展

交易类

class Transaction {
  constructor(from, to, amount) {
    this.from   = from;
    this.to     = to;
    this.amount = amount;
  }
}
module.exports = Transaction;

Blockdata 字段传入 Transaction[] 即可模拟转账场景。

单位测试

添加 tests/block.test.js(使用 Jest):

npm i -D jest
test('hash integrity', () => {
  const b = new Block(0, Date.now(), {});
  const snapshot = b.hash;
  b.data.amount = 999;
  expect(b.hash).toBe(snapshot); // 应为 false
});

运行 npx jest --watch


性能与安全优化

👉 点击获取性能提升 70% 的 Merkle 树示例源码!


下一步:从「玩具」迈向「生产级」

  1. 引入 钱包体系:生成密钥对 + 离线签名。
  2. 增加 UTXO 模型:比特币式资金控制,兼容多笔输入输出。
  3. 搭建 RESTful API:Express 开放 /blocks、/transactions 端点,供前端 DApp 交互。
  4. 接入 WebSocket:实时广播新块,防止因块高度不一致产生分叉。

结语

通过本文,你已学会用 Node.js 与 Crypto-JS 快速搭建一条可做演示的区块链原型。它虽简,却涵盖哈希、链式存储、工作量证明三大关键组件,为深入阅读以太坊源码或 Hyperledger Fabric 打下坚实基础。下一步,动手写 API 吧!