JS 实现区块链

JS 实现区块链

比较基础的区块链实现,并不一定要用 JS 写,TS 也可以,只是一个 prototype,很多随机、验证没有做,顺便加一些自己的学习笔记相关。

简介

顾名思义,区块链是由一个个区块(block) 串联 起来的产物,而每一个 block 至少包含以下 5 个属性:

interface Block {
    
    
  index: number; // 可选
  timestamp: number;
  transactions: Transaction[];
  nonce: number;
  hash: string;
  previousBlockHash: string;
}

其中 index 并不是必须的,这个属性只是为了方便获取最后一个 block,其他的包含时间戳、当前 block 所包含的交易(transactions)、nonce、hash 和上一个 block 的 hash,除了 nonce 之外的其他属性都比较好理解。

nonce 是一个电脑生成的随机数,它的用途是用来生成当前 block 的特定 hash 值,和证明整个 block chain 的合法性。nonce 又被称之为 proof of work(工作证明)或是 magical number(神奇数字)。

它也是保证整个 blockchain 无法被修改的原因,每一个 block 的 hash value 都会基于上一个 block 的 hash value 和 nonce,如果有人想要黑掉中间某一个 block,那么它就必须重新计算被黑掉的 block 后所有 block 的 hash value。基于加密算法的复杂性,以及现在挖一个矿都要耗费大量的时间成本和能源成本,我觉得与其说是无法被修改……不如说是基于个人能力无法被修改吧,如果是企业级以上说不定真的有可能……

而 transaction 的定义如下:

interface Transaction {
    
    
  amount: number;
  sender: string;
  recipient: string;
}

当然,这个实现中参考的是货币的定义,如果是其他应用长,transaction 也可以根据具体的业务需求进行修改。

代码实现

下面的代码也可以不用 JS 写。

构造函数及基本定义

import sha256 from 'sha256';

interface Block {
    
    
  index: number;
  timestamp: number;
  transactions: Transaction[];
  nonce: number;
  hash: string;
  previousBlockHash: string;
}

interface Transaction {
    
    
  amount: number;
  sender: string;
  recipient: string;
}

class Blockchain {
    
    
  chain: Block[];
  pendingTransactions: Transaction[];

  constructor() {
    
    
    this.chain = [];
    this.pendingTransactions = [];
  }
}

interface 之前都提到过了,这里讲一下构造函数中包含的内容,也就是 chainpendingTransactions

chain 比较好理解,就是当前真个 block chain 的信息,这里使用数组实现,当然也可以使用链表等其他数据结构。

pendingTransactions 指的是被加到当前 block 的交易,因为只有当前的 block 被挖矿(mining)挖出来之后,当前的 block 才会被加到整个区块链中,同样交易才会被推到当前的区块链中。

因此 transactions 和 block 是有依存关系的,只有当前的 block 被挖出来了,那么当前的 transactions 才算是被挖出来,也才算是真的落实了。

创建新的 block

class Blockchain {
    
    
  constructor() {
    
    
    this.chain = [];
    this.pendingTransactions = [];
    // arbitrary values
    this.createNewBlock(100, '0', '0');
  }

  createNewBlock = (
    nonce: number,
    previousBlockHash: string,
    hash: string
  ): Block => {
    
    
    const newBlock: Block = {
    
    
      index: this.chain.length + 1,
      timestamp: Date.now(),
      transactions: this.pendingTransactions,
      nonce,
      hash,
      previousBlockHash,
    };

    this.pendingTransactions = [];
    this.chain.push(newBlock);

    return newBlock;
  };
}

这个方法用来创建一个新的 block,根据之前提到的定义,只有当前的 block 被 mine(发掘/创建),当前的 transaction 才会被推入整个 blockchain。

另外这个 constructor 的值只是随便用了几个数字代替并创建当前 chain 中的第一个 block,第一个 block 又被称之为 Genesis Block。基于整个 blockchain system 是一个链式结构,并且只有一条链 [ 3 ],当前的设计用来做 demo 是够了。

获取最后一个 block

getLastBlock = (): Block => {
    
    
  return this.chain[this.chain.length - 1];
};

创建新的交易

createNewTransaction = (amount: number, sender: string, recipient: string) => {
    
    
  const newTransaction = {
    
    
    amount,
    sender,
    recipient,
  };

  this.pendingTransactions.push(newTransaction);

  return this.getLastBlock()['index'] + 1;
};

该实现也是基于之前的定义,也就是当前 block 没有被 mine 之前,所有的 transaction 都是在 pending 状态。

hash block

hashBlock = (
  prevBlockHash: string,
  currBlockData: Transaction | Transaction[],
  nonce: number
) => {
    
    
  const dataAsString: string =
    prevBlockHash + nonce.toString() + JSON.stringify(currBlockData);

  const hash: string = sha256(dataAsString);

  return hash;
};

这也是基于之前的理论:当前 block 的 hash value 是基于之前 block 的 hash value、当前 block 的数据,以及 nonce。一个合法的 hash 前四位数字必须都是 0,即 0000XXXXX

proof of work

// repeatedly hash block until it finds correct hash => '0000XXXXXXX'
// use the current block for the hash, but also the prevBlockHash
// continuously changes nonce value until it finds the correct hash
// return the nonce value that creates the correct hash
proofOfWork = (
  prevBlockHash: string,
  currBlockData: Transaction | Transaction[]
) => {
    
    
  let nonce = 0;
  let hash = this.hashBlock(prevBlockHash, currBlockData, nonce);
  while (hash.substring(0, 4) !== '0000') {
    
    
    hash = this.hashBlock(prevBlockHash, currBlockData, ++nonce);
  }

  return nonce;
};

这里是一个比较傻瓜和暴力的做法,也就是从 0 开始一个个往上加,知道满足 hash value 开头是 0000。在实际应用中,nonce 则是一个半随机的数字,而且 bicoin 的机制会设置一个数字,使得矿工必须要找到一个 nonce 小于等于机制提出的数字,这些限定都会让挖矿的难度往上翻几个等级。

reference

猜你喜欢

转载自blog.csdn.net/weixin_42938619/article/details/130476739