我们知道,区块包含的最重要的信息是交易数据,从发起者(sender)到接收者(receiver),总共发起的金额(amount),交易时间(timestamp)。我们可以将之前区块data进行细化,为此我们引入Transaction类。
- class Transaction {
- constructor(sender, receiver, amount) {
- this.sender = sender;
- this.receiver = receiver;
- this.amount = amount;
- this.timestamp = Date.now();
- }
- }
复制代码
在比特币构想中,比特币是通过个人挖矿的方式,系统奖励比特币的方式将区块添加到链上。因此,我们为区块添加一个时间戳(区块创建时间),哈希值应由交易数据、随机数、时间戳、前区哈希值几部分组成计算;此外,为链添加交易资源池,用于存储所有交易信息,矿工奖励金额两个属性。
我们用代码完成上述操作。
- class Block {
- constructor(transactions, previousHash, Hash) {
- this.transactions = transactions;
- this.previousHash = previousHash;
- this.hash = this.computeHash();
- // 添加成员属性:随机数,参与区块哈希值的运算
- // 初始值设置为1
- this.nonce = 1;
- // 添加属性:区块创建时间
- this.timestamp = Date.now();
- }
- // 计算当前区块的哈希值
- computeHash() {
- return sha256(
- // 将交易数据“们”字符串化
- JSON.stringify(this.transactions) +
- this.previousHash +
- this.nonce +
- this.timestamp
- ).toString();
- }
- class Chain {
- constructor() {
- this.chain = [this.createGenesisBlock()];
- // 增加一个属性:工作量证明机制的难度
- // 设置为3:检查区块哈希值起始3位为0
- this.difficulty = 3;
- // 交易数据资源池:将所有交易数据添加进池中
- this.transactionsPool = [];
- // 矿工奖励:2个币
- this.minerReward = 2;
- }
复制代码
我们想一下矿工“挖矿”操作,其实就是上篇文章《Node.js实现简易区块链(二):工作量证明机制的引入》中实现的生成满足工作量证明机制难度要求的区块哈希值,因此我们复用这块代码就OK了。
- // 增加方法:用于生成满足工作量证明机制难度的区块哈希值
- // 可视为“挖矿操作”
- createHashForPoW(difficulty) {
- // 如果区块哈希值前几位不满足工作量证明机制要求
- // 重新计算区块哈希值
- while (true) {
- if (this.hash.substring(0, difficulty)
- !== this.getNumberOfZeros(difficulty)) {
- this.nonce++;
- this.hash = this.computeHash();
- }
- else {
- break;
- }
- }
- }
复制代码
我们接下来需要实现链将交易数据添加到资源池,以及发放矿工奖励两个方法的实现。
- // 将交易数据添加至交易资源池中
- addTransactionToPool(transaction) {
- this.transactionsPool.push(transaction);
- }
- // 将交易数据“们”们添加至交易资源池中
- addTransactionsToPool(transactions) {
- for (let transaction of transactions) {
- this.addTransactionToPool(transaction);
- }
- }
- // 发放矿工奖励
- sendMinerReward(minerAddress) {
- const minerRewardTransaction = new Transaction(
- '',
- minerAddress,
- this.minerReward
- );
- this.transactionsPool.push(minerRewardTransaction);
- // 创建一个新区块
- const newBlock = new Block(
- this.transactionsPool,
- this.getPreviousBlock().hash
- );
- // 挖矿
- newBlock.createHashForPoW(this.difficulty);
- // 将区块添加到链上
- this.chain.push(newBlock);
- // 资源池清空:不需要所有区块重复记录交易数据
- this.transactionsPool = [];
- }
复制代码
到此,代码实现基本完成了,我们再写一段测试代码进行测试:我们创建两条交易数据,并将它们添加至链上,通过发放矿工奖励的方式吸引矿工们挖矿。
- // 创建一个新链
- const chain = new Chain();
- // 创建两条交易数据
- const transaction1 = new Transaction('addr1', 'addr2', 2);
- const transaction2 = new Transaction('addr2', 'addr1', 1);
- const transactions = [transaction1, transaction2];
- // 将交易数据“们”添加到链上
- chain.addTransactionsToPool(transactions);
- // 发放矿工奖励
- chain.sendMinerReward('addr3');
- // 查看链上信息
- console.log(chain);
- // 查看第二个区块的交易数据
- console.log(chain.chain[1].transactions);
复制代码
测试代码如下:
- // 打印结果
- Chain {
- chain: [
- Block {
- transactions: 'genesisBlock',
- previousHash: '',
- hash: '83b54c90a4bf904ef7b15fa78d3a08cbe3122bc590ec2a7ce9f7e0915f2a4a41',
- nonce: 1,
- timestamp: 1585799587502
- },
- Block {
- transactions: [Array],
- previousHash: '83b54c90a4bf904ef7b15fa78d3a08cbe3122bc590ec2a7ce9f7e0915f2a4a41',
- hash: '00004f9116557429477ab3d71f76b155dcc049cc4dec49f30726498a803c0178',
- nonce: 2733,
- timestamp: 1585799587502
- }
- ],
- difficulty: 3,
- transactionsPool: [],
- minerReward: 2
- }
- [
- Transaction {
- sender: 'addr1',
- receiver: 'addr2',
- amount: 2,
- timestamp: 1585799587502
- },
- Transaction {
- sender: 'addr2',
- receiver: 'addr1',
- amount: 1,
- timestamp: 1585799587502
- },
- Transaction {
- sender: '',
- receiver: 'addr3',
- amount: 2,
- timestamp: 1585799587502
- }
- ]
复制代码
可以看到区块已经包含了三条交易数据,分别是我们创建的两条交易数据以及发放的矿工奖励数据,测试成功。
但是我们知道,交易数据并非完全是合法有效的,如果挖取非真实的交易数据带来的是CPU算力的浪费,我们需要一种方式去验证其有效性。在下篇文章中,遵循中本聪的构想,我们采用数字签名的方式解决这一问题,欢迎大家持续关注!
Ps:个人博客与公众号同步更新,点击阅读原文即可访问!
|
|