Skip to content

Commit

Permalink
Generalize quorumState and Justificator interface
Browse files Browse the repository at this point in the history
  • Loading branch information
ranchalp committed Jan 4, 2024
1 parent 56ed7d1 commit dd73283
Showing 1 changed file with 36 additions and 21 deletions.
57 changes: 36 additions & 21 deletions f3/granite.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ type instance struct {
// Messages received earlier but not yet justified.
pending *pendingQueue
// Quality phase state (only for round 0)
quality *quorumState
quality *quorumStateImplicit
// State for each round of phases.
// State from prior rounds must be maintained to provide justification for values in subsequent rounds.
rounds map[uint32]*roundState
Expand Down Expand Up @@ -160,8 +160,8 @@ func newInstance(

type roundState struct {
converged *convergeState
prepared *quorumState
committed *quorumState
prepared *quorumStateImplicit
committed *quorumStateImplicit
}

func newRoundState(powerTable PowerTable) *roundState {
Expand Down Expand Up @@ -257,14 +257,14 @@ func (i *instance) receiveOne(msg *GMessage) {
// Receive each prefix of the proposal independently.
for j := range msg.Value.Suffix() {
prefix := msg.Value.Prefix(j + 1)
i.quality.Receive(msg.Sender, prefix)
i.quality.Receive(msg.Sender, prefix, msg.Signature)
}
case CONVERGE:
round.converged.Receive(msg.Value, msg.Ticket)
case PREPARE:
round.prepared.Receive(msg.Sender, msg.Value)
round.prepared.Receive(msg.Sender, msg.Value, msg.Signature)
case COMMIT:
round.committed.Receive(msg.Sender, msg.Value)
round.committed.Receive(msg.Sender, msg.Value, msg.Signature)
default:
i.log("unexpected message %v", msg)
}
Expand Down Expand Up @@ -346,7 +346,7 @@ func (i *instance) beginQuality() {
// Broadcast input value and wait up to Δ to receive from others.
i.phase = QUALITY
i.phaseTimeout = i.alarmAfterSynchrony(QUALITY)
i.broadcast(QUALITY, i.input, nil)
i.broadcast(QUALITY, i.input, nil, AggregatedEvidence{})
}

// Attempts to end the QUALITY phase and begin PREPARE based on current state.
Expand Down Expand Up @@ -378,7 +378,7 @@ func (i *instance) beginConverge() {
i.phase = CONVERGE
ticket := i.vrf.MakeTicket(i.beacon, i.instanceID, i.round, i.participantID)
i.phaseTimeout = i.alarmAfterSynchrony(CONVERGE)
i.broadcast(CONVERGE, i.proposal, ticket)
i.broadcast(CONVERGE, i.proposal, ticket, AggregatedEvidence{})
}

// Attempts to end the CONVERGE phase and begin PREPARE based on current state.
Expand Down Expand Up @@ -414,7 +414,7 @@ func (i *instance) beginPrepare() {
// Broadcast preparation of value and wait for everyone to respond.
i.phase = PREPARE
i.phaseTimeout = i.alarmAfterSynchrony(PREPARE)
i.broadcast(PREPARE, i.value, nil)
i.broadcast(PREPARE, i.value, nil, AggregatedEvidence{})
}

// Attempts to end the PREPARE phase and begin COMMIT based on current state.
Expand Down Expand Up @@ -443,7 +443,7 @@ func (i *instance) tryPrepare() {
func (i *instance) beginCommit() {
i.phase = COMMIT
i.phaseTimeout = i.alarmAfterSynchrony(PREPARE)
i.broadcast(COMMIT, i.value, nil)
i.broadcast(COMMIT, i.value, nil, AggregatedEvidence{})
}

func (i *instance) tryCommit(round uint32) {
Expand Down Expand Up @@ -588,12 +588,19 @@ func (v *pendingQueue) getRound(round uint32) map[string][]*GMessage {
return rv
}

///// Incremental quorum-calculation helper /////
// TODO Add comments
type Justificator interface {
Receive(sender ActorID, value ECChain, sig []byte)

isJustified(msg *GMessage, justification ECChain) bool
Justify(msg *GMessage) bool
}

// /// Incremental quorum-calculation helper /////
// Accumulates values from a collection of senders and incrementally calculates
// which values have reached a strong quorum of support.
// Supports receiving multiple values from each sender, and hence multiple strong quorum values.
type quorumState struct {
type quorumStateImplicit struct {
// CID of each chain received, by sender. Allows detecting and ignoring duplicates.
received map[ActorID]senderSent
// The power supporting each chain so far.
Expand All @@ -618,8 +625,8 @@ type chainPower struct {
}

// Creates a new, empty quorum state.
func newQuorumState(powerTable PowerTable) *quorumState {
return &quorumState{
func newQuorumState(powerTable PowerTable) *quorumStateImplicit {
return &quorumStateImplicit{
received: map[ActorID]senderSent{},
chainPower: map[TipSetID]chainPower{},
sendersTotalPower: 0,
Expand All @@ -628,7 +635,7 @@ func newQuorumState(powerTable PowerTable) *quorumState {
}

// Receives a new chain from a sender.
func (q *quorumState) Receive(sender ActorID, value ECChain) {
func (q *quorumStateImplicit) Receive(sender ActorID, value ECChain, _ []byte) {
head := value.HeadCIDOrZero()
fromSender, ok := q.received[sender]
if ok {
Expand Down Expand Up @@ -663,14 +670,14 @@ func (q *quorumState) Receive(sender ActorID, value ECChain) {
}

// Checks whether a value has been received before.
func (q *quorumState) HasReceived(value ECChain) bool {
func (q *quorumStateImplicit) HasReceived(value ECChain) bool {
_, ok := q.chainPower[value.HeadCIDOrZero()]
return ok
}

// Lists all values that have been received from any sender.
// The order of returned values is not defined.
func (q *quorumState) ListAllValues() []ECChain {
func (q *quorumStateImplicit) ListAllValues() []ECChain {
var chains []ECChain
for _, cp := range q.chainPower {
chains = append(chains, cp.chain)
Expand All @@ -679,24 +686,24 @@ func (q *quorumState) ListAllValues() []ECChain {
}

// Checks whether at most one distinct value has been received.
func (q *quorumState) HasAgreement() bool {
func (q *quorumStateImplicit) HasAgreement() bool {
return len(q.chainPower) <= 1
}

// Checks whether at least one message has been received from a strong quorum of senders.
func (q *quorumState) ReceivedFromQuorum() bool {
func (q *quorumStateImplicit) ReceivedFromQuorum() bool {
return q.sendersTotalPower > q.powerTable.Total*2/3
}

// Checks whether a chain (head) has reached quorum.
func (q *quorumState) HasQuorumAgreement(cid TipSetID) bool {
func (q *quorumStateImplicit) HasQuorumAgreement(cid TipSetID) bool {
cp, ok := q.chainPower[cid]
return ok && cp.hasQuorum
}

// Returns a list of the chains which have reached an agreeing quorum.
// The order of returned values is not defined.
func (q *quorumState) ListQuorumAgreedValues() []ECChain {
func (q *quorumStateImplicit) ListQuorumAgreedValues() []ECChain {
var withQuorum []ECChain
for cid, cp := range q.chainPower {
if cp.hasQuorum {
Expand All @@ -707,6 +714,14 @@ func (q *quorumState) ListQuorumAgreedValues() []ECChain {
return withQuorum
}

func (q *quorumStateImplicit) isJustified(msg *GMessage, value ECChain) bool {
return q.HasQuorumAgreement(value.HeadCIDOrZero())
}

func (q *quorumStateImplicit) Justify(msg *GMessage) bool {
return true // Implicit justification
}

//// CONVERGE phase helper /////

type convergeState struct {
Expand Down

0 comments on commit dd73283

Please sign in to comment.