用 60 行 Javascript 创建区块链

2025-05-24

用 60 行 Javascript 创建区块链

近年来,加密货币和区块链是两个新兴领域,所以今天,我将分享仅用 60 行代码在 Javascript 中创建区块链的方法。

YouTube 上也有我的完整教程。你可以去看看,了解更多详情。

另外,我的新文章发布了,快来看看吧!这篇文章讲的是为我们的区块链创建交易,这基本上是构建加密货币的第一步。

如果您对其中两篇已经很熟悉,可以考虑阅读第三篇关于如何创建 P2P 网络并发布加密货币的文章。不妨看看!

什么是区块链?

在编写任何代码之前,我们需要了解什么是区块链。从技术角度来看,区块链本质上只是一个包含对象的列表,其中包含一些基本信息,例如时间戳、交易、哈希值等。其数据必须是不可篡改且不可被黑客攻击的。以太坊、Cardano、Polkadot 等现代平台拥有更复杂的内容,但本文我们将简单介绍一下。

设置

我们在这个项目中使用 Node.js,因此如果您还没有安装它,请务必安装它。

在整篇文章中,我将使用面向对象的编程风格,所以我希望你了解有关它的基本知识。

创建一个块

正如我所说,块只是一个包含一些信息的对象,所以我们应该有一个Block这样的类:



class Block {
    constructor(timestamp = "", data = []) {
        this.timestamp = timestamp;
        // this.data should contain information like transactions.
        this.data = data;
    }
}


Enter fullscreen mode Exit fullscreen mode

所以我们有 和timestampdata但区块链需要不变性。我们可以通过使用哈希函数来获得这种效果,该函数会将区块中的所有属性进行哈希处理。我建议阅读维基百科上关于哈希函数的内容,它在区块链中起着至关重要的作用。简单来说,它接收一条消息并输出一条固定长度的“哈希”消息,消息的细微变化都会导致输出完全不同。

我正在使用该sha256算法。为了实现它的哈希函数,我将使用 Nodejs 的内置crypto包:



const crypto = require("crypto"), SHA256 = message => crypto.createHash("sha256").update(message).digest("hex");


Enter fullscreen mode Exit fullscreen mode

上面的代码应该能满足我们的要求,但是如果你想知道它是如何工作的,请查看Node.js 关于哈希类的官方文档

我们应该有类似这样的内容:



// Get the sha256 hash function.
const crypto = require("crypto"), SHA256 = message => crypto.createHash("sha256").update(message).digest("hex");

class Block {
    constructor(timestamp = "", data = []) {
        this.timestamp = timestamp;
        this.data = data;
        this.hash = this.getHash();
        this.prevHash = ""; // previous block's hash
    }

    // Our hash function.
    getHash() {
        return SHA256(this.prevHash + this.timestamp + JSON.stringify(this.data));
    }
}


Enter fullscreen mode Exit fullscreen mode

因为每次任何东西发生变化时,SHA256 都会抛出一些完全不同的东西,这样就可以在某种程度上确保不变性。

prevHash属性在不变性方面也发挥着重要作用,它确保区块在区块链的整个生命周期内保持不变。它包含前一个区块的哈希值,因此您可以确保前一个区块的不变性,因为任何细微的变化都会导致当前区块的哈希值getHash发生变化。您可以看到它是空的,但我们将在本文后面对其进行处理。

区块链

让我们转到区块链课程。

正如我所说的,区块链是一个包含区块的列表,因此我们可以有这样的基本形式:



class Blockchain {
    constructor() {
        // This property will contain all the blocks.
        this.chain = [];
    }
}


Enter fullscreen mode Exit fullscreen mode

你必须有一个创世块,从技术上讲,它只是第一个块:



class Blockchain {
    constructor() {
        // Create our genesis block
        this.chain = [new Block(Date.now().toString())];
    }
}


Enter fullscreen mode Exit fullscreen mode

为了方便起见,我将创建一个函数来获取最新的区块:



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


Enter fullscreen mode Exit fullscreen mode

现在,我们应该有一种方法来向区块链添加一个区块。



    addBlock(block) {
        // Since we are adding a new block, prevHash will be the hash of the old latest block
        block.prevHash = this.getLastBlock().hash;
        // Since now prevHash has a value, we must reset the block's hash
        block.hash = block.getHash();

        // Object.freeze ensures immutability in our code
        this.chain.push(Object.freeze(block));
    }


Enter fullscreen mode Exit fullscreen mode

验证

我们需要知道链是否仍然有效,因此我们需要一个方法来检查其有效性。如果一个区块的哈希值等于其哈希方法返回的值,则该链有效;而一个区块的prevHash属性应该等于前一个区块的哈希值。



    isValid(blockchain = this) {
        // Iterate over the chain, we need to set i to 1 because there are nothing before the genesis block, so we start at the second block.
        for (let i = 1; i < blockchain.chain.length; i++) {
            const currentBlock = blockchain.chain[i];
            const prevBlock = blockchain.chain[i-1];

            // Check validation
            if (currentBlock.hash !== currentBlock.getHash() || prevBlock.hash !== currentBlock.prevHash) {
                return false;
            }
        }

        return true;
    }


Enter fullscreen mode Exit fullscreen mode

当我们的区块链在 p2p 网络上运行的时候,这种方法将发挥非常重要的作用。

工作量证明

在点对点网络中,没有第三方系统来批准人们的行为,也没有任何共识机制,节点(简单来说就是人)会同意大多数人的意见,但其他人可能会成为攻击者并控制大多数人,所以我们需要一种共识机制。共识机制的存在并非完全是为了阻止攻击,而是为了让人们不成为攻击者。工作量证明就是其中之一。

