LOADING...
LOADING...
LOADING...
当前位置: 玩币族首页 > 新闻观点 > 如何使用node.js语言实现PBFT协议 part3

如何使用node.js语言实现PBFT协议 part3

2019-08-09 不详 来源:网络
区块类

接下来我们将创建区块类。在项目目录中,创建一个文件block.js并在其中创建一个类Block。Block将具有以下属性:

· timestamp - 区块的生成时间
· lastHash - 最后一个区块的哈希
· hash - 当前区块的哈希值
· data - 区块所持有的事务
· proposer - 区块的创建者的公钥
· signature - 区块的签名哈希
· sequenceNo - 区块的序列号

// Import SHA256 used for hashing and ChainUtil for verifying signature
const SHA256 = require("crypto-js/sha256");
const ChainUtil = require("./chain-util");

class Block {
constructor(
timestamp,
lastHash,
hash,
data,
proposer,
signature,
sequenceNo
) {
this.timestamp = timestamp;
this.lastHash = lastHash;
this.hash = hash;
this.data = data;
this.proposer = proposer;
this.signature = signature;
this.sequenceNo = sequenceNo;
}

// A function to print the block
toString() {
return `Block -
Timestamp : ${this.timestamp}
Last Hash : ${this.lastHash}
Hash : ${this.hash}
Data : ${this.data}
proposer : ${this.proposer}
Signature : ${this.signature}
Sequence No : ${this.sequenceNo}`;
}

// The first block by default will the genesis block
// this function generates the genesis block with random values
static genesis() {
return new this(
`genesis time`,
"----",
"genesis-hash",
[],
"[email protected]@53R",
"SIGN",
0
);
}

// creates a block using the passed lastblock, transactions and wallet instance
static createBlock(lastBlock, data, wallet) {
let hash;
let timestamp = Date.now();
const lastHash = lastBlock.hash;
hash = Block.hash(timestamp, lastHash, data);
let proposer = wallet.getPublicKey();
let signature = Block.signBlockHash(hash, wallet);
return new this(
timestamp,
lastHash,
hash,
data,
proposer,
signature,
1 + lastBlock.sequenceNo
);
}

// hashes the passed values
static hash(timestamp, lastHash, data) {
return SHA256(JSON.stringify(`${timestamp}${lastHash}${data}`)).toString();
}

// returns the hash of a block
static blockHash(block) {
const { timestamp, lastHash, data } = block;
return Block.hash(timestamp, lastHash, data);
}

// signs the passed block using the passed wallet instance
static signBlockHash(hash, wallet) {
return wallet.sign(hash);
}

// checks if the block is valid
static verifyBlock(block) {
return ChainUtil.verifySignature(
block.proposer,
block.signature,
Block.hash(block.timestamp, block.lastHash, block.data)
);
}

// verifies the proposer of the block with the passed public key
static verifyProposer(block, proposer) {
return block.proposer == proposer true : false;
}
}

module.exports = Block;
pbft-block.js

TransactionPool类

我们需要一个地方来存储从其他节点接收到的事务。因此,我们将创建一个TransactionPool类来存储所有事务。创建名为transaction-pool.js的文件。

// Import transaction class used for verification
const Transaction = require("./transaction");

// Transaction threshold is the limit or the holding capacity of the nodes
// Once this exceeds a new block is generated
const { TRANSACTION_THRESHOLD } = require("./config");

class TransactionPool {
constructor() {
this.transactions = [];
}

// pushes transactions in the list
// returns true if it is full
// else returns false
addTransaction(transaction) {
this.transactions.push(transaction);
if (this.transactions.length >= TRANSACTION_THRESHOLD) {
return true;
} else {
return false;
}
}

// wrapper function to verify transactions
verifyTransaction(transaction) {
return Transaction.verifyTransaction(transaction);
}

// checks if transactions exists or not
transactionExists(transaction) {
let exists = this.transactions.find(t => t.id === transaction.id);
return exists;
}

// empties the pool
clear() {
console.log("TRANSACTION POOL CLEARED");
this.transactions = [];
}
}

module.exports = TransactionPool;
pbft-txn-pool.js

BlockPool类

为了临时存储块,我们还将生成块池。创建一个block-pool.js文件,其中blockpool类保存块,直到将其添加到链中。当收到PRE-PREPARE消息时,块被添加到块池中。

const Block = require("./block");

class BlockPool {
constructor() {
this.list = [];
}

// check if the block exisits or not
exisitingBlock(block) {
let exists = this.list.find(b => b.hash === block.hash);
return exists;
}

// pushes block to the chain
addBlock(block) {
this.list.push(block);
console.log("added block to pool");
}

// returns the blcok for the given hash
getBlock(hash) {
let exists = this.list.find(b => b.hash === hash);
return exists;
}
}

