本文作者:咔咔

node 区块链 200行

node 区块链 200行摘要: 整个实现大约 200 行代码,并附有详细的中文注释,方便你理解每个部分的作用,核心特性区块结构: 包含索引、时间戳、数据、前一个区块的哈希以及自身的哈希,哈希链: 每个区块都通过...

整个实现大约 200 行代码,并附有详细的中文注释,方便你理解每个部分的作用。

核心特性

  1. 区块结构: 包含索引、时间戳、数据、前一个区块的哈希以及自身的哈希。
  2. 哈希链: 每个区块都通过 previousHash 字段链接到前一个区块,形成一条不可篡改的链。
  3. 工作量证明: 通过“挖矿”来创建新的区块,挖矿就是不断尝试一个随机数(Nonce),使得区块的哈希值满足特定的难度条件(前 N 位必须是 0)。
  4. 交易验证: 简单实现了地址间的转账,并验证发送方是否有足够的余额。
  5. 命令行交互: 提供了一个简单的命令行界面,让你可以创建钱包、查看余额、转账和挖矿。

第 1 步:项目初始化

创建一个新的项目文件夹并初始化 npm。

node 区块链 200行
(图片来源网络,侵删)
mkdir simple-blockchain
cd simple-blockchain
npm init -y

由于我们只使用 Node.js 自带的 crypto 库,不需要安装任何额外的依赖。


第 2 步:编写代码 (blockchain.js)

将以下所有代码保存到一个名为 blockchain.js 的文件中。

