中本聪在《Bitcoin: A Peer-to-Peer Electronic Cash System》一文中为建立起分布式时间戳服务器以及保证区块链掌握在绝大多数忠诚节点中(其控制大多数CPU算力保证不受少部分恶意攻击者危害整个区块链安全),引入了工作量证明机制。
在原文中,中本聪描述工作量证明机制应该做这样的工作:工作量证明机制包括审查哈希值,例如用SHA-256,审查哈希值开头一连串“0”bit位。直译可能会让人一头雾水,简言之,工作量证明机制应该检查区块的哈希值是否满足起始有一序列的“0”值(具体几位可以设置难度),如果不满足将会重新计算。
那么问题来了,在上篇《Node.js实现简易区块链(一):区块链的创建及区块链合法性验证》一文中,我们说过区块包含区块数据、区块前区哈希值,由它们计算的哈希值应该唯一才对。为此,中本聪将随机数(nonce)加入区块中,参与计算区块哈希值,生成满足工作量证明机制难度要求的哈希值。
思路确定了,我们用代码实现一下。在Block类中我们添加了nonce(随机数)这个成员属性,用于参与区块哈希值的计算。另外,我们添加了两个用于生成符合工作量证明机制要求的哈希值计算方法。
- class Block {
- constructor(data, previousHash, Hash) {
- this.data = data;
- this.previousHash = previousHash;
- this.hash = this.computeHash();
- // 添加成员属性:随机数,参与区块哈希值的运算
- // 初始值设置为1
- this.nonce = 1;
- }
- ···
- // 增加方法:用于确定工作量证明机制要求区块前几位哈希值为0
- getNumberOfZeros(difficulty) {
- let substr = '';
- for (let i = 0; i < difficulty; i++) {
- substr += '0';
- }
- return substr;
- }
- // 增加方法:用于生成满足工作量证明机制难度的区块哈希值
- createHashForPoW(difficulty) {
- // 如果区块哈希值前几位不满足工作量证明机制要求
- // 重新计算区块哈希值
- while (true) {
- if (this.hash.substring(0, difficulty)
- !== this.getNumberOfZeros(difficulty)) {
- this.nonce++;
- this.hash = this.computeHash();
- }
- else {
- break;
- }
- }
- }
复制代码
在Chain类中,我们增加了一个难度的成员属性。用于调整工作量证明机制的难度,其也对应要求区块哈希值起始几位需是0(创世区块可不受限)。在将区块添加到链之前,我们需要启用工作量证明机制检查区块是否满足要求:不满足需重新计算区块哈希值,满足则可将区块添加到链上。
- class Chain {
- constructor() {
- this.chain = [this.createGenesisBlock()];
- // 增加一个属性:工作量证明机制的难度
- // 设置为3:检查区块哈希值起始3位为0
- this.difficulty = 3
- }
- ···
- addBlockToChain(block) {
- block.previousHash = this.getPreviousBlock().hash;
- // 添加工作量证明机制之后,我们需要判断区块哈希是否满足要求
- // 不满足会重新进行计算
- block.createHashForPoW(this.difficulty);
- // 满足工作量证明机制要求后,添加该区块
- this.chain.push(block);
- }
复制代码
工作量机制引入基本完成。我们用测试代码进行一下测试,此时难度设置为3。可以看到区块1和区块2的哈希值起始3位均是0,与难度一致。
- // 创建两个新区块
- // 第一个区块篡改前区哈希值
- const block1 = new Block('4月1日');
- const block2 = new Block('3月结束了');
- // 创建一个新链
- const chain = new Chain();
- // 将新区块添加到链上
- chain.addBlockToChain(block1);
- chain.addBlockToChain(block2);
- // 查看链上信息
- console.log(chain);
- // 打印结果
- Chain {
- chain: [
- Block {
- data: 'genesisBlock',
- previousHash: '',
- hash: 'db0ac926ec1d28c019e17155c1bbb17620372fde962a1278b208b3a685df4a0d',
- nonce: 1
- },
- Block {
- data: '4月1日',
- previousHash: 'db0ac926ec1d28c019e17155c1bbb17620372fde962a1278b208b3a685df4a0d',
- hash: '000a6029b75fabd61d8c07f400eca0f261af8b646d02666673b709552eb28fea',
- nonce: 1198
- },
- Block {
- data: '3月结束了',
- previousHash: '000a6029b75fabd61d8c07f400eca0f261af8b646d02666673b709552eb28fea',
- hash: '0009957be8667ea51f5821d8783f103ee4827eaa35d38a6d9196c0a6c6ef0c49',
- nonce: 1050
- }
- ],
- difficulty: 3
- }
复制代码
我们再将难度设置为5测试一下。明显感觉打印过程慢了很多,同样可以看到哈希值前5位是0,与当前难度一致。
- // 打印结果
- Chain {
- chain: [
- Block {
- data: 'genesisBlock',
- previousHash: '',
- hash: 'db0ac926ec1d28c019e17155c1bbb17620372fde962a1278b208b3a685df4a0d',
- nonce: 1
- },
- Block {
- data: '4月1日',
- previousHash: 'db0ac926ec1d28c019e17155c1bbb17620372fde962a1278b208b3a685df4a0d',
- hash: '00000ed01e52c379c8b2febdcb9d0525f8a69858d09e5de328acc5f9ab76ece7',
- nonce: 406246
- },
- Block {
- data: '3月结束了',
- previousHash: '00000ed01e52c379c8b2febdcb9d0525f8a69858d09e5de328acc5f9ab76ece7',
- hash: '00000c671d937202b2c6f9ff06beb8d58bb30b2c8066be1f142fe34296bf8dd9',
- nonce: 649811
- }
- ],
- difficulty: 5
- }
复制代码
在中本聪论文中说到:要求起始0位个数的工作量是呈指数上升的。调整工作量证明机制的难度,产生一个比特币的速度变得可控。这也是比特币的独到之处。
本期的文章就分享到这里了,下期我们会引入Transaction(交易)信息,进一步完善我们的区块链代码,欢迎大家持续关注!
Ps:个人博客与公众号同步更新,点击阅读原文即可访问!
|
|