sVc区块链数字货币资讯|
当前位置:首页 > 币圈学院

币圈学院

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

时间:2019-08-09 13:59:31   作者:VcKing   来源:VcKing   阅读:230   评论:0
内容摘要: 区块类接下来我们将创建区块类。在项目目录中,创建一个文件block.js并在其中创建一个类Block。Block将具有以下属性:· timestamp - 区块的生成时间· lastHash - 最后一个区块的哈希· hash - 当前区块的哈希值· data - 区块所持有的......
区块类

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

标签:block  quot  PREPARE  message  hash  

相关文章

相关评论