您當前的位置:首頁 > 收藏

從零開始構建一個區塊鏈(三): API

作者:由 梁培利 發表于 收藏時間:2017-10-09

一、挖礦獎勵

在開始改造我們的程式碼之前,我們先來看一下什麼是挖礦獎勵。

上一篇專欄講解了挖礦的原理並且實現了POW演算法,可是伺服器為什麼願意去耗費自己的CPU資源來打包區塊呢?答案就是挖礦時有一個獎勵機制。礦工在打包一個時間段的交易後,會在區塊的第一筆交易的位置建立一筆新的交易。這筆交易沒有傳送人,接收人可以設為任何人(一般就是自己啦),獎勵的數額是多少呢?目前比特幣中是12。5個BTC。這筆獎勵交易是由系統保證的,可以透過任何一個其他節點的驗證。

這裡面幾個問題。首先是獎勵金額的問題。比特幣剛開始發行時,每個區塊的獎勵是50BTC,其後每隔四年時間減半,今年剛剛減半到12。5個了。另外一個是礦工能否建立多比獎勵交易或者加大獎勵金額?礦工當然可以這麼幹,但是這麼做以後廣播出去的區塊是無法透過其它節點驗證的,因此其他節點收到區塊後會丟棄該區塊,而該區塊最終也不會被新增到

區塊鏈

中。

二、程式碼重構

為了把我們當前的程式碼改造成適合透過API對外提供的形式,我們需要做幾個處理:

1。 在Blockchain類中新增屬性currentTransactions,由於收集最新交易,並且準備打包到

下一個

區塊中。

constructor() {

this。chain = [this。createGenesisBlock()];

this。difficulty = 3;

this。currentTransactions = [];

}

2。 把Block類中的addNewTransaction方法移到Blockchain類裡。

3。 把Block類和Blockchain類export出去,重新命名app。js為

blockchain。js

最後的blockchain。js應該為:

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 = 3;

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: ‘Leo’,

recipient: ‘Janice’,

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

}

注意上面順便修改了Blockchain裡的

方法createGenesisBloc

k的程式碼。

三、 使用Express提供API服務

為了能夠提供API服務,這裡我們採用Node。js中最流行的Express框架,對外提供三個介面:

/transactions/new 新增新的交易,格式為JSON;

/mine 打包目前的交易到新的區塊

/chain 返回當前的區塊鏈

基礎程式碼如下:

const express = require(‘express’);

const uuidv4 = require(‘uuid/v4’);

const Blockchain = require(‘。/blockchain’)。Blockchain;

const port = process。env。PORT || 3000;

const app = express();

const nodeIdentifier = uuidv4();

const testCoin = new Blockchain();

app。get(‘/mine’, (req, res) => {

res。send(“We‘ll mine a new block。”);

});

app。post(’/transactions/new‘, (req, res) => {

res。send(“We’ll add a new transaction。”)

});

app。get(‘/chain’, (req, res) => {

const response = {

chain: testCoin。chain,

length: testCoin。chain。length

}

res。send(response);

})

app。listen(port, () => {

console。log(`Server is up on port ${port}`);

});

下面我們完善路由‘/mine’以及‘/transactions/new’,並新增一些日誌的功能(非必需)。

先來看路由/transactions/new,在這個介面中,我們接受一個JSON格式的交易,內容為

{

“sender”: “my address”,

“recipient”: “someone else‘s address”,

“amount”: 5

}

然後把該交易新增到當前區塊鏈的currentTransactions中。這裡會利用到

body-parser

模組,最後的程式碼為:

const bodyParser = require(“body-parser”);

const jsonParser = bodyParser。json();

app。post(’/transactions/new‘, jsonParser, (req, res) => {

const newTransaction = req。body;

testCoin。addNewTransaction(newTransaction)

res。send(`The transaction ${JSON。stringify(newTransaction)} is successfully added to the blockchain。`);

});

接下來是路由/mine。該介面實現的功能是收集當前未被打包的交易,打包到一個新的區塊中;新增獎勵交易(這裡設定為50,接收地址為uuid);進行符合難度要求的挖礦,返回新區塊資訊。程式碼實現如下:

app。get(’/mine‘, (req, res) => {

const latestBlockIndex = testCoin。chain。length;

const newBlock = new Block(latestBlockIndex, new Date()。toString());

newBlock。transactions = testCoin。currentTransactions;

// Get a reward for mining

the new block

newBlock。transactions。unshift({

sender: ’0‘,

recipient: nodeIdentifier,

amount: 50

})

testCoin。addBlock(newBlock);

testCoin。currentTransactions = [];

res。send(`Mined new block ${JSON。stringify(newBlock, undefined, 2)}`);

});

至此程式碼基本完成,最後我們新增一個記錄日誌的中介軟體

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();

})

完整程式碼請參考Github liangpeili/testcoin

四、 測試API

使用node server。js啟動應用,我們使用Postman來對當前的API進行測試。

在啟動應用後,當前區塊鏈應該只有一個創世區塊,我們使用/chain來獲取當前區塊鏈資訊;

從零開始構建一個區塊鏈(三): API

從零開始構建一個區塊鏈(三): API

可以看到當前區塊鏈只有一個區塊。那怎麼新增新的交易呢?我們使用以下方式:

從零開始構建一個區塊鏈(三): API

從零開始構建一個區塊鏈(三): API

把交易以JSON的形式新增到請求的body中,應該會返回以下結果:

從零開始構建一個區塊鏈(三): API

從零開始構建一個區塊鏈(三): API

接下來我們可以進行挖礦了:把當前的交易打包到新的區塊,並給自己分配獎勵。這次我們使用/mine介面。

從零開始構建一個區塊鏈(三): API

從零開始構建一個區塊鏈(三): API

返回結果如下:

從零開始構建一個區塊鏈(三): API

從零開始構建一個區塊鏈(三): API

可以看到交易已經被打包到新的區塊中了。新的區塊中包含一筆獎勵交易,難度也符合要求(連續3個0)。

至此三個介面全部工作正常,我們也可以繼續新增交易、挖礦,一直進行下去。

有人會問:如果不新增交易是否可以挖礦呢?答案是Yes!一般在一個區塊鏈專案的早期,交易的數量可能一天也沒有幾筆。但是挖礦的工作是要一直進行下去的,只不過每個區塊除了獎勵交易再沒有其他了,這種區塊一般被成為“空塊”。在我們這裡也可以實現,不新增交易,直接呼叫mine介面:

從零開始構建一個區塊鏈(三): API

從零開始構建一個區塊鏈(三): API

此時再檢視區塊鏈資訊,就可以看到剛剛的兩個區塊了。

從零開始構建一個區塊鏈(三): API

從零開始構建一個區塊鏈(三): API

參考:

https://

hackernoon。com/learn-bl

ockchains-by-building-one-117428612f46

標簽: 區塊  const  chain  NEW  Transactions