在我们进一步讨论之前,系统的工作原理是让你增加一个称为 nonce 的值来获取以等于/与难度相关的多个零开头的哈希值。

PoW 有两个好处:它可以防止攻击者,因为单凭一己之力几乎不可能追上其他节点;它还提供挖矿奖励,这样人们就会尽量保持中立,而不是成为攻击者。我们将在下一篇文章中,在交易系统完善后,实现挖矿奖励。

我们可以通过向我们的区块添加mine方法和属性来实现 PoW 系统:nonce



class Block {
    constructor(timestamp = "", data = []) {
        this.timestamp = timestamp;
        this.data = data;
        this.hash = this.getHash();
        this.prevHash = ""; // previous block's hash
        this.nonce = 0;
    }

    // Our hash function.
    getHash() {
        return SHA256(this.prevHash + this.timestamp + JSON.stringify(this.data) + this.nonce);
    }

    mine(difficulty) {
        // Basically, it loops until our hash starts with 
        // the string 0...000 with length of <difficulty>.
        while (!this.hash.startsWith(Array(difficulty + 1).join("0"))) {
            // We increases our nonce so that we can get a whole different hash.
            this.nonce++;
            // Update our new hash with the new nonce value.
            this.hash = this.getHash();
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

因为当我们改变块中的一个小细节时,哈希值将完全不同,所以我们只是一遍又一遍地增加随机数,直到哈希值与我们需要的哈希值匹配。

(请注意,比特币和其他货币通常使用不同的方式来检查难度,但我们保持简单)

转到Blockchain类,我们应该创建一个难度属性:



    this.difficulty = 1;


Enter fullscreen mode Exit fullscreen mode

我将把它设置为 1,难度应该根据挖掘的区块数量进行更新。

addBlock我们也必须从区块链更新方法:



    addBlock(block) {
        block.prevHash = this.getLastBlock().hash;
        block.hash = block.getHash();
        block.mine(this.difficulty);
        this.chain.push(Object.freeze(block));
    }


Enter fullscreen mode Exit fullscreen mode

现在,所有区块在添加到链之前都需要被挖掘。

快速说明

为了保持简单,我在这个区块链中使用了工作量证明系统。需要注意的是,大多数现代区块链都使用一种更先进的系统,称为权益证明(或其许多升级版本)。

测试链条!

创建一个新文件,该文件将作为入口文件。

让我们使用刚刚创建的区块链吧!我JeChain现在就调用它。

首先导出所需的类:



module.exports = { Block, Blockchain };


Enter fullscreen mode Exit fullscreen mode


const { Block, Blockchain } = require("./your-blockchain-file.js");

const JeChain = new Blockchain();
// Add a new block
JeChain.addBlock(new Block(Date.now().toString(), { from: "John", to: "Bob", amount: 100 }));
// (This is just a fun example, real cryptocurrencies often have some more steps to implement).

// Prints out the updated chain
console.log(JeChain.chain); 


Enter fullscreen mode Exit fullscreen mode

它看起来应该是这样的:

图片描述

第一个区块是我们的创世区块,第二个区块是添加的区块。

更新奖励:难度和区块时间

区块时间

区块时间是一个常数值,代表着区块被添加到链上的预计时间。比特币等平台的区块时间为 10 分钟,而以太坊的区块时间为 13 秒。

比特币的难度公式

比特币每挖出 2016 个区块,其难度就会更新一次。它使用以下公式计算新的难度:



old difficulty * (2016 blocks * 10 minutes) / mining time for the previous 2016 blocks


Enter fullscreen mode Exit fullscreen mode

现在,让我们开始编码吧!

首先,我们必须先确定区块时间,我将其设置为 30 秒,相当于 30000 毫秒。我使用毫秒,因为它与 配合得更好Date.now()



    this.blockTime = 30000;


Enter fullscreen mode Exit fullscreen mode

(请注意,我们在课堂上进行编码Blockchain)。

仅作为一个例子,我将创建自己的系统:如果区块时间小于区块开采的实际时间,则难度将增加 1,否则难度将减少。



    addBlock(block) {
        block.prevHash = this.getLastBlock().hash;
        block.hash = block.getHash();
        block.mine(this.difficulty);
        this.chain.push(Object.freeze(block));

        this.difficulty += Date.now() - parseInt(this.getLastBlock().timestamp) < this.blockTime ? 1 : -1;
    }


Enter fullscreen mode Exit fullscreen mode

重要提示!!!

由于我们之前检查难度的方式,这应该没问题。但是,最好使用难度值log16(difficulty)而不是难度本身来检查难度,这样你就可以使用比特币的难度公式了。

不过,你可以想出自己的方案。你应该考虑在保证良好性能的同时,如何才能最大限度地保障安全。

源代码

您可以在此 repo 中获取完整的源代码:

GitHub 徽标 阮福明/ JeChain

JeChain去中心化应用平台及智能合约区块链网络

荣誉奖

我从Simply Explained学到了很多关于区块链的知识。如果没有他们的视频帮助,这篇文章可能永远都不会出现。建议大家在 YouTube 上看看,他们有非常棒的区块链教程系列。

我也从这篇文章中获取了一些信息。快去看看吧!

题外话

我应该继续这个系列吗?如果是的话,我应该写些什么?权益证明?完整的加密货币?还是智能合约?请在评论区留言告诉我。

联系方式

文章来源:https://dev.to/freakcdev297/creating-a-blockchain-in-60-lines-of-javascript-5fka
PREV
寻找远程编程工作的热门网站
NEXT
我在学习 Web 前端开发和 Web 服务器配置时遇到的有用的网站和应用程序