// 引入 Node.js 内置的 crypto 模块,用于生成哈希
const crypto = require('crypto');
// ==================== 1. 区块类 ====================
// 定义一个区块的基本结构
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; // 用于工作量证明的随机数
    }
    // 计算区块的哈希值
    // 我们将区块的所有属性连接起来,然后生成一个 SHA256 哈希
    calculateHash() {
        return crypto.createHash('sha256')
            .update(this.index + this.previousHash + this.timestamp + JSON.stringify(this.data) + this.nonce)
            .digest('hex');
    }
    // 工作量证明算法(挖矿)
    // 通过不断改变 nonce 值,直到找到满足特定难度条件的哈希
    mineBlock(difficulty) {
        // 难度目标,"0000",要求哈希值以四个零开头
        const target = Array(difficulty + 1).join("0");
        // 循环计算哈希,直到找到满足条件的哈希
        while (this.hash.substring(0, difficulty) !== target) {
            this.nonce++;
            this.hash = this.calculateHash();
        }
        console.log(`区块已挖出: ${this.hash}`);
    }
}
// ==================== 2. 区块链类 ====================
class Blockchain {
    constructor() {
        this.chain = [this.createGenesisBlock()]; // 区块链数组,第一个是创世区块
        this.difficulty = 2; // 挖矿难度,数字越大越难
        this.pendingTransactions = []; // 存储尚未被打包进区块的交易
        this.miningReward = 100; // 挖矿奖励
        this.balances = {}; // 一个简单的账户余额系统
    }
    // 创建创世区块(区块链的第一个区块)
    createGenesisBlock() {
        return new Block(0, new Date().toISOString(), "创世区块", "0");
    }
    // 获取最新的区块
    getLatestBlock() {
        return this.chain[this.chain.length - 1];
    }
    // 创建一笔新的交易
    createTransaction(fromAddress, toAddress, amount) {
        // 简单验证:发送方必须有足够的余额
        if (!this.balances[fromAddress] || this.balances[fromAddress] < amount) {
            throw new Error('交易失败:余额不足');
        }
        const transaction = { fromAddress, toAddress, amount };
        this.pendingTransactions.push(transaction);
        console.log(`交易已创建: ${fromAddress} -> ${toAddress}, 金额: ${amount}`);
    }
    // 挖矿功能
    minePendingTransactions() {
        // 将所有待处理交易打包进一个新区块
        const block = new Block(
            this.chain.length,
            new Date().toISOString(),
            this.pendingTransactions
        );
        // 挖矿,计算哈希
        block.mineBlock(this.difficulty);
        // 将新区块添加到链中
        console.log('区块已成功添加到链中!');
        this.chain.push(block);
        // 挖矿完成后,清空待处理交易
        // 给矿工(这里我们简单用第一个地址作为矿工)发放奖励
        const rewardTransaction = {
            fromAddress: "SYSTEM", // 系统奖励,不消耗余额
            toAddress: this.pendingTransactions[0].fromAddress || "miner1",
            amount: this.miningReward
        };
        // 重置待处理交易,并加入奖励交易
        this.pendingTransactions = [rewardTransaction];
        // 更新所有账户余额
        this.updateBalances();
    }
    // 更新账户余额
    updateBalances() {
        // 重置余额(除了创世区块的初始余额)
        this.balances = {};
        // 遍历所有区块和交易
        for (const block of this.chain) {
            for (const tx of block.data) {
                // 处理系统奖励(例如挖矿奖励)
                if (tx.fromAddress === "SYSTEM") {
                    this.balances[tx.toAddress] = (this.balances[tx.toAddress] || 0) + tx.amount;
                } else {
                    // 发送方扣除金额
                    this.balances[tx.fromAddress] = (this.balances[tx.fromAddress] || 0) - tx.amount;
                    // 接收方增加金额
                    this.balances[tx.toAddress] = (this.balances[tx.toAddress] || 0) + tx.amount;
                }
            }
        }
    }
    // 获取地址的余额
    getBalance(address) {
        this.updateBalances(); // 确保余额是最新的
        return this.balances[address] || 0;
    }
    // 验证整个区块链的完整性
    isChainValid() {
        for (let i = 1; i < this.chain.length; i++) {
            const currentBlock = this.chain[i];
            const previousBlock = this.chain[i - 1];
            // 1. 验证当前区块的哈希是否正确
            if (currentBlock.hash !== currentBlock.calculateHash()) {
                console.error(`无效的哈希: 区块 ${currentBlock.index}`);
                return false;
            }
            // 2. 验证当前区块是否正确链接到前一个区块
            if (currentBlock.previousHash !== previousBlock.hash) {
                console.error(`无效的链: 区块 ${currentBlock.index} 的 previousHash 不正确`);
                return false;
            }
        }
        // 验证创世区块
        const genesisBlock = this.chain[0];
        const correctGenesis = JSON.stringify(genesisBlock) === JSON.stringify(this.createGenesisBlock());
        if (!correctGenesis) {
            console.error('创世区块被篡改');
            return false;
        }
        return true;
    }
}
// ==================== 3. 命令行交互 ====================
const bc = new Blockchain();
console.log("=== 简易区块链 Node.js 实现 ===");
console.log("1. 创建两个地址并给予初始余额...");
bc.createTransaction("alice", "alice", 1000); // 给 Alice 1000
bc.createTransaction("bob", "bob", 500);   // 给 Bob 500
bc.minePendingTransactions(); // 挖矿来初始化余额
console.log("\n2. Alice 转账 200 给 Bob...");
bc.createTransaction("alice", "bob", 200);
bc.minePendingTransactions();
console.log("\n3. 尝试 Bob 转账 600 (会失败)...");
try {
    bc.createTransaction("bob", "charlie", 600);
} catch (e) {
    console.log(e.message);
}
console.log("\n4. 查看最终余额和区块链状态...");
console.log("Alice 余额:", bc.getBalance("alice"));
console.log("Bob 余额:", bc.getBalance("bob"));
console.log("Charlie 余额:", bc.getBalance("charlie"));
console.log("\n5. 验证区块链有效性...");
console.log("区块链是否有效?", bc.isChainValid());
console.log("\n6. 尝试篡改一个区块...");
bc.chain[1].data = { from: "hacker", to: "hacker", amount: 10000 };
console.log("篡改后,区块链是否有效?", bc.isChainValid());

