Documentation ¶
Overview ¶
Package consensus is a generic implementation of consensus algorithm.
Achieves consensus on the next ledger. Two things need consensus: 1. The set of transactions included in the ledger. 2. The close time for the ledger. The basic flow: 1. A call to `startRound` places the node in the `Open` phase. In this phase, the node is waiting for transactions to include in its open ledger. 2. Successive calls to `timerEntry` check if the node can close the ledger. Once the node `Close`s the open ledger, it transitions to the `Establish` phase. In this phase, the node shares/receives peer proposals on which transactions should be accepted in the closed ledger. 3. During a subsequent call to `timerEntry`, the node determines it has reached consensus with its peers on which transactions to include. It transitions to the `Accept` phase. In this phase, the node works on applying the transactions to the prior ledger to generate a new closed ledger. Once the new ledger is completed, the node shares the validated ledger with the network, does some book-keeping, then makes a call to `startRound` to start the cycle again. This class uses a generic interface to allow adapting Consensus for specific applications. The Adaptor template implements a set of helper functions that plug the consensus algorithm into a specific application. It also identifies the types that play important roles in Consensus (transactions, ledgers, ...). The code stubs below outline the interface and type requirements. The traits types must be copy constructible and assignable. @warning The generic implementation is not thread safe and the public methods are not intended to be run concurrently. When in a concurrent environment, the application is responsible for ensuring thread-safety. Simply locking whenever touching the Consensus instance is one option.
Index ¶
- Variables
- func EffCloseTime(closeTime time.Time, resolution time.Duration, priorCloseTime time.Time) time.Time
- func IndexOfFunc(l *Ledger, acquireLedger func(LedgerID) (*Ledger, error)) func(s Seq) LedgerID
- func SetUseRoundedCloseTime(y bool)
- type Adaptor
- type CloseTimes
- type Consensus
- func (c *Consensus) GotTxSet(now time.Time, ts TxSet)
- func (c *Consensus) PeerProposal(now time.Time, newPeerPos *Proposal) bool
- func (c *Consensus) PrevLedgerID() LedgerID
- func (c *Consensus) StartRound(now time.Time, prevLedgerID LedgerID, prevLedger *Ledger, ...)
- func (c *Consensus) TimerEntry(now time.Time)
- type DisputedTx
- type Ledger
- type LedgerID
- type Mode
- type NodeID
- type Peer
- func (p *Peer) AcquireLedger(ledgerID LedgerID) (*Ledger, error)
- func (p *Peer) AcquireTxSet(setID TxSetID) (TxSet, error)
- func (p *Peer) AddProposal(prop *Proposal)
- func (p *Peer) AddValidation(v *Validation) bool
- func (p *Peer) GetPrevLedger(ledgerID LedgerID, ledger *Ledger, mode Mode) LedgerID
- func (p *Peer) HasOpenTransactions() bool
- func (p *Peer) OnAccept(result *Result, prevLedger *Ledger, closeResolution time.Duration, ...)
- func (p *Peer) OnClose(prevLedger *Ledger, closeTime time.Time, mode Mode) *Result
- func (p *Peer) OnModeChange(from, to Mode)
- func (p *Peer) PrevLedgerID() LedgerID
- func (p *Peer) Propose(pos *Proposal)
- func (p *Peer) ProposersFinished(prevLedger *Ledger, prevLedgerID LedgerID) uint
- func (p *Peer) ProposersValidated(prevLedger LedgerID) uint
- func (p *Peer) SharePosition(pos *Proposal)
- func (p *Peer) ShareTx(tx TxT)
- func (p *Peer) ShareTxset(ts TxSet)
- func (p *Peer) ShouldAccept(result *Result) bool
- func (p *Peer) Start(ctx context.Context)
- func (p *Peer) UpdateOurProposal(positions map[NodeID]*Proposal, ourSet, newSet TxSet) TxSet
- type PeerInterface
- type Proposal
- type ProposalID
- type Result
- type Seq
- type SeqEnforcer
- type Span
- type State
- type Timer
- type TxID
- type TxSet
- type TxSetID
- type TxT
- type ValStatus
- type Validation
- type ValidationAdaptor
- type ValidationID
- type Validations
- func (v *Validations) Add(nodeID NodeID, val *Validation) ValStatus
- func (v *Validations) CanValidateSeq(s Seq) bool
- func (v *Validations) CurrentTrusted() []*Validation
- func (v *Validations) Expire()
- func (v *Validations) Fees(id LedgerID, baseFee uint32) []uint32
- func (v *Validations) Flush()
- func (v *Validations) GetCurrentNodeIDs() map[NodeID]struct{}
- func (v *Validations) GetNodesAfter(l *Ledger, id LedgerID) uint32
- func (v *Validations) GetPreferred(curr *Ledger) (Seq, LedgerID)
- func (v *Validations) GetPreferred2(curr *Ledger, minValidSeq Seq) LedgerID
- func (v *Validations) GetPreferredLCL(lcl *Ledger, minSeq Seq, peerCounts map[LedgerID]uint32) LedgerID
- func (v *Validations) GetTrustedForLedger(id LedgerID) []*Validation
- func (v *Validations) NumTrustedForLedger(id LedgerID) uint
- func (v *Validations) TrustChanged(added, removed map[NodeID]struct{})
Constants ¶
This section is empty.
Variables ¶
var ( //LedgerIdleInterval is the duration a ledger may remain idle before closing LedgerIdleInterval = 15 * time.Second //LedgerGranularity determines how often we check state or change positions LedgerGranularity = 1 * time.Second //LedgerPrevInterval defines interval between ledgers. LedgerPrevInterval = 10 * time.Minute )
var DoForceToClose = true
DoForceToClose is if force to close a propose when others closes them.
var Genesis = &Ledger{ Seq: 0, CloseTimeResolution: LedgerDefaultTimeResolution, CloseTimeAgree: true, }
Genesis is a genesis Ledger.
var LedgerDefaultTimeResolution = LedgerPossibleTimeResolutions[2]
LedgerDefaultTimeResolution is the initial resolution of ledger close time.
var LedgerPossibleTimeResolutions = []time.Duration{ 10 * time.Second, 20 * time.Second, 30 * time.Second, 60 * time.Second, 90 * time.Second, 120 * time.Second, }
LedgerPossibleTimeResolutions is the Possible ledger close time resolutions.
Values should not be duplicated. @see getNextLedgerTimeResolution
Functions ¶
func EffCloseTime ¶
func EffCloseTime(closeTime time.Time, resolution time.Duration, priorCloseTime time.Time) time.Time
EffCloseTime calculate the effective ledger close time. After adjusting the ledger close time based on the current resolution, also ensure it is sufficiently separated from the prior close time.
@param closeTime The raw ledger close time @param resolution The current close time resolution @param priorCloseTime The close time of the prior ledger
func IndexOfFunc ¶
IndexOfFunc returns the IndexOf func for the ledger l.
func SetUseRoundedCloseTime ¶
func SetUseRoundedCloseTime(y bool)
SetUseRoundedCloseTime is only for testing.
Types ¶
type Adaptor ¶
type Adaptor interface { //----------------------------------------------------------------------- // // Attempt to acquire a specific ledger. AcquireLedger(LedgerID) (*Ledger, error) // Acquire the transaction set associated with a proposed position. AcquireTxSet(TxSetID) (TxSet, error) // Whether any transactions are in the open ledger HasOpenTransactions() bool // Number of proposers that have validated the given ledger ProposersValidated(LedgerID) uint // Number of proposers that have validated a ledger descended from the // given ledger; if prevLedger.id() != prevLedgerID, use prevLedgerID // for the determination ProposersFinished(*Ledger, LedgerID) uint // Return the ID of the last closed (and validated) ledger that the // application thinks consensus should use as the prior ledger. GetPrevLedger(LedgerID, *Ledger, Mode) LedgerID // Called whenever consensus operating mode changes OnModeChange(Mode, Mode) // Called when ledger closes OnClose(*Ledger, time.Time, Mode) *Result // Called when ledger is accepted by consensus OnAccept(*Result, *Ledger, time.Duration, *CloseTimes, Mode) // Propose the position to peers. Propose(*Proposal) SharePosition(*Proposal) ShareTx(TxT) ShareTxset(TxSet) //ShouldAccept returns false if we should wait accept ShouldAccept(*Result) bool //UpdateOurProposal updates our proposal from otherss proposals UpdateOurProposal(props map[NodeID]*Proposal, ourSet, newSet TxSet) TxSet }
The Adaptor template implements a set of helper functions that plug the consensus algorithm into a specific application. It also identifies the types that play important roles in Consensus (transactions, ledgers, ...). Normally you should use Peer and PeerInterface instead.
type CloseTimes ¶
type CloseTimes struct { //! Close time estimates, keep ordered for predictable traverse Peers map[unixTime]int //! Our close time estimate Self time.Time }
CloseTimes Stores the set of initial close times The initial consensus proposal from each peer has that peer's view of when the ledger closed. This object stores all those close times for analysis of clock drift between peers.
type Consensus ¶
type Consensus struct {
// contains filtered or unexported fields
}
Consensus is a generic implementation of consensus algorithm.
func NewConsensus ¶
NewConsensus is the constructor.
@param clock The clock used to internally sample consensus progress @param adaptor The instance of the adaptor class @param j The journal to log debug output
func (*Consensus) GotTxSet ¶
GotTxSet processes a transaction set acquired from the network
@param now The network adjusted time @param txSet the transaction set
func (*Consensus) PeerProposal ¶
PeerProposal notifies that a peer has proposed a new position, adjust our tracking.
@param now The network adjusted time @param newProposal The new proposal from a peer @return Whether we should do delayed relay of this proposal.
func (*Consensus) PrevLedgerID ¶
PrevLedgerID gets the previous ledger ID. The previous ledger is the last ledger seen by the consensus code and should correspond to the most recent validated ledger seen by this peer.
@return ID of previous ledger
func (*Consensus) StartRound ¶
func (c *Consensus) StartRound(now time.Time, prevLedgerID LedgerID, prevLedger *Ledger, nowUntrusted map[NodeID]struct{}, isProposing bool)
StartRound Kick-off the next round of consensus. Called by the client code to start each round of consensus.
@param now The network adjusted time @param prevLedgerID the ID of the last ledger @param prevLedger The last ledger @param nowUntrusted ID of nodes that are newly untrusted this round @param proposing Whether we want to send proposals to peers this round. @note prevLedgerID is not required to the ID of prevLedger since the ID may be known locally before the contents of the ledger arrive
func (*Consensus) TimerEntry ¶
TimerEntry drives consensus forward by calling periodically. @param now The network adjusted time
type DisputedTx ¶
type DisputedTx struct { Yays int //< Number of yes votes Nays int //< Number of no votes OurVote bool //< Our vote (true is yes) Tx TxT //< Transaction under dispute Votes map[NodeID]bool //< Map from NodeID to vote }
DisputedTx is A transaction discovered to be in dispute during conensus. During consensus, a @ref DisputedTx is created when a transaction is discovered to be disputed. The object persists only as long as the dispute. Undisputed transactions have no corresponding DisputedTx object.
@tparam Tx_t The type for a transaction @tparam NodeID_t The type for a node identifier
type Ledger ¶
type Ledger struct { ParentID LedgerID Seq Seq Txs TxSet CloseTimeResolution time.Duration CloseTime time.Time ParentCloseTime time.Time CloseTimeAgree bool IndexOf func(Seq) LedgerID `msgpack:"-"` // contains filtered or unexported fields }
Ledger which is Agreed upon state that consensus transactions will modify
func (*Ledger) IsDescendantOf ¶
IsDescendantOf returns true if a is an ancestor of l.
type Mode ¶
type Mode byte
Mode of consensus
const ( //ModeProposing means we are normal participant in consensus and propose our position ModeProposing Mode = iota //ModeObserving means we are ModeObserving peer positions, but not proposing our position ModeObserving //ModeWrongLedger means we have the wrong ledger and are attempting to acquire it ModeWrongLedger //ModeSwitchedLedger means we switched ledgers since we started this consensus round but are now //running on what we believe is the correct ledger. This mode is as //if we entered the round observing, but is used to indicate we did //have the wrongLedger at some point. ModeSwitchedLedger )
type Peer ¶
type Peer struct { sync.RWMutex //! Map from Ledger::ID to vector of Positions with that ledger //! as the prior ledger PeerPositions map[LedgerID][]*Proposal // contains filtered or unexported fields }
Peer is the API interface for consensus.
func NewPeer ¶
func NewPeer(adaptor PeerInterface, i NodeID, unl []NodeID, runAsValidator bool, lastLedger *Ledger) *Peer
NewPeer returns a peer object.
func (*Peer) AcquireLedger ¶
AcquireLedger gets the ledger whose ID is ledgerID.
func (*Peer) AcquireTxSet ¶
AcquireTxSet Attempt to acquire the TxSet associated with the given ID
func (*Peer) AddValidation ¶
func (p *Peer) AddValidation(v *Validation) bool
AddValidation adds a trusted validation and return true if it is worth forwarding
func (*Peer) GetPrevLedger ¶
GetPrevLedger returns the ID of the last closed (and validated) ledger that the application thinks consensus should use as the prior ledger.
func (*Peer) HasOpenTransactions ¶
HasOpenTransactions returns true if having txs that should be approved.
func (*Peer) OnAccept ¶
func (p *Peer) OnAccept(result *Result, prevLedger *Ledger, closeResolution time.Duration, rawCloseTime *CloseTimes, mode Mode)
OnAccept is called when ledger is accepted by consensus
func (*Peer) OnModeChange ¶
OnModeChange is Called whenever consensus operating mode changes
func (*Peer) PrevLedgerID ¶
PrevLedgerID gets the previous ledger ID. The previous ledger is the last ledger seen by the consensus code and should correspond to the most recent validated ledger seen by this peer.
@return ID of previous ledger
func (*Peer) ProposersFinished ¶
ProposersFinished is the number of proposers that have validated a ledger descended from the given ledger; if prevLedger.id() != prevLedgerID, use prevLedgerID for the determination
func (*Peer) ProposersValidated ¶
ProposersValidated is the number of proposers that have validated the given ledger
func (*Peer) SharePosition ¶
SharePosition share a received peer proposal with other peer's.
func (*Peer) ShareTxset ¶
ShareTxset shares given transaction set with peers
func (*Peer) ShouldAccept ¶
ShouldAccept returns true if the result should be accepted.
type PeerInterface ¶
type PeerInterface interface { // Attempt to acquire a specific ledger. AcquireLedger(LedgerID) (*Ledger, error) // Handle a newly stale validation, this should do minimal work since // it is called by Validations while it may be iterating Validations // under lock OnStale(*Validation) // Flush the remaining validations (typically done on shutdown) Flush(remaining map[NodeID]*Validation) // Acquire the transaction set associated with a proposed position. AcquireTxSet(TxSetID) ([]TxT, error) // Whether any transactions are in the open ledger HasOpenTransactions() bool // Called whenever consensus operating mode changes OnModeChange(Mode, Mode) // Called when ledger closes OnClose(*Ledger, time.Time, Mode) TxSet // Called when ledger is accepted by consensus with fully validaded ledger // (not accepted ledger) OnAccept(newledger, fullyValidatedLedger *Ledger) // Propose the position to Peers. Propose(*Proposal) SharePosition(*Proposal) ShareTx(TxT) ShareTxset(TxSet) ShareValidaton(*Validation) ShouldAccept(*Result) bool //UpdateOurProposal updates our proposal from otherss proposals UpdateOurProposal(props map[NodeID]*Proposal, ourSet, newSet TxSet) TxSet }
PeerInterface is the funcs for intererctint Peer struct. don't call funcs in peer struct in these callbacks or will cause deadlock.
type Proposal ¶
type Proposal struct { //! Unique identifier of prior ledger this proposal is based on PreviousLedger LedgerID //! Unique identifier of the Position this proposal is taking Position TxSetID //! The ledger close time this position is taking CloseTime time.Time // !The time this position was last updated Time time.Time //! The sequence number of these positions taken by this node ProposeSeq Seq //! The identifier of the node taking this position NodeID NodeID Signature []byte }
Proposal represents a proposed position taken during a round of consensus. During consensus, peers seek agreement on a set of transactions to apply to the prior ledger to generate the next ledger. Each peer takes a position on whether to include or exclude potential transactions. The position on the set of transactions is proposed to its peers as an instance of the ConsensusProposal class. An instance of ConsensusProposal can be either our own proposal or one of our peer's. As consensus proceeds, peers may change their position on the transaction, or choose to abstain. Each successive proposal includes a strictly monotonically increasing number (or, if a peer is choosing to abstain, the special value `seqLeave`). Refer to @ref Consensus for requirements of the template arguments.
@tparam NodeID_t Type used to uniquely identify nodes/peers @tparam LedgerID_t Type used to uniquely identify ledgers @tparam Position_t Type used to represent the position taken on transactions under consideration during this round of consensus
type Result ¶
type Result struct { //! The set of transactions consensus agrees go in the ledger // You must fill it when OnClose is called. Txns TxSet //! Our proposed Position on transactions/close time // You must fill it when OnClose is called. Position *Proposal //! Transactions which are under dispute with our peers Disputes map[TxID]*DisputedTx // Measures the duration of the establish phase for this consensus round RoundTime Timer // Indicates State in which consensus ended. Once in the accept phase // will be either Yes or MovedOn State State //= ConsensusState::No; // The number of peers proposing during the round Proposers uint // contains filtered or unexported fields }
Result encapsulates the result of consensus. Stores all relevant data for the outcome of consensus on a single ledger.
@tparam Traits Traits class defining the concrete consensus types used by the application.
type SeqEnforcer ¶
type SeqEnforcer struct {
// contains filtered or unexported fields
}
SeqEnforcer enforce validation increasing sequence requirement. Helper class for enforcing that a validation must be larger than all unexpired validation sequence numbers previously issued by the validator tracked by the instance of this class.
func (*SeqEnforcer) Try ¶
func (sf *SeqEnforcer) Try(now time.Time, s Seq) bool
Try advancing the largest observed validation ledger sequence Try setting the largest validation sequence observed, but return false if it violates the invariant that a validation must be larger than all unexpired validation sequence numbers.
@param now The current time @param s The sequence number we want to validate @param p Validation parameters @return Whether the validation satisfies the invariant
type Span ¶
type Span struct {
// contains filtered or unexported fields
}
Span handles Span of a ledger.
type Validation ¶
type Validation struct { LedgerID LedgerID Seq Seq SignTime time.Time SeenTime time.Time NodeID NodeID Trusted bool //must be set by receivers, not by the sender Full bool Fee uint32 Signature []byte }
Validation is a validation info of ledger.
type ValidationAdaptor ¶
type ValidationAdaptor interface { // Attempt to acquire a specific ledger. AcquireLedger(LedgerID) (*Ledger, error) // Handle a newly stale validation, this should do minimal work since // it is called by Validations while it may be iterating Validations // under lock OnStale(*Validation) // Flush the remaining validations (typically done on shutdown) Flush(remaining map[NodeID]*Validation) // Return the current network time (used to determine staleness) Now() time.Time }
The ValidationAdaptor template implements a set of helper functions that plug the consensus algorithm into a specific application. It also identifies the types that play important roles in Consensus (transactions, ledgers, ...). Normally you should use Peer and PeerInterface instead.
type Validations ¶
type Validations struct { // Adaptor instance // Is NOT managed by the mutex_ above Adaptor ValidationAdaptor // contains filtered or unexported fields }
Validations maintains current and recent ledger Validations. Manages storage and queries related to Validations received on the network. Stores the most current validation from nodes and sets of recent Validations grouped by ledger identifier. Stored Validations are not necessarily from trusted nodes, so clients and implementations should take care to use `trusted` member functions or check the validation's trusted status. This class uses a generic interface to allow adapting Validations for specific applications. The Adaptor template implements a set of helper functions and type definitions. The code stubs below outline the interface and type requirements.
@warning The Adaptor::MutexType is used to manage concurrent access to private members of Validations but does not manage any data in the Adaptor instance itself.
func NewValidations ¶
func NewValidations(a ValidationAdaptor, c clock) *Validations
NewValidations is the constructor of Validatoins
@param p ValidationParms to control staleness/expiration of validations @param c Clock to use for expiring validations stored by ledger @param ts Parameters for constructing Adaptor instance
func (*Validations) Add ¶
func (v *Validations) Add(nodeID NodeID, val *Validation) ValStatus
Add a new validation. Attempt to Add a new validation.
@param nodeID The identity of the node issuing this validation @param val The validation to store @return The outcome
func (*Validations) CanValidateSeq ¶
func (v *Validations) CanValidateSeq(s Seq) bool
CanValidateSeq returns whether the local node can issue a validation for the given sequence number
@param s The sequence number of the ledger the node wants to validate @return Whether the validation satisfies the invariant, updating the largest sequence number seen accordingly
func (*Validations) CurrentTrusted ¶
func (v *Validations) CurrentTrusted() []*Validation
CurrentTrusted gets the currently trusted full validations
@return Vector of validations from currently trusted validators
func (*Validations) Expire ¶
func (v *Validations) Expire()
Expire old validation sets. Remove validation sets that were accessed more than validationSET_EXPIRES ago.
func (*Validations) Fees ¶
func (v *Validations) Fees(id LedgerID, baseFee uint32) []uint32
Fees returns fees reported by trusted full validators in the given ledger
@param ledgerID The identifier of ledger of interest @param baseFee The fee to report if not present in the validation @return Vector of Fees
func (*Validations) GetCurrentNodeIDs ¶
func (v *Validations) GetCurrentNodeIDs() map[NodeID]struct{}
GetCurrentNodeIDs gets the set of node ids associated with current validations
@return The set of node ids for active, listed validators
func (*Validations) GetNodesAfter ¶
func (v *Validations) GetNodesAfter(l *Ledger, id LedgerID) uint32
GetNodesAfter counts the number of current trusted validators working on a ledger after the specified one.
@param ledger The working ledger @param ledgerID The preferred ledger @return The number of current trusted validators working on a descendan of the preferred ledger @note If ledger.id() != ledgerID, only counts immediate child ledgers of ledgerID
func (*Validations) GetPreferred ¶
func (v *Validations) GetPreferred(curr *Ledger) (Seq, LedgerID)
GetPreferred returns the sequence number and ID of the preferred working ledger A ledger is preferred if it has more support amongst trusted validators and is *not* an ancestor of the current working ledger; otherwise it remains the current working ledger. (Parent of preferred stays put)
@param curr The local node's current working ledger @return The sequence and id of the preferred working ledger, or Seq{0},ID{0} if no trusted validations are available to determine the preferred ledger.
func (*Validations) GetPreferred2 ¶
func (v *Validations) GetPreferred2(curr *Ledger, minValidSeq Seq) LedgerID
GetPreferred2 Gets the ID of the preferred working ledger that exceeds a minimum valid ledger sequence number
@param curr Current working ledger @param minValidSeq Minimum allowed sequence number @return ID Of the preferred ledger, or curr if the preferred ledger is not valid
func (*Validations) GetPreferredLCL ¶
func (v *Validations) GetPreferredLCL(lcl *Ledger, minSeq Seq, peerCounts map[LedgerID]uint32) LedgerID
GetPreferredLCL Determines the preferred last closed ledger for the next consensus round. Called before starting the next round of ledger consensus to determine the preferred working ledger. Uses the dominant peerCount ledger if no trusted validations are available.
@param lcl Last closed ledger by this node @param minSeq Minimum allowed sequence number of the trusted preferred ledger @param peerCounts Map from ledger ids to count of peers with that as the last closed ledger @return The preferred last closed ledger ID @note The minSeq does not apply to the peerCounts, since this function does not know their sequence number
func (*Validations) GetTrustedForLedger ¶
func (v *Validations) GetTrustedForLedger(id LedgerID) []*Validation
GetTrustedForLedger gets trusted full validations for a specific ledger
@param ledgerID The identifier of ledger of interest @return Trusted validations associated with ledger
func (*Validations) NumTrustedForLedger ¶
func (v *Validations) NumTrustedForLedger(id LedgerID) uint
NumTrustedForLedger counts the number of trusted full validations for the given ledger
@param ledgerID The identifier of ledger of interest @return The number of trusted validations
func (*Validations) TrustChanged ¶
func (v *Validations) TrustChanged(added, removed map[NodeID]struct{})
TrustChanged updates trust status of validations Updates the trusted status of known validations to account for nodes that have been added or removed from the UNL. This also updates the trie to ensure only currently trusted nodes' validations are used.
@param added Identifiers of nodes that are now trusted @param removed Identifiers of nodes that are no longer trusted