区块类 接下来我们将创建区块类。在项目目录中,创建一个文件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 —- 编译者/作者:不详 玩币族申明:玩币族作为开放的资讯翻译/分享平台,所提供的所有资讯仅代表作者个人观点,与玩币族平台立场无关,且不构成任何投资理财建议。文章版权归原作者所有。 |
如何使用node.js语言实现PBFT协议 part3
2019-08-09 不详 来源:网络
LOADING...