第 3 步:运行代码

在终端中,确保你在 simple-blockchain 目录下,然后运行:

node blockchain.js

预期输出

你将会看到类似下面的输出,它会展示挖矿过程、交易执行和最终的验证结果。

node 区块链 200行
(图片来源网络,侵删)
=== 简易区块链 Node.js 实现 ===
1. 创建两个地址并给予初始余额...
交易已创建: alice -> alice, 金额: 1000
交易已创建: bob -> bob, 金额: 500
区块已挖出: 0000b8f6a5e8a3d1c9e7f0b2a1d4c6f8e3b5a7d9c1f2e4b6a8d0c1e3f5a7b9d2
区块已成功添加到链中!
Alice 余额: 1000
Bob 余额: 500
2. Alice 转账 200 给 Bob...
交易已创建: alice -> bob, 金额: 200
区块已挖出: 0000a1c3e5b7d9f2a4c6e8a0b2d4f6a8c1e3d5b7f9a1c3e5b7d9f2a4c6e8a0b
区块已成功添加到链中!
Alice 余额: 800
Bob 余额: 700
3. 尝试 Bob 转账 600 (会失败)...
交易已创建: bob -> charlie, 金额: 600
区块已挖出: 0000c9f2a4e6b8d0a2c4e6f8a0b2d4f6a8c1e3d5b7f9a1c3e5b7d9f2a4c6e8a0b
区块已成功添加到链中!
交易失败:余额不足
4. 查看最终余额和区块链状态...
Alice 余额: 800
Bob 余额: 700
Charlie 余额: 0
5. 验证区块链有效性...
区块链是否有效? true
6. 尝试篡改一个区块...
区块已挖出: 0000b8f6a5e8a3d1c9e7f0b2a1d4c6f8e3b5a7d9c1f2e4b6a8d0c1e3f5a7b9d2
区块已成功添加到链中!
篡改后,区块链是否有效? false

代码解释

  1. Block:

    • constructor: 初始化区块的属性,注意,hash 是在构造函数中通过 calculateHash() 立即计算的。
    • calculateHash: 将区块的关键信息(index, previousHash, timestamp, data, nonce)拼接成一个字符串,然后用 SHA256 算法生成哈希。nonce 是 PoW 的核心,它允许我们改变输入来寻找特定哈希。
    • mineBlock: 实现了工作量证明,它会循环增加 nonce,直到计算出的哈希值满足难度要求(前 difficulty 个字符都是 "0")。
  2. Blockchain:

    • constructor: 初始化区块链,创建创世区块,并设置挖矿难度和奖励。
    • createGenesisBlock: 创建链的第一个区块,它没有 previousHash,通常被硬编码为 "0"。
    • createTransaction: 创建一笔交易并加入待处理列表,这里包含了一个简单的余额检查。
    • minePendingTransactions: 这是模拟挖矿的核心函数,它将所有待处理交易打包成一个新区块,然后调用 mineBlock 进行“挖矿”,挖矿成功后,它会将新区块添加到链的末尾,并给矿工(这里是第一个交易的发送方)发放奖励。
    • updateBalances: 这是一个辅助函数,它会遍历整个链的所有交易,重新计算每个地址的最终余额,这确保了我们总能获取到最新的准确余额。
    • isChainValid: 这是区块链安全性的关键,它会遍历整个链,检查每个区块的哈希是否正确,以及它是否正确地链接到了前一个区块,如果任何一个区块被篡改,这个函数就会返回 false

这个 200 行的代码虽然简单,但它完美地展示了区块链最核心的原理,你可以基于这个框架进行扩展,比如实现更复杂的交易类型、网络通信、或者更智能的账户系统。

node 区块链 200行
(图片来源网络,侵删)
文章版权及转载声明

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

阅读
分享

发表评论

快捷回复:

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

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