Skip to content

Commit

Permalink
go/keymanager/churp: Fix committee size verification
Browse files Browse the repository at this point in the history
  • Loading branch information
peternose committed Apr 10, 2024
1 parent 252782e commit d811103
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 19 deletions.
Empty file added .changelog/5633.trivial.md
Empty file.
10 changes: 5 additions & 5 deletions go/consensus/cometbft/apps/keymanager/churp/epoch.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ func (ext *churpExt) onEpochChange(ctx *tmapi.Context, epoch beacon.EpochTime) e

switch epoch {
case status.NextHandoff:
// The epoch for the handoff just started, meaning that registrations
// are now closed. If not enough nodes applied for the next committee,
// we need to reset applications and start collecting again.
minCommitteeSize := int(status.Threshold)*2 + 1
if len(status.Applications) >= minCommitteeSize {
// The epoch for the handoff just started, meaning that
// application submissions are now closed. If not enough
// nodes applied for the next committee, we need to reset
// applications and start collecting again.
if len(status.Applications) >= status.MinApplicants() {
continue
}
case status.NextHandoff + 1:
Expand Down
22 changes: 8 additions & 14 deletions go/consensus/cometbft/apps/keymanager/churp/txs.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,20 +454,14 @@ func tryFinalizeHandoff(status *churp.Status, epochChange bool) bool {
}
}

// Verify the size of the committee.
switch epochChange {
case true:
// At the end of the handoff epoch, a threshold number of applicants
// will suffice.
minCommitteeSize := status.Threshold + 1 + status.ExtraShares
for len(committee) < int(minCommitteeSize) {
return false
}
case false:
// During the handoff epoch, all applicants must send confirmation.
for len(committee) != len(status.Applications) {
return false
}
// Verify the committee size if the number of extra shares has changed.
if len(committee) < status.MinCommitteeSize() {
return false
}

// During the handoff epoch, all applicants must send confirmation.
if !epochChange && len(committee) != len(status.Applications) {
return false
}

// Sort the committee to ensure a deterministic order.
Expand Down
74 changes: 74 additions & 0 deletions go/keymanager/churp/status.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package churp

import (
"math"

beacon "github.com/oasisprotocol/oasis-core/go/beacon/api"
"github.com/oasisprotocol/oasis-core/go/common"
"github.com/oasisprotocol/oasis-core/go/common/crypto/hash"
Expand All @@ -18,6 +20,34 @@ const (
EccNistP384 uint8 = iota
)

// HandoffKind represents the kind of a handoff.
type HandoffKind int

const (
// HandoffKindDealingPhase represents the initial setup phase.
HandoffKindDealingPhase HandoffKind = iota
// HandoffKindCommitteeUnchanged represents a handoff where the committee
// doesn't change.
HandoffKindCommitteeUnchanged
// HandoffKindCommitteeChanged represents a handoff where the committee
// changes.
HandoffKindCommitteeChanged
)

// String returns the string representation of the HandoffKind.
func (h HandoffKind) String() string {
switch h {
case HandoffKindDealingPhase:
return "dealing phase"
case HandoffKindCommitteeUnchanged:
return "committee unchanged"
case HandoffKindCommitteeChanged:
return "committee changed"
default:
return "unknown"
}
}

// ConsensusParameters are the key manager CHURP consensus parameters.
type ConsensusParameters struct {
GasCosts transaction.Costs `json:"gas_costs,omitempty"`
Expand Down Expand Up @@ -107,6 +137,50 @@ type Status struct {
Applications map[signature.PublicKey]Application `json:"applications,omitempty"`
}

// HandoffKind returns the type of the next handoff depending on which nodes
// submitted an application to form the next committee.
func (s *Status) HandoffKind() HandoffKind {
if len(s.Committee) == 0 {
return HandoffKindDealingPhase
}

if len(s.Committee) != len(s.Applications) {
return HandoffKindCommitteeChanged
}

for _, id := range s.Committee {
if _, ok := s.Applications[id]; !ok {
return HandoffKindCommitteeChanged
}
}

return HandoffKindCommitteeUnchanged
}

// MinCommitteeSize returns the minimum number of nodes in the committee.
func (s *Status) MinCommitteeSize() int {
t := int(s.Threshold)
e := int(s.ExtraShares)
return t + e + 1
}

// MinApplicants returns the minimum number of nodes that must participate
// in a handoff.
func (s *Status) MinApplicants() int {
t := int(s.Threshold)
e := int(s.ExtraShares)

switch s.HandoffKind() {
case HandoffKindDealingPhase, HandoffKindCommitteeUnchanged:
return t + e + 1
case HandoffKindCommitteeChanged:
return max(t+e+1, 2*t+1)
default:
// Dead code.
return math.MaxInt
}
}

// HandoffsDisabled returns true if and only if handoffs are disabled.
func (s *Status) HandoffsDisabled() bool {
return s.HandoffInterval == HandoffsDisabled
Expand Down

0 comments on commit d811103

Please sign in to comment.