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

区块类

接下来我们将创建区块类。在项目目录中,创建一个文件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协议  

该内容来自于互联网公开内容,非区块链原创内容,如若转载,请注明出处:https://htzkw.com/archives/28572

联系我们

aliyinhang@gmail.com