关键词:区块链入门、Node.js 区块链、Crypto-JS 教程、去中心化账本、哈希算法、工作量证明、区块链测试、JavaScript 挖矿、区块结构
项目总览
要想真正理解区块链,亲手撸一个是最快的方法。本文带你用不到 200 行代码,实现一个具备链式结构、哈希校验以及区块追加功能的「玩具级」区块链,适合用作教学演示或技术入门原型。
技术背景
核心概念速记
- 区块(Block)
每笔交易集合被打包后的数据单元,包含索引、时间戳、交易数据、前一区块哈希、自身哈希。 - 链(Chain)
通过前一区块哈希连续指向形成的历史记录,不可篡改。 - 挖矿(Mining)
找到符合条件的随机数,让区块哈希满足n
个前导零的要求,确保工作量证明。
原理速览
- 用户发起交易。
- 节点收集交易,组成新区块。
- 哈希指针 指向上一区块,保证时间轴不可回滚。
- 网络通过共识算法确认区块合法性,分布式同步账本。
新手易踩坑
- 误用 MD5 等非安全哈希;请坚持
SHA-256
。 - 直接 float 存储代币余额,精度易“销魂”;用整数最小单位存储。
- 把随机函数 Math.random() 用来抽节点,结果可预测,改用 Node.js 的
crypto.randomBytes()
。
环境与工具
必要项 | 版本建议 |
---|---|
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;
在 Block
的 data
字段传入 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
。
性能与安全优化
- 空间:使用 LevelDB 存储链式数据,减少内存占用。
- 速度:用 Merkle Tree 取代完整交易列表哈希,提高校验效率。
- 安全:对交易字段做 ECDSA 签名验证,隔离双花与伪造。
- 共识:将 PoW 换成 PBFT(联盟链场景),单核 CPU 即可秒级出块。
👉 点击获取性能提升 70% 的 Merkle 树示例源码!
下一步:从「玩具」迈向「生产级」
- 引入 钱包体系:生成密钥对 + 离线签名。
- 增加 UTXO 模型:比特币式资金控制,兼容多笔输入输出。
- 搭建 RESTful API:Express 开放 /blocks、/transactions 端点,供前端 DApp 交互。
- 接入 WebSocket:实时广播新块,防止因块高度不一致产生分叉。
结语
通过本文,你已学会用 Node.js 与 Crypto-JS 快速搭建一条可做演示的区块链原型。它虽简,却涵盖哈希、链式存储、工作量证明三大关键组件,为深入阅读以太坊源码或 Hyperledger Fabric 打下坚实基础。下一步,动手写 API 吧!