D~DIDI~DIDIDI!!!!

0%

区块链学习三-API

1、挖矿奖励

一、挖矿奖励

开始写代码之前,先了解一下挖矿奖励这个概念。

在前一篇文章中,我们了解到了服务器消耗了自己的计算资源,并计算出了预期结果后,会在区块的第一笔交易的位置创建一笔新的交易,这个交易没有发送人,接手人可以是任何人(通常为自己),奖励的数额由系统设定,在BTC中,最开始的奖励为50BTC,每隔四年减半一次。这笔奖励室友系统保证的,并且可以通过任何一个其他节点的验证。

二、代码重构

我们要将当前的代码改造成适合通过API对外提供的形式,进行一下的处理:

  1. 在Blockchain类中添加属性currentTransactions,由于收集最新交易,并且准备打包到下一个区块中

1

  • 把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框架,对完来提供三个接口:

  • /transactionns/new 添加新的交易,格式为JSON;

  • /mini 打包目前的交易到新的区块;

  • /chain 返回当前的区块链

新建一个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 启动程序

2

我们使用Postman来进行后面的测试,为了测试方便,将困难度调整到较低的3

访问127.0.0.1:3000/chain获取当前区块链的信息,能够看到创世块的信息

3

我们再使用post的方式向127.0.0.1/3000/transactions/new 发送一个Json交易信息,成功的添加了一个新的交易信息

4

接下来再访问127.0.0.1/mine 开始挖矿,并成功的将一笔交易打包添加到了新的区块中,而且难度也符合要求

5

这时候如果不添加交易挖矿会怎样么样呢?其实,一般在一个区块链项目的早起,交易的数量每天都很少很少,但是挖矿的工作需要一直持续下去,只不过每个区块中就只有奖励信息,这种区块一般被叫做”空块”,测试的时候,不添加交易,直接调用mine接口,返回的结果就没有交易数据:

6

我们再次访问chain接口,查看当前区块链的信息,就可以看到我们添加的区块了

7

源码地址

2019040126