用 60 行可读的 TypeScript 编写区块链 无论如何,这是代码 那么...就这样吗?

2025-06-07

用 60 行可读的 TypeScript 代码编写区块链

无论如何,这是代码

那么...就这样吗?

区块链:它每年都变得越来越令人困惑,因为人们用奇怪的比喻来解释它,而不是花 5 分钟来谈论它所涉及的实际数学和代码

这让我很沮丧,因为区块链的概念实际上非常简单:

  • 区块链是区块的列表
  • 一个块是
    • 一些数据
    • 前一个区块的哈希值
    • 一些象征
    • 上述内容的哈希值

如果哈希值具有魔法前缀(即以一定数量的零开头),则区块有效,因此,如果您想向区块链添加一些数据,则必须将其打包成一个区块,为此,您必须找到一个能产生以一定数量的零开头的区块哈希值的代币,这需要大量计算,这就是您获得工作量证明的方式。

好吧,说实话,我只是在谈论一种特定类型的区块链,但这不是重点。编写下面的区块链代码帮助我理解了一些加密货币的基础知识,希望对你也有帮助。我并不是吹嘘自己有多聪明,能用60行代码写出和比特币一样的技术。

无论如何,这是代码

我正在使用 Deno 和 TypeScript。

让我们首先导入一些稍后需要的哈希器

import { createHash } from "https://deno.land/std@0.91.0/hash/mod.ts";
Enter fullscreen mode Exit fullscreen mode

让我们来定义一下区块和代理。代理指的是在你的计算机上运行的程序,可以与世界各地的其他代理进行比特币交易。

type Block = {
  data: string;
  prev: string;
  token: string;
  hash: string;
};
type Agent = {
  addAgent(agent: Agent): void;
  receiveBlock(block: Block): void;
  addData(data: Block["data"]): void;
};
Enter fullscreen mode Exit fullscreen mode

我们将使用 md5 作为哈希函数。它肯定不是最安全的,但我们并不在意。我们00000在这里也定义一个魔法前缀,这样以后就不会重复了。魔法0前缀越多,挖掘新区块的难度就越大。

const hashOf = (str: string) => createHash("md5")
  .update(str)
  .toString();
const magicPrefix = Array(5).fill("0").join("");
Enter fullscreen mode Exit fullscreen mode

现在让我们创建代理工厂。在内部,它将整个区块链保存在内存中,并列出在挖掘新区块时需要广播的所有代理的列表。

第一个区块是“创世区块”,它无需指向前一个区块的哈希值,也无需具有魔法前缀。

const createAgent = (): Agent => {
  const chain: Block[] = [{
    data: "",
    prev: "",
    token: "",
    hash: hashOf(""),
  }];
  const agents: Agent[] = [];
  return {
    addAgent(agent) { /* ... */ },
    addData(data) { /* ... */ },
    receiveBlock(block) { /* ... */ },
  };
};
Enter fullscreen mode Exit fullscreen mode

addAgent方法不需要进一步解释:

addAgent(agent) {
  agents.push(agent);
},
Enter fullscreen mode Exit fullscreen mode

addData挖矿基本上就是在这里进行的。这是一个计算密集型的循环,用于找到能够生成带有魔法前缀的区块哈希值的代币。

我选择hashOf(Math.random().toString())生成一个随机字符串,因为这是一个非常简洁的方法,但不需要散列。

addData(data) {
  while (true) {
    const prev = chain[chain.length - 1].hash;
    const token = hashOf(Math.random().toString());
    const hash = hashOf(data + prev + token);
    if (hash.startsWith(magicPrefix)) {
      const block: Block = { data, prev, token, hash };
      chain.push(block);
      for (const agent of agents) {
        agent.receiveBlock(block);
      }
      return;
    }
  }
},
Enter fullscreen mode Exit fullscreen mode

receiveBlock根据上述条件验证是否可以在链顶部添加新块,如果一切正常,则添加,否则抛出。

receiveBlock(block) {
  if (block.prev != chain[chain.length - 1].hash) {
    throw new Error(
      "Hash does not point to the previous hash in the chain",
    );
  }
  if (!block.hash.startsWith(magicPrefix)) {
    throw new Error("Hash does not start with the magic prefix");
  }
  const actualHash = hashOf(block.data + block.prev + block.token);
  if (actualHash !== block.hash) {
    throw new Error("Hash is not the hash of data|prev|token");
  }
  chain.push(block);
},
Enter fullscreen mode Exit fullscreen mode

而且...就是这样!

你可以用这样的代理运行“模拟”,例如这个,其中两个代理添加了问候语块。它运行时不会打印任何内容,也不会抛出任何异常:

const alice = createAgent();
const bob = createAgent();
alice.addAgent(bob);
bob.addAgent(alice);
alice.addData("Hello Bob! -Alice");
bob.addData("Hello Alice! -Bob");
Enter fullscreen mode Exit fullscreen mode

或者在这个“模拟”中,我们尝试将恶意区块注入区块链,但它被捕获了:

const alice = createAgent();
const data = "bad things";
const prev = hashOf("");
alice.receiveBlock({
  data,
  prev,
  token: "",
  hash: hashOf(data + prev),
});
// error: Uncaught Error: Hash does not start with the magic prefix
Enter fullscreen mode Exit fullscreen mode

那么...就这样吗?

是的,从本质上讲,区块链并没有比这更多的东西。但为了从这里开始构建一个真正的加密货币,你可能需要

  • 用签名和时间戳的交易替换字符串数据有效负载
  • 寻找补偿矿工劳动的方法
  • 构建p2p网络层
  • 让人们相信这种加密货币具有价值
  • ETC

TL;DR 通常来说,区块链本身并不难,而其周围的事物才是难点

文章来源:https://dev.to/ninofiliu/writing-a-blockchain-in-60-readed-lines-of-typescript-1009
PREV
Node.js 中使用 Google API 的 OAuth 2.0
NEXT
用 20 行 JavaScript 编写的简单屏幕录像机