diff --git a/.github/workflows/genesis-spec-alignment.yml b/.github/workflows/genesis-spec-alignment.yml new file mode 100644 index 0000000000..1ba39bf011 --- /dev/null +++ b/.github/workflows/genesis-spec-alignment.yml @@ -0,0 +1,34 @@ +name: Genesis Spec Alignment + +on: + push: + branches: + - "**" + +jobs: + align: + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: "1.20.x" + + - name: Tidy up dependencies + run: go mod tidy + + - name: Install Differ + run: cd ./scripts/differ && go install . + + - name: Run Differ + run: cd ./scripts/spec-alignment && ./genesis_differ.sh + + - name: Upload genesis_output.diff + if: failure() + uses: actions/upload-artifact@v3 + with: + name: genesis_output.diff + path: ./scripts/spec-alignment/genesis_output.diff diff --git a/message/validation/fork.go b/message/validation/fork.go index 42636dd750..263bfb1335 100644 --- a/message/validation/fork.go +++ b/message/validation/fork.go @@ -24,8 +24,10 @@ func (f *ForkingMessageValidation) Validate(ctx context.Context, p peer.ID, pmsg } func (f *ForkingMessageValidation) ValidatorForTopic(topic string) func(ctx context.Context, p peer.ID, pmsg *pubsub.Message) pubsub.ValidationResult { - if f.NetworkConfig.PastAlanFork() { - return f.Alan.ValidatorForTopic(topic) + return func(ctx context.Context, p peer.ID, pmsg *pubsub.Message) pubsub.ValidationResult { + if f.NetworkConfig.PastAlanFork() { + return f.Alan.ValidatorForTopic(topic)(ctx, p, pmsg) + } + return f.Genesis.ValidatorForTopic(topic)(ctx, p, pmsg) } - return f.Genesis.ValidatorForTopic(topic) } diff --git a/network/p2p/p2p_setup.go b/network/p2p/p2p_setup.go index b104999628..586865a183 100644 --- a/network/p2p/p2p_setup.go +++ b/network/p2p/p2p_setup.go @@ -199,8 +199,10 @@ func (n *p2pNetwork) setupPeerServices(logger *zap.Logger) error { } filters := func() []connections.HandshakeFilter { + newDomain := n.cfg.Network.DomainType() + newDomainString := "0x" + hex.EncodeToString(newDomain[:]) return []connections.HandshakeFilter{ - connections.NetworkIDFilter(domain), + connections.NetworkIDFilter(newDomainString), } } diff --git a/network/p2p/p2p_validation_test.go b/network/p2p/p2p_validation_test.go index c427043eb7..e7d26478c3 100644 --- a/network/p2p/p2p_validation_test.go +++ b/network/p2p/p2p_validation_test.go @@ -24,6 +24,7 @@ import ( spectypes "github.com/ssvlabs/ssv-spec/types" spectestingutils "github.com/ssvlabs/ssv-spec/types/testingutils" "github.com/stretchr/testify/require" + "golang.org/x/exp/slices" "github.com/ssvlabs/ssv/message/validation" beaconprotocol "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" @@ -144,7 +145,7 @@ func TestP2pNetwork_MessageValidation(t *testing.T) { require.NoError(t, vNet.Close()) }() - time.Sleep(5 * time.Second) + time.Sleep(1 * time.Second) // Prepare a pool of broadcasters. mu := sync.Mutex{} @@ -153,7 +154,7 @@ func TestP2pNetwork_MessageValidation(t *testing.T) { broadcasters := pool.New().WithErrors().WithContext(ctx) broadcaster := func(node *VirtualNode, roles ...spectypes.RunnerRole) { broadcasters.Go(func(ctx context.Context) error { - for i := 0; i < 50; i++ { + for i := 0; i < 12; i++ { role := roles[i%len(roles)] mu.Lock() @@ -176,10 +177,16 @@ func TestP2pNetwork_MessageValidation(t *testing.T) { // - node 1 broadcasts ignored messages. // - node 2 broadcasts rejected messages. // - node 3 broadcasts all messages (equal distribution). - broadcaster(vNet.Nodes[0], acceptedRole) - broadcaster(vNet.Nodes[1], ignoredRole) - broadcaster(vNet.Nodes[2], rejectedRole) - broadcaster(vNet.Nodes[3], acceptedRole, ignoredRole, rejectedRole) + messageTypesByNodeIndex := map[int][]spectypes.RunnerRole{ + 0: {acceptedRole}, + 1: {ignoredRole}, + 2: {rejectedRole}, + 3: {acceptedRole, ignoredRole, rejectedRole}, + } + + for i := 0; i < nodeCount; i++ { + broadcaster(vNet.Nodes[i], messageTypesByNodeIndex[i]...) + } // Wait for the broadcasters to finish. err := broadcasters.Wait() @@ -187,9 +194,15 @@ func TestP2pNetwork_MessageValidation(t *testing.T) { time.Sleep(1 * time.Second) // Assert that the messages were distributed as expected. - deadline := time.Now().Add(7 * time.Second) + time.Sleep(7 * time.Second) + interval := 100 * time.Millisecond for i := 0; i < nodeCount; i++ { + // Messages from nodes broadcasting rejected role become rejected once score threshold is reached + if slices.Contains(messageTypesByNodeIndex[i], rejectedRole) { + continue + } + // better lock inside loop than wait interval locked mtx.Lock() var errors []error @@ -203,12 +216,7 @@ func TestP2pNetwork_MessageValidation(t *testing.T) { errors = append(errors, fmt.Errorf("node %d rejected %d messages (expected %d)", i, messageValidators[i].TotalRejected, roleBroadcasts[rejectedRole])) } mtx.Unlock() - if len(errors) == 0 { - break - } - if time.Now().After(deadline) { - require.Empty(t, errors) - } + require.Empty(t, errors) time.Sleep(interval) } diff --git a/network/p2p/test_utils.go b/network/p2p/test_utils.go index 8746aed4ba..07bf4f024f 100644 --- a/network/p2p/test_utils.go +++ b/network/p2p/test_utils.go @@ -155,14 +155,21 @@ func (ln *LocalNet) NewTestP2pNetwork(ctx context.Context, nodeIndex int, keys t for _, share := range options.Shares { for _, sm := range share.Committee { - _, err := nodeStorage.SaveOperatorData(nil, ®istrystorage.OperatorData{ - ID: sm.Signer, - PublicKey: operatorPubkey, - OwnerAddress: common.BytesToAddress([]byte("testOwnerAddress")), - }) + _, ok, err := nodeStorage.GetOperatorData(nil, sm.Signer) if err != nil { return nil, err } + + if !ok { + _, err := nodeStorage.SaveOperatorData(nil, ®istrystorage.OperatorData{ + ID: sm.Signer, + PublicKey: operatorPubkey, + OwnerAddress: common.BytesToAddress([]byte("testOwnerAddress")), + }) + if err != nil { + return nil, err + } + } } } diff --git a/operator/validator/controller.go b/operator/validator/controller.go index d229d61f2d..99a9de0b23 100644 --- a/operator/validator/controller.go +++ b/operator/validator/controller.go @@ -1488,10 +1488,9 @@ func SetupGenesisRunners(ctx context.Context, logger *zap.Logger, options valida leader := genesisspecqbft.RoundRobinProposer(state, round) return leader }, - Storage: options.GenesisOptions.Storage.Get(role), - Network: options.GenesisOptions.Network, - Timer: genesisroundtimer.New(ctx, options.NetworkConfig.Beacon, role, nil), - SignatureVerification: true, + Storage: options.GenesisOptions.Storage.Get(role), + Network: options.GenesisOptions.Network, + Timer: genesisroundtimer.New(ctx, options.NetworkConfig.Beacon, role, nil), } config.ValueCheckF = valueCheckF identifier := genesisspectypes.NewMsgID(genesisssvtypes.GetDefaultDomain(), options.SSVShare.Share.ValidatorPubKey[:], role) @@ -1528,8 +1527,7 @@ func SetupGenesisRunners(ctx context.Context, logger *zap.Logger, options valida qbftCtrl := buildController(genesisspectypes.BNRoleSyncCommitteeContribution, syncCommitteeContributionValueCheckF) runners[role] = genesisrunner.NewSyncCommitteeAggregatorRunner(genesisDomainType, genesisBeaconNetwork, share, qbftCtrl, options.GenesisBeacon, options.GenesisOptions.Network, options.GenesisOptions.Signer, syncCommitteeContributionValueCheckF, 0) case genesisspectypes.BNRoleValidatorRegistration: - qbftCtrl := buildController(genesisspectypes.BNRoleValidatorRegistration, nil) - runners[role] = genesisrunner.NewValidatorRegistrationRunner(genesisDomainType, genesisBeaconNetwork, share, qbftCtrl, options.GenesisBeacon, options.GenesisOptions.Network, options.GenesisOptions.Signer) + runners[role] = genesisrunner.NewValidatorRegistrationRunner(genesisDomainType, genesisBeaconNetwork, share, options.GenesisBeacon, options.GenesisOptions.Network, options.GenesisOptions.Signer) case genesisspectypes.BNRoleVoluntaryExit: runners[role] = genesisrunner.NewVoluntaryExitRunner(genesisDomainType, genesisBeaconNetwork, share, options.GenesisBeacon, options.GenesisOptions.Network, options.GenesisOptions.Signer) } diff --git a/protocol/genesis/qbft/config.go b/protocol/genesis/qbft/config.go index 1357a22fcf..942c5853ad 100644 --- a/protocol/genesis/qbft/config.go +++ b/protocol/genesis/qbft/config.go @@ -27,20 +27,17 @@ type IConfig interface { GetStorage() qbftstorage.QBFTStore // GetTimer returns round timer GetTimer() roundtimer.Timer - // VerifySignatures returns if signature is checked - VerifySignatures() bool } type Config struct { - Signer genesisspectypes.SSVSigner - SigningPK []byte - Domain genesisspectypes.DomainType - ValueCheckF genesisspecqbft.ProposedValueCheckF - ProposerF genesisspecqbft.ProposerF - Storage qbftstorage.QBFTStore - Network genesisspecqbft.Network - Timer roundtimer.Timer - SignatureVerification bool + Signer genesisspectypes.SSVSigner + SigningPK []byte + Domain genesisspectypes.DomainType + ValueCheckF genesisspecqbft.ProposedValueCheckF + ProposerF genesisspecqbft.ProposerF + Storage qbftstorage.QBFTStore + Network genesisspecqbft.Network + Timer roundtimer.Timer } // GetSigner returns a Signer instance @@ -82,7 +79,3 @@ func (c *Config) GetStorage() qbftstorage.QBFTStore { func (c *Config) GetTimer() roundtimer.Timer { return c.Timer } - -func (c *Config) VerifySignatures() bool { - return c.SignatureVerification -} diff --git a/protocol/genesis/qbft/instance/commit.go b/protocol/genesis/qbft/instance/commit.go index 23e435934e..5ca983bc72 100644 --- a/protocol/genesis/qbft/instance/commit.go +++ b/protocol/genesis/qbft/instance/commit.go @@ -141,10 +141,8 @@ func BaseCommitValidation( return errors.Wrap(err, "signed commit invalid") } - if config.VerifySignatures() { - if err := types.VerifyByOperators(signedCommit.Signature, signedCommit, config.GetSignatureDomainType(), genesisspectypes.QBFTSignatureType, operators); err != nil { - return errors.Wrap(err, "msg signature invalid") - } + if err := types.VerifyByOperators(signedCommit.Signature, signedCommit, config.GetSignatureDomainType(), genesisspectypes.QBFTSignatureType, operators); err != nil { + return errors.Wrap(err, "msg signature invalid") } return nil diff --git a/protocol/genesis/qbft/instance/prepare.go b/protocol/genesis/qbft/instance/prepare.go index 0b8616d363..f8464b1fe0 100644 --- a/protocol/genesis/qbft/instance/prepare.go +++ b/protocol/genesis/qbft/instance/prepare.go @@ -157,10 +157,8 @@ func validSignedPrepareForHeightRoundAndRoot( return errors.New("msg allows 1 signer") } - if config.VerifySignatures() { - if err := types.VerifyByOperators(signedPrepare.Signature, signedPrepare, config.GetSignatureDomainType(), genesisspectypes.QBFTSignatureType, operators); err != nil { - return errors.Wrap(err, "msg signature invalid") - } + if err := types.VerifyByOperators(signedPrepare.Signature, signedPrepare, config.GetSignatureDomainType(), genesisspectypes.QBFTSignatureType, operators); err != nil { + return errors.Wrap(err, "msg signature invalid") } return nil diff --git a/protocol/genesis/qbft/instance/proposal.go b/protocol/genesis/qbft/instance/proposal.go index 5879ffbc3e..b3f0929141 100644 --- a/protocol/genesis/qbft/instance/proposal.go +++ b/protocol/genesis/qbft/instance/proposal.go @@ -78,10 +78,8 @@ func isValidProposal( if len(signedProposal.GetSigners()) != 1 { return errors.New("msg allows 1 signer") } - if config.VerifySignatures() { - if err := genesisssvtypes.VerifyByOperators(signedProposal.Signature, signedProposal, config.GetSignatureDomainType(), genesisspectypes.QBFTSignatureType, operators); err != nil { - return errors.Wrap(err, "msg signature invalid") - } + if err := genesisssvtypes.VerifyByOperators(signedProposal.Signature, signedProposal, config.GetSignatureDomainType(), genesisspectypes.QBFTSignatureType, operators); err != nil { + return errors.Wrap(err, "msg signature invalid") } if !signedProposal.MatchedSigners([]genesisspectypes.OperatorID{proposer(state, config, signedProposal.Message.Round)}) { return errors.New("proposal leader invalid") diff --git a/protocol/genesis/qbft/instance/round_change.go b/protocol/genesis/qbft/instance/round_change.go index ba47b88ce5..38d70a8888 100644 --- a/protocol/genesis/qbft/instance/round_change.go +++ b/protocol/genesis/qbft/instance/round_change.go @@ -260,10 +260,8 @@ func validRoundChangeForData( return errors.New("msg allows 1 signer") } - if config.VerifySignatures() { - if err := types.VerifyByOperators(signedMsg.Signature, signedMsg, config.GetSignatureDomainType(), genesisspectypes.QBFTSignatureType, state.Share.Committee); err != nil { - return errors.Wrap(err, "msg signature invalid") - } + if err := types.VerifyByOperators(signedMsg.Signature, signedMsg, config.GetSignatureDomainType(), genesisspectypes.QBFTSignatureType, state.Share.Committee); err != nil { + return errors.Wrap(err, "msg signature invalid") } if err := signedMsg.Message.Validate(); err != nil { diff --git a/protocol/genesis/qbft/testing/utils.go b/protocol/genesis/qbft/testing/utils.go index 74aeb5947b..995f099039 100644 --- a/protocol/genesis/qbft/testing/utils.go +++ b/protocol/genesis/qbft/testing/utils.go @@ -34,10 +34,9 @@ var TestingConfig = func(logger *zap.Logger, keySet *testingutils.TestKeySet, ro ProposerF: func(state *genesisspecqbft.State, round genesisspecqbft.Round) genesisspectypes.OperatorID { return 1 }, - Storage: TestingStores(logger).Get(role), - Network: testingutils.NewTestingNetwork(), - Timer: roundtimer.NewTestingTimer(), - SignatureVerification: true, + Storage: TestingStores(logger).Get(role), + Network: testingutils.NewTestingNetwork(), + Timer: roundtimer.NewTestingTimer(), } } diff --git a/protocol/genesis/ssv/runner/validator_registration.go b/protocol/genesis/ssv/runner/validator_registration.go index 253545346a..aa6614c249 100644 --- a/protocol/genesis/ssv/runner/validator_registration.go +++ b/protocol/genesis/ssv/runner/validator_registration.go @@ -4,6 +4,7 @@ import ( "crypto/sha256" "encoding/hex" "encoding/json" + spectypes "github.com/ssvlabs/ssv-spec/types" v1 "github.com/attestantio/go-eth2-client/api/v1" @@ -16,7 +17,6 @@ import ( "go.uber.org/zap" "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/controller" "github.com/ssvlabs/ssv/protocol/genesis/ssv/runner/metrics" ) @@ -35,7 +35,6 @@ func NewValidatorRegistrationRunner( domainType spectypes.DomainType, beaconNetwork genesisspectypes.BeaconNetwork, share *genesisspectypes.Share, - qbftController *controller.Controller, beacon genesisspecssv.BeaconNode, network genesisspecssv.Network, signer genesisspectypes.KeyManager, @@ -46,7 +45,6 @@ func NewValidatorRegistrationRunner( DomainType: domainType, BeaconNetwork: beaconNetwork, Share: share, - QBFTController: qbftController, }, beacon: beacon, diff --git a/protocol/genesis/ssv/testing/runner.go b/protocol/genesis/ssv/testing/runner.go index aa8c4f8a03..a5b6653b6d 100644 --- a/protocol/genesis/ssv/testing/runner.go +++ b/protocol/genesis/ssv/testing/runner.go @@ -150,7 +150,6 @@ var baseRunner = func(logger *zap.Logger, role genesisspectypes.BeaconRole, valC networkconfig.TestNetwork.GenesisDomainType, genesisspectypes.BeaconTestNetwork, share, - contr, spectestingutils.NewTestingBeaconNode(), net, km, diff --git a/protocol/v2/qbft/instance/prepare.go b/protocol/v2/qbft/instance/prepare.go index 246d1cd6ba..7c8e9ffe0d 100644 --- a/protocol/v2/qbft/instance/prepare.go +++ b/protocol/v2/qbft/instance/prepare.go @@ -26,6 +26,12 @@ func (i *Instance) uponPrepare(logger *zap.Logger, msg *specqbft.ProcessingMessa return nil // uponPrepare was already called } + proposedRoot := i.State.ProposalAcceptedForCurrentRound.QBFTMessage.Root + logger.Debug("📬 got prepare message", + fields.Round(i.State.Round), + zap.Any("prepare-signers", i.State.ProposalAcceptedForCurrentRound.SignedMessage.OperatorIDs), + fields.Root(proposedRoot)) + if hasQuorumBefore { return nil // already moved to commit stage } @@ -34,13 +40,6 @@ func (i *Instance) uponPrepare(logger *zap.Logger, msg *specqbft.ProcessingMessa return nil // no quorum yet } - proposedRoot := i.State.ProposalAcceptedForCurrentRound.QBFTMessage.Root - - logger.Debug("📬 got prepare message", - fields.Round(i.State.Round), - zap.Any("prepare-signers", i.State.ProposalAcceptedForCurrentRound.SignedMessage.OperatorIDs), - fields.Root(proposedRoot)) - i.State.LastPreparedValue = i.State.ProposalAcceptedForCurrentRound.SignedMessage.FullData i.State.LastPreparedRound = i.State.Round diff --git a/protocol/v2/ssv/runner/proposer.go b/protocol/v2/ssv/runner/proposer.go index 0976634f76..65badf9025 100644 --- a/protocol/v2/ssv/runner/proposer.go +++ b/protocol/v2/ssv/runner/proposer.go @@ -246,7 +246,8 @@ func (r *ProposerRunner) ProcessPostConsensus(logger *zap.Logger, signedMsg *spe r.metrics.EndPostConsensus() logger.Debug("🧩 reconstructed partial post consensus signatures proposer", zap.Uint64s("signers", getPostConsensusProposerSigners(r.GetState(), root)), - fields.PostConsensusTime(r.metrics.GetPostConsensusTime())) + fields.PostConsensusTime(r.metrics.GetPostConsensusTime()), + fields.Round(r.GetState().RunningInstance.State.Round)) endSubmission := r.metrics.StartBeaconSubmission() validatorConsensusData := &spectypes.ValidatorConsensusData{} diff --git a/registry/storage/shares.go b/registry/storage/shares.go index e3162bdb13..120b07b7ad 100644 --- a/registry/storage/shares.go +++ b/registry/storage/shares.go @@ -240,18 +240,24 @@ func (s *sharesStorage) unsafeSave(rw basedb.ReadWriter, shares ...*types.SSVSha return err } + updateShares := make([]*types.SSVShare, 0, len(shares)) + addShares := make([]*types.SSVShare, 0, len(shares)) + for _, share := range shares { key := hex.EncodeToString(share.ValidatorPubKey[:]) // Update validatorStore indices. if _, ok := s.shares[key]; ok { - s.validatorStore.handleShareUpdated(share) + updateShares = append(updateShares, share) } else { - s.validatorStore.handleSharesAdded(share) + addShares = append(addShares, share) } - s.shares[key] = share } + + s.validatorStore.handleSharesUpdated(updateShares...) + s.validatorStore.handleSharesAdded(addShares...) + return nil } diff --git a/registry/storage/validatorstore.go b/registry/storage/validatorstore.go index e2fbae9a38..beb306d3c3 100644 --- a/registry/storage/validatorstore.go +++ b/registry/storage/validatorstore.go @@ -296,35 +296,37 @@ func (c *validatorStore) handleShareRemoved(pk spectypes.ValidatorPK) { } } -func (c *validatorStore) handleShareUpdated(share *types.SSVShare) { +func (c *validatorStore) handleSharesUpdated(shares ...*types.SSVShare) { c.mu.Lock() defer c.mu.Unlock() - // Update byValidatorIndex - if share.HasBeaconMetadata() { - c.byValidatorIndex[share.BeaconMetadata.Index] = share - } + for _, share := range shares { - // Update byCommitteeID - for _, committee := range c.byCommitteeID { - if committee.ID != share.CommitteeID() { - continue + // Update byValidatorIndex + if share.HasBeaconMetadata() { + c.byValidatorIndex[share.BeaconMetadata.Index] = share } - for i, validator := range committee.Validators { - if validator.ValidatorPubKey == share.ValidatorPubKey { - committee.Validators[i] = share - committee.Indices[i] = share.ValidatorIndex - break + + // Update byCommitteeID + committee, ok := c.byCommitteeID[share.CommitteeID()] + if ok { + for i, validator := range committee.Validators { + if validator.ValidatorPubKey == share.ValidatorPubKey { + committee.Validators[i] = share + committee.Indices[i] = share.ValidatorIndex + break + } } } - } - // Update byOperatorID - for _, data := range c.byOperatorID { - for i, s := range data.shares { - if s.ValidatorPubKey == share.ValidatorPubKey { - data.shares[i] = share - break + // Update byOperatorID + for _, shareMember := range share.Committee { + data := c.byOperatorID[shareMember.Signer] + for i, s := range data.shares { + if s.ValidatorPubKey == share.ValidatorPubKey { + data.shares[i] = share + break + } } } } diff --git a/registry/storage/validatorstore_test.go b/registry/storage/validatorstore_test.go index d9a3f5f6e8..290852c674 100644 --- a/registry/storage/validatorstore_test.go +++ b/registry/storage/validatorstore_test.go @@ -1,9 +1,15 @@ package storage import ( + "encoding/binary" + "fmt" + "math/rand" + "os" "sync" + "sync/atomic" "testing" + "github.com/aquasecurity/table" eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/ethereum/go-ethereum/common" @@ -156,7 +162,7 @@ func TestValidatorStore(t *testing.T) { }) shareMap[share2.ValidatorPubKey] = updatedShare2 - store.handleShareUpdated(updatedShare2) + store.handleSharesUpdated(updatedShare2) // TODO: updatedShare2 now only changes Quorum field, which doesn't affect any indices. If handleShareUpdated expects to receive shares where indexes field are updated, this needs to be tested. @@ -339,3 +345,98 @@ func TestSelfValidatorStore_NilOperatorID(t *testing.T) { require.Nil(t, selfStore.SelfParticipatingCommittees(99)) require.Nil(t, selfStore.SelfParticipatingCommittees(201)) } + +func BenchmarkValidatorStore_Update(b *testing.B) { + shares := map[spectypes.ValidatorPK]*ssvtypes.SSVShare{} + + const ( + totalOperators = 500 + totalValidators = 50_000 + ) + + var validatorIndex atomic.Int64 + createShare := func(operators []spectypes.OperatorID) *ssvtypes.SSVShare { + index := validatorIndex.Add(1) + + var pk spectypes.ValidatorPK + binary.LittleEndian.PutUint64(pk[:], uint64(index)) + + var committee []*spectypes.ShareMember + for _, signer := range operators { + committee = append(committee, &spectypes.ShareMember{Signer: signer}) + } + + return &ssvtypes.SSVShare{ + Metadata: ssvtypes.Metadata{ + BeaconMetadata: &beaconprotocol.ValidatorMetadata{ + Index: phase0.ValidatorIndex(index), + }, + }, + Share: spectypes.Share{ + ValidatorIndex: phase0.ValidatorIndex(index), + ValidatorPubKey: pk, + SharePubKey: pk[:], + Committee: committee, + FeeRecipientAddress: [20]byte{10, 20, 30}, + Graffiti: []byte("example"), + }, + } + } + + for i := 0; i < totalValidators; i++ { + committee := make([]spectypes.OperatorID, 4) + if rand.Float64() < 0.02 { + // 2% chance of a purely random committee. + for i, id := range rand.Perm(totalOperators)[:4] { + committee[i] = spectypes.OperatorID(id) + } + } else { + // 98% chance to form big committees. + first := rand.Intn(totalOperators * 0.2) // 20% of the operators. + for i := range committee { + committee[i] = spectypes.OperatorID((first + i) % totalOperators) + } + } + share := createShare(committee) + shares[share.ValidatorPubKey] = share + } + + // Print table of committees and validator counts for debugging. + committees := map[[4]spectypes.OperatorID]int{} + for _, share := range shares { + committee := [4]spectypes.OperatorID{} + for i, member := range share.Committee { + committee[i] = member.Signer + } + committees[committee]++ + } + tbl := table.New(os.Stdout) + tbl.SetHeaders("Committee", "Validators") + for committee, count := range committees { + tbl.AddRow(fmt.Sprintf("%v", committee), fmt.Sprintf("%d", count)) + } + // tbl.Render() // Uncomment to print. + + b.Logf("Total committees: %d", len(committees)) + + store := newValidatorStore( + func() []*ssvtypes.SSVShare { return maps.Values(shares) }, + func(pubKey []byte) *ssvtypes.SSVShare { + return shares[spectypes.ValidatorPK(pubKey)] + }, + ) + store.handleSharesAdded(maps.Values(shares)...) + + pubKeys := maps.Keys(shares) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + randomShares := make([]*ssvtypes.SSVShare, 500) + first := rand.Intn(len(pubKeys)) + for j := 0; j < 500; j++ { + randomShares[j] = shares[pubKeys[(first+j)%len(pubKeys)]] + } + + store.handleSharesUpdated(randomShares...) + } +} diff --git a/scripts/spec-alignment/.gitignore b/scripts/spec-alignment/.gitignore index 8589d14883..66e7befe90 100644 --- a/scripts/spec-alignment/.gitignore +++ b/scripts/spec-alignment/.gitignore @@ -1 +1,2 @@ -output.diff \ No newline at end of file +output.diff +genesis_output.diff \ No newline at end of file diff --git a/scripts/spec-alignment/differ.config.yaml b/scripts/spec-alignment/differ.config.yaml index 120bb9c45d..58dddc44e4 100644 --- a/scripts/spec-alignment/differ.config.yaml +++ b/scripts/spec-alignment/differ.config.yaml @@ -1,4 +1,4 @@ -ApprovedChanges: ["870a3a66aeccd737","4e22a08543b079b","56ceb03cd44ff702","188adfe8914e04c1","2438f9c5b82b69a3","1a716ee3bdb3170","90b166f78390af18","68219b82a1d9d829","c4c4caa5d0938b85","dfe99ce1d27b6cb1","35f5dab1f128d193","9a3973b64d7e8932","f33f07301a770d03","3e9e0dddfad3b302","d4fef6512374c1f5","b49f54cb45787e4b","59b2375130aef5df","f094cd0460432170","8e51881e527dd603","a7d6d58d9fa06379","1d124224ca4d0fe3","39ea06bfd1477d2d","7e2550bab51f22b2","87ebd29bd49fc52f","ef39dd5223e0d080","fe14e7f0503ea188","6146023d4d5708a2","aebb8e4348b6d667","973a2e6704dbf3","fb4cac598a68c592","257c7eb81d6eb245","2a8e94fe037e13fd","5e7eb878de54eec6","960a9c64cd4ec93c","57dfd255520bd849","ec333ff8a708db69","1cc1ff39ad91ee69","5714652b88e2d44f","7a53b3b037c56325","8c02ef1964464c30","19a268910a20da3d","af6e01ed565029f3","318b5169ac4dabb6","372c6b332e8ba699","c0d8a364c0db855a","4287381be4fb1841","b1614afc1da7794f","c214975412f3fd7","8bbf7eba3fa0cf7e","8e4ec8debe331b36","7a671d8fcefc3793","e2b0e9c6454c1c08","6707ecfefa5fec21","d5a7389d730464f1","8dfae3b3223d2de0","a81c092c985de728","968df5082c727ed6","9e53c73ee60b1cc2","9d265e99dd31d4f5","a34619e078d2e42f","17e8cec4f0625d53","e913f373aa88f333","cfc1e05c372d88dc","e5de6901d78b8833","57c1885b43dd8d19","e8a49856a5edd893","22ea21d10a2f861c","954e4fce01631c4e","108b9575f7c1d4bc","1f8d076449068f64","5a7ad98296703f6","159536003eeddac8","8ca8f82e67ddd3dd","16ebe47404323cc1","48bfe5cf1e578b47","dd83182b693a7216","308d21d9830f7047","6dde03147e012b1a","730c3e5e59393b7d","5b44a4b425ecc397","df5debc50ec8babc","92a41554b2910bb8","c36c680554dde59f","447feaa5cdc1a010","fda90c61f44cb149","cdbb4930eced584c","274336ec1127e6c0","2a496f5b3ad542d2","6b395912dde33b0e","cac56ec14994216b","8850900b5d9bcc65","15e7706486c6359e","cc22f28953b787ea","3bad6ae11596a574","8f84422a240d889c","5b265432dfbbaac7","43794bf5953db193","7975821460ebe1e7","173c505e12aabb8f","47ee0d148148a56f","8cc38593ebe049b6","bda3aec7157b095a","248712911696a851","f4d9c910f1dbaef7","1a2146fcad37acb8","b0b146f9bdab64b6","edfd442b4d725fbb","122f053573538a32","d720d714a20833e1"] +ApprovedChanges: ["50e5bb7eda99594e", "870a3a66aeccd737","4e22a08543b079b","56ceb03cd44ff702","188adfe8914e04c1","2438f9c5b82b69a3","1a716ee3bdb3170","90b166f78390af18","68219b82a1d9d829","c4c4caa5d0938b85","dfe99ce1d27b6cb1","35f5dab1f128d193","9a3973b64d7e8932","f33f07301a770d03","3e9e0dddfad3b302","d4fef6512374c1f5","b49f54cb45787e4b","59b2375130aef5df","f094cd0460432170","8e51881e527dd603","a7d6d58d9fa06379","1d124224ca4d0fe3","39ea06bfd1477d2d","7e2550bab51f22b2","87ebd29bd49fc52f","ef39dd5223e0d080","fe14e7f0503ea188","6146023d4d5708a2","aebb8e4348b6d667","973a2e6704dbf3","fb4cac598a68c592","257c7eb81d6eb245","2a8e94fe037e13fd","5e7eb878de54eec6","960a9c64cd4ec93c","57dfd255520bd849","ec333ff8a708db69","1cc1ff39ad91ee69","5714652b88e2d44f","7a53b3b037c56325","8c02ef1964464c30","19a268910a20da3d","af6e01ed565029f3","318b5169ac4dabb6","372c6b332e8ba699","c0d8a364c0db855a","4287381be4fb1841","b1614afc1da7794f","c214975412f3fd7","8bbf7eba3fa0cf7e","8e4ec8debe331b36","7a671d8fcefc3793","e2b0e9c6454c1c08","6707ecfefa5fec21","d5a7389d730464f1","8dfae3b3223d2de0","a81c092c985de728","968df5082c727ed6","9e53c73ee60b1cc2","9d265e99dd31d4f5","a34619e078d2e42f","17e8cec4f0625d53","e913f373aa88f333","cfc1e05c372d88dc","e5de6901d78b8833","57c1885b43dd8d19","e8a49856a5edd893","22ea21d10a2f861c","954e4fce01631c4e","108b9575f7c1d4bc","1f8d076449068f64","5a7ad98296703f6","159536003eeddac8","8ca8f82e67ddd3dd","16ebe47404323cc1","48bfe5cf1e578b47","dd83182b693a7216","308d21d9830f7047","6dde03147e012b1a","730c3e5e59393b7d","5b44a4b425ecc397","df5debc50ec8babc","92a41554b2910bb8","c36c680554dde59f","447feaa5cdc1a010","fda90c61f44cb149","cdbb4930eced584c","274336ec1127e6c0","2a496f5b3ad542d2","6b395912dde33b0e","cac56ec14994216b","8850900b5d9bcc65","15e7706486c6359e","cc22f28953b787ea","3bad6ae11596a574","8f84422a240d889c","5b265432dfbbaac7","43794bf5953db193","7975821460ebe1e7","173c505e12aabb8f","47ee0d148148a56f","8cc38593ebe049b6","bda3aec7157b095a","248712911696a851","f4d9c910f1dbaef7","1a2146fcad37acb8","b0b146f9bdab64b6","edfd442b4d725fbb","122f053573538a32","d720d714a20833e1"] IgnoredIdentifiers: - logger diff --git a/scripts/spec-alignment/genesis_differ.config.yaml b/scripts/spec-alignment/genesis_differ.config.yaml new file mode 100644 index 0000000000..e81efbc18d --- /dev/null +++ b/scripts/spec-alignment/genesis_differ.config.yaml @@ -0,0 +1,42 @@ +ApprovedChanges: ["52b93267ba812308","9f2881f9e89b4c3","f8718ef9598a2d28","ea4da0c78bc1e930","11481543a56b03e7","4bc55d173976f499","5a326429bd7d816a","57938492d36e5b72","ea83b3555f29e44e","39a395cc56c381d8","2092a46a009de5e9","f9e12bb821abda59","74490095cad1f871","fef6a577794897e9","e243efb1fef8baca","b612f4f4bee5726c","b4072ece06d92c84","487d349a6296651e","1329fd2f0f7101e5","6ea163caa000821c","cb2a3fac03c9f70d","c155c7005d298b8a","5462556ab33327ae","66591f5d3e9c299d","ef530512222fa3a3","54f7ee00c5223d56","136792991a713119","519dec1f394a29bb","c16537938b23bb1c","930f8003cc73658a","b4d4b7c288d15580","8e871e3dc302502c","264f6c3cb6c044e","73b442121276436f","a5d665260b9545e7","e76da25dcc1b8b7b","3021b027e65343dc","37abca362396de40","10331350bdd5cea5","ff66465e82a0bce","7008ba0e5fb3bc50","17d86fc521251084","60ee89aed3dca274","cfb5a31338393004","774c67a1117bb600","bbbac3fd539c9da","4120ef6224cd7653","c13c14ac8b912a99","e34eb83c1de17e7b","d60440779e512dda","8b474f07634c3b5b","ac42b9ed129f763c","67809ff9e1f78fba","436d37b16e59e851","d201c716184904d6","422221ab59ac4658","30ed9a822232b7e1","c08c6d84582b86c1","c07315929c5bfdae","751997d95ea9340","7715acc5b4c5aa2","14d6cdfdf92136fc","c9db895746d32d2","a0a0164bd2ecb327","c0cb3984d0a20d8","1c8beb7d60ffa18a","b44005e951042d3","45749213deaece88","6afb57c28a55854c","5619c6b724070584","81385e7b399b3235","856eb69df47300bc","68ab7316969c38b","c8f63fe574c9cd3","a1dd0a169df78d67","4bb11f08323af914","466839f492add207","6c3507bea504fcc","560bb093d1aea569","a13eb5088003f50b","9f5f0eff2dca5e9"] + +IgnoredIdentifiers: + - logger +ReducedPackageNames: + - spectypes + - specssv + - specqbft + - types + - ssv + - qbft + - instance + - controller + - genesisspecssv + - genesisspectypes + - genesisspecqbft +Comparisons: + - Packages: + Left: + - ./protocol/genesis/ssv/runner + - ./protocol/genesis/types + Right: + - ./ssv + Hints: + ReconstructSignature: PartialSigContainer.ReconstructSignature + + - Packages: + Left: + - ./protocol/genesis/qbft/controller + - ./protocol/genesis/qbft/instance + Right: + - ./qbft + Hints: + BaseCommitValidation: baseCommitValidation + + - Packages: + Left: + - ./protocol/genesis/types + Right: + - ./types + Hints: + VerifyByOperators: Signature.VerifyByOperators \ No newline at end of file diff --git a/scripts/spec-alignment/genesis_differ.sh b/scripts/spec-alignment/genesis_differ.sh new file mode 100755 index 0000000000..728c9da25e --- /dev/null +++ b/scripts/spec-alignment/genesis_differ.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Get the path to the ssv-spec folder. +SPEC_PATH=$(go mod download -json github.com/ssvlabs/ssv-spec-pre-cc | jq -r .Dir) + +# Run Differ. +differ \ + --config ./genesis_differ.config.yaml \ + --output ./genesis_output.diff \ + ../../ \ + $SPEC_PATH \ No newline at end of file