Skip to content

Commit

Permalink
merge quorums
Browse files Browse the repository at this point in the history
  • Loading branch information
nkryuchkov committed Oct 29, 2024
1 parent 1249010 commit c405abd
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 2 deletions.
26 changes: 24 additions & 2 deletions protocol/v2/ssv/validator/non_committee_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,12 @@ func (ncv *CommitteeObserver) ProcessMessage(msg *queue.SSVMessage) error {
return fmt.Errorf("could not get participants %w", err)
}

if len(existingQuorum) > len(quorum) {
mergedQuorum := mergeQuorums(existingQuorum, quorum)
if slices.Equal(existingQuorum, existingQuorum) {
continue
}

if err := roleStorage.SaveParticipants(msgID, slot, quorum); err != nil {
if err := roleStorage.SaveParticipants(msgID, slot, mergedQuorum); err != nil {
return fmt.Errorf("could not save participants: %w", err)
}

Expand All @@ -193,6 +194,27 @@ func (ncv *CommitteeObserver) ProcessMessage(msg *queue.SSVMessage) error {
return nil
}

func mergeQuorums(quorum1, quorum2 []spectypes.OperatorID) []spectypes.OperatorID {
seen := make(map[spectypes.OperatorID]struct{})

for _, operatorID := range quorum1 {
seen[operatorID] = struct{}{}
}

for _, operatorID := range quorum2 {
seen[operatorID] = struct{}{}
}

result := make([]spectypes.OperatorID, 0, len(seen))
for operatorID := range seen {
result = append(result, operatorID)
}

slices.Sort(result)

return result
}

func (ncv *CommitteeObserver) getBeaconRoles(msg *queue.SSVMessage, root phase0.Root) []convert.RunnerRole {
if msg.MsgID.GetRoleType() == spectypes.RoleCommittee {
attester := ncv.attesterRoots.Get(root)
Expand Down
73 changes: 73 additions & 0 deletions protocol/v2/ssv/validator/non_committee_validator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package validator

import (
"testing"

spectypes "github.com/ssvlabs/ssv-spec/types"
"github.com/stretchr/testify/require"
)

func Test_mergeQuorums(t *testing.T) {
tests := []struct {
name string
quorum1 []spectypes.OperatorID
quorum2 []spectypes.OperatorID
expected []spectypes.OperatorID
}{
{
name: "Both quorums empty",
quorum1: []spectypes.OperatorID{},
quorum2: []spectypes.OperatorID{},
expected: []spectypes.OperatorID{},
},
{
name: "First quorum empty",
quorum1: []spectypes.OperatorID{},
quorum2: []spectypes.OperatorID{1, 2, 3},
expected: []spectypes.OperatorID{1, 2, 3},
},
{
name: "Second quorum empty",
quorum1: []spectypes.OperatorID{1, 2, 3},
quorum2: []spectypes.OperatorID{},
expected: []spectypes.OperatorID{1, 2, 3},
},
{
name: "No duplicates",
quorum1: []spectypes.OperatorID{1, 3, 5},
quorum2: []spectypes.OperatorID{2, 4, 6},
expected: []spectypes.OperatorID{1, 2, 3, 4, 5, 6},
},
{
name: "With duplicates",
quorum1: []spectypes.OperatorID{1, 2, 3, 5},
quorum2: []spectypes.OperatorID{3, 4, 5, 6},
expected: []spectypes.OperatorID{1, 2, 3, 4, 5, 6},
},
{
name: "All duplicates",
quorum1: []spectypes.OperatorID{1, 2, 3},
quorum2: []spectypes.OperatorID{1, 2, 3},
expected: []spectypes.OperatorID{1, 2, 3},
},
{
name: "Unsorted input quorums",
quorum1: []spectypes.OperatorID{5, 1, 3},
quorum2: []spectypes.OperatorID{4, 2, 6},
expected: []spectypes.OperatorID{1, 2, 3, 4, 5, 6},
},
{
name: "Large quorum size",
quorum1: []spectypes.OperatorID{1, 3, 5, 7, 9, 11, 13},
quorum2: []spectypes.OperatorID{2, 4, 6, 8, 10, 12, 14},
expected: []spectypes.OperatorID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := mergeQuorums(tt.quorum1, tt.quorum2)
require.Equal(t, tt.expected, result)
})
}
}

0 comments on commit c405abd

Please sign in to comment.