module.exports = BlockPool;
pbft-block-pool.js

从节点接收的许多其他数据对象需要存储。PREPARE,COMMIT和NEW_ROUND消息。

因此,将创建另外三个池,即PreparePool,CommitPool和MessagePool。MessagePool将保存NEW_ROUND消息。

PreparePool类

const ChainUtil = require("./chain-util");

class PreparePool {
// list object is mapping that holds a list prepare messages for a hash of a block
constructor() {
this.list = {};
}

// prepare function initialize a list of prepare message for a block
// and adds the prepare message for the current node and
// returns it
prepare(block, wallet) {
let prepare = this.createPrepare(block, wallet);
this.list[block.hash] = [];
this.list[block.hash].push(prepare);
return prepare;
}

// creates a prepare message for the given block
createPrepare(block, wallet) {
let prepare = {
blockHash: block.hash,
publicKey: wallet.getPublicKey(),
signature: wallet.sign(block.hash)
};

return prepare;
}

// pushes the prepare message for a block hash into the list
addPrepare(prepare) {
this.list[prepare.blockHash].push(prepare);
}

// checks if the prepare message already exists
existingPrepare(prepare) {
let exists = this.list[prepare.blockHash].find(
p => p.publicKey === prepare.publicKey
);
return exists;
}

// checks if the prepare message is valid or not
isValidPrepare(prepare) {
return ChainUtil.verifySignature(
prepare.publicKey,
prepare.signature,
prepare.blockHash
);
}
}

module.exports = PreparePool;
pbft-prepare-pool.js

CommitPool类

在收到2f + 1准备消息后添加提交消息,因此我们使用准备消息来获取块哈希而不是传递整个区块。
const ChainUtil = require("./chain-util");

class CommitPool {
// list object is mapping that holds a list commit messages for a hash of a block
constructor() {
this.list = {};
}

// commit function initialize a list of commit message for a prepare message
// and adds the commit message for the current node and
// returns it
commit(prepare, wallet) {
let commit = this.createCommit(prepare, wallet);
this.list[prepare.blockHash] = [];
this.list[prepare.blockHash].push(commit);
return commit;
}

// creates a commit message for the given prepare message
createCommit(prepare, wallet) {
let commit = {};
commit.blockHash = prepare.blockHash;
commit.publicKey = wallet.getPublicKey();
commit.signature = wallet.sign(prepare.blockHash);
return commit;
}

// checks if the commit message already exists
existingCommit(commit) {
let exists = this.list[commit.blockHash].find(
p => p.publicKey === commit.publicKey
);
return exists;
}

// checks if the commit message is valid or not
isValidCommit(commit) {
return ChainUtil.verifySignature(
commit.publicKey,
commit.signature,
commit.blockHash
);
}

// pushes the commit message for a block hash into the list
addCommit(commit) {
this.list[commit.blockHash].push(commit);
}
}

module.exports = CommitPool;
pbft-cimmit-pool.js

MessagePool类

MessagePool将与其他两个池类似地工作。唯一的区别是它带来的额外信息。

const ChainUtil = require("./chain-util");

class MessagePool {
// list object is mapping that holds a list messages for a hash of a block
constructor() {
this.list = {};
this.message = "INITIATE NEW ROUND";
}

// creates a round change message for the given block hash
createMessage(blockHash, wallet) {
let roundChange = {
publicKey: wallet.getPublicKey(),
message: this.message,
signature: wallet.sign(ChainUtil.hash(this.message + blockHash)),
blockHash: blockHash
};

this.list[blockHash] = [roundChange];
return roundChange;
}

// checks if the message already exists
existingMessage(message) {
if (this.list[message.blockHash]) {
let exists = this.list[message.blockHash].find(
p => p.publicKey === message.publicKey
);
return exists;
} else {
return false;
}
}

// checks if the message is valid or not
isValidMessage(message) {
console.log("in valid here");
return ChainUtil.verifySignature(
message.publicKey,
message.signature,
ChainUtil.hash(message.message + message.blockHash)
);
}

// pushes the message for a block hash into the list
addMessage(message) {
this.list[message.blockHash].push(message);
}
}

module.exports = MessagePool;

区块类

我们拥有制作区块类所需的所有类。我们现在可以创建一个文件blockchain.js Blockchain类将具有以下属性:

· chain - 已确认的块列表
· validatorsList - 给定网络的验证器列表

// Import total number of nodes used to create validators list
const { NUMBER_OF_NODES } = require("./config");

// Used to verify block
const Block = require("./block");

class Blockchain {
// the constructor takes an argument validators class object
// this is used to create a list of validators
constructor(validators) {
this.validatorList = validators.generateAddresses(NUMBER_OF_NODES);
this.chain = [Block.genesis()];
}

// pushes confirmed blocks into the chain
addBlock(block) {
this.chain.push(block);
console.log("NEW BLOCK ADDED TO CHAIN");
return block;
}

// wrapper function to create blocks
createBlock(transactions, wallet) {
const block = Block.createBlock(
this.chain[this.chain.length - 1],
transactions,
wallet
);
return block;
}

// calculates the next propsers by calculating a random index of the validators list
// index is calculated using the hash of the latest block
getProposer() {
let index =
this.chain[this.chain.length - 1].hash[0].charCodeAt(0) % NUMBER_OF_NODES;
return this.validatorList[index];
}

// checks if the received block is valid
isValidBlock(block) {
const lastBlock = this.chain[this.chain.length - 1];
if (
lastBlock.sequenceNo + 1 == block.sequenceNo &&
block.lastHash === lastBlock.hash &&
block.hash === Block.blockHash(block) &&
Block.verifyBlock(block) &&
Block.verifyProposer(block, this.getProposer())
) {
console.log("BLOCK VALID");
return true;
} else {
console.log("BLOCK INVLAID");
return false;
}
}

// updates the block by appending the prepare and commit messages to the block
addUpdatedBlock(hash, blockPool, preparePool, commitPool) {
let block = blockPool.getBlock(hash);
block.prepareMessages = preparePool.list[hash];
block.commitMessages = commitPool.list[hash];
this.addBlock(block);
}
}
module.exports = Blockchain;

p2pserver类

我们如何向其他节点发送消息?我们将制作一个P2P服务器。在p2p-server.js文件中创建p2pserver类

为了创建一个P2P服务器,我们将使用sockets。为了使用sockets,我们将安装一个“ws”模块。这个模块使得使用sockets非常容易。

npm i --save ws

P2pserver类是实现一致性算法的地方。这是该项目的核心, 该类负责处理消息并广播它们。

// import the ws module
const WebSocket = require("ws");

// import the min approval constant which will be used to compare the count the messages
const { MIN_APPROVALS } = require("./config");

// decalre a p2p server port on which it would listen for messages
// we will pass the port through command line
const P2P_PORT = process.env.P2P_PORT || 5001;

// the neighbouring nodes socket addresses will be passed in command line
// this statemet splits them into an array
const peers = process.env.PEERS process.env.PEERS.split(",") : [];

// message types used to avoid typing messages
// also used in swtich statement in message handlers
const MESSAGE_TYPE = {
transaction: "TRANSACTION",
prepare: "PREPARE",
pre_prepare: "PRE-PREPARE",
commit: "COMMIT",
round_change: "ROUND_CHANGE"
};

class P2pserver {
constructor(
blockchain,
transactionPool,
wallet,
blockPool,
preparePool,
commitPool,
messagePool,
validators
) {
this.blockchain = blockchain;
this.sockets = [];
this.transactionPool = transactionPool;
this.wallet = wallet;
this.blockPool = blockPool;
this.preparePool = preparePool;
this.commitPool = commitPool;
this.messagePool = messagePool;
this.validators = validators;
}

// Creates a server on a given port
listen() {
const server = new WebSocket.Server({ port: P2P_PORT });
server.on("connection", socket => {
console.log("new connection");
this.connectSocket(socket);
});
this.connectToPeers();
console.log(`Listening for peer to peer connection on port : ${P2P_PORT}`);
}

// connects to a given socket and registers the message handler on it
connectSocket(socket) {
this.sockets.push(socket);
console.log("Socket connected");
this.messageHandler(socket);
}

// connects to the peers passed in command line
connectToPeers() {
peers.forEach(peer => {
const socket = new WebSocket(peer);
socket.on("open", () => this.connectSocket(socket));
});
}

// broadcasts transactions
broadcastTransaction(transaction) {
this.sockets.forEach(socket => {
this.sendTransaction(socket, transaction);
});
}

// sends transactions to a perticular socket
sendTransaction(socket, transaction) {
socket.send(
JSON.stringify({
type: MESSAGE_TYPE.transaction,
transaction: transaction
})
&a
  • 添加新手交流群:币种分析、每日早晚盘分析
  • 添加虎哥微信,一对一亲自指导:hugelunbi02
  • —-

    编译者/作者:不详

    玩币族申明:玩币族作为开放的资讯翻译/分享平台,所提供的所有资讯仅代表作者个人观点,与玩币族平台立场无关,且不构成任何投资理财建议。文章版权归原作者所有。

    LOADING...
    LOADING...