PrevUpHomeNext

Consensus Type Requirements

The consensus type requirements are given below as minimal implementation stubs. Actual implementations would augment these stubs with members appropriate for managing the details of transactions and ledgers within the larger application framework.

Transaction

The transaction type Tx encapsulates a single transaction under consideration by consensus.

struct Tx
{
   using ID = ...;
   ID const & id() const;

   //... implementation specific
};
Transaction Set

The transaction set type TxSet represents a set of Txs that are collectively under consideration by consensus. A TxSet can be compared against other TxSets (typically from peers) and can be modified to add or remove transactions via the mutable subtype.

struct TxSet
{
  using Tx = Tx;
  using ID = ...;

  ID const & id() const;

  bool exists(Tx::ID const &) const;
  Tx const * find(Tx::ID const &) const ;

  // Return set of transactions that are not common with another set
  // Bool in map is true if in our set, false if in other
  std::map<Tx::ID, bool> compare(TxSet const & other) const;

  // A mutable view that allows changing transactions in the set
  struct MutableTxSet
  {
      MutableTxSet(TxSet const &);
      bool insert(Tx const &);
      bool erase(Tx::ID const &);
  };

  // Construct from a mutable view.
  TxSet(MutableTxSet const &);

  // Alternatively, if the TxSet is itself mutable
  // just alias MutableTxSet = TxSet

  //... implementation specific
};
Ledger

The Ledger type represents the state shared amongst the distributed participants. Notice that the details of how the next ledger is generated from the prior ledger and the consensus accepted transaction set is not part of the interface. Within the generic code, this type is primarily used to know that peers are working on the same tip of the ledger chain and to provide some basic timing data for consensus.

struct Ledger
{
  using ID = ...;

  using Seq = //std::uint32_t?...;

  ID const & id() const;

  // Sequence number that is 1 more than the parent ledger's seq()
  Seq seq() const;

  // Whether the ledger's close time was a non-trivial consensus result
  bool closeAgree() const;

  // The close time resolution used in determing the close time
  NetClock::duration closeTimeResolution() const;

  // The (effective) close time, based on the closeTimeResolution
  NetClock::time_point closeTime() const;

  // The parent ledger's close time
  NetClock::time_point parentCloseTime() const;

  Json::Value getJson() const;

  //... implementation specific
};
PeerProposal

The PeerProposal type represents the signed position taken by a peer during consensus. The only type requirement is owning an instance of a generic ConsensusProposal.

// Represents our proposed position or a peer's proposed position
// and is provided with the generic code
template <class NodeID_t, class LedgerID_t, class Position_t> class ConsensusProposal;

struct PeerPosition
{
  ConsensusProposal<
      NodeID_t,
      typename Ledger::ID,
      typename TxSet::ID> const &
  proposal() const;

  // ... implementation specific
};
Generic Consensus Interface

The generic Consensus relies on Adaptor template class to implement a set of helper functions that plug the consensus algorithm into a specific application. The Adaptor class also defines the types above needed by the algorithm. Below are excerpts of the generic consensus implementation and of helper types that will interact with the concrete implementing class.

// Represents a transction under dispute this round
template <class Tx_t, class NodeID_t> class DisputedTx;

// Represents how the node participates in Consensus this round
enum class ConsensusMode { proposing, observing, wrongLedger, switchedLedger};

// Measure duration of phases of consensus
class ConsensusTimer
{
public:
    std::chrono::milliseconds read() const;
    // details omitted ...
};

// Initial ledger close times, not rounded by closeTimeResolution
// Used to gauge degree of synchronization between a node and its peers
struct ConsensusCloseTimes
{
    std::map<NetClock::time_point, int> peers;
    NetClock::time_point self;
};

// Encapsulates the result of consensus.
template <class Adaptor>
struct ConsensusResult
{
    //! The set of transactions consensus agrees go in the ledger
    Adaptor::TxSet_t set;

    //! Our proposed position on transactions/close time
    ConsensusProposal<...> position;

    //! Transactions which are under dispute with our peers
    hash_map<Adaptor::Tx_t::ID, DisputedTx<...>> disputes;

    // Set of TxSet ids we have already compared/created disputes
    hash_set<typename Adaptor::TxSet_t::ID> compares;

    // Measures the duration of the establish phase for this consensus round
    ConsensusTimer roundTime;

    // Indicates state in which consensus ended.  Once in the accept phase
    // will be either Yes or MovedOn
    ConsensusState state = ConsensusState::No;
};

template <class Adaptor>
class Consensus
{
public:
    Consensus(clock_type, Adaptor &, beast::journal);

    // Kick-off the next round of consensus.
    void startRound(
        NetClock::time_point const& now,
        typename Ledger_t::ID const& prevLedgerID,
        Ledger_t const& prevLedger,
        bool proposing);

    // Call periodically to drive consensus forward.
    void timerEntry(NetClock::time_point const& now);

    // A peer has proposed a new position, adjust our tracking.  Return true if the proposal
    // was used.
    bool peerProposal(NetClock::time_point const& now, Proposal_t const& newProposal);

    // Process a transaction set acquired from the network
    void gotTxSet(NetClock::time_point const& now, TxSet_t const& txSet);

    // ... details
};
Adapting Generic Consensus

The stub below shows the set of callback/helper functions required in the implementing class.

struct Adaptor
{
    using Ledger_t = Ledger;
    using TxSet_t = TxSet;
    using PeerProposal_t = PeerProposal;
    using NodeID_t = ...; // Integer-like std::uint32_t to uniquely identify a node


    // Attempt to acquire a specific ledger from the network.
    boost::optional<Ledger> acquireLedger(Ledger::ID const & ledgerID);

    // Acquire the transaction set associated with a proposed position.
    boost::optional<TxSet> acquireTxSet(TxSet::ID const & setID);

    // Whether any transactions are in the open ledger
    bool hasOpenTransactions() const;

    // Number of proposers that have validated the given ledger
    std::size_t proposersValidated(Ledger::ID const & prevLedger) const;

    // Number of proposers that have validated a ledger descended from the
    // given ledger
    std::size_t proposersFinished(Ledger::ID const & prevLedger) const;

    // Return the ID of the last closed (and validated) ledger that the
    // application thinks consensus should use as the prior ledger.
    Ledger::ID getPrevLedger(Ledger::ID const & prevLedgerID,
                    Ledger const & prevLedger,
                    ConsensusMode mode);

    // Called when consensus operating mode changes
    void onModeChange(ConsensuMode before, ConsensusMode after);

    // Called when ledger closes.  Implementation should generate an initial Result
    // with position based on the current open ledger's transactions.
    ConsensusResult onClose(Ledger const &, Ledger const & prev, ConsensusMode mode);

    // Called when ledger is accepted by consensus
    void onAccept(ConsensusResult const & result,
      RCLCxLedger const & prevLedger,
      NetClock::duration closeResolution,
      ConsensusCloseTimes const & rawCloseTimes,
      ConsensusMode const & mode);

    // Propose the position to peers.
    void propose(ConsensusProposal<...> const & pos);

    // Share a received peer proposal with other peers.
    void share(PeerPosition_t const & pos);

    // Share a disputed transaction with peers
    void share(TxSet::Tx const & tx);

    // Share given transaction set with peers
    void share(TxSet const &s);

    //... implementation specific
};

The implementing class hides many details of the peer communication model from the generic code.


PrevUpHomeNext