1、挖矿奖励
一、挖矿奖励
开始写代码之前,先了解一下挖矿奖励这个概念。
在前一篇文章中,我们了解到了服务器消耗了自己的计算资源,并计算出了预期结果后,会在区块的第一笔交易的位置创建一笔新的交易,这个交易没有发送人,接手人可以是任何人(通常为自己),奖励的数额由系统设定,在BTC中,最开始的奖励为50BTC,每隔四年减半一次。这笔奖励室友系统保证的,并且可以通过任何一个其他节点的验证。
二、代码重构
我们要将当前的代码改造成适合通过API对外提供的形式,进行一下的处理:
- 在Blockchain类中添加属性currentTransactions,由于收集最新交易,并且准备打包到下一个区块中
把Block类中的addNewTransaction方法一道Blockchain类里面
把Block类和Blockchain类export出去
并修改Blockchain类中的createGenesisBlock代码
最后修改完的代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| const SHA256 = require('crypto-js/sha256'); class Block { constructor(index, timestamp) { this.index = index; this.timestamp = timestamp; this.transactions = []; this.previousHash = ''; this.hash = this.calculateHash(); this.nonce = 0; }
calculateHash() { return SHA256(this.index + this.previousHash + this.timestamp + JSON.stringify(this.transactions) + this.nonce).toString(); }
mineBlock(difficulty) { console.log(`Mining block ${this.index}`); while (this.hash.substring(0, difficulty) !== Array(difficulty + 1).join("0")) { this.nonce++; this.hash = this.calculateHash(); } console.log("BLOCK MINED: " + this.hash); }
getTransactions() { return this.transactions; } }
class Blockchain { constructor() { this.chain = [this.createGenesisBlock()]; this.difficulty = 4; this.currentTransactions = []; }
addNewTransaction(sender, recipient, amount) { this.currentTransactions.push({ sender, recipient, amount }) }
createGenesisBlock() { const genesisBlock = new Block(0, "01/10/2017"); genesisBlock.previousHash = '0'; genesisBlock.transactions.push({ sender: 'xianyu', recipient: 'five', amount: 520 }) return genesisBlock; }
getLatestBlock() { return this.chain[this.chain.length - 1]; }
addBlock(newBlock) { newBlock.previousHash = this.getLatestBlock().hash; newBlock.mineBlock(this.difficulty); this.chain.push(newBlock); }
isChainValid() { for (let i = 1; i < this.chain.length; i++){ const currentBlock = this.chain[i]; const previousBlock = this.chain[i - 1];
if(currentBlock.hash !== currentBlock.calculateHash()){ return false; }
if(currentBlock.previousHash !== previousBlock.hash){ return false; } } return true; } }
module.exports = { Block, Blockchain }
|
三、使用Express提供API服务
我们使用Node.js中的Express框架,对完来提供三个接口:
新建一个server.js,基础代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const express = require('express'); const uuidv4 = require('uuis/v4'); const Blockchain = require('./BlockChain3').Blockchain; const port = process.env.port || 3000; cosnt app = express(); const nodeIdentifier = uuidv4(); const xianyuCoin = new Blockchain(); app.get('/mine', (req,res) =>{ res.send("我们打包了一个新的区块。"); });
app.post('/transaction/new', (req,res) => { const response = { chain: xianyuCoin.chain, length: xianyuCoin.chain.length } res.send(response); }) app.listen(port, () => { console.log('服务已运行在 ${port} 端口上。'); });
|
继续完善路由 ‘/mine’ 和’/transactions/new’,有需要也可以添加一些日志功能
先看路由 /transaction/new ,在这个接口中,我们接受一个JSON格式的交易,内容为
1 2 3 4 5
| { "sender": "my address", "recipient": "someone else's address", "amount": 5 }
|
然后把这个交易添加到当前区块链的currentTransactions中。这里会用到body-parser模块,组后代码为:
1 2 3 4 5 6 7
| const bodyParser = require("body-parser"); const jsonParser = bodyParser.json(); app.post('/transactions/new', jsonParser, (req, res) => { const newTransaction = req.body; xianyuCoin.addNewTransaction(newTransaction) res.send(`交易: ${JSON.stringify(newTransaction)} 已成功添加到区块链中!`); });
|
接下来是路由/mine。该接口实现的功能是收集当前未被打包的交易,打包到一个新的区块中;添加奖励交易(这里设置为100,接收地址为uuid);进行符合难度要求的挖矿,返回新区块信息。代码实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| app.get('/mine', (req, res) => { const latestBlockIndex = xianyuCoin.chain.length; const newBlock = new Block(latestBlockIndex, new Date().toString()); newBlock.transactions = xianyuCoin.currentTransactions; newBlock.transactions.unshift({ sender: '0', recipient: nodeIdentifier, amount: 50 }) xianyuCoin.addBlock(newBlock); xianyuCoin.currentTransactions = []; res.send(`Mined new block ${JSON.stringify(newBlock, undefined, 2)}`); });
|
在添加一个日志记录中间件(卑微的报错了:Reference Error: fs is not defined
只能把日志记录代码注释掉,暂时搁置了~~~)
1 2 3 4 5 6 7 8 9
| app.use((req, res, next) => { var now = new Date().toString(); var log = `${now}: ${req.method} ${req.url}`; console.log(log); fs.appendFile('server.log', log + '\n', (err) => { if(err) console.log(err); }); next; })
|
四、测试API
Node server.js
启动程序
我们使用Postman来进行后面的测试,为了测试方便,将困难度调整到较低的3
访问127.0.0.1:3000/chain
获取当前区块链的信息,能够看到创世块的信息
我们再使用post的方式向127.0.0.1/3000/transactions/new
发送一个Json交易信息,成功的添加了一个新的交易信息
接下来再访问127.0.0.1/mine
开始挖矿,并成功的将一笔交易打包添加到了新的区块中,而且难度也符合要求
这时候如果不添加交易挖矿会怎样么样呢?其实,一般在一个区块链项目的早起,交易的数量每天都很少很少,但是挖矿的工作需要一直持续下去,只不过每个区块中就只有奖励信息,这种区块一般被叫做”空块”,测试的时候,不添加交易,直接调用mine接口,返回的结果就没有交易数据:
我们再次访问chain接口,查看当前区块链的信息,就可以看到我们添加的区块了
源码地址