diff --git a/beacon/goclient/aggregator.go b/beacon/goclient/aggregator.go index 1c571138ad..107cb4e024 100644 --- a/beacon/goclient/aggregator.go +++ b/beacon/goclient/aggregator.go @@ -30,7 +30,7 @@ func (gc *GoClient) SubmitAggregateSelectionProof( gc.waitToSlotTwoThirds(slot) // differ from spec because we need to subscribe to subnet - isAggregator := isAggregator(committeeLength, slotSig) + isAggregator := gc.isAggregator(committeeLength, slotSig) if !isAggregator { return nil, DataVersionNil, fmt.Errorf("validator is not an aggregator") } @@ -39,7 +39,9 @@ func (gc *GoClient) SubmitAggregateSelectionProof( if err != nil { return nil, DataVersionNil, fmt.Errorf("failed to get attestation data: %w", err) } - if gc.DataVersion(gc.network.EstimatedEpochAtSlot(attData.Slot)) < spec.DataVersionElectra { + + dataVersion, _ := gc.beaconConfig.ForkAtEpoch(gc.getBeaconConfig().EstimatedEpochAtSlot(attData.Slot)) + if dataVersion < spec.DataVersionElectra { attData.Index = committeeIndex } @@ -188,8 +190,8 @@ func (gc *GoClient) SubmitSignedAggregateSelectionProof( // committee = get_beacon_committee(state, slot, index) // modulo = max(1, len(committee) // TARGET_AGGREGATORS_PER_COMMITTEE) // return bytes_to_uint64(hash(slot_signature)[0:8]) % modulo == 0 -func isAggregator(committeeCount uint64, slotSig []byte) bool { - modulo := committeeCount / TargetAggregatorsPerCommittee +func (gc *GoClient) isAggregator(committeeCount uint64, slotSig []byte) bool { + modulo := committeeCount / gc.beaconConfig.TargetAggregatorsPerCommittee if modulo == 0 { // Modulo must be at least 1. modulo = 1 @@ -201,9 +203,9 @@ func isAggregator(committeeCount uint64, slotSig []byte) bool { // waitToSlotTwoThirds waits until two-third of the slot has transpired (SECONDS_PER_SLOT * 2 / 3 seconds after slot start time) func (gc *GoClient) waitToSlotTwoThirds(slot phase0.Slot) { - oneThird := gc.network.SlotDurationSec() / 3 /* one third of slot duration */ - - finalTime := gc.slotStartTime(slot).Add(2 * oneThird) + config := gc.getBeaconConfig() + oneInterval := config.IntervalDuration() + finalTime := config.GetSlotStartTime(slot).Add(2 * oneInterval) wait := time.Until(finalTime) if wait <= 0 { return diff --git a/beacon/goclient/attest_test.go b/beacon/goclient/attest_test.go index 114ce58dc4..b9f9d3e203 100644 --- a/beacon/goclient/attest_test.go +++ b/beacon/goclient/attest_test.go @@ -15,12 +15,9 @@ import ( "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/sourcegraph/conc/pool" - "github.com/ssvlabs/ssv-spec/types" "github.com/stretchr/testify/require" "go.uber.org/zap" - "github.com/ssvlabs/ssv/operator/slotticker" - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" "github.com/ssvlabs/ssv/utils/hashmap" ) @@ -61,17 +58,28 @@ var ( }`), "/eth/v1/beacon/genesis": []byte(`{ "data": { - "genesis_time": "1695902400", - "genesis_validators_root": "0x9143aa7c615a7f7115e2b6aac319c03529df8242ae705fba9df39b79c59fa8b1", + "genesis_time": "1606824023", + "genesis_validators_root": "0x4b363db94e28612020049ce3795b0252c16c4241df2bc9ef221abde47527c0d0", "genesis_fork_version": "0x00000000" } }`), "/eth/v1/config/spec": []byte(`{ "data": { - "CONFIG_NAME": "holesky", + "CONFIG_NAME": "mainnet", "GENESIS_FORK_VERSION": "0x00000000", - "CAPELLA_FORK_VERSION": "0x04017000", - "MIN_GENESIS_TIME": "1695902100", + "ALTAIR_FORK_VERSION": "0x01000000", + "ALTAIR_FORK_EPOCH": "74240", + "BELLATRIX_FORK_VERSION": "0x02000000", + "BELLATRIX_FORK_EPOCH": "144896", + "CAPELLA_FORK_VERSION": "0x03000000", + "CAPELLA_FORK_EPOCH": "194048", + "DENEB_FORK_VERSION": "0x04000000", + "DENEB_FORK_EPOCH": "269568", + "ELECTRA_FORK_VERSION": "0x05000000", + "ELECTRA_FORK_EPOCH": "364032", + "FULU_FORK_VERSION": "0x06000000", + "FULU_FORK_EPOCH": "18446744073709551615", + "MIN_GENESIS_TIME": "1606824000", "SECONDS_PER_SLOT": "12", "SLOTS_PER_EPOCH": "32", "EPOCHS_PER_SYNC_COMMITTEE_PERIOD": "256", @@ -79,12 +87,7 @@ var ( "SYNC_COMMITTEE_SUBNET_COUNT": "4", "TARGET_AGGREGATORS_PER_COMMITTEE": "16", "TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE": "16", - "INTERVALS_PER_SLOT": "3", - "ALTAIR_FORK_EPOCH": "74240", - "BELLATRIX_FORK_EPOCH": "144896", - "CAPELLA_FORK_EPOCH": "194048", - "DENEB_FORK_EPOCH": "269568", - "ELECTRA_FORK_EPOCH": "18446744073709551615" + "INTERVALS_PER_SLOT": "3" } }`), } @@ -205,17 +208,10 @@ func TestGoClient_GetAttestationData_Simple(t *testing.T) { t.Context(), zap.NewNop(), Options{ - Network: beacon.NewNetwork(types.MainNetwork), BeaconNodeAddr: server.URL, CommonTimeout: 1 * time.Second, LongTimeout: 1 * time.Second, }, - func() slotticker.SlotTicker { - return slotticker.New(zap.NewNop(), slotticker.Config{ - SlotDuration: 12 * time.Second, - GenesisTime: time.Now(), - }) - }, ) require.NoError(t, err) @@ -501,18 +497,11 @@ func createClient( ctx, zap.NewNop(), Options{ - Network: beacon.NewNetwork(types.MainNetwork), BeaconNodeAddr: beaconServerURL, CommonTimeout: defaultHardTimeout, LongTimeout: time.Second, WithWeightedAttestationData: withWeightedAttestationData, }, - func() slotticker.SlotTicker { - return slotticker.New(zap.NewNop(), slotticker.Config{ - SlotDuration: 12 * time.Second, - GenesisTime: time.Now(), - }) - }, ) return client, err } diff --git a/beacon/goclient/current_fork.go b/beacon/goclient/current_fork.go deleted file mode 100644 index 813c25a9ea..0000000000 --- a/beacon/goclient/current_fork.go +++ /dev/null @@ -1,46 +0,0 @@ -package goclient - -import ( - "context" - "fmt" - "net/http" - "time" - - "github.com/attestantio/go-eth2-client/api" - "github.com/attestantio/go-eth2-client/spec/phase0" - "go.uber.org/zap" -) - -func (gc *GoClient) ForkAtEpoch(ctx context.Context, epoch phase0.Epoch) (*phase0.Fork, error) { - start := time.Now() - // ForkSchedule result is cached in the client and updated once in a while. - // So calling this method often shouldn't worsen the performance. - schedule, err := gc.multiClient.ForkSchedule(ctx, &api.ForkScheduleOpts{}) - recordRequestDuration(ctx, "ForkSchedule", gc.multiClient.Address(), http.MethodGet, time.Since(start), err) - if err != nil { - gc.log.Error(clResponseErrMsg, - zap.String("api", "ForkSchedule"), - zap.Error(err), - ) - return nil, err - } - if schedule.Data == nil { - gc.log.Error(clNilResponseForkDataErrMsg, - zap.String("api", "ForkSchedule"), - ) - return nil, fmt.Errorf("fork schedule response data is nil") - } - - var forkAtEpoch *phase0.Fork - for _, fork := range schedule.Data { - if fork.Epoch <= epoch && (forkAtEpoch == nil || fork.Epoch > forkAtEpoch.Epoch) { - forkAtEpoch = fork - } - } - - if forkAtEpoch == nil { - return nil, fmt.Errorf("could not find fork at epoch %d", epoch) - } - - return forkAtEpoch, nil -} diff --git a/beacon/goclient/current_fork_test.go b/beacon/goclient/current_fork_test.go deleted file mode 100644 index ae95c7d2eb..0000000000 --- a/beacon/goclient/current_fork_test.go +++ /dev/null @@ -1,139 +0,0 @@ -package goclient - -import ( - "encoding/json" - "net/http" - "testing" - "time" - - "github.com/attestantio/go-eth2-client/spec/phase0" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv-spec/types" - - "github.com/ssvlabs/ssv/beacon/goclient/tests" - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" -) - -const forkSchedulePath = "/eth/v1/config/fork_schedule" - -func TestCurrentFork(t *testing.T) { - network := beacon.NewNetwork(types.MainNetwork) - - t.Run("success", func(t *testing.T) { - mockServer := tests.MockServer(func(r *http.Request, resp json.RawMessage) (json.RawMessage, error) { - if r.URL.Path == forkSchedulePath { - return json.RawMessage(`{ - "data": [ - { - "previous_version": "0x00010203", - "current_version": "0x04050607", - "epoch": "100" - }, - { - "previous_version": "0x04050607", - "current_version": "0x08090a0b", - "epoch": "150" - }, - { - "previous_version": "0x08090a0b", - "current_version": "0x0c0d0e0f", - "epoch": "300" - } - ] - }`), nil - } - return resp, nil - }) - defer mockServer.Close() - - client, err := New( - t.Context(), - zap.NewNop(), - Options{ - Network: network, - BeaconNodeAddr: mockServer.URL, - CommonTimeout: 100 * time.Millisecond, - LongTimeout: 500 * time.Millisecond, - }, - tests.MockSlotTickerProvider, - ) - require.NoError(t, err) - - currentFork, err := client.ForkAtEpoch(t.Context(), 200) - require.NoError(t, err) - require.NotNil(t, currentFork) - - require.EqualValues(t, 150, currentFork.Epoch) - require.Equal(t, phase0.Version{0x04, 0x05, 0x06, 0x07}, currentFork.PreviousVersion) - require.Equal(t, phase0.Version{0x08, 0x09, 0x0a, 0x0b}, currentFork.CurrentVersion) - }) - - t.Run("nil_data", func(t *testing.T) { - mockServer := tests.MockServer(func(r *http.Request, resp json.RawMessage) (json.RawMessage, error) { - if r.URL.Path == forkSchedulePath { - return json.RawMessage(`{"data": null}`), nil - } - return resp, nil - }) - defer mockServer.Close() - - client, err := New( - t.Context(), - zap.NewNop(), - Options{ - Network: network, - BeaconNodeAddr: mockServer.URL, - CommonTimeout: 100 * time.Millisecond, - LongTimeout: 500 * time.Millisecond, - }, - tests.MockSlotTickerProvider, - ) - require.NoError(t, err) - - _, err = client.ForkAtEpoch(t.Context(), 1) - require.Error(t, err) - require.Contains(t, err.Error(), "fork schedule response data is nil") - }) - - t.Run("no_current_fork", func(t *testing.T) { - mockServer := tests.MockServer(func(r *http.Request, resp json.RawMessage) (json.RawMessage, error) { - if r.URL.Path == forkSchedulePath { - return json.RawMessage(`{ - "data": [ - { - "previous_version": "0x00010203", - "current_version": "0x04050607", - "epoch": "200" - }, - { - "previous_version": "0x04050607", - "current_version": "0x08090a0b", - "epoch": "300" - } - ] - }`), nil - } - return resp, nil - }) - defer mockServer.Close() - - client, err := New( - t.Context(), - zap.NewNop(), - Options{ - Network: network, - BeaconNodeAddr: mockServer.URL, - CommonTimeout: 100 * time.Millisecond, - LongTimeout: 500 * time.Millisecond, - }, - tests.MockSlotTickerProvider, - ) - require.NoError(t, err) - - _, err = client.ForkAtEpoch(t.Context(), 100) - require.Error(t, err) - require.Contains(t, err.Error(), "could not find fork at epoch 100") - }) -} diff --git a/beacon/goclient/dataversion.go b/beacon/goclient/dataversion.go deleted file mode 100644 index 78a1e0732a..0000000000 --- a/beacon/goclient/dataversion.go +++ /dev/null @@ -1,92 +0,0 @@ -package goclient - -import ( - "fmt" - - "github.com/attestantio/go-eth2-client/spec" - "github.com/attestantio/go-eth2-client/spec/phase0" -) - -func (gc *GoClient) DataVersion(epoch phase0.Epoch) spec.DataVersion { - gc.ForkLock.RLock() - defer gc.ForkLock.RUnlock() - if epoch < gc.ForkEpochAltair { - return spec.DataVersionPhase0 - } else if epoch < gc.ForkEpochBellatrix { - return spec.DataVersionAltair - } else if epoch < gc.ForkEpochCapella { - return spec.DataVersionBellatrix - } else if epoch < gc.ForkEpochDeneb { - return spec.DataVersionCapella - } else if epoch < gc.ForkEpochElectra { - return spec.DataVersionDeneb - } - return spec.DataVersionElectra -} - -func (gc *GoClient) checkForkValues(specResponse map[string]any) error { - // Validate the response. - if specResponse == nil { - return fmt.Errorf("spec response is nil") - } - - // Lock the fork values to ensure atomic read and update. - gc.ForkLock.Lock() - defer gc.ForkLock.Unlock() - - // We'll compute candidate new values first and update the fields only if all validations pass. - var newAltair, newBellatrix, newCapella, newDeneb, newElectra phase0.Epoch - - // processFork is a helper to handle required forks. - // It retrieves the candidate fork epoch from the response, - // and compares it with the current stored value. - // If the candidate is greater than the current value, that's an error. - // Otherwise, it returns the lower value (or the candidate if the current value is zero). - processFork := func(forkName, key string, current phase0.Epoch, required bool) (phase0.Epoch, error) { - raw, ok := specResponse[key] - if !ok { - if required { - return 0, fmt.Errorf("%s fork epoch not known by chain", forkName) - } - return FarFutureEpoch, nil - } - forkVal, ok := raw.(uint64) - if !ok { - return 0, fmt.Errorf("failed to decode %s fork epoch", forkName) - } - if current != FarFutureEpoch && current != phase0.Epoch(forkVal) { - // Reject if candidate is missing the fork epoch that we've already seen. - return 0, fmt.Errorf("new %s fork epoch (%d) doesn't match current value (%d)", forkName, phase0.Epoch(forkVal), current) - } - return phase0.Epoch(forkVal), nil - } - - var err error - // Process required forks. - if newAltair, err = processFork("ALTAIR", "ALTAIR_FORK_EPOCH", gc.ForkEpochAltair, true); err != nil { - return err - } - if newBellatrix, err = processFork("BELLATRIX", "BELLATRIX_FORK_EPOCH", gc.ForkEpochBellatrix, true); err != nil { - return err - } - if newCapella, err = processFork("CAPELLA", "CAPELLA_FORK_EPOCH", gc.ForkEpochCapella, true); err != nil { - return err - } - if newDeneb, err = processFork("DENEB", "DENEB_FORK_EPOCH", gc.ForkEpochDeneb, true); err != nil { - return err - } - alreadySeenElectra := gc.ForkEpochElectra != FarFutureEpoch - if newElectra, err = processFork("ELECTRA", "ELECTRA_FORK_EPOCH", gc.ForkEpochElectra, alreadySeenElectra); err != nil { - return err - } - - // At this point, no error was encountered. - // Update all fork values atomically. - gc.ForkEpochAltair = newAltair - gc.ForkEpochBellatrix = newBellatrix - gc.ForkEpochCapella = newCapella - gc.ForkEpochDeneb = newDeneb - gc.ForkEpochElectra = newElectra - - return nil -} diff --git a/beacon/goclient/dataversion_test.go b/beacon/goclient/dataversion_test.go deleted file mode 100644 index 1a579a16ad..0000000000 --- a/beacon/goclient/dataversion_test.go +++ /dev/null @@ -1,216 +0,0 @@ -package goclient - -import ( - "strings" - "testing" - - "github.com/attestantio/go-eth2-client/spec" - "github.com/attestantio/go-eth2-client/spec/phase0" -) - -// TestDataVersion verifies that DataVersion returns the correct version based on fork epochs. -func TestDataVersion(t *testing.T) { - // Create a client with preset fork epochs. - client := &GoClient{ - ForkEpochAltair: phase0.Epoch(10), - ForkEpochBellatrix: phase0.Epoch(20), - ForkEpochCapella: phase0.Epoch(30), - ForkEpochDeneb: phase0.Epoch(40), - ForkEpochElectra: phase0.Epoch(50), - } - - tests := []struct { - epoch phase0.Epoch - expected spec.DataVersion - }{ - {epoch: 0, expected: spec.DataVersionPhase0}, - {epoch: 9, expected: spec.DataVersionPhase0}, - {epoch: 10, expected: spec.DataVersionAltair}, - {epoch: 15, expected: spec.DataVersionAltair}, - {epoch: 20, expected: spec.DataVersionBellatrix}, - {epoch: 25, expected: spec.DataVersionBellatrix}, - {epoch: 30, expected: spec.DataVersionCapella}, - {epoch: 35, expected: spec.DataVersionCapella}, - {epoch: 40, expected: spec.DataVersionDeneb}, - {epoch: 45, expected: spec.DataVersionDeneb}, - {epoch: 50, expected: spec.DataVersionElectra}, - {epoch: 55, expected: spec.DataVersionElectra}, - } - - for _, tc := range tests { - got := client.DataVersion(tc.epoch) - if got != tc.expected { - t.Errorf("DataVersion(%d): expected %v, got %v", tc.epoch, tc.expected, got) - } - } -} - -// TestCheckForkValues verifies the checkForkValues function across various scenarios. -func TestCheckForkValues(t *testing.T) { - tests := []struct { - name string - // initial fork values - initialAltair, initialBellatrix, initialCapella, - initialDeneb, initialElectra phase0.Epoch - // input response and expected outcomes - response map[string]any - expectedErr string - expectedAltair, expectedBellatrix, expectedCapella, - expectedDeneb, expectedElectra phase0.Epoch - }{ - { - name: "nil response", - response: nil, - expectedErr: "spec response is nil", - }, - { - name: "missing ALTAIR", - initialAltair: FarFutureEpoch, - initialBellatrix: FarFutureEpoch, - initialCapella: FarFutureEpoch, - initialDeneb: FarFutureEpoch, - initialElectra: FarFutureEpoch, - response: map[string]any{ - "BELLATRIX_FORK_EPOCH": uint64(20), - "CAPELLA_FORK_EPOCH": uint64(30), - "DENEB_FORK_EPOCH": uint64(40), - }, - expectedErr: "ALTAIR fork epoch not known by chain", - }, - { - name: "invalid type for ALTAIR", - initialAltair: FarFutureEpoch, - initialBellatrix: FarFutureEpoch, - initialCapella: FarFutureEpoch, - initialDeneb: FarFutureEpoch, - initialElectra: FarFutureEpoch, - response: map[string]any{ - "ALTAIR_FORK_EPOCH": "not a uint", - "BELLATRIX_FORK_EPOCH": uint64(20), - "CAPELLA_FORK_EPOCH": uint64(30), - "DENEB_FORK_EPOCH": uint64(40), - }, - expectedErr: "failed to decode ALTAIR fork epoch", - }, - { - name: "valid update with initial zeros and electra provided", - initialAltair: FarFutureEpoch, - initialBellatrix: FarFutureEpoch, - initialCapella: FarFutureEpoch, - initialDeneb: FarFutureEpoch, - initialElectra: FarFutureEpoch, - response: map[string]any{ - "ALTAIR_FORK_EPOCH": uint64(10), - "BELLATRIX_FORK_EPOCH": uint64(20), - "CAPELLA_FORK_EPOCH": uint64(30), - "DENEB_FORK_EPOCH": uint64(40), - "ELECTRA_FORK_EPOCH": uint64(50), - }, - expectedAltair: phase0.Epoch(10), - expectedBellatrix: phase0.Epoch(20), - expectedCapella: phase0.Epoch(30), - expectedDeneb: phase0.Epoch(40), - expectedElectra: phase0.Epoch(50), - }, - { - name: "optional ELECTRA not provided, remains unchanged", - initialAltair: FarFutureEpoch, - initialBellatrix: FarFutureEpoch, - initialCapella: FarFutureEpoch, - initialDeneb: FarFutureEpoch, - initialElectra: FarFutureEpoch, - response: map[string]any{ - "ALTAIR_FORK_EPOCH": uint64(10), - "BELLATRIX_FORK_EPOCH": uint64(20), - "CAPELLA_FORK_EPOCH": uint64(30), - "DENEB_FORK_EPOCH": uint64(40), - }, - expectedAltair: phase0.Epoch(10), - expectedBellatrix: phase0.Epoch(20), - expectedCapella: phase0.Epoch(30), - expectedDeneb: phase0.Epoch(40), - expectedElectra: FarFutureEpoch, - }, - { - name: "optional ELECTRA provided and can't change", - initialAltair: 10, - initialBellatrix: 20, - initialCapella: 30, - initialDeneb: 40, - initialElectra: 99, - response: map[string]any{ - "ALTAIR_FORK_EPOCH": uint64(10), - "BELLATRIX_FORK_EPOCH": uint64(20), - "CAPELLA_FORK_EPOCH": uint64(30), - "DENEB_FORK_EPOCH": uint64(40), - "ELECTRA_FORK_EPOCH": uint64(50), - }, - expectedAltair: phase0.Epoch(10), - expectedBellatrix: phase0.Epoch(20), - expectedCapella: phase0.Epoch(30), - expectedDeneb: phase0.Epoch(40), - expectedElectra: phase0.Epoch(50), - expectedErr: "new ELECTRA fork epoch (50) doesn't match current value (99)", - }, - { - name: "optional ELECTRA provided, candidate greater than current", - initialAltair: 10, - initialBellatrix: 20, - initialCapella: 30, - initialDeneb: 40, - initialElectra: 50, - response: map[string]any{ - "ALTAIR_FORK_EPOCH": uint64(10), - "BELLATRIX_FORK_EPOCH": uint64(20), - "CAPELLA_FORK_EPOCH": uint64(30), - "DENEB_FORK_EPOCH": uint64(40), - "ELECTRA_FORK_EPOCH": uint64(60), - }, - expectedErr: "new ELECTRA fork epoch (60) doesn't match current value (50)", - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - // Create a client with initial fork values. - client := &GoClient{ - ForkEpochAltair: tc.initialAltair, - ForkEpochBellatrix: tc.initialBellatrix, - ForkEpochCapella: tc.initialCapella, - ForkEpochDeneb: tc.initialDeneb, - ForkEpochElectra: tc.initialElectra, - } - - err := client.checkForkValues(tc.response) - if tc.expectedErr != "" { - if err == nil { - t.Fatalf("expected error containing %q but got nil", tc.expectedErr) - } - if !strings.Contains(err.Error(), tc.expectedErr) { - t.Fatalf("expected error containing %q, got %q", tc.expectedErr, err.Error()) - } - return - } - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - // Verify that the fork epoch fields have been updated as expected. - if client.ForkEpochAltair != tc.expectedAltair { - t.Errorf("ForkEpochAltair: expected %d, got %d", tc.expectedAltair, client.ForkEpochAltair) - } - if client.ForkEpochBellatrix != tc.expectedBellatrix { - t.Errorf("ForkEpochBellatrix: expected %d, got %d", tc.expectedBellatrix, client.ForkEpochBellatrix) - } - if client.ForkEpochCapella != tc.expectedCapella { - t.Errorf("ForkEpochCapella: expected %d, got %d", tc.expectedCapella, client.ForkEpochCapella) - } - if client.ForkEpochDeneb != tc.expectedDeneb { - t.Errorf("ForkEpochDeneb: expected %d, got %d", tc.expectedDeneb, client.ForkEpochDeneb) - } - if client.ForkEpochElectra != tc.expectedElectra { - t.Errorf("ForkEpochElectra: expected %d, got %d", tc.expectedElectra, client.ForkEpochElectra) - } - }) - } -} diff --git a/beacon/goclient/events_test.go b/beacon/goclient/events_test.go index a380750f62..e807992d9e 100644 --- a/beacon/goclient/events_test.go +++ b/beacon/goclient/events_test.go @@ -8,13 +8,11 @@ import ( "time" apiv1 "github.com/attestantio/go-eth2-client/api/v1" - "github.com/ssvlabs/ssv-spec/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" "github.com/ssvlabs/ssv/beacon/goclient/tests" - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" ) func TestSubscribeToHeadEvents(t *testing.T) { @@ -85,9 +83,7 @@ func eventsTestClient(t *testing.T, serverURL string) *GoClient { zap.NewNop(), Options{ BeaconNodeAddr: serverURL, - Network: beacon.NewNetwork(types.MainNetwork), - }, - tests.MockSlotTickerProvider) + }) require.NoError(t, err) return server diff --git a/beacon/goclient/genesis.go b/beacon/goclient/genesis.go index 254dedcaf3..07fa781316 100644 --- a/beacon/goclient/genesis.go +++ b/beacon/goclient/genesis.go @@ -12,10 +12,6 @@ import ( "go.uber.org/zap" ) -func (gc *GoClient) Genesis(ctx context.Context) (*apiv1.Genesis, error) { - return genesisForClient(ctx, gc.log, gc.multiClient) -} - // It's used in both Genesis and singleClientHooks, so we need some common implementation to avoid code repetition. func genesisForClient(ctx context.Context, log *zap.Logger, provider client.Service) (*apiv1.Genesis, error) { start := time.Now() diff --git a/beacon/goclient/genesis_test.go b/beacon/goclient/genesis_test.go index 61122f6552..b1f009c0c7 100644 --- a/beacon/goclient/genesis_test.go +++ b/beacon/goclient/genesis_test.go @@ -7,19 +7,22 @@ import ( "time" "github.com/stretchr/testify/require" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv-spec/types" "github.com/ssvlabs/ssv/beacon/goclient/tests" - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" + "github.com/ssvlabs/ssv/logging" + "github.com/ssvlabs/ssv/networkconfig" ) -const genesisPath = "/eth/v1/beacon/genesis" +const ( + genesisPath = "/eth/v1/beacon/genesis" + specPath = "/eth/v1/config/spec" +) -func TestGenesis(t *testing.T) { +func Test_genesisForClient(t *testing.T) { ctx := t.Context() + logger := logging.TestLogger(t) + t.Run("success", func(t *testing.T) { mockServer := tests.MockServer(func(r *http.Request, resp json.RawMessage) (json.RawMessage, error) { if r.URL.Path == genesisPath { @@ -31,24 +34,52 @@ func TestGenesis(t *testing.T) { } }`), nil } + if r.URL.Path == specPath { + return json.RawMessage(`{ + "data": { + "CONFIG_NAME": "mainnet", + "GENESIS_FORK_VERSION": "0x00000000", + "ALTAIR_FORK_VERSION": "0x01000000", + "ALTAIR_FORK_EPOCH": "74240", + "BELLATRIX_FORK_VERSION": "0x02000000", + "BELLATRIX_FORK_EPOCH": "144896", + "CAPELLA_FORK_VERSION": "0x03000000", + "CAPELLA_FORK_EPOCH": "194048", + "DENEB_FORK_VERSION": "0x04000000", + "DENEB_FORK_EPOCH": "269568", + "ELECTRA_FORK_VERSION": "0x05000000", + "ELECTRA_FORK_EPOCH": "364032", + "FULU_FORK_VERSION": "0x06000000", + "FULU_FORK_EPOCH": "18446744073709551615", + "MIN_GENESIS_TIME": "1606824000", + "SECONDS_PER_SLOT": "12", + "SLOTS_PER_EPOCH": "32", + "EPOCHS_PER_SYNC_COMMITTEE_PERIOD": "256", + "SYNC_COMMITTEE_SIZE": "512", + "SYNC_COMMITTEE_SUBNET_COUNT": "4", + "TARGET_AGGREGATORS_PER_COMMITTEE": "16", + "TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE": "16", + "INTERVALS_PER_SLOT": "3" + } + }`), nil + } return resp, nil }) defer mockServer.Close() client, err := New( ctx, - zap.NewNop(), + logger, Options{ - Network: beacon.NewNetwork(types.MainNetwork), + BeaconConfig: networkconfig.TestNetwork.BeaconConfig, BeaconNodeAddr: mockServer.URL, CommonTimeout: 100 * time.Millisecond, LongTimeout: 500 * time.Millisecond, }, - tests.MockSlotTickerProvider, ) require.NoError(t, err) - genesis, err := client.Genesis(ctx) + genesis, err := genesisForClient(ctx, logger, client.multiClient) require.NoError(t, err) require.NotNil(t, genesis) @@ -67,20 +98,17 @@ func TestGenesis(t *testing.T) { client, err := New( ctx, - zap.NewNop(), + logger, Options{ - Network: beacon.NewNetwork(types.MainNetwork), + BeaconConfig: networkconfig.TestNetwork.BeaconConfig, BeaconNodeAddr: mockServer.URL, CommonTimeout: 100 * time.Millisecond, LongTimeout: 500 * time.Millisecond, }, - tests.MockSlotTickerProvider, ) - require.NoError(t, err) - - _, err = client.Genesis(ctx) require.Error(t, err) - require.Contains(t, err.Error(), "genesis response data is nil") + require.Contains(t, err.Error(), "timed out awaiting config initialization") // node cannot initialize if it cannot get genesis + require.Nil(t, client) }) t.Run("error", func(t *testing.T) { @@ -94,19 +122,16 @@ func TestGenesis(t *testing.T) { client, err := New( ctx, - zap.NewNop(), + logger, Options{ - Network: beacon.NewNetwork(types.MainNetwork), + BeaconConfig: networkconfig.TestNetwork.BeaconConfig, BeaconNodeAddr: mockServer.URL, CommonTimeout: 100 * time.Millisecond, LongTimeout: 500 * time.Millisecond, }, - tests.MockSlotTickerProvider, ) - require.NoError(t, err) - - _, err = client.Genesis(ctx) require.Error(t, err) - require.Contains(t, err.Error(), "failed to request genesis") + require.Contains(t, err.Error(), "timed out awaiting config initialization") // node cannot initialize if it cannot get genesis + require.Nil(t, client) }) } diff --git a/beacon/goclient/goclient.go b/beacon/goclient/goclient.go index 3a2b36ee88..5c9166900a 100644 --- a/beacon/goclient/goclient.go +++ b/beacon/goclient/goclient.go @@ -19,14 +19,12 @@ import ( "github.com/jellydator/ttlcache/v3" "github.com/pkg/errors" "github.com/rs/zerolog" - spectypes "github.com/ssvlabs/ssv-spec/types" "go.uber.org/zap" "tailscale.com/util/singleflight" "github.com/ssvlabs/ssv/logging/fields" + "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/operator/slotticker" - beaconprotocol "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" - "github.com/ssvlabs/ssv/utils/casts" ) const ( @@ -121,8 +119,12 @@ const ( // GoClient implementing Beacon struct type GoClient struct { - log *zap.Logger - network beaconprotocol.Network + log *zap.Logger + + beaconConfigMu sync.RWMutex + beaconConfig *networkconfig.BeaconConfig + beaconConfigInit chan struct{} + clients []Client multiClient MultiClient @@ -166,14 +168,6 @@ type GoClient struct { lastProcessedEventSlotLock sync.Mutex lastProcessedEventSlot phase0.Slot - genesisForkVersion phase0.Version - ForkLock sync.RWMutex - ForkEpochElectra phase0.Epoch - ForkEpochDeneb phase0.Epoch - ForkEpochCapella phase0.Epoch - ForkEpochBellatrix phase0.Epoch - ForkEpochAltair phase0.Epoch - // voluntaryExitDomainCached is voluntary exit domain value calculated lazily and re-used // since it doesn't change over time voluntaryExitDomainCached atomic.Pointer[phase0.Domain] @@ -184,9 +178,8 @@ func New( ctx context.Context, logger *zap.Logger, opt Options, - slotTickerProvider slotticker.Provider, ) (*GoClient, error) { - logger.Info("consensus client: connecting", fields.Address(opt.BeaconNodeAddr), fields.Network(string(opt.Network.BeaconNetwork))) + logger.Info("consensus client: connecting", fields.Address(opt.BeaconNodeAddr)) commonTimeout := opt.CommonTimeout if commonTimeout == 0 { @@ -198,18 +191,10 @@ func New( } client := &GoClient{ - log: logger.Named("consensus_client"), - network: opt.Network, - syncDistanceTolerance: phase0.Slot(opt.SyncDistanceTolerance), - registrations: map[phase0.BLSPubKey]*validatorRegistration{}, - attestationDataCache: ttlcache.New( - // we only fetch attestation data during the slot of the relevant duty (and never later), - // hence caching it for 2 slots is sufficient - ttlcache.WithTTL[phase0.Slot, *phase0.AttestationData](2 * opt.Network.SlotDurationSec()), - ), - blockRootToSlotCache: ttlcache.New(ttlcache.WithCapacity[phase0.Root, phase0.Slot]( - opt.Network.SlotsPerEpoch() * BlockRootToSlotCacheCapacityEpochs), - ), + log: logger.Named("consensus_client"), + beaconConfigInit: make(chan struct{}), + syncDistanceTolerance: phase0.Slot(opt.SyncDistanceTolerance), + registrations: map[phase0.BLSPubKey]*validatorRegistration{}, commonTimeout: commonTimeout, longTimeout: longTimeout, withWeightedAttestationData: opt.WithWeightedAttestationData, @@ -217,13 +202,6 @@ func New( weightedAttestationDataSoftTimeout: time.Duration(float64(commonTimeout) / 2.5), weightedAttestationDataHardTimeout: commonTimeout, supportedTopics: []EventTopic{EventTopicHead, EventTopicBlock}, - genesisForkVersion: phase0.Version(opt.Network.ForkVersion()), - // Initialize forks with FAR_FUTURE_EPOCH. - ForkEpochAltair: math.MaxUint64, - ForkEpochBellatrix: math.MaxUint64, - ForkEpochCapella: math.MaxUint64, - ForkEpochDeneb: math.MaxUint64, - ForkEpochElectra: math.MaxUint64, } if opt.BeaconNodeAddr == "" { @@ -249,6 +227,41 @@ func New( client.nodeSyncingFn = client.nodeSyncing + initCtx, initCtxCancel := context.WithTimeout(ctx, client.longTimeout) + defer initCtxCancel() + + select { + case <-initCtx.Done(): + logger.Warn("timeout occurred while waiting for beacon config initialization", + zap.Duration("timeout", client.longTimeout), + zap.Error(initCtx.Err()), + ) + return nil, fmt.Errorf("timed out awaiting config initialization: %w", initCtx.Err()) + case <-client.beaconConfigInit: + } + + config := client.getBeaconConfig() + if config == nil { + return nil, fmt.Errorf("no beacon config set") + } + + client.blockRootToSlotCache = ttlcache.New( + ttlcache.WithCapacity[phase0.Root, phase0.Slot](config.SlotsPerEpoch * BlockRootToSlotCacheCapacityEpochs), + ) + + client.attestationDataCache = ttlcache.New( + // we only fetch attestation data during the slot of the relevant duty (and never later), + // hence caching it for 2 slots is sufficient + ttlcache.WithTTL[phase0.Slot, *phase0.AttestationData](2 * config.SlotDuration), + ) + + slotTickerProvider := func() slotticker.SlotTicker { + return slotticker.New(logger, slotticker.Config{ + SlotDuration: config.SlotDuration, + GenesisTime: config.GenesisTime, + }) + } + go client.registrationSubmitter(ctx, slotTickerProvider) // Start automatic expired item deletion for attestationDataCache. go client.attestationDataCache.Start() @@ -261,6 +274,13 @@ func New( return client, nil } +// getBeaconConfig provides thread-safe access to the beacon configuration +func (gc *GoClient) getBeaconConfig() *networkconfig.BeaconConfig { + gc.beaconConfigMu.RLock() + defer gc.beaconConfigMu.RUnlock() + return gc.beaconConfig +} + func (gc *GoClient) initMultiClient(ctx context.Context) error { var services []eth2client.Service for _, client := range gc.clients { @@ -310,72 +330,51 @@ func (gc *GoClient) addSingleClient(ctx context.Context, addr string) error { func (gc *GoClient) singleClientHooks() *eth2clienthttp.Hooks { return ð2clienthttp.Hooks{ OnActive: func(ctx context.Context, s *eth2clienthttp.Service) { + logger := gc.log.With( + fields.Name(s.Name()), + fields.Address(s.Address()), + ) // If err is nil, nodeVersionResp is never nil. nodeVersionResp, err := s.NodeVersion(ctx, &api.NodeVersionOpts{}) if err != nil { - gc.log.Error(clResponseErrMsg, - zap.String("address", s.Address()), + logger.Error(clResponseErrMsg, zap.String("api", "NodeVersion"), zap.Error(err), ) return } - gc.log.Info("consensus client connected", - fields.Name(s.Name()), - fields.Address(s.Address()), + logger.Info("consensus client connected", zap.String("client", string(ParseNodeClient(nodeVersionResp.Data))), zap.String("version", nodeVersionResp.Data), ) - genesis, err := genesisForClient(ctx, gc.log, s) + beaconConfig, err := gc.fetchBeaconConfig(ctx, s) if err != nil { - gc.log.Error(clResponseErrMsg, - zap.String("address", s.Address()), - zap.String("api", "Genesis"), + logger.Error(clResponseErrMsg, + zap.String("api", "fetchBeaconConfig"), zap.Error(err), ) return } - if expected, err := gc.assertSameGenesisVersion(genesis.GenesisForkVersion); err != nil { - gc.log.Fatal("client returned unexpected genesis fork version, make sure all clients use the same Ethereum network", - zap.String("address", s.Address()), - zap.Any("client_genesis", genesis.GenesisForkVersion), - zap.Any("expected_genesis", expected), - zap.Error(err), - ) - return // Tests may override Fatal's behavior - } - - spec, err := specImpl(ctx, gc.log, s) + currentConfig, err := gc.applyBeaconConfig(s.Address(), beaconConfig) if err != nil { - gc.log.Error(clResponseErrMsg, - zap.String("address", s.Address()), - zap.String("api", "Spec"), + logger.Fatal("client returned unexpected beacon config, make sure all clients use the same Ethereum network", zap.Error(err), + zap.Any("current_forks", currentConfig.Forks), + zap.Any("got_forks", beaconConfig.Forks), + zap.Stringer("client_config", beaconConfig), + zap.Stringer("expected_config", currentConfig), ) - return + return // Tests may override Fatal's behavior } - if err := gc.checkForkValues(spec); err != nil { - gc.log.Error("failed to check fork values", - zap.String("address", s.Address()), - zap.Error(err), - ) - return - } - gc.ForkLock.RLock() - gc.log.Info("retrieved fork epochs", - zap.String("node_addr", s.Address()), - zap.Uint64("current_data_version", uint64(gc.DataVersion(gc.network.EstimatedCurrentEpoch()))), - zap.Uint64("altair", uint64(gc.ForkEpochAltair)), - zap.Uint64("bellatrix", uint64(gc.ForkEpochBellatrix)), - zap.Uint64("capella", uint64(gc.ForkEpochCapella)), - zap.Uint64("deneb", uint64(gc.ForkEpochDeneb)), - zap.Uint64("electra", uint64(gc.ForkEpochElectra)), + dataVersion, _ := currentConfig.ForkAtEpoch(currentConfig.EstimatedCurrentEpoch()) + logger.Info("retrieved beacon config", + zap.Uint64("data_version", uint64(dataVersion)), + zap.Stringer("config", currentConfig), ) - gc.ForkLock.RUnlock() }, OnInactive: func(ctx context.Context, s *eth2clienthttp.Service) { gc.log.Warn("consensus client disconnected", @@ -398,18 +397,26 @@ func (gc *GoClient) singleClientHooks() *eth2clienthttp.Hooks { } } -// assertSameGenesis checks if genesis is same. -// Clients may have different values returned by Spec call, -// so we decided that it's best to assert that GenesisForkVersion is the same. -// To add more assertions, we check the whole apiv1.Genesis (GenesisTime and GenesisValidatorsRoot) -// as they should be same too. -func (gc *GoClient) assertSameGenesisVersion(genesisVersion phase0.Version) (phase0.Version, error) { - if gc.genesisForkVersion != genesisVersion { - fmt.Printf("genesis fork version mismatch, expected %v, got %v", gc.genesisForkVersion, genesisVersion) - return gc.genesisForkVersion, fmt.Errorf("genesis fork version mismatch, expected %v, got %v", gc.genesisForkVersion, genesisVersion) +func (gc *GoClient) applyBeaconConfig(nodeAddress string, beaconConfig networkconfig.BeaconConfig) (networkconfig.BeaconConfig, error) { + gc.beaconConfigMu.Lock() + defer gc.beaconConfigMu.Unlock() + + if gc.beaconConfig == nil { + gc.beaconConfig = &beaconConfig + close(gc.beaconConfigInit) + + gc.log.Info("beacon config has been initialized", + zap.Stringer("beacon_config", beaconConfig), + fields.Address(nodeAddress), + ) + return beaconConfig, nil + } + + if err := gc.beaconConfig.AssertSame(beaconConfig); err != nil { + return *gc.beaconConfig, fmt.Errorf("beacon config misalign: %w", err) } - return gc.genesisForkVersion, nil + return *gc.beaconConfig, nil } func (gc *GoClient) nodeSyncing(ctx context.Context, opts *api.NodeSyncingOpts) (*api.Response[*apiv1.SyncState], error) { @@ -462,16 +469,3 @@ func (gc *GoClient) Healthy(ctx context.Context) error { return nil } - -// GetBeaconNetwork returns the beacon network the node is on -func (gc *GoClient) GetBeaconNetwork() spectypes.BeaconNetwork { - return gc.network.BeaconNetwork -} - -// SlotStartTime returns the start time in terms of its unix epoch -// value. -func (gc *GoClient) slotStartTime(slot phase0.Slot) time.Time { - duration := time.Second * casts.DurationFromUint64(uint64(slot)*uint64(gc.network.SlotDurationSec().Seconds())) - startTime := time.Unix(gc.network.MinGenesisTime(), 0).Add(duration) - return startTime -} diff --git a/beacon/goclient/goclient_test.go b/beacon/goclient/goclient_test.go index 1d52c4e703..ab17fb7dfa 100644 --- a/beacon/goclient/goclient_test.go +++ b/beacon/goclient/goclient_test.go @@ -11,7 +11,6 @@ import ( "github.com/attestantio/go-eth2-client/api" v1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec/phase0" - "github.com/ssvlabs/ssv-spec/types" "github.com/stretchr/testify/require" "go.uber.org/zap" @@ -135,6 +134,10 @@ func TestTimeouts(t *testing.T) { fastServer := tests.MockServer(func(r *http.Request, resp json.RawMessage) (json.RawMessage, error) { time.Sleep(commonTimeout / 2) switch r.URL.Path { + case "/eth/v1/config/spec": + case "/eth/v1/beacon/genesis": + case "/eth/v1/node/syncing": + case "/eth/v1/node/version": case "/eth/v2/debug/beacon/states/head": time.Sleep(longTimeout / 2) } @@ -153,70 +156,14 @@ func TestTimeouts(t *testing.T) { } } -func TestAssertSameGenesisVersionWhenSame(t *testing.T) { - networks := []types.BeaconNetwork{types.MainNetwork, types.HoleskyNetwork, types.PraterNetwork, types.BeaconTestNetwork} - - for _, network := range networks { - forkVersion := phase0.Version(beacon.NewNetwork(network).ForkVersion()) - - callback := func(r *http.Request, resp json.RawMessage) (json.RawMessage, error) { - if r.URL.Path == "/eth/v1/beacon/genesis" { - resp2 := json.RawMessage(fmt.Sprintf(`{"data": { - "genesis_time": "1606824023", - "genesis_validators_root": "0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95", - "genesis_fork_version": "%s" - }}`, forkVersion)) - return resp2, nil - } - return resp, nil - } - - server := tests.MockServer(callback) - defer server.Close() - t.Run(fmt.Sprintf("When genesis versions are the same (%s)", string(network)), func(t *testing.T) { - c, err := mockClientWithNetwork(t.Context(), server.URL, 100*time.Millisecond, 500*time.Millisecond, network) - require.NoError(t, err, "failed to create client") - client := c.(*GoClient) - - output, err := client.assertSameGenesisVersion(forkVersion) - require.Equal(t, forkVersion, output) - require.NoError(t, err, "failed to assert same genesis version: %s", err) - }) - } -} - -func TestAssertSameGenesisVersionWhenDifferent(t *testing.T) { - network := types.MainNetwork - networkVersion := phase0.Version(beacon.NewNetwork(network).ForkVersion()) - - t.Run("When genesis versions are different", func(t *testing.T) { - server := tests.MockServer(nil) - defer server.Close() - c, err := mockClientWithNetwork(t.Context(), server.URL, 100*time.Millisecond, 500*time.Millisecond, network) - require.NoError(t, err, "failed to create client") - client := c.(*GoClient) - forkVersion := phase0.Version{0x01, 0x02, 0x03, 0x04} - - output, err := client.assertSameGenesisVersion(forkVersion) - require.Equal(t, networkVersion, output, "expected genesis version to be %s, got %s", networkVersion, output) - require.Error(t, err, "expected error when genesis versions are different") - }) -} - func mockClient(ctx context.Context, serverURL string, commonTimeout, longTimeout time.Duration) (beacon.BeaconNode, error) { - return mockClientWithNetwork(ctx, serverURL, commonTimeout, longTimeout, types.MainNetwork) -} - -func mockClientWithNetwork(ctx context.Context, serverURL string, commonTimeout, longTimeout time.Duration, network types.BeaconNetwork) (beacon.BeaconNode, error) { return New( ctx, zap.NewNop(), Options{ - Network: beacon.NewNetwork(network), BeaconNodeAddr: serverURL, CommonTimeout: commonTimeout, LongTimeout: longTimeout, }, - tests.MockSlotTickerProvider, ) } diff --git a/beacon/goclient/options.go b/beacon/goclient/options.go index 0b9702aa13..300a7e52c8 100644 --- a/beacon/goclient/options.go +++ b/beacon/goclient/options.go @@ -3,12 +3,12 @@ package goclient import ( "time" - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" + "github.com/ssvlabs/ssv/networkconfig" ) // Options defines beacon client options type Options struct { - Network beacon.Network + BeaconConfig networkconfig.BeaconConfig BeaconNodeAddr string `yaml:"BeaconNodeAddr" env:"BEACON_NODE_ADDR" env-required:"true" env-description:"Beacon node URL(s). Multiple nodes are supported via semicolon-separated URLs (e.g. 'http://localhost:5052;http://localhost:5053')"` SyncDistanceTolerance uint64 `yaml:"SyncDistanceTolerance" env:"BEACON_SYNC_DISTANCE_TOLERANCE" env-default:"4" env-description:"Maximum number of slots behind head considered in-sync"` WithWeightedAttestationData bool `yaml:"WithWeightedAttestationData" env:"WITH_WEIGHTED_ATTESTATION_DATA" env-default:"false" env-description:"Enable attestation data scoring across multiple beacon nodes"` diff --git a/beacon/goclient/signing.go b/beacon/goclient/signing.go index ddb263ab75..4cc990caa8 100644 --- a/beacon/goclient/signing.go +++ b/beacon/goclient/signing.go @@ -9,6 +9,7 @@ import ( "sync" "time" + "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/phase0" ssz "github.com/ferranbt/fastssz" "github.com/pkg/errors" @@ -16,13 +17,13 @@ import ( "go.uber.org/zap" ) -func (gc *GoClient) voluntaryExitDomain(ctx context.Context) (phase0.Domain, error) { +func (gc *GoClient) voluntaryExitDomain() (phase0.Domain, error) { value := gc.voluntaryExitDomainCached.Load() if value != nil { return *value, nil } - v, err := gc.computeVoluntaryExitDomain(ctx) + v, err := gc.computeVoluntaryExitDomain() if err != nil { return phase0.Domain{}, fmt.Errorf("compute voluntary exit domain: %w", err) } @@ -30,33 +31,14 @@ func (gc *GoClient) voluntaryExitDomain(ctx context.Context) (phase0.Domain, err return v, nil } -func (gc *GoClient) computeVoluntaryExitDomain(ctx context.Context) (phase0.Domain, error) { - specResponse, err := gc.Spec(ctx) - if err != nil { - return phase0.Domain{}, fmt.Errorf("fetch spec: %w", err) - } - - // EIP-7044 requires using CAPELLA_FORK_VERSION for DomainVoluntaryExit: https://eips.ethereum.org/EIPS/eip-7044 - forkVersionRaw, ok := specResponse["CAPELLA_FORK_VERSION"] - if !ok { - return phase0.Domain{}, fmt.Errorf("capella fork version not known by chain") - } - forkVersion, ok := forkVersionRaw.(phase0.Version) - if !ok { - return phase0.Domain{}, fmt.Errorf("failed to decode capella fork version") - } +func (gc *GoClient) computeVoluntaryExitDomain() (phase0.Domain, error) { + beaconConfig := gc.getBeaconConfig() forkData := &phase0.ForkData{ - CurrentVersion: forkVersion, + CurrentVersion: beaconConfig.Forks[spec.DataVersionCapella].CurrentVersion, + GenesisValidatorsRoot: beaconConfig.GenesisValidatorsRoot, } - genesis, err := gc.Genesis(ctx) - if err != nil { - return phase0.Domain{}, fmt.Errorf("failed to obtain genesis response: %w", err) - } - - forkData.GenesisValidatorsRoot = genesis.GenesisValidatorsRoot - root, err := forkData.HashTreeRoot() if err != nil { return phase0.Domain{}, fmt.Errorf("failed to calculate signature domain, err: %w", err) @@ -80,7 +62,7 @@ func (gc *GoClient) DomainData( // to (Mainnet, Hoodi, etc.) var appDomain phase0.Domain forkData := phase0.ForkData{ - CurrentVersion: gc.network.ForkVersion(), + CurrentVersion: gc.getBeaconConfig().GenesisForkVersion, GenesisValidatorsRoot: phase0.Root{}, } root, err := forkData.HashTreeRoot() @@ -93,7 +75,7 @@ func (gc *GoClient) DomainData( case spectypes.DomainVoluntaryExit: // Deneb upgrade introduced https://eips.ethereum.org/EIPS/eip-7044 that requires special // handling for DomainVoluntaryExit - return gc.voluntaryExitDomain(ctx) + return gc.voluntaryExitDomain() } start := time.Now() diff --git a/beacon/goclient/signing_test.go b/beacon/goclient/signing_test.go index 9dd487f007..dc406fef8b 100644 --- a/beacon/goclient/signing_test.go +++ b/beacon/goclient/signing_test.go @@ -11,7 +11,7 @@ import ( "go.uber.org/zap" "github.com/ssvlabs/ssv/beacon/goclient/tests" - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" + "github.com/ssvlabs/ssv/networkconfig" ) func Test_computeVoluntaryExitDomain(t *testing.T) { @@ -25,16 +25,15 @@ func Test_computeVoluntaryExitDomain(t *testing.T) { ctx, zap.NewNop(), Options{ - Network: beacon.NewNetwork(spectypes.MainNetwork), + BeaconConfig: networkconfig.TestNetwork.BeaconConfig, BeaconNodeAddr: mockServer.URL, CommonTimeout: 100 * time.Millisecond, LongTimeout: 500 * time.Millisecond, }, - tests.MockSlotTickerProvider, ) require.NoError(t, err) - domain, err := client.computeVoluntaryExitDomain(ctx) + domain, err := client.computeVoluntaryExitDomain() require.NoError(t, err) require.NotNil(t, domain) diff --git a/beacon/goclient/spec.go b/beacon/goclient/spec.go index 8b3660d8f7..5a5e09e0e6 100644 --- a/beacon/goclient/spec.go +++ b/beacon/goclient/spec.go @@ -8,15 +8,34 @@ import ( client "github.com/attestantio/go-eth2-client" "github.com/attestantio/go-eth2-client/api" + eth2clienthttp "github.com/attestantio/go-eth2-client/http" + "github.com/attestantio/go-eth2-client/spec" + "github.com/attestantio/go-eth2-client/spec/phase0" "go.uber.org/zap" + + "github.com/ssvlabs/ssv/networkconfig" +) + +const ( + DefaultSlotDuration = 12 * time.Second + DefaultSlotsPerEpoch = uint64(32) + DefaultEpochsPerSyncCommitteePeriod = uint64(256) + DefaultSyncCommitteeSize = uint64(512) + DefaultSyncCommitteeSubnetCount = uint64(4) + DefaultTargetAggregatorsPerSyncSubcommittee = uint64(16) + DefaultTargetAggregatorsPerCommittee = uint64(16) + DefaultIntervalsPerSlot = uint64(3) ) -func (gc *GoClient) Spec(ctx context.Context) (map[string]any, error) { - return specImpl(ctx, gc.log, gc.multiClient) +// BeaconConfig returns the network Beacon configuration +func (gc *GoClient) BeaconConfig() networkconfig.BeaconConfig { + // BeaconConfig must be called if GoClient is initialized (gc.beaconConfig is set) + // It fails otherwise. + config := gc.getBeaconConfig() + return *config } -// It's used in both Spec and singleClientHooks, so we need some common implementation to avoid code repetition. -func specImpl(ctx context.Context, log *zap.Logger, provider client.Service) (map[string]any, error) { +func specForClient(ctx context.Context, log *zap.Logger, provider client.Service) (map[string]any, error) { start := time.Now() specResponse, err := provider.(client.SpecProvider).Spec(ctx, &api.SpecOpts{}) recordRequestDuration(ctx, "Spec", provider.Address(), http.MethodGet, time.Since(start), err) @@ -42,3 +61,261 @@ func specImpl(ctx context.Context, log *zap.Logger, provider client.Service) (ma return specResponse.Data, nil } + +// fetchBeaconConfig must be called once on GoClient's initialization +func (gc *GoClient) fetchBeaconConfig(ctx context.Context, client *eth2clienthttp.Service) (networkconfig.BeaconConfig, error) { + specResponse, err := specForClient(ctx, gc.log, client) + if err != nil { + gc.log.Error(clResponseErrMsg, zap.String("api", "Spec"), zap.Error(err)) + return networkconfig.BeaconConfig{}, fmt.Errorf("failed to obtain spec response: %w", err) + } + + // types of most values are already cast: https://github.com/attestantio/go-eth2-client/blob/v0.21.7/http/spec.go#L78 + + networkName, err := get[string](specResponse, "CONFIG_NAME") + if err != nil { + return networkconfig.BeaconConfig{}, fmt.Errorf("get CONFIG_NAME: %w", err) + } + + slotDuration, err := get[time.Duration](specResponse, "SECONDS_PER_SLOT") + if err != nil { + gc.log.Warn("could not get extract parameter from beacon node response, using default value", + zap.Error(err), + zap.String("parameter", "SECONDS_PER_SLOT"), + zap.Any("default_value", DefaultSlotDuration)) + + slotDuration = DefaultSlotDuration + } + + slotsPerEpoch, err := get[uint64](specResponse, "SLOTS_PER_EPOCH") + if err != nil { + gc.log.Warn("could not get extract parameter from beacon node response, using default value", + zap.Error(err), + zap.String("parameter", "SLOTS_PER_EPOCH"), + zap.Any("default_value", DefaultSlotsPerEpoch)) + + slotsPerEpoch = DefaultSlotsPerEpoch + } + + epochsPerSyncCommitteePeriod, err := get[uint64](specResponse, "EPOCHS_PER_SYNC_COMMITTEE_PERIOD") + if err != nil { + gc.log.Warn("could not get extract parameter from beacon node response, using default value", + zap.Error(err), + zap.String("parameter", "EPOCHS_PER_SYNC_COMMITTEE_PERIOD"), + zap.Any("default_value", DefaultEpochsPerSyncCommitteePeriod)) + + epochsPerSyncCommitteePeriod = DefaultEpochsPerSyncCommitteePeriod + } + + syncCommitteeSize, err := get[uint64](specResponse, "SYNC_COMMITTEE_SIZE") + if err != nil { + gc.log.Warn("could not get extract parameter from beacon node response, using default value", + zap.Error(err), + zap.String("parameter", "SYNC_COMMITTEE_SIZE"), + zap.Any("default_value", DefaultSyncCommitteeSize)) + + syncCommitteeSize = DefaultSyncCommitteeSize + } + + targetAggregatorsPerCommittee, err := get[uint64](specResponse, "TARGET_AGGREGATORS_PER_COMMITTEE") + if err != nil { + gc.log.Warn("could not get extract parameter from beacon node response, using default value", + zap.Error(err), + zap.String("parameter", "TARGET_AGGREGATORS_PER_COMMITTEE"), + zap.Any("default_value", DefaultTargetAggregatorsPerCommittee)) + + targetAggregatorsPerCommittee = DefaultTargetAggregatorsPerCommittee + } + + targetAggregatorsPerSyncSubcommittee, err := get[uint64](specResponse, "TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE") + if err != nil { + gc.log.Warn("could not get extract parameter from beacon node response, using default value", + zap.Error(err), + zap.String("parameter", "TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE"), + zap.Any("default_value", DefaultTargetAggregatorsPerSyncSubcommittee)) + + targetAggregatorsPerSyncSubcommittee = DefaultTargetAggregatorsPerSyncSubcommittee + } + + intervalsPerSlot, err := get[uint64](specResponse, "INTERVALS_PER_SLOT") + if err != nil { + gc.log.Warn("could not get extract parameter from beacon node response, using default value", + zap.Error(err), + zap.String("parameter", "INTERVALS_PER_SLOT"), + zap.Any("default_value", DefaultIntervalsPerSlot)) + + intervalsPerSlot = DefaultIntervalsPerSlot + } + + syncCommitteeSubnetCount, err := get[uint64](specResponse, "SYNC_COMMITTEE_SUBNET_COUNT") + if err != nil { + gc.log.Warn("could not get extract parameter from beacon node response, using default value", + zap.Error(err), + zap.String("parameter", "SYNC_COMMITTEE_SUBNET_COUNT"), + zap.Any("default_value", DefaultSyncCommitteeSubnetCount)) + + syncCommitteeSubnetCount = DefaultSyncCommitteeSubnetCount + } + + forkData, err := gc.getForkData(specResponse) + if err != nil { + gc.log.Error(clResponseErrMsg, zap.String("api", "Spec"), zap.Error(err)) + return networkconfig.BeaconConfig{}, fmt.Errorf("failed to extract fork data: %w", err) + } + + genesisResponse, err := genesisForClient(ctx, gc.log, client) + if err != nil { + gc.log.Error(clResponseErrMsg, zap.String("api", "Genesis"), zap.Error(err)) + return networkconfig.BeaconConfig{}, fmt.Errorf("failed to obtain genesis response: %w", err) + } + + beaconConfig := networkconfig.BeaconConfig{ + NetworkName: networkName, + SlotDuration: slotDuration, + SlotsPerEpoch: slotsPerEpoch, + EpochsPerSyncCommitteePeriod: epochsPerSyncCommitteePeriod, + SyncCommitteeSize: syncCommitteeSize, + SyncCommitteeSubnetCount: syncCommitteeSubnetCount, + TargetAggregatorsPerSyncSubcommittee: targetAggregatorsPerSyncSubcommittee, + TargetAggregatorsPerCommittee: targetAggregatorsPerCommittee, + IntervalsPerSlot: intervalsPerSlot, + GenesisForkVersion: genesisResponse.GenesisForkVersion, + GenesisTime: genesisResponse.GenesisTime, + GenesisValidatorsRoot: genesisResponse.GenesisValidatorsRoot, + Forks: forkData, + } + + return beaconConfig, nil +} + +func (gc *GoClient) getForkData(specResponse map[string]any) (map[spec.DataVersion]phase0.Fork, error) { + if specResponse == nil { + return nil, fmt.Errorf("spec response is nil") + } + + getForkEpoch := func(key string, required bool) (phase0.Epoch, error) { + raw, ok := specResponse[key] + if !ok { + if required { + return 0, fmt.Errorf("%s is not known by chain", key) + } + return FarFutureEpoch, nil + } + forkVal, ok := raw.(uint64) + if !ok { + return 0, fmt.Errorf("failed to decode %s", key) + } + return phase0.Epoch(forkVal), nil + } + + getForkVersion := func(key string) (phase0.Version, error) { + raw, ok := specResponse[key] + if !ok { + return phase0.Version{}, fmt.Errorf("%s is not known by chain", key) + } + versionVal, ok := raw.(phase0.Version) + if !ok { + return phase0.Version{}, fmt.Errorf("failed to decode %s", key) + } + return versionVal, nil + } + + genesisForkVersion, err := getForkVersion("GENESIS_FORK_VERSION") + if err != nil { + return nil, err + } + altairEpoch, err := getForkEpoch("ALTAIR_FORK_EPOCH", true) + if err != nil { + return nil, err + } + altairForkVersion, err := getForkVersion("ALTAIR_FORK_VERSION") + if err != nil { + return nil, err + } + bellatrixEpoch, err := getForkEpoch("BELLATRIX_FORK_EPOCH", true) + if err != nil { + return nil, err + } + bellatrixForkVersion, err := getForkVersion("BELLATRIX_FORK_VERSION") + if err != nil { + return nil, err + } + capellaEpoch, err := getForkEpoch("CAPELLA_FORK_EPOCH", true) + if err != nil { + return nil, err + } + capellaForkVersion, err := getForkVersion("CAPELLA_FORK_VERSION") + if err != nil { + return nil, err + } + denebEpoch, err := getForkEpoch("DENEB_FORK_EPOCH", true) + if err != nil { + return nil, err + } + denebForkVersion, err := getForkVersion("DENEB_FORK_VERSION") + if err != nil { + return nil, err + } + // After Electra fork happens on all networks, + // - Electra check should become required + // - Fulu check might be added as non-required + electraEpoch, err := getForkEpoch("ELECTRA_FORK_EPOCH", false) + if err != nil { + return nil, err + } + electraForkVersion, err := getForkVersion("ELECTRA_FORK_VERSION") + if err != nil { + return nil, err + } + + forkEpochs := map[spec.DataVersion]phase0.Fork{ + spec.DataVersionPhase0: { + PreviousVersion: genesisForkVersion, + CurrentVersion: genesisForkVersion, + Epoch: 0, + }, + spec.DataVersionAltair: { + PreviousVersion: genesisForkVersion, + CurrentVersion: altairForkVersion, + Epoch: altairEpoch, + }, + spec.DataVersionBellatrix: { + PreviousVersion: altairForkVersion, + CurrentVersion: bellatrixForkVersion, + Epoch: bellatrixEpoch, + }, + spec.DataVersionCapella: { + PreviousVersion: bellatrixForkVersion, + CurrentVersion: capellaForkVersion, + Epoch: capellaEpoch, + }, + spec.DataVersionDeneb: { + PreviousVersion: capellaForkVersion, + CurrentVersion: denebForkVersion, + Epoch: denebEpoch, + }, + spec.DataVersionElectra: { + PreviousVersion: denebForkVersion, + CurrentVersion: electraForkVersion, + Epoch: electraEpoch, + }, + } + + return forkEpochs, nil +} + +func get[T any](response map[string]any, key string) (T, error) { + var zero T + + val, ok := response[key] + if !ok { + return zero, fmt.Errorf("missing key '%s' in response", key) + } + + switch t := val.(type) { + case T: + return t, nil + default: + return zero, fmt.Errorf("key %s of type '%T' cannot be converted to '%T'", key, val, zero) + } +} diff --git a/beacon/goclient/spec_test.go b/beacon/goclient/spec_test.go index f1fdda9f9a..0c99cea966 100644 --- a/beacon/goclient/spec_test.go +++ b/beacon/goclient/spec_test.go @@ -7,13 +7,11 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap" - "github.com/ssvlabs/ssv-spec/types" - "github.com/ssvlabs/ssv/beacon/goclient/tests" - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" + "github.com/ssvlabs/ssv/networkconfig" ) -func TestSpec(t *testing.T) { +func Test_specForClient(t *testing.T) { ctx := t.Context() t.Run("success", func(t *testing.T) { @@ -24,16 +22,15 @@ func TestSpec(t *testing.T) { ctx, zap.NewNop(), Options{ - Network: beacon.NewNetwork(types.MainNetwork), + BeaconConfig: networkconfig.TestNetwork.BeaconConfig, BeaconNodeAddr: mockServer.URL, CommonTimeout: 100 * time.Millisecond, LongTimeout: 500 * time.Millisecond, }, - tests.MockSlotTickerProvider, ) require.NoError(t, err) - spec, err := client.Spec(ctx) + spec, err := specForClient(ctx, client.log, client.multiClient) require.NoError(t, err) require.NotNil(t, spec) diff --git a/beacon/goclient/sync_committee_contribution.go b/beacon/goclient/sync_committee_contribution.go index dacc9b6b29..0a50bfcaf5 100644 --- a/beacon/goclient/sync_committee_contribution.go +++ b/beacon/goclient/sync_committee_contribution.go @@ -24,7 +24,10 @@ func (gc *GoClient) IsSyncCommitteeAggregator(proof []byte) bool { hash := sha256.Sum256(proof) // Keep the signature if it's an aggregator. - modulo := SyncCommitteeSize / SyncCommitteeSubnetCount / TargetAggregatorsPerSyncSubcommittee + cfg := gc.BeaconConfig() + + // as per spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/validator.md#aggregation-selection + modulo := cfg.SyncCommitteeSize / cfg.SyncCommitteeSubnetCount / cfg.TargetAggregatorsPerSyncSubcommittee if modulo == uint64(0) { // Modulo must be at least 1. modulo = 1 @@ -34,7 +37,7 @@ func (gc *GoClient) IsSyncCommitteeAggregator(proof []byte) bool { // SyncCommitteeSubnetID returns sync committee subnet ID from subcommittee index func (gc *GoClient) SyncCommitteeSubnetID(index phase0.CommitteeIndex) uint64 { - return uint64(index) / (SyncCommitteeSize / SyncCommitteeSubnetCount) + return uint64(index) / (gc.BeaconConfig().SyncCommitteeSize / gc.BeaconConfig().SyncCommitteeSubnetCount) } // GetSyncCommitteeContribution returns @@ -153,8 +156,9 @@ func (gc *GoClient) SubmitSignedContributionAndProof( // waitForOneThirdSlotDuration waits until one-third of the slot has transpired (SECONDS_PER_SLOT / 3 seconds after slot start time) func (gc *GoClient) waitForOneThirdSlotDuration(slot phase0.Slot) { - delay := gc.network.SlotDurationSec() / 3 /* a third of the slot duration */ - finalTime := gc.slotStartTime(slot).Add(delay) + config := gc.getBeaconConfig() + delay := config.IntervalDuration() + finalTime := config.GetSlotStartTime(slot).Add(delay) wait := time.Until(finalTime) if wait <= 0 { return diff --git a/beacon/goclient/types.go b/beacon/goclient/types.go index 9589c2a87c..28a4325674 100644 --- a/beacon/goclient/types.go +++ b/beacon/goclient/types.go @@ -7,12 +7,6 @@ import ( ) var ( - SyncCommitteeSize uint64 = 512 - SyncCommitteeSubnetCount uint64 = 4 - TargetAggregatorsPerSyncSubcommittee uint64 = 16 - EpochsPerSyncCommitteePeriod uint64 = 256 - TargetAggregatorsPerCommittee uint64 = 16 // FarFutureEpoch is the null representation of an epoch. - FarFutureEpoch phase0.Epoch = math.MaxUint64 - IntervalsPerSlot uint64 = 3 + FarFutureEpoch phase0.Epoch = math.MaxUint64 ) diff --git a/beacon/goclient/validator.go b/beacon/goclient/validator.go index 1fe40facc7..e16dce98b8 100644 --- a/beacon/goclient/validator.go +++ b/beacon/goclient/validator.go @@ -103,9 +103,10 @@ func (gc *GoClient) registrationSubmitter(ctx context.Context, slotTickerProvide } // Distribute the registrations evenly across the epoch based on the pubkeys. - slotInEpoch := uint64(currentSlot) % gc.network.SlotsPerEpoch() + config := gc.getBeaconConfig() + slotInEpoch := uint64(currentSlot) % config.SlotsPerEpoch validatorDescriptor := xxhash.Sum64(validatorPk[:]) - shouldSubmit := validatorDescriptor%gc.network.SlotsPerEpoch() == slotInEpoch + shouldSubmit := validatorDescriptor%config.SlotsPerEpoch == slotInEpoch if r.new || shouldSubmit { r.new = false diff --git a/cli/GENERATE_CONFIG.md b/cli/GENERATE_CONFIG.md new file mode 100644 index 0000000000..470b63d28a --- /dev/null +++ b/cli/GENERATE_CONFIG.md @@ -0,0 +1,160 @@ +# SSV Config Generator + +**SSV Config Generator** is a command-line tool designed to generate configuration file for SSV Node. It streamlines the setup process by allowing users to specify various parameters through flags, which are then compiled into a YAML configuration file. This tool ensures that the SSV network setup is consistent, customizable, and easy to manage. + +## Table of Contents + +- [Usage](#usage) + - [Flags](#flags) +- [Examples](#examples) +- [Configuration](#configuration) + +## Usage + +The `generate-config` command allows you to generate a YAML configuration file by specifying various parameters through command-line flags. + +### Syntax + +```bash + ssvnode generate-config [flags] +``` + +### Flags + +| Flag | Type | Default | Description | +|--------------------------------------|--------|-----------------------------------|----------------------------------------------------------------------------| +| `--output-path` | string | `./config/config.local.yaml` | Output path for the generated configuration file. | +| `--log-level` | string | `info` | Sets the logging level (e.g., `debug`, `info`, `warn`, `error`). | +| `--db-path` | string | `./data/db` | Path to the database directory. | +| `--discovery` | string | `mdns` | Discovery method. | +| `--consensus-client` | string | _Mandatory_ | Address of the consensus client (e.g., `http://localhost:9000`). | +| `--execution-client` | string | _Mandatory_ | Address of the execution client (e.g., `http://localhost:8545`). | +| `--operator-private-key` | string | | Secret key for the operator. | +| `--metrics-api-port` | int | `0` | Port number for the Metrics API (set to `0` to disable). | +| `--ssv-domain` | string | Derived from local testnet config | Hex-encoded domain type (prefixed with `0x`). | +| `--ssv-registry-sync-offset` | uint64 | Derived from local testnet config | Registry sync offset for the network. | +| `--ssv-registry-contract-addr` | string | Derived from local testnet config | Ethereum address of the network registry contract (e.g., `0xYourAddress`). | +| `--ssv-bootnodes` | string | Derived from local testnet config | Comma-separated list of network bootnodes. | +| `--ssv-discovery-protocol-id` | string | Derived from local testnet config | Hex-encoded discovery protocol ID (prefixed with `0x`). | +| `--ssv-alan-fork-epoch` | uint64 | Derived from local testnet config | Epoch at which the Alan fork occurs in the network. | +| `--ssv-max-validators-per-committee` | int | Derived from local testnet config | Max validators per committee. | + + +**Note:** The `--consensus-client` and `--execution-client` flags are mandatory and must be provided when running the CLI. + +## Examples + +### Basic Configuration + +Generate a configuration file with default settings, specifying only the mandatory flags: + +```bash +ssvnode generate-config \ + --consensus-client "http://localhost:9000" \ + --execution-client "http://localhost:8545" +``` + +This command generates a `config.local.yaml` file in the `./config` directory with default settings for all other parameters. + +### Custom Output Path and Log Level + +Generate a configuration file with a custom output path and set the log level to `debug`: + +```bash +ssvnode generate-config \ + --consensus-client "http://consensus.example.com:9000" \ + --execution-client "http://execution.example.com:8545" \ + --output-path "/etc/ssv/config.yaml" \ + --log-level "debug" +``` + +### Specify Operator Private Key and Enable Metrics API + +Generate a configuration with an operator's private key and enable the Metrics API on port `8080`: + +```bash +ssvnode generate-config \ + --consensus-client "http://consensus.example.com:9000" \ + --execution-client "http://execution.example.com:8545" \ + --operator-private-key "your-operator-private-key" \ + --metrics-api-port 8080 +``` + +### Advanced Network Configuration + +Customize network settings such as bootnodes, discovery protocol ID, fork epoch, etc: + +```bash +ssvnode generate-config \ + --consensus-client "http://consensus.example.com:9000" \ + --execution-client "http://execution.example.com:8545" \ + --ssv-domain "0x12345678" \ + --ssv-registry-sync-offset 50 \ + --ssv-registry-contract-addr "0xYourRegistryContractAddress" \ + --ssv-bootnodes "enode://bootnode1@127.0.0.1:30303,enode://bootnode2@127.0.0.1:30304" \ + --ssv-discovery-protocol-id "0x1234567890ab" \ + --ssv-max-validators-per-committee 560 +``` + +## Configuration + +The generated YAML configuration file (`config.local.yaml` by default) follows the structure defined by the `Config` struct. Below is an overview of each section and its corresponding fields: + +### YAML Structure + +```yaml +global: + LogLevel: info +db: + Path: ./data/db +eth2: + BeaconNodeAddr: http://localhost:9000 +eth1: + ETH1Addr: http://localhost:8545 +p2p: + Discovery: mdns +ssv: + Network: LocalTestnetSSV + CustomNetwork: + DomainType: "0x12345678" + RegistrySyncOffset: 0 + RegistryContractAddr: "0xYourRegistryContractAddress" + Bootnodes: + - "enode://bootnode1@127.0.0.1:30303" + - "enode://bootnode2@127.0.0.1:30304" + DiscoveryProtocolID: "0x1234567890ab" +OperatorPrivateKey: your-operator-private-key +MetricsAPIPort: 8080 +``` + +### Sections + +- **global** + - `LogLevel`: Specifies the logging level (e.g., `debug`, `info`, `warn`, `error`). + +- **db** + - `Path`: Path to the database directory. + +- **eth2** + - `BeaconNodeAddr`: Address of the consensus client (Beacon Node). + +- **eth1** + - `ETH1Addr`: Address of the execution client (ETH1 Node). + +- **p2p** + - `Discovery`: Peer-to-peer discovery method (e.g., `mdns`). + +- **ssv** + - `Network`: Name of the network. + - `CustomNetwork`: Contains custom network parameters. + - `DomainType`: Hex-encoded domain type (prefixed with `0x`). + - `RegistrySyncOffset`: Registry sync offset for the network. + - `RegistryContractAddr`: Ethereum address of the network registry contract. + - `Bootnodes`: List of network bootnodes. + - `DiscoveryProtocolID`: Hex-encoded discovery protocol ID (prefixed with `0x`). + +- **OperatorPrivateKey** + - `OperatorPrivateKey`: Secret key for the operator. + +- **MetricsAPIPort** + - `MetricsAPIPort`: Port number for the Metrics API. \ No newline at end of file diff --git a/cli/bootnode/boot_node.go b/cli/bootnode/boot_node.go index 2b1a564ef3..475ad0b4b7 100644 --- a/cli/bootnode/boot_node.go +++ b/cli/bootnode/boot_node.go @@ -54,7 +54,7 @@ var StartBootNodeCmd = &cobra.Command{ logger.Info(fmt.Sprintf("starting %v", commons.GetBuildData())) - networkConfig, err := networkconfig.GetNetworkConfigByName(cfg.Options.Network) + networkConfig, err := networkconfig.GetSSVConfigByName(cfg.Options.Network) if err != nil { logger.Fatal("failed to get network config", zap.Error(err)) } diff --git a/cli/flags/export_keys_from_mnemonic.go b/cli/flags/export_keys_from_mnemonic.go index 153ddd12f6..b2ef32c3f9 100644 --- a/cli/flags/export_keys_from_mnemonic.go +++ b/cli/flags/export_keys_from_mnemonic.go @@ -36,7 +36,7 @@ func GetKeyIndexFlagValue(c *cobra.Command) (uint64, error) { // AddNetworkFlag adds the network key flag to the command func AddNetworkFlag(c *cobra.Command) { - cliflag.AddPersistentStringFlag(c, networkFlag, string(networkconfig.Mainnet.Beacon.GetBeaconNetwork()), "network", false) + cliflag.AddPersistentStringFlag(c, networkFlag, networkconfig.MainnetName, "network", false) } // GetNetworkFlag gets the network key flag from the command diff --git a/cli/generate_config.go b/cli/generate_config.go new file mode 100644 index 0000000000..32fb2a9ab7 --- /dev/null +++ b/cli/generate_config.go @@ -0,0 +1,149 @@ +package cli + +import ( + "encoding/hex" + "fmt" + "log" + "math/big" + "os" + "strings" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/spf13/cobra" + spectypes "github.com/ssvlabs/ssv-spec/types" + "gopkg.in/yaml.v3" + + "github.com/ssvlabs/ssv/networkconfig" +) + +const ( + defaultOutputPath = "./config/config.local.yaml" + defaultLogLevel = "info" + defaultDBPath = "./data/db" + defaultDiscovery = "mdns" + sliceSeparator = "," + configFilePermissions = 0644 +) + +var ( + defaultNetwork = networkconfig.LocalTestnetSSV +) + +var ( + outputPath string + logLevel string + dbPath string + discovery string + consensusClient string + executionClient string + operatorPrivateKey string + metricsAPIPort int + ssvDomain string + ssvRegistrySyncOffset uint64 + ssvRegistryContractAddr string + ssvBootnodes string + ssvDiscoveryProtocolID string +) + +type SSVConfig struct { + Global struct { + LogLevel string `yaml:"LogLevel,omitempty"` + } `yaml:"global,omitempty"` + DB struct { + Path string `yaml:"Path,omitempty"` + } `yaml:"db,omitempty"` + ConsensusClient struct { + Address string `yaml:"BeaconNodeAddr,omitempty"` + } `yaml:"eth2,omitempty"` + ExecutionClient struct { + Address string `yaml:"ETH1Addr,omitempty"` + } `yaml:"eth1,omitempty"` + P2P struct { + Discovery string `yaml:"Discovery,omitempty"` + } `yaml:"p2p,omitempty"` + SSV struct { + NetworkName string `yaml:"Network,omitempty" env:"NETWORK" env-description:"Network is the network of this node,omitempty"` + CustomNetwork *networkconfig.SSVConfig `yaml:"CustomNetwork,omitempty" env:"CUSTOM_NETWORK" env-description:"Custom network parameters,omitempty"` + } `yaml:"ssv,omitempty"` + OperatorPrivateKey string `yaml:"OperatorPrivateKey,omitempty"` + MetricsAPIPort int `yaml:"MetricsAPIPort,omitempty"` +} + +// generateConfigCmd is the command to generate ssv operator config. +var generateConfigCmd = &cobra.Command{ + Use: "generate-config", + Short: "generates ssv operator config", + Run: func(cmd *cobra.Command, args []string) { + parsedDomain, err := hex.DecodeString(strings.TrimPrefix(ssvDomain, "0x")) + if err != nil { + log.Fatalf("Failed to decode network domain: %v", err) + } + + parsedDiscoveryProtocolID, err := hex.DecodeString(strings.TrimPrefix(ssvDiscoveryProtocolID, "0x")) + if err != nil { + log.Fatalf("Failed to decode discovery protocol ID: %v", err) + } + + var parsedDiscoveryProtocolIDArr [6]byte + if len(parsedDiscoveryProtocolID) != 0 { + parsedDiscoveryProtocolIDArr = [6]byte(parsedDiscoveryProtocolID) + } + + var bootnodes []string + if ssvBootnodes != "" { + bootnodes = strings.Split(ssvBootnodes, sliceSeparator) + } + + var config SSVConfig + config.Global.LogLevel = logLevel + config.DB.Path = dbPath + config.ConsensusClient.Address = consensusClient + config.ExecutionClient.Address = executionClient + config.P2P.Discovery = discovery + config.OperatorPrivateKey = operatorPrivateKey + config.MetricsAPIPort = metricsAPIPort + config.SSV.CustomNetwork = &networkconfig.SSVConfig{ + DomainType: spectypes.DomainType(parsedDomain), + RegistrySyncOffset: new(big.Int).SetUint64(ssvRegistrySyncOffset), + RegistryContractAddr: ethcommon.HexToAddress(ssvRegistryContractAddr), + Bootnodes: bootnodes, + DiscoveryProtocolID: parsedDiscoveryProtocolIDArr, + } + + data, err := yaml.Marshal(&config) + if err != nil { + log.Fatalf("Failed to marshal YAML: %v", err) + } + + err = os.WriteFile(outputPath, data, configFilePermissions) + if err != nil { + log.Fatalf("Failed to write file: %v", err) + } + + log.Printf("Saved config into '%s':", outputPath) + fmt.Println(string(data)) + }, +} + +func init() { + generateConfigCmd.Flags().StringVarP(&outputPath, "output-path", "o", defaultOutputPath, "Output path for generated config") + generateConfigCmd.Flags().StringVar(&logLevel, "log-level", defaultLogLevel, "Log level") + generateConfigCmd.Flags().StringVar(&dbPath, "db-path", defaultDBPath, "DB path") + generateConfigCmd.Flags().StringVar(&discovery, "discovery", defaultDiscovery, "Discovery") + generateConfigCmd.Flags().StringVar(&consensusClient, "consensus-client", "", "Consensus client (required)") + _ = generateConfigCmd.MarkFlagRequired("consensus-client") + generateConfigCmd.Flags().StringVar(&executionClient, "execution-client", "", "Execution client (required)") + _ = generateConfigCmd.MarkFlagRequired("execution-client") + generateConfigCmd.Flags().StringVar(&operatorPrivateKey, "operator-private-key", "", "Secret key") + generateConfigCmd.Flags().IntVar(&metricsAPIPort, "metrics-api-port", 0, "Metrics API port") + + ssvDomainDefault := "0x" + hex.EncodeToString(defaultNetwork.DomainType[:]) + generateConfigCmd.Flags().StringVar(&ssvDomain, "ssv-domain", ssvDomainDefault, "SSV domain type") + generateConfigCmd.Flags().Uint64Var(&ssvRegistrySyncOffset, "ssv-registry-sync-offset", defaultNetwork.RegistrySyncOffset.Uint64(), "SSV registry sync offset") + generateConfigCmd.Flags().StringVar(&ssvRegistryContractAddr, "ssv-registry-contract-addr", defaultNetwork.RegistryContractAddr.String(), "SSV registry contract addr") + generateConfigCmd.Flags().StringVar(&ssvBootnodes, "ssv-bootnodes", strings.Join(defaultNetwork.Bootnodes, sliceSeparator), "SSV bootnodes (comma-separated)") + ssvDiscoveryProtocolIDDefault := "0x" + hex.EncodeToString(defaultNetwork.DiscoveryProtocolID[:]) + generateConfigCmd.Flags().StringVar(&ssvDiscoveryProtocolID, "ssv-discovery-protocol-id", ssvDiscoveryProtocolIDDefault, "SSV discovery protocol ID") + + RootCmd.AddCommand(generateConfigCmd) +} diff --git a/cli/operator/node.go b/cli/operator/node.go index a749d86244..c85f5bfe72 100644 --- a/cli/operator/node.go +++ b/cli/operator/node.go @@ -15,8 +15,8 @@ import ( "sync" "time" + "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/phase0" - ethcommon "github.com/ethereum/go-ethereum/common" "github.com/ilyakaznacheev/cleanenv" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -142,10 +142,23 @@ var StartNodeCmd = &cobra.Command{ } }() - networkConfig, err := setupSSVNetwork(logger) + ssvNetworkConfig, err := setupSSVNetwork(logger) if err != nil { logger.Fatal("could not setup network", zap.Error(err)) } + + consensusClient, err := goclient.New(cmd.Context(), logger, cfg.ConsensusClient) + if err != nil { + logger.Fatal("failed to create beacon go-client", zap.Error(err), + fields.Address(cfg.ConsensusClient.BeaconNodeAddr)) + } + + networkConfig := networkconfig.NetworkConfig{ + Name: cfg.SSVOptions.NetworkName, + SSVConfig: ssvNetworkConfig, + BeaconConfig: consensusClient.BeaconConfig(), + } + cfg.DBOptions.Ctx = cmd.Context() db, err := setupDB(cmd.Context(), logger, networkConfig) if err != nil { @@ -261,17 +274,7 @@ var StartNodeCmd = &cobra.Command{ } cfg.P2pNetworkConfig.Ctx = cmd.Context() - - slotTickerProvider := func() slotticker.SlotTicker { - return slotticker.New(logger, slotticker.Config{ - SlotDuration: networkConfig.SlotDurationSec(), - GenesisTime: networkConfig.GetGenesisTime(), - }) - } - - cfg.ConsensusClient.Network = networkConfig.Beacon.GetNetwork() operatorDataStore := setupOperatorDataStore(logger, nodeStorage, operatorPubKeyBase64) - consensusClient := setupConsensusClient(cmd.Context(), logger, slotTickerProvider) executionAddrList := strings.Split(cfg.ExecutionClient.Addr, ";") // TODO: Decide what symbol to use as a separator. Bootnodes are currently separated by ";". Deployment bot currently uses ",". if len(executionAddrList) == 0 { @@ -284,7 +287,7 @@ var StartNodeCmd = &cobra.Command{ ec, err := executionclient.New( cmd.Context(), executionAddrList[0], - ethcommon.HexToAddress(networkConfig.RegistryContractAddr), + ssvNetworkConfig.RegistryContractAddr, executionclient.WithLogger(logger), executionclient.WithFollowDistance(executionclient.DefaultFollowDistance), executionclient.WithConnectionTimeout(cfg.ExecutionClient.ConnectionTimeout), @@ -302,7 +305,7 @@ var StartNodeCmd = &cobra.Command{ ec, err := executionclient.NewMulti( cmd.Context(), executionAddrList, - ethcommon.HexToAddress(networkConfig.RegistryContractAddr), + ssvNetworkConfig.RegistryContractAddr, executionclient.WithLoggerMulti(logger), executionclient.WithFollowDistanceMulti(executionclient.DefaultFollowDistance), executionclient.WithConnectionTimeoutMulti(cfg.ExecutionClient.ConnectionTimeout), @@ -326,9 +329,7 @@ var StartNodeCmd = &cobra.Command{ logger, networkConfig, ssvSignerClient, - consensusClient, db, - networkConfig, operatorDataStore.GetOperatorID, ) if err != nil { @@ -353,7 +354,7 @@ var StartNodeCmd = &cobra.Command{ cfg.P2pNetworkConfig.OperatorPubKeyHash = format.OperatorID(operatorDataStore.GetOperatorData().PublicKey) cfg.P2pNetworkConfig.OperatorDataStore = operatorDataStore cfg.P2pNetworkConfig.FullNode = cfg.SSVOptions.ValidatorOptions.FullNode - cfg.P2pNetworkConfig.Network = networkConfig + cfg.P2pNetworkConfig.NetworkConfig = networkConfig validatorsMap := validators.New(cmd.Context()) @@ -368,7 +369,7 @@ var StartNodeCmd = &cobra.Command{ nodeStorage, dutyStore, signatureVerifier, - consensusClient.ForkEpochElectra, + networkConfig.Forks[spec.DataVersionElectra].Epoch, validation.WithLogger(logger), ) @@ -381,7 +382,7 @@ var StartNodeCmd = &cobra.Command{ cfg.SSVOptions.DB = db cfg.SSVOptions.BeaconNode = consensusClient cfg.SSVOptions.ExecutionClient = executionClient - cfg.SSVOptions.Network = networkConfig + cfg.SSVOptions.NetworkConfig = networkConfig cfg.SSVOptions.P2PNetwork = p2pNetwork cfg.SSVOptions.ValidatorOptions.NetworkConfig = networkConfig cfg.SSVOptions.ValidatorOptions.Context = cmd.Context() @@ -399,7 +400,7 @@ var StartNodeCmd = &cobra.Command{ ws := exporterapi.NewWsServer(cmd.Context(), logger, nil, http.NewServeMux(), cfg.WithPing) cfg.SSVOptions.WS = ws cfg.SSVOptions.WsAPIPort = cfg.WsAPIPort - cfg.SSVOptions.ValidatorOptions.NewDecidedHandler = decided.NewStreamPublisher(networkConfig, logger, ws) + cfg.SSVOptions.ValidatorOptions.NewDecidedHandler = decided.NewStreamPublisher(logger, networkConfig.DomainType, ws) } cfg.SSVOptions.ValidatorOptions.DutyRoles = []spectypes.BeaconRole{spectypes.BNRoleAttester} // TODO could be better to set in other place @@ -421,9 +422,16 @@ var StartNodeCmd = &cobra.Command{ storageMap.Add(storageRole, s) } + slotTickerProvider := func() slotticker.SlotTicker { + return slotticker.New(logger, slotticker.Config{ + SlotDuration: networkConfig.SlotDuration, + GenesisTime: networkConfig.GenesisTime, + }) + } + if cfg.SSVOptions.ValidatorOptions.Exporter { retain := cfg.SSVOptions.ValidatorOptions.ExporterRetainSlots - threshold := cfg.SSVOptions.Network.Beacon.EstimatedCurrentSlot() + threshold := cfg.SSVOptions.NetworkConfig.EstimatedCurrentSlot() initSlotPruning(cmd.Context(), logger, storageMap, slotTickerProvider, threshold, retain) } @@ -443,7 +451,7 @@ var StartNodeCmd = &cobra.Command{ logger, nodeStorage.Shares(), nodeStorage.ValidatorStore().WithOperatorID(operatorDataStore.GetOperatorID), - networkConfig, + networkConfig.BeaconConfig, consensusClient, fixedSubnets, metadata.WithSyncInterval(cfg.SSVOptions.ValidatorOptions.MetadataUpdateInterval), @@ -453,7 +461,7 @@ var StartNodeCmd = &cobra.Command{ var doppelgangerHandler doppelganger.Provider if cfg.EnableDoppelgangerProtection { doppelgangerHandler = doppelganger.NewHandler(&doppelganger.Options{ - Network: networkConfig, + BeaconConfig: networkConfig.BeaconConfig, BeaconNode: consensusClient, ValidatorProvider: nodeStorage.ValidatorStore().WithOperatorID(operatorDataStore.GetOperatorID), SlotTickerProvider: slotTickerProvider, @@ -706,7 +714,6 @@ func validateConfig(nodeStorage operatorstorage.Storage, networkName string, usi return fmt.Errorf("incompatible config change: %w", err) } } else { - if err := nodeStorage.SaveConfig(nil, currentConfig); err != nil { return fmt.Errorf("failed to store config: %w", err) } @@ -748,7 +755,7 @@ func setupGlobal() (*zap.Logger, error) { return zap.L(), nil } -func setupDB(ctx context.Context, logger *zap.Logger, networkConfig networkconfig.NetworkConfig) (*kv.BadgerDB, error) { +func setupDB(ctx context.Context, logger *zap.Logger, beaconConfig networkconfig.Beacon) (*kv.BadgerDB, error) { db, err := kv.New(logger, cfg.DBOptions) if err != nil { return nil, errors.Wrap(err, "failed to open db") @@ -762,9 +769,9 @@ func setupDB(ctx context.Context, logger *zap.Logger, networkConfig networkconfi } migrationOpts := migrations.Options{ - Db: db, - DbPath: cfg.DBOptions.Path, - NetworkConfig: networkConfig, + Db: db, + DbPath: cfg.DBOptions.Path, + BeaconConfig: beaconConfig, } applied, err := migrations.Run(cfg.DBOptions.Ctx, logger, migrationOpts) if err != nil { @@ -893,30 +900,41 @@ func ensureOperatorPubKey(nodeStorage operatorstorage.Storage, operatorPubKeyBas return nil } -func setupSSVNetwork(logger *zap.Logger) (networkconfig.NetworkConfig, error) { - networkConfig, err := networkconfig.GetNetworkConfigByName(cfg.SSVOptions.NetworkName) - if err != nil { - return networkconfig.NetworkConfig{}, err +func setupSSVNetwork(logger *zap.Logger) (networkconfig.SSVConfig, error) { + var ssvConfig networkconfig.SSVConfig + + if cfg.SSVOptions.CustomNetwork != nil { + ssvConfig = *cfg.SSVOptions.CustomNetwork + logger.Info("using custom network config") + } else if cfg.SSVOptions.NetworkName != "" { + snc, err := networkconfig.GetSSVConfigByName(cfg.SSVOptions.NetworkName) + if err != nil { + return ssvConfig, err + } + ssvConfig = snc + logger.Info("found network config by name", + zap.String("name", cfg.SSVOptions.NetworkName), + ) } if cfg.SSVOptions.CustomDomainType != "" { if !strings.HasPrefix(cfg.SSVOptions.CustomDomainType, "0x") { - return networkconfig.NetworkConfig{}, errors.New("custom domain type must be a hex string") + return networkconfig.SSVConfig{}, errors.New("custom domain type must be a hex string") } domainBytes, err := hex.DecodeString(cfg.SSVOptions.CustomDomainType[2:]) if err != nil { - return networkconfig.NetworkConfig{}, errors.Wrap(err, "failed to decode custom domain type") + return networkconfig.SSVConfig{}, errors.Wrap(err, "failed to decode custom domain type") } if len(domainBytes) != 4 { - return networkconfig.NetworkConfig{}, errors.New("custom domain type must be 4 bytes") + return networkconfig.SSVConfig{}, errors.New("custom domain type must be 4 bytes") } // https://github.com/ssvlabs/ssv/pull/1808 incremented the post-fork domain type by 1, so we have to maintain the compatibility. postForkDomain := binary.BigEndian.Uint32(domainBytes) + 1 - binary.BigEndian.PutUint32(networkConfig.DomainType[:], postForkDomain) + binary.BigEndian.PutUint32(ssvConfig.DomainType[:], postForkDomain) - logger.Info("running with custom domain type", - fields.Domain(networkConfig.DomainType), + logger.Warn("running with custom domain type; it's deprecated, consider using custom network instead", + fields.Domain(ssvConfig.DomainType), ) } @@ -926,14 +944,12 @@ func setupSSVNetwork(logger *zap.Logger) (networkconfig.NetworkConfig, error) { } logger.Info("setting ssv network", - fields.Network(networkConfig.Name), - fields.Domain(networkConfig.DomainType), + zap.Any("config", ssvConfig), zap.String("nodeType", nodeType), - zap.Any("beaconNetwork", networkConfig.Beacon.GetNetwork().BeaconNetwork), - zap.String("registryContract", networkConfig.RegistryContractAddr), + zap.String("registryContract", ssvConfig.RegistryContractAddr.String()), ) - return networkConfig, nil + return ssvConfig, nil } func setupP2P(logger *zap.Logger, db basedb.Database) network.P2PNetwork { @@ -951,20 +967,6 @@ func setupP2P(logger *zap.Logger, db basedb.Database) network.P2PNetwork { return n } -func setupConsensusClient( - ctx context.Context, - logger *zap.Logger, - slotTickerProvider slotticker.Provider, -) *goclient.GoClient { - cl, err := goclient.New(ctx, logger, cfg.ConsensusClient, slotTickerProvider) - if err != nil { - logger.Fatal("failed to create beacon go-client", zap.Error(err), - fields.Address(cfg.ConsensusClient.BeaconNodeAddr)) - } - - return cl -} - // syncContractEvents blocks until historical events are synced and then spawns a goroutine syncing ongoing events. func syncContractEvents( ctx context.Context, diff --git a/doppelganger/doppelganger.go b/doppelganger/doppelganger.go index 12e361c85e..276006b460 100644 --- a/doppelganger/doppelganger.go +++ b/doppelganger/doppelganger.go @@ -55,7 +55,7 @@ type BeaconNode interface { // Options contains the configuration options for the Doppelgänger protection. type Options struct { - Network networkconfig.NetworkConfig + BeaconConfig networkconfig.BeaconConfig BeaconNode BeaconNode ValidatorProvider ValidatorProvider SlotTickerProvider slotticker.Provider @@ -68,7 +68,7 @@ type handler struct { mu sync.RWMutex validatorsState map[phase0.ValidatorIndex]*doppelgangerState - network networkconfig.NetworkConfig + beaconConfig networkconfig.BeaconConfig beaconNode BeaconNode validatorProvider ValidatorProvider slotTickerProvider slotticker.Provider @@ -78,7 +78,7 @@ type handler struct { // NewHandler initializes a new instance of the Doppelgänger protection. func NewHandler(opts *Options) *handler { return &handler{ - network: opts.Network, + beaconConfig: opts.BeaconConfig, beaconNode: opts.BeaconNode, validatorProvider: opts.ValidatorProvider, slotTickerProvider: opts.SlotTickerProvider, @@ -176,7 +176,7 @@ func (h *handler) Start(ctx context.Context) error { var startEpoch, previousEpoch phase0.Epoch firstRun := true ticker := h.slotTickerProvider() - slotsPerEpoch := h.network.Beacon.SlotsPerEpoch() + slotsPerEpoch := h.beaconConfig.SlotsPerEpoch for { select { @@ -184,7 +184,7 @@ func (h *handler) Start(ctx context.Context) error { return ctx.Err() case <-ticker.Next(): currentSlot := ticker.Slot() - currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(currentSlot) + currentEpoch := h.beaconConfig.EstimatedEpochAtSlot(currentSlot) buildStr := fmt.Sprintf("e%v-s%v-#%v", currentEpoch, currentSlot, currentSlot%32+1) h.logger.Debug("🛠 ticker event", zap.String("epoch_slot_pos", buildStr)) @@ -233,7 +233,7 @@ func (h *handler) Start(ctx context.Context) error { func (h *handler) checkLiveness(ctx context.Context, slot phase0.Slot, epoch phase0.Epoch) { // Set a deadline until the start of the next slot, with a 100ms safety margin - ctx, cancel := context.WithDeadline(ctx, h.network.Beacon.GetSlotStartTime(slot+1).Add(100*time.Millisecond)) + ctx, cancel := context.WithDeadline(ctx, h.beaconConfig.GetSlotStartTime(slot+1).Add(100*time.Millisecond)) defer cancel() h.mu.RLock() diff --git a/doppelganger/doppelganger_test.go b/doppelganger/doppelganger_test.go index 9cc9a047d6..13d5766ba4 100644 --- a/doppelganger/doppelganger_test.go +++ b/doppelganger/doppelganger_test.go @@ -19,7 +19,7 @@ func newTestDoppelgangerHandler(t *testing.T) *handler { logger := logging.TestLogger(t) return NewHandler(&Options{ - Network: networkconfig.TestNetwork, + BeaconConfig: networkconfig.TestNetwork.BeaconConfig, BeaconNode: mockBeaconNode, ValidatorProvider: mockValidatorProvider, Logger: logger, diff --git a/e2e/cmd/ssv-e2e/beacon_proxy.go b/e2e/cmd/ssv-e2e/beacon_proxy.go index 988eda9827..4733f27d50 100644 --- a/e2e/cmd/ssv-e2e/beacon_proxy.go +++ b/e2e/cmd/ssv-e2e/beacon_proxy.go @@ -125,7 +125,7 @@ func (cmd *BeaconProxyCmd) Run(logger *zap.Logger, globals Globals) error { networkCfg := networkconfig.HoleskyE2E const startEpochDelay = 1 // TODO: change to 2 after debugging is done - startEpoch := networkCfg.Beacon.EstimatedCurrentEpoch() + startEpochDelay + startEpoch := networkCfg.EstimatedCurrentEpoch() + startEpochDelay interceptor := slashinginterceptor.New(logger, networkCfg.Beacon.GetNetwork(), startEpoch, true, slices.Collect(maps.Values(validatorsData))) go interceptor.WatchSubmissions() diff --git a/eth/eventhandler/event_handler.go b/eth/eventhandler/event_handler.go index 1b7eb0df94..f819a91444 100644 --- a/eth/eventhandler/event_handler.go +++ b/eth/eventhandler/event_handler.go @@ -65,7 +65,7 @@ type EventHandler struct { nodeStorage nodestorage.Storage taskExecutor taskExecutor eventParser eventparser.Parser - networkConfig networkconfig.NetworkConfig + networkConfig networkconfig.Network operatorDataStore operatordatastore.OperatorDataStore operatorDecrypter keys.OperatorDecrypter keyManager ekm.KeyManager @@ -79,7 +79,7 @@ func New( nodeStorage nodestorage.Storage, eventParser eventparser.Parser, taskExecutor taskExecutor, - networkConfig networkconfig.NetworkConfig, + networkConfig networkconfig.Network, operatorDataStore operatordatastore.OperatorDataStore, operatorDecrypter keys.OperatorDecrypter, keyManager ekm.KeyManager, diff --git a/eth/eventhandler/event_handler_test.go b/eth/eventhandler/event_handler_test.go index 3691a0d228..6493a7593f 100644 --- a/eth/eventhandler/event_handler_test.go +++ b/eth/eventhandler/event_handler_test.go @@ -69,9 +69,7 @@ func TestHandleBlockEventsStream(t *testing.T) { operatorsCount += uint64(len(ops)) currentSlot := &utils.SlotValue{} - mockBeaconNetwork := utils.SetupMockBeaconNetwork(t, currentSlot) - mockNetworkConfig := &networkconfig.NetworkConfig{} - mockNetworkConfig.Beacon = mockBeaconNetwork + mockNetworkConfig := utils.SetupMockNetworkConfig(t, networkconfig.TestNetwork.DomainType, currentSlot) eh, _, err := setupEventHandler(t, ctx, logger, mockNetworkConfig, ops[0], false) if err != nil { @@ -853,8 +851,8 @@ func TestHandleBlockEventsStream(t *testing.T) { require.True(t, found) require.NotNil(t, highestAttestation) - require.Equal(t, highestAttestation.Source.Epoch, mockBeaconNetwork.EstimatedEpochAtSlot(currentSlot.GetSlot())-1) - require.Equal(t, highestAttestation.Target.Epoch, mockBeaconNetwork.EstimatedEpochAtSlot(currentSlot.GetSlot())) + require.Equal(t, highestAttestation.Source.Epoch, mockNetworkConfig.EstimatedEpochAtSlot(currentSlot.GetSlot())-1) + require.Equal(t, highestAttestation.Target.Epoch, mockNetworkConfig.EstimatedEpochAtSlot(currentSlot.GetSlot())) highestProposal, found, err := eh.keyManager.RetrieveHighestProposal(phase0.BLSPubKey(sharePubKey)) require.NoError(t, err) @@ -902,8 +900,8 @@ func TestHandleBlockEventsStream(t *testing.T) { require.NoError(t, err) require.True(t, found) require.NotNil(t, highestAttestation) - require.Equal(t, highestAttestation.Source.Epoch, mockBeaconNetwork.EstimatedEpochAtSlot(currentSlot.GetSlot())-1) - require.Equal(t, highestAttestation.Target.Epoch, mockBeaconNetwork.EstimatedEpochAtSlot(currentSlot.GetSlot())) + require.Equal(t, highestAttestation.Source.Epoch, mockNetworkConfig.EstimatedEpochAtSlot(currentSlot.GetSlot())-1) + require.Equal(t, highestAttestation.Target.Epoch, mockNetworkConfig.EstimatedEpochAtSlot(currentSlot.GetSlot())) highestProposal, found, err := eh.keyManager.RetrieveHighestProposal(phase0.BLSPubKey(sharePubKey)) require.NoError(t, err) @@ -993,8 +991,8 @@ func TestHandleBlockEventsStream(t *testing.T) { require.NoError(t, err) require.True(t, found) require.NotNil(t, highestAttestation) - require.Greater(t, highestAttestation.Source.Epoch, mockBeaconNetwork.EstimatedEpochAtSlot(currentSlot.GetSlot())-1) - require.Greater(t, highestAttestation.Target.Epoch, mockBeaconNetwork.EstimatedEpochAtSlot(currentSlot.GetSlot())) + require.Greater(t, highestAttestation.Source.Epoch, mockNetworkConfig.EstimatedEpochAtSlot(currentSlot.GetSlot())-1) + require.Greater(t, highestAttestation.Target.Epoch, mockNetworkConfig.EstimatedEpochAtSlot(currentSlot.GetSlot())) highestProposal, found, err := eh.keyManager.RetrieveHighestProposal(phase0.BLSPubKey(sharePubKey)) require.NoError(t, err) @@ -1350,7 +1348,7 @@ func TestHandleBlockEventsStream(t *testing.T) { }) } -func setupEventHandler(t *testing.T, ctx context.Context, logger *zap.Logger, network *networkconfig.NetworkConfig, operator *testOperator, useMockCtrl bool) (*EventHandler, *mocks.MockController, error) { +func setupEventHandler(t *testing.T, ctx context.Context, logger *zap.Logger, network networkconfig.Network, operator *testOperator, useMockCtrl bool) (*EventHandler, *mocks.MockController, error) { db, err := kv.NewInMemory(logger, basedb.Options{ Ctx: ctx, }) @@ -1362,11 +1360,10 @@ func setupEventHandler(t *testing.T, ctx context.Context, logger *zap.Logger, ne operatorDataStore := operatordatastore.New(operatorData) if network == nil { - network = &networkconfig.NetworkConfig{} - network.Beacon = utils.SetupMockBeaconNetwork(t, &utils.SlotValue{}) + network = utils.SetupMockNetworkConfig(t, networkconfig.TestNetwork.DomainType, &utils.SlotValue{}) } - keyManager, err := ekm.NewLocalKeyManager(logger, db, *network, operator.privateKey) + keyManager, err := ekm.NewLocalKeyManager(logger, db, network, operator.privateKey) if err != nil { return nil, nil, err } @@ -1388,7 +1385,7 @@ func setupEventHandler(t *testing.T, ctx context.Context, logger *zap.Logger, ne nodeStorage, parser, validatorCtrl, - *network, + network, operatorDataStore, operator.privateKey, keyManager, @@ -1405,7 +1402,7 @@ func setupEventHandler(t *testing.T, ctx context.Context, logger *zap.Logger, ne validatorCtrl := validator.NewController(logger, validator.ControllerOptions{ Context: ctx, - NetworkConfig: *network, + NetworkConfig: network, DB: db, RegistryStorage: nodeStorage, BeaconSigner: keyManager, @@ -1423,7 +1420,7 @@ func setupEventHandler(t *testing.T, ctx context.Context, logger *zap.Logger, ne nodeStorage, parser, validatorCtrl, - *network, + network, operatorDataStore, operator.privateKey, keyManager, diff --git a/eth/eventhandler/handlers.go b/eth/eventhandler/handlers.go index ae798af8a4..6c76205847 100644 --- a/eth/eventhandler/handlers.go +++ b/eth/eventhandler/handlers.go @@ -257,7 +257,7 @@ func (eh *EventHandler) handleShareCreation( // Note: The current epoch can differ from the epoch set in slashing protection // due to the passage of time between saving slashing protection data and setting // the minimum participation epoch - share.SetMinParticipationEpoch(eh.networkConfig.Beacon.EstimatedCurrentEpoch() + contractParticipationDelay) + share.SetMinParticipationEpoch(eh.networkConfig.EstimatedCurrentEpoch() + contractParticipationDelay) } // Save share to DB. @@ -320,7 +320,7 @@ func (eh *EventHandler) validatorAddedEventToShare( encryptedKey = encryptedKeys[i] } - validatorShare.DomainType = eh.networkConfig.DomainType + validatorShare.DomainType = eh.networkConfig.GetDomainType() validatorShare.Committee = shareMembers return &validatorShare, encryptedKey, nil @@ -434,7 +434,7 @@ func (eh *EventHandler) handleClusterReactivated(txn basedb.Txn, event *contract // Note: The current epoch can differ from the epoch set in slashing protection // due to the passage of time between saving slashing protection data and setting // the minimum participation epoch - share.SetMinParticipationEpoch(eh.networkConfig.Beacon.EstimatedCurrentEpoch() + contractParticipationDelay) + share.SetMinParticipationEpoch(eh.networkConfig.EstimatedCurrentEpoch() + contractParticipationDelay) } if len(enabledPubKeys) > 0 { diff --git a/exporter/api/decided/stream.go b/exporter/api/decided/stream.go index 52a02a6c7c..56fd00ef11 100644 --- a/exporter/api/decided/stream.go +++ b/exporter/api/decided/stream.go @@ -5,18 +5,18 @@ import ( "time" "github.com/patrickmn/go-cache" + spectypes "github.com/ssvlabs/ssv-spec/types" "go.uber.org/zap" "github.com/ssvlabs/ssv/exporter/api" "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/protocol/v2/qbft/controller" qbftstorage "github.com/ssvlabs/ssv/protocol/v2/qbft/storage" ) // NewStreamPublisher handles incoming newly decided messages. // it forward messages to websocket stream, where messages are cached (1m TTL) to avoid flooding -func NewStreamPublisher(domainTypeProvider networkconfig.NetworkConfig, logger *zap.Logger, ws api.WebSocketServer) controller.NewDecidedHandler { +func NewStreamPublisher(logger *zap.Logger, domainType spectypes.DomainType, ws api.WebSocketServer) controller.NewDecidedHandler { c := cache.New(time.Minute, time.Minute*3/2) feed := ws.BroadcastFeed() return func(msg qbftstorage.Participation) { @@ -28,6 +28,6 @@ func NewStreamPublisher(domainTypeProvider networkconfig.NetworkConfig, logger * c.SetDefault(key, true) logger.Debug("broadcast decided stream", fields.PubKey(msg.PubKey[:]), fields.Slot(msg.Slot)) - feed.Send(api.NewParticipantsAPIMsg(domainTypeProvider.DomainType, msg)) + feed.Send(api.NewParticipantsAPIMsg(domainType, msg)) } } diff --git a/exporter/api/query_handlers_test.go b/exporter/api/query_handlers_test.go index 1f4b79a47f..2f67164ef2 100644 --- a/exporter/api/query_handlers_test.go +++ b/exporter/api/query_handlers_test.go @@ -105,8 +105,7 @@ func TestHandleDecidedQuery(t *testing.T) { for _, role := range roles { pk := sks[1].GetPublicKey() - networkConfig, err := networkconfig.GetNetworkConfigByName(networkconfig.HoleskyStage.Name) - require.NoError(t, err) + ssvConfig := networkconfig.TestNetwork.SSVConfig decided250Seq, err := protocoltesting.CreateMultipleStoredInstances(rsaKeys, specqbft.Height(0), specqbft.Height(250), func(height specqbft.Height) ([]spectypes.OperatorID, *specqbft.Message) { return oids, &specqbft.Message{ MsgType: specqbft.CommitMsgType, @@ -131,7 +130,7 @@ func TestHandleDecidedQuery(t *testing.T) { t.Run("valid range", func(t *testing.T) { nm := newParticipantsAPIMsg(pk.SerializeToHexStr(), spectypes.BNRoleAttester, 0, 250) h := NewHandler(l) - h.HandleParticipantsQuery(ibftStorage, nm, networkConfig.DomainType) + h.HandleParticipantsQuery(ibftStorage, nm, ssvConfig.DomainType) require.NotNil(t, nm.Msg.Data) msgs, ok := nm.Msg.Data.([]*ParticipantsAPI) @@ -142,7 +141,7 @@ func TestHandleDecidedQuery(t *testing.T) { t.Run("invalid range", func(t *testing.T) { nm := newParticipantsAPIMsg(pk.SerializeToHexStr(), spectypes.BNRoleAttester, 400, 404) h := NewHandler(l) - h.HandleParticipantsQuery(ibftStorage, nm, networkConfig.DomainType) + h.HandleParticipantsQuery(ibftStorage, nm, ssvConfig.DomainType) require.NotNil(t, nm.Msg.Data) data, ok := nm.Msg.Data.([]string) require.True(t, ok) @@ -152,7 +151,7 @@ func TestHandleDecidedQuery(t *testing.T) { t.Run("non-existing validator", func(t *testing.T) { nm := newParticipantsAPIMsg("xxx", spectypes.BNRoleAttester, 400, 404) h := NewHandler(l) - h.HandleParticipantsQuery(ibftStorage, nm, networkConfig.DomainType) + h.HandleParticipantsQuery(ibftStorage, nm, ssvConfig.DomainType) require.NotNil(t, nm.Msg.Data) errs, ok := nm.Msg.Data.([]string) require.True(t, ok) @@ -162,7 +161,7 @@ func TestHandleDecidedQuery(t *testing.T) { t.Run("non-existing role", func(t *testing.T) { nm := newParticipantsAPIMsg(pk.SerializeToHexStr(), math.MaxUint64, 0, 250) h := NewHandler(l) - h.HandleParticipantsQuery(ibftStorage, nm, networkConfig.DomainType) + h.HandleParticipantsQuery(ibftStorage, nm, ssvConfig.DomainType) require.NotNil(t, nm.Msg.Data) errs, ok := nm.Msg.Data.([]string) require.True(t, ok) @@ -172,7 +171,7 @@ func TestHandleDecidedQuery(t *testing.T) { t.Run("non-existing storage", func(t *testing.T) { nm := newParticipantsAPIMsg(pk.SerializeToHexStr(), spectypes.BNRoleSyncCommitteeContribution, 0, 250) h := NewHandler(l) - h.HandleParticipantsQuery(ibftStorage, nm, networkConfig.DomainType) + h.HandleParticipantsQuery(ibftStorage, nm, ssvConfig.DomainType) require.NotNil(t, nm.Msg.Data) errs, ok := nm.Msg.Data.([]string) require.True(t, ok) diff --git a/go.mod b/go.mod index ea5168d58c..6f33aebd1d 100644 --- a/go.mod +++ b/go.mod @@ -37,9 +37,9 @@ require ( github.com/sanity-io/litter v1.5.6 github.com/sourcegraph/conc v0.3.0 github.com/spf13/cobra v1.8.1 - github.com/ssvlabs/eth2-key-manager v1.5.2 + github.com/ssvlabs/eth2-key-manager v1.5.5 github.com/ssvlabs/ssv-spec v1.1.3 - github.com/ssvlabs/ssv/ssvsigner v0.0.0-20250512164336-eaf440d63581 + github.com/ssvlabs/ssv/ssvsigner v0.0.0-20250527163031-8fc6fde91908 github.com/status-im/keycard-go v0.2.0 github.com/stretchr/testify v1.9.0 github.com/wealdtech/go-eth2-types/v2 v2.8.1 diff --git a/go.sum b/go.sum index 0e6fa1bea5..67c629f4fc 100644 --- a/go.sum +++ b/go.sum @@ -750,14 +750,14 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= -github.com/ssvlabs/eth2-key-manager v1.5.2 h1:gF+8FJkoV1VXpVCPspyVW/Jdky0kt9Pndk88W8ePqx8= -github.com/ssvlabs/eth2-key-manager v1.5.2/go.mod h1:yeUzAP+SBJXgeXPiGBrLeLuHIQCpeJZV7Jz3Fwzm/zk= +github.com/ssvlabs/eth2-key-manager v1.5.5 h1:AzwNowGvcmVpRCVHq10FVnpkVIUpoDo5YbJWNMnYA8Q= +github.com/ssvlabs/eth2-key-manager v1.5.5/go.mod h1:yeUzAP+SBJXgeXPiGBrLeLuHIQCpeJZV7Jz3Fwzm/zk= github.com/ssvlabs/go-eth2-client v0.6.31-0.20250417062221-9cd9b891d4d6 h1:26sqPsxh434N7emUfOcLJ9oPGnnsz8XOHzKRZVKu4LU= github.com/ssvlabs/go-eth2-client v0.6.31-0.20250417062221-9cd9b891d4d6/go.mod h1:fvULSL9WtNskkOB4i+Yyr6BKpNHXvmpGZj9969fCrfY= github.com/ssvlabs/ssv-spec v1.1.3 h1:46K31kI4/vA7Vp3DaOuN7t2IABAmzeiMniCqYfzzpo8= github.com/ssvlabs/ssv-spec v1.1.3/go.mod h1:pto7dDv99uVfCZidiLrrKgFR6VYy6WY3PGI1TiGCsIU= -github.com/ssvlabs/ssv/ssvsigner v0.0.0-20250512164336-eaf440d63581 h1:BTh8A1s2HNIHrussld4QZTguuRO1tuqqkyvT+0kThpE= -github.com/ssvlabs/ssv/ssvsigner v0.0.0-20250512164336-eaf440d63581/go.mod h1:HkcbtVHpGBPnJjdWWd08Z3lv7l0VWejJU80yUUqY5Go= +github.com/ssvlabs/ssv/ssvsigner v0.0.0-20250527163031-8fc6fde91908 h1:kL742CdbS76LbVCNuzOl+/nT8i8ow4yqfn0rDiw7V4c= +github.com/ssvlabs/ssv/ssvsigner v0.0.0-20250527163031-8fc6fde91908/go.mod h1:pGWwC0CvKjs7WEGLj83HAmdULrAp32B7QmL1Js+gMUY= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/integration/qbft/tests/temp_testing_beacon_network.go b/integration/qbft/tests/temp_testing_beacon_network.go index ca96d705c3..5eb3b5fc90 100644 --- a/integration/qbft/tests/temp_testing_beacon_network.go +++ b/integration/qbft/tests/temp_testing_beacon_network.go @@ -34,9 +34,6 @@ func (bn *TestingBeaconNodeWrapped) GetBeaconNode() *spectestingutils.TestingBea func (bn *TestingBeaconNodeWrapped) GetAttestationData(ctx context.Context, slot phase0.Slot) (*phase0.AttestationData, spec.DataVersion, error) { return bn.Bn.GetAttestationData(slot) } -func (bn *TestingBeaconNodeWrapped) DataVersion(epoch phase0.Epoch) spec.DataVersion { - return bn.Bn.DataVersion(epoch) -} func (bn *TestingBeaconNodeWrapped) DomainData(ctx context.Context, epoch phase0.Epoch, domain phase0.DomainType) (phase0.Domain, error) { return bn.Bn.DomainData(epoch, domain) } diff --git a/message/validation/common_checks.go b/message/validation/common_checks.go index 6df942cb32..cc1c63929b 100644 --- a/message/validation/common_checks.go +++ b/message/validation/common_checks.go @@ -30,22 +30,22 @@ func (mv *messageValidator) validateSlotTime(messageSlot phase0.Slot, role spect // messageEarliness returns how early message is or 0 if it's not func (mv *messageValidator) messageEarliness(slot phase0.Slot, receivedAt time.Time) time.Duration { - return mv.netCfg.Beacon.GetSlotStartTime(slot).Sub(receivedAt) + return mv.netCfg.GetSlotStartTime(slot).Sub(receivedAt) } // messageLateness returns how late message is or 0 if it's not func (mv *messageValidator) messageLateness(slot phase0.Slot, role spectypes.RunnerRole, receivedAt time.Time) time.Duration { - var ttl phase0.Slot + var ttl uint64 switch role { case spectypes.RoleProposer, spectypes.RoleSyncCommitteeContribution: ttl = 1 + LateSlotAllowance case spectypes.RoleCommittee, spectypes.RoleAggregator: - ttl = MaxStoredSlots(mv.netCfg) + ttl = mv.maxStoredSlots() case spectypes.RoleValidatorRegistration, spectypes.RoleVoluntaryExit: return 0 } - deadline := mv.netCfg.Beacon.GetSlotStartTime(slot + ttl). + deadline := mv.netCfg.GetSlotStartTime(slot + phase0.Slot(ttl)). Add(lateMessageMargin) return receivedAt.Sub(deadline) @@ -57,7 +57,7 @@ func (mv *messageValidator) validateDutyCount( validatorIndices []phase0.ValidatorIndex, signerStateBySlot *OperatorState, ) error { - dutyCount := signerStateBySlot.DutyCount(mv.netCfg.Beacon.EstimatedEpochAtSlot(msgSlot)) + dutyCount := signerStateBySlot.DutyCount(mv.netCfg.EstimatedEpochAtSlot(msgSlot)) dutyLimit, exists := mv.dutyLimit(msgID, msgSlot, validatorIndices) if !exists { @@ -90,7 +90,7 @@ func (mv *messageValidator) dutyLimit(msgID spectypes.MessageID, slot phase0.Slo case spectypes.RoleCommittee: validatorIndexCount := uint64(len(validatorIndices)) - slotsPerEpoch := mv.netCfg.Beacon.SlotsPerEpoch() + slotsPerEpoch := mv.netCfg.GetSlotsPerEpoch() // Skip duty search if validators * 2 exceeds slots per epoch, // as the maximum duties per epoch is capped at the number of slots. @@ -98,7 +98,7 @@ func (mv *messageValidator) dutyLimit(msgID spectypes.MessageID, slot phase0.Slo if validatorIndexCount < slotsPerEpoch/2 { // Check if there is at least one validator in the sync committee. // If so, the duty limit is equal to the number of slots per epoch. - period := mv.netCfg.Beacon.EstimatedSyncCommitteePeriodAtEpoch(mv.netCfg.Beacon.EstimatedEpochAtSlot(slot)) + period := mv.netCfg.EstimatedSyncCommitteePeriodAtEpoch(mv.netCfg.EstimatedEpochAtSlot(slot)) for _, i := range validatorIndices { if mv.dutyStore.SyncCommittee.Duty(period, i) != nil { return slotsPerEpoch, true @@ -119,7 +119,7 @@ func (mv *messageValidator) validateBeaconDuty( indices []phase0.ValidatorIndex, randaoMsg bool, ) error { - epoch := mv.netCfg.Beacon.EstimatedEpochAtSlot(slot) + epoch := mv.netCfg.EstimatedEpochAtSlot(slot) // Rule: For a proposal duty message, we check if the validator is assigned to it if role == spectypes.RoleProposer { @@ -127,7 +127,7 @@ func (mv *messageValidator) validateBeaconDuty( // while duties are still being fetched from the Beacon node. // // Note: we allow current slot to be lower because of the ErrEarlyMessage rule. - if randaoMsg && mv.netCfg.Beacon.IsFirstSlotOfEpoch(slot) && mv.netCfg.Beacon.EstimatedCurrentSlot() <= slot { + if randaoMsg && mv.netCfg.IsFirstSlotOfEpoch(slot) && mv.netCfg.EstimatedCurrentSlot() <= slot { if !mv.dutyStore.Proposer.IsEpochSet(epoch) { return nil } @@ -142,7 +142,7 @@ func (mv *messageValidator) validateBeaconDuty( // Rule: For a sync committee aggregation duty message, we check if the validator is assigned to it if role == spectypes.RoleSyncCommitteeContribution { - period := mv.netCfg.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) + period := mv.netCfg.EstimatedSyncCommitteePeriodAtEpoch(epoch) // Non-committee roles always have one validator index. validatorIndex := indices[0] if mv.dutyStore.SyncCommittee.Duty(period, validatorIndex) == nil { diff --git a/message/validation/consensus_state.go b/message/validation/consensus_state.go index a3adb606e6..5079221ff8 100644 --- a/message/validation/consensus_state.go +++ b/message/validation/consensus_state.go @@ -7,7 +7,7 @@ import ( // ValidatorState keeps track of the signers for a given public key and role. type ValidatorState struct { operators []*OperatorState - storedSlotCount phase0.Slot + storedSlotCount uint64 } func (cs *ValidatorState) Signer(idx int) *OperatorState { @@ -26,7 +26,7 @@ type OperatorState struct { prevEpochDuties uint64 } -func newOperatorState(size phase0.Slot) *OperatorState { +func newOperatorState(size uint64) *OperatorState { return &OperatorState{ signers: make([]*SignerState, size), } diff --git a/message/validation/consensus_state_test.go b/message/validation/consensus_state_test.go index eb8faca0eb..80b9f7a22d 100644 --- a/message/validation/consensus_state_test.go +++ b/message/validation/consensus_state_test.go @@ -3,61 +3,60 @@ package validation import ( "testing" - "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/stretchr/testify/require" ) func TestOperatorState(t *testing.T) { t.Run("TestNewOperatorState", func(t *testing.T) { - size := phase0.Slot(10) + const size = 10 os := newOperatorState(size) require.NotNil(t, os) - require.Equal(t, len(os.signers), int(size)) + require.Equal(t, len(os.signers), size) }) t.Run("TestGetAndSet", func(t *testing.T) { - size := phase0.Slot(10) + const size = 10 os := newOperatorState(size) - slot := phase0.Slot(5) - epoch := phase0.Epoch(1) + const slot = 5 + const epoch = 1 signerState := &SignerState{Slot: slot} os.Set(slot, epoch, signerState) retrievedState := os.Get(slot) require.NotNil(t, retrievedState) - require.Equal(t, retrievedState.Slot, slot) + require.EqualValues(t, retrievedState.Slot, slot) }) t.Run("TestGetInvalidSlot", func(t *testing.T) { - size := phase0.Slot(10) + const size = 10 os := newOperatorState(size) - slot := phase0.Slot(5) + const slot = 5 retrievedState := os.Get(slot) require.Nil(t, retrievedState) }) t.Run("TestMaxSlot", func(t *testing.T) { - size := phase0.Slot(10) + const size = 10 os := newOperatorState(size) - slot := phase0.Slot(5) - epoch := phase0.Epoch(1) + const slot = 5 + const epoch = 1 signerState := &SignerState{Slot: slot} os.Set(slot, epoch, signerState) - require.Equal(t, os.MaxSlot(), slot) + require.EqualValues(t, os.MaxSlot(), slot) }) t.Run("TestDutyCount", func(t *testing.T) { - size := phase0.Slot(10) + const size = 10 os := newOperatorState(size) - slot := phase0.Slot(5) - epoch := phase0.Epoch(1) + const slot = 5 + const epoch = 1 signerState1 := &SignerState{Slot: slot} os.Set(slot, epoch, signerState1) @@ -65,8 +64,8 @@ func TestOperatorState(t *testing.T) { require.Equal(t, os.DutyCount(epoch), uint64(1)) require.Equal(t, os.DutyCount(epoch-1), uint64(0)) - slot2 := phase0.Slot(6) - epoch2 := phase0.Epoch(2) + const slot2 = 6 + const epoch2 = 2 signerState2 := &SignerState{Slot: slot2} os.Set(slot2, epoch2, signerState2) @@ -77,17 +76,17 @@ func TestOperatorState(t *testing.T) { }) t.Run("TestIncrementLastEpochDuties", func(t *testing.T) { - size := phase0.Slot(10) + const size = 10 os := newOperatorState(size) - slot := phase0.Slot(5) - epoch := phase0.Epoch(1) + const slot = 5 + const epoch = 1 signerState1 := &SignerState{Slot: slot} os.Set(slot, epoch, signerState1) require.Equal(t, os.DutyCount(epoch), uint64(1)) - slot2 := phase0.Slot(6) + const slot2 = 6 signerState2 := &SignerState{Slot: slot2} os.Set(slot2, epoch, signerState2) diff --git a/message/validation/consensus_validation.go b/message/validation/consensus_validation.go index 20a74f2bac..8bd6a52140 100644 --- a/message/validation/consensus_validation.go +++ b/message/validation/consensus_validation.go @@ -306,7 +306,7 @@ func (mv *messageValidator) updateConsensusState( consensusState *ValidatorState, ) error { msgSlot := phase0.Slot(consensusMessage.Height) - msgEpoch := mv.netCfg.Beacon.EstimatedEpochAtSlot(msgSlot) + msgEpoch := mv.netCfg.EstimatedEpochAtSlot(msgSlot) for _, signer := range signedSSVMessage.OperatorIDs { stateBySlot := consensusState.Signer(committeeInfo.signerIndex(signer)) @@ -430,7 +430,7 @@ func (mv *messageValidator) roundBelongsToAllowedSpread( consensusMessage *specqbft.Message, receivedAt time.Time, ) error { - slotStartTime := mv.netCfg.Beacon.GetSlotStartTime(phase0.Slot(consensusMessage.Height)) /*. + slotStartTime := mv.netCfg.GetSlotStartTime(phase0.Slot(consensusMessage.Height)) /*. Add(mv.waitAfterSlotStart(role))*/ // TODO: not supported yet because first round is non-deterministic now sinceSlotStart := time.Duration(0) diff --git a/message/validation/const.go b/message/validation/const.go index a7ae4208f4..1f40abfc7d 100644 --- a/message/validation/const.go +++ b/message/validation/const.go @@ -2,8 +2,6 @@ package validation import ( "time" - - "github.com/attestantio/go-eth2-client/spec/phase0" ) // To add some encoding overhead for ssz, we use (N + N/encodingOverheadDivisor + 4) for a structure with expected size N @@ -15,8 +13,7 @@ const ( clockErrorTolerance = time.Millisecond * 50 allowedRoundsInFuture = 1 allowedRoundsInPast = 2 - LateSlotAllowance = phase0.Slot(2) - syncCommitteeSize = 512 + LateSlotAllowance = 2 rsaSignatureSize = 256 operatorIDSize = 8 // uint64 slotSize = 8 // uint64 diff --git a/message/validation/logger_fields.go b/message/validation/logger_fields.go index 6060691303..e2a667dbd9 100644 --- a/message/validation/logger_fields.go +++ b/message/validation/logger_fields.go @@ -92,13 +92,13 @@ func (mv *messageValidator) addDutyIDField(lf *LoggerFields) { if lf.Role == spectypes.RoleCommittee { c, ok := mv.validatorStore.Committee(spectypes.CommitteeID(lf.DutyExecutorID[16:])) if ok { - lf.DutyID = fields.FormatCommitteeDutyID(c.Operators, mv.netCfg.Beacon.EstimatedEpochAtSlot(lf.Slot), lf.Slot) + lf.DutyID = fields.FormatCommitteeDutyID(c.Operators, mv.netCfg.EstimatedEpochAtSlot(lf.Slot), lf.Slot) } } else { // get the validator index from the msgid v, ok := mv.validatorStore.Validator(lf.DutyExecutorID) if ok { - lf.DutyID = fields.FormatDutyID(mv.netCfg.Beacon.EstimatedEpochAtSlot(lf.Slot), lf.Slot, lf.Role.String(), v.ValidatorIndex) + lf.DutyID = fields.FormatDutyID(mv.netCfg.EstimatedEpochAtSlot(lf.Slot), lf.Slot, lf.Role.String(), v.ValidatorIndex) } } } diff --git a/message/validation/partial_validation.go b/message/validation/partial_validation.go index 5dca1b2430..cb6f8300ed 100644 --- a/message/validation/partial_validation.go +++ b/message/validation/partial_validation.go @@ -197,7 +197,8 @@ func (mv *messageValidator) validatePartialSigMessagesByDutyLogic( if signedSSVMessage.SSVMessage.MsgID.GetRoleType() == spectypes.RoleCommittee { // Rule: The number of signatures must be <= min(2*V, V + SYNC_COMMITTEE_SIZE) where V is the number of validators assigned to the cluster - if partialSignatureMessageCount > min(2*clusterValidatorCount, clusterValidatorCount+syncCommitteeSize) { + // #nosec G115 + if partialSignatureMessageCount > min(2*clusterValidatorCount, clusterValidatorCount+int(mv.netCfg.GetSyncCommitteeSize())) { return ErrTooManyPartialSignatureMessages } @@ -236,7 +237,7 @@ func (mv *messageValidator) updatePartialSignatureState( ) error { stateBySlot := state.Signer(committeeInfo.signerIndex(signer)) messageSlot := partialSignatureMessages.Slot - messageEpoch := mv.netCfg.Beacon.EstimatedEpochAtSlot(messageSlot) + messageEpoch := mv.netCfg.EstimatedEpochAtSlot(messageSlot) signerState := stateBySlot.Get(messageSlot) if signerState == nil || signerState.Slot != messageSlot { diff --git a/message/validation/pubsub_validation.go b/message/validation/pubsub_validation.go index f97b7916b8..0e81040f8c 100644 --- a/message/validation/pubsub_validation.go +++ b/message/validation/pubsub_validation.go @@ -12,7 +12,7 @@ func (mv *messageValidator) validatePubSubMessage(pMsg *pubsub.Message) error { maxMsgSize := MaxEncodedMsgSizeBeforePectra - if mv.netCfg.Beacon.EstimatedCurrentEpoch() >= mv.pectraForkEpoch { + if mv.netCfg.EstimatedCurrentEpoch() >= mv.pectraForkEpoch { maxMsgSize = MaxEncodedMsgSize } diff --git a/message/validation/signed_ssv_message.go b/message/validation/signed_ssv_message.go index 4283c44a01..272d7c90b1 100644 --- a/message/validation/signed_ssv_message.go +++ b/message/validation/signed_ssv_message.go @@ -120,7 +120,7 @@ func (mv *messageValidator) validateSSVMessage(ssvMessage *spectypes.SSVMessage) } // Rule: If domain is different then self domain - domain := mv.netCfg.DomainType + domain := mv.netCfg.GetDomainType() if !bytes.Equal(ssvMessage.GetID().GetDomain(), domain[:]) { err := ErrWrongDomain err.got = hex.EncodeToString(ssvMessage.MsgID.GetDomain()) diff --git a/message/validation/validation.go b/message/validation/validation.go index 39fb9c8963..c5e330dc54 100644 --- a/message/validation/validation.go +++ b/message/validation/validation.go @@ -11,8 +11,6 @@ import ( "sync" "time" - "github.com/ssvlabs/ssv/utils/casts" - "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/jellydator/ttlcache/v3" pubsub "github.com/libp2p/go-libp2p-pubsub" @@ -55,7 +53,7 @@ type peerIDWithMessageID struct { type messageValidator struct { logger *zap.Logger - netCfg networkconfig.NetworkConfig + netCfg networkconfig.Network pectraForkEpoch phase0.Epoch state *ttlcache.Cache[peerIDWithMessageID, *ValidatorState] validatorStore validatorStore @@ -81,7 +79,7 @@ type messageValidator struct { // New returns a new MessageValidator with the given network configuration and options. // It starts a goroutine that cleans up the state. func New( - netCfg networkconfig.NetworkConfig, + netCfg networkconfig.Network, validatorStore validatorStore, operators operators, dutyStore *dutystore.Store, @@ -89,14 +87,9 @@ func New( pectraForkEpoch phase0.Epoch, opts ...Option, ) MessageValidator { - ttl := time.Duration(MaxStoredSlots(netCfg)) * netCfg.SlotDurationSec() // #nosec G115 -- amount of slots cannot exceed int64 - mv := &messageValidator{ - logger: zap.NewNop(), - netCfg: netCfg, - state: ttlcache.New( - ttlcache.WithTTL[peerIDWithMessageID, *ValidatorState](ttl), - ), + logger: zap.NewNop(), + netCfg: netCfg, validationLockCache: ttlcache.New[peerIDWithMessageID, *sync.Mutex](), validatorStore: validatorStore, operators: operators, @@ -105,6 +98,11 @@ func New( pectraForkEpoch: pectraForkEpoch, } + ttl := time.Duration(mv.maxStoredSlots()) * netCfg.GetSlotDuration() // #nosec G115 -- amount of slots cannot exceed int64 + mv.state = ttlcache.New( + ttlcache.WithTTL[peerIDWithMessageID, *ValidatorState](ttl), + ) + for _, opt := range opts { opt(mv) } @@ -248,7 +246,6 @@ func (mv *messageValidator) getValidationLock(key peerIDWithMessageID) *sync.Mut lock := &sync.Mutex{} - epochDuration := casts.DurationFromUint64(mv.netCfg.Beacon.SlotsPerEpoch()) * mv.netCfg.Beacon.SlotDurationSec() // validationLockTTL specifies how much time a particular validation lock is meant to // live. It must be large enough for validation lock to never expire while we still are // expecting to process messages targeting that same validation lock. For a message @@ -257,8 +254,7 @@ func (mv *messageValidator) getValidationLock(key peerIDWithMessageID) *sync.Mut // be allowed to take place). // 2 epoch duration is a safe TTL to use - message validation will reject processing // for any message older than that. - validationLockTTL := 2 * epochDuration - mv.validationLockCache.Set(key, lock, validationLockTTL) + mv.validationLockCache.Set(key, lock, 2*mv.netCfg.EpochDuration()) return lock, nil }) @@ -302,7 +298,7 @@ func (mv *messageValidator) getCommitteeAndValidatorIndices(msgID spectypes.Mess } // Rule: If validator is not active - if !share.IsAttesting(mv.netCfg.Beacon.EstimatedCurrentEpoch()) { + if !share.IsAttesting(mv.netCfg.EstimatedCurrentEpoch()) { e := ErrValidatorNotAttesting e.got = share.Status.String() return CommitteeInfo{}, e @@ -324,14 +320,14 @@ func (mv *messageValidator) validatorState(key peerIDWithMessageID, committee [] cs := &ValidatorState{ operators: make([]*OperatorState, len(committee)), - storedSlotCount: MaxStoredSlots(mv.netCfg), + storedSlotCount: mv.maxStoredSlots(), } mv.state.Set(key, cs, ttlcache.DefaultTTL) return cs } -// MaxStoredSlots stores max amount of slots message validation stores. +// maxStoredSlots stores max amount of slots message validation stores. // It's exported to allow usage outside of message validation -func MaxStoredSlots(netCfg networkconfig.NetworkConfig) phase0.Slot { - return phase0.Slot(netCfg.Beacon.SlotsPerEpoch()) + LateSlotAllowance +func (mv *messageValidator) maxStoredSlots() uint64 { + return mv.netCfg.GetSlotsPerEpoch() + LateSlotAllowance } diff --git a/message/validation/validation_test.go b/message/validation/validation_test.go index dcdf5ea106..e56712f8a5 100644 --- a/message/validation/validation_test.go +++ b/message/validation/validation_test.go @@ -140,10 +140,10 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("happy flow", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.NoError(t, err) @@ -153,7 +153,7 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("message counts", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) height := specqbft.Height(slot) key := peerIDWithMessageID{ @@ -168,7 +168,7 @@ func Test_ValidateSSVMessage(t *testing.T) { signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -211,7 +211,7 @@ func Test_ValidateSSVMessage(t *testing.T) { message.MsgType = specqbft.CommitMsgType }) signedSSVMessage.FullData = nil - _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt.Add(netCfg.Beacon.SlotDurationSec())) + _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt.Add(netCfg.SlotDuration)) require.NoError(t, err) storedState = stateBySlot.Get(phase0.Slot(height) + 1) @@ -219,11 +219,11 @@ func Test_ValidateSSVMessage(t *testing.T) { require.EqualValues(t, 1, storedState.Round) require.EqualValues(t, SeenMsgTypes{v: 0b1000}, storedState.SeenMsgTypes) - _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt.Add(netCfg.Beacon.SlotDurationSec())) + _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt.Add(netCfg.SlotDuration)) require.ErrorContains(t, err, ErrDuplicatedMessage.Error()) signedSSVMessage = generateMultiSignedMessage(ks, committeeIdentifier, slot+1) - _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt.Add(netCfg.Beacon.SlotDurationSec())) + _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt.Add(netCfg.SlotDuration)) require.NoError(t, err) require.NotNil(t, stateBySlot) require.EqualValues(t, 1, storedState.Round) @@ -234,11 +234,11 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("pubsub message has no data", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) pmsg := &pubsub.Message{} - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) _, err := validator.handlePubsubMessage(pmsg, receivedAt) require.ErrorIs(t, err, ErrPubSubMessageHasNoData) @@ -248,7 +248,7 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("pubsub data too big", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) topic := commons.GetTopicFullName(commons.CommitteeTopicID(committeeID)[0]) msgSize := maxSignedMsgSize*2 + MessageOffset @@ -261,7 +261,7 @@ func Test_ValidateSSVMessage(t *testing.T) { }, } - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) _, err = validator.handlePubsubMessage(pmsg, receivedAt) e := ErrPubSubDataTooBig @@ -273,7 +273,7 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("empty pubsub message", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) topic := commons.GetTopicFullName(commons.CommitteeTopicID(committeeID)[0]) pmsg := &pubsub.Message{ @@ -284,7 +284,7 @@ func Test_ValidateSSVMessage(t *testing.T) { }, } - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) _, err = validator.handlePubsubMessage(pmsg, receivedAt) require.ErrorContains(t, err, ErrMalformedPubSubMessage.Error()) @@ -294,12 +294,12 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("bad data format", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) signedSSVMessage.SSVMessage.Data = bytes.Repeat([]byte{1}, 500) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -310,12 +310,12 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("no data", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) signedSSVMessage.SSVMessage.Data = []byte{} - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorIs(t, err, ErrEmptyData) @@ -329,14 +329,14 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("data too big", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) tooBigMsgSize := maxPayloadDataSize * 2 signedSSVMessage.SSVMessage.Data = bytes.Repeat([]byte{1}, tooBigMsgSize) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -350,12 +350,12 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("data size borderline / malformed message", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) signedSSVMessage.SSVMessage.Data = bytes.Repeat([]byte{1}, maxPayloadDataSize) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -366,7 +366,7 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("invalid SSV message type", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) signedSSVMessage.SSVMessage.MsgType = math.MaxUint64 @@ -380,7 +380,7 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("unknown validator", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) sk, err := eth2types.GenerateBLSPrivateKey() require.NoError(t, err) @@ -402,7 +402,7 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("unknown committee ID", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) unknownCommitteeID := bytes.Repeat([]byte{1}, 48) unknownIdentifier := spectypes.NewMsgID(netCfg.DomainType, unknownCommitteeID, committeeRole) @@ -419,14 +419,14 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("wrong domain", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) wrongDomain := spectypes.DomainType{math.MaxUint8, math.MaxUint8, math.MaxUint8, math.MaxUint8} badIdentifier := spectypes.NewMsgID(wrongDomain, encodedCommitteeID, committeeRole) signedSSVMessage := generateSignedMessage(ks, badIdentifier, slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) expectedErr := ErrWrongDomain expectedErr.got = hex.EncodeToString(wrongDomain[:]) @@ -439,13 +439,13 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("invalid role", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) badIdentifier := spectypes.NewMsgID(netCfg.DomainType, encodedCommitteeID, math.MaxInt32) signedSSVMessage := generateSignedMessage(ks, badIdentifier, slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorIs(t, err, ErrInvalidRole) }) @@ -454,13 +454,13 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("unexpected consensus message", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) badIdentifier := spectypes.NewMsgID(netCfg.DomainType, shares.active.ValidatorPubKey[:], spectypes.RoleValidatorRegistration) signedSSVMessage := generateSignedMessage(ks, badIdentifier, slot) topicID := commons.CommitteeTopicID(committeeID)[0] - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) expectedErr := ErrUnexpectedConsensusMessage expectedErr.got = spectypes.RoleValidatorRegistration @@ -478,13 +478,13 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("liquidated validator", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) liquidatedIdentifier := spectypes.NewMsgID(netCfg.DomainType, shares.liquidated.ValidatorPubKey[:], nonCommitteeRole) signedSSVMessage := generateSignedMessage(ks, liquidatedIdentifier, slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) expectedErr := ErrValidatorLiquidated require.ErrorIs(t, err, expectedErr) @@ -494,13 +494,13 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("unknown state validator", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) inactiveIdentifier := spectypes.NewMsgID(netCfg.DomainType, shares.inactive.ValidatorPubKey[:], nonCommitteeRole) signedSSVMessage := generateSignedMessage(ks, inactiveIdentifier, slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) expectedErr := ErrNoShareMetadata require.ErrorIs(t, err, expectedErr) @@ -510,12 +510,12 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("pending queued state validator", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) nonUpdatedMetadataNextEpochIdentifier := spectypes.NewMsgID(netCfg.DomainType, shares.nonUpdatedMetadataNextEpoch.ValidatorPubKey[:], nonCommitteeRole) signedSSVMessage := generateSignedMessage(ks, nonUpdatedMetadataNextEpochIdentifier, slot) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) expectedErr := ErrValidatorNotAttesting @@ -528,7 +528,7 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("active validator with pending queued state", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.EstimatedCurrentSlot() + slot := netCfg.EstimatedCurrentSlot() nonUpdatedMetadataIdentifier := spectypes.NewMsgID(netCfg.DomainType, shares.nonUpdatedMetadata.ValidatorPubKey[:], nonCommitteeRole) qbftMessage := &specqbft.Message{ @@ -546,7 +546,7 @@ func Test_ValidateSSVMessage(t *testing.T) { signedSSVMessage := spectestingutils.SignQBFTMsg(ks.OperatorKeys[leader], leader, qbftMessage) signedSSVMessage.FullData = spectestingutils.TestingQBFTFullData - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(committeeID)[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.NoError(t, err) @@ -556,12 +556,12 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("no share metadata", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) noMetadataIdentifier := spectypes.NewMsgID(netCfg.DomainType, shares.noMetadata.ValidatorPubKey[:], nonCommitteeRole) signedSSVMessage := generateSignedMessage(ks, noMetadataIdentifier, slot) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorIs(t, err, ErrNoShareMetadata) @@ -572,7 +572,7 @@ func Test_ValidateSSVMessage(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) epoch := phase0.Epoch(1) - slot := netCfg.Beacon.FirstSlotAtEpoch(epoch) + slot := netCfg.FirstSlotAtEpoch(epoch) dutyStore.Proposer.Set(epoch, []dutystore.StoreDuty[eth2apiv1.ProposerDuty]{ {Slot: slot, ValidatorIndex: shares.active.ValidatorIndex, Duty: ð2apiv1.ProposerDuty{}, InCommittee: true}, @@ -586,39 +586,39 @@ func Test_ValidateSSVMessage(t *testing.T) { // First duty. topicID := commons.CommitteeTopicID(committeeID)[0] - _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, netCfg.Beacon.GetSlotStartTime(slot)) + _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, netCfg.GetSlotStartTime(slot)) require.NoError(t, err) // Second duty. signedSSVMessage = generateSignedMessage(ks, identifier, slot+4) - _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, netCfg.Beacon.GetSlotStartTime(slot+4)) + _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, netCfg.GetSlotStartTime(slot+4)) require.NoError(t, err) // Second duty (another message). signedSSVMessage = generateSignedMessage(ks, identifier, slot+4, func(qbftMessage *specqbft.Message) { qbftMessage.MsgType = specqbft.RoundChangeMsgType }) - _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, netCfg.Beacon.GetSlotStartTime(slot+4)) + _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, netCfg.GetSlotStartTime(slot+4)) require.NoError(t, err) // Third duty. // TODO: this should fail, see https://github.com/ssvlabs/ssv/pull/1758 signedSSVMessage = generateSignedMessage(ks, identifier, slot+8) - _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, netCfg.Beacon.GetSlotStartTime(slot+8)) + _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, netCfg.GetSlotStartTime(slot+8)) require.NoError(t, err) // Third duty (another message). signedSSVMessage = generateSignedMessage(ks, identifier, slot+8, func(qbftMessage *specqbft.Message) { qbftMessage.MsgType = specqbft.RoundChangeMsgType }) - _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, netCfg.Beacon.GetSlotStartTime(slot+8)) + _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, netCfg.GetSlotStartTime(slot+8)) require.ErrorContains(t, err, ErrTooManyDutiesPerEpoch.Error()) }) // Throw error if getting a message for proposal and see there is no message from beacon t.Run("no proposal duties", func(t *testing.T) { const epoch = 1 - slot := netCfg.Beacon.FirstSlotAtEpoch(epoch) + slot := netCfg.FirstSlotAtEpoch(epoch) ds := dutystore.New() ds.Proposer.Set(epoch, []dutystore.StoreDuty[eth2apiv1.ProposerDuty]{ @@ -630,7 +630,7 @@ func Test_ValidateSSVMessage(t *testing.T) { signedSSVMessage := generateSignedMessage(ks, identifier, slot) topicID := commons.CommitteeTopicID(committeeID)[0] - _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, netCfg.Beacon.GetSlotStartTime(slot)) + _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, netCfg.GetSlotStartTime(slot)) require.ErrorContains(t, err, ErrNoDuty.Error()) ds = dutystore.New() @@ -638,17 +638,16 @@ func Test_ValidateSSVMessage(t *testing.T) { {Slot: slot, ValidatorIndex: shares.active.ValidatorIndex, Duty: ð2apiv1.ProposerDuty{}, InCommittee: true}, }) validator = New(netCfg, validatorStore, operators, ds, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, netCfg.Beacon.GetSlotStartTime(slot)) + _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, netCfg.GetSlotStartTime(slot)) require.NoError(t, err) }) t.Run("accept pre-consensus randao message when epoch duties are not set", func(t *testing.T) { currentSlot := &utils.SlotValue{} - mockNetworkConfig := networkconfig.NetworkConfig{} - mockNetworkConfig.Beacon = utils.SetupMockBeaconNetwork(t, currentSlot) + mockNetworkConfig := utils.SetupMockNetworkConfig(t, networkconfig.TestNetwork.DomainType, currentSlot) const epoch = 1 - currentSlot.SetSlot(netCfg.Beacon.FirstSlotAtEpoch(epoch)) + currentSlot.SetSlot(netCfg.FirstSlotAtEpoch(epoch)) ds := dutystore.New() @@ -661,13 +660,13 @@ func Test_ValidateSSVMessage(t *testing.T) { dutyExecutorID := shares.active.ValidatorPubKey[:] ssvMessage := &spectypes.SSVMessage{ MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: spectypes.NewMsgID(mockNetworkConfig.DomainType, dutyExecutorID, spectypes.RoleProposer), + MsgID: spectypes.NewMsgID(mockNetworkConfig.GetDomainType(), dutyExecutorID, spectypes.RoleProposer), Data: encodedMessages, } signedSSVMessage := spectestingutils.SignedSSVMessageWithSigner(1, ks.OperatorKeys[1], ssvMessage) - receivedAt := mockNetworkConfig.Beacon.GetSlotStartTime(currentSlot.GetSlot()) + receivedAt := mockNetworkConfig.GetSlotStartTime(currentSlot.GetSlot()) topicID := commons.CommitteeTopicID(committeeID)[0] require.False(t, ds.Proposer.IsEpochSet(epoch)) @@ -678,11 +677,10 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("reject pre-consensus randao message when epoch duties are set", func(t *testing.T) { currentSlot := &utils.SlotValue{} - mockNetworkConfig := networkconfig.NetworkConfig{} - mockNetworkConfig.Beacon = utils.SetupMockBeaconNetwork(t, currentSlot) + mockNetworkConfig := utils.SetupMockNetworkConfig(t, networkconfig.TestNetwork.DomainType, currentSlot) const epoch = 1 - currentSlot.SetSlot(mockNetworkConfig.Beacon.FirstSlotAtEpoch(epoch)) + currentSlot.SetSlot(mockNetworkConfig.FirstSlotAtEpoch(epoch)) ds := dutystore.New() ds.Proposer.Set(epoch, make([]dutystore.StoreDuty[eth2apiv1.ProposerDuty], 0)) @@ -696,13 +694,13 @@ func Test_ValidateSSVMessage(t *testing.T) { dutyExecutorID := shares.active.ValidatorPubKey[:] ssvMessage := &spectypes.SSVMessage{ MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: spectypes.NewMsgID(mockNetworkConfig.DomainType, dutyExecutorID, spectypes.RoleProposer), + MsgID: spectypes.NewMsgID(mockNetworkConfig.GetDomainType(), dutyExecutorID, spectypes.RoleProposer), Data: encodedMessages, } signedSSVMessage := spectestingutils.SignedSSVMessageWithSigner(1, ks.OperatorKeys[1], ssvMessage) - receivedAt := mockNetworkConfig.Beacon.GetSlotStartTime(currentSlot.GetSlot()) + receivedAt := mockNetworkConfig.GetSlotStartTime(currentSlot.GetSlot()) topicID := commons.CommitteeTopicID(committeeID)[0] require.True(t, ds.Proposer.IsEpochSet(epoch)) @@ -713,7 +711,7 @@ func Test_ValidateSSVMessage(t *testing.T) { //// Get error when receiving a message with over 13 partial signatures t.Run("partial message too big", func(t *testing.T) { - // slot := netCfg.Beacon.FirstSlotAtEpoch(1) + // slot := netCfg.FirstSlotAtEpoch(1) msg := spectestingutils.PostConsensusAttestationMsg(ks.Shares[1], 1, spec.DataVersionPhase0) for i := 0; i < 1512; i++ { msg.Messages = append(msg.Messages, msg.Messages[0]) @@ -727,7 +725,7 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("signer ID not in committee", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.EstimatedCurrentSlot() + slot := netCfg.EstimatedCurrentSlot() qbftMessage := &specqbft.Message{ MsgType: specqbft.ProposalMsgType, @@ -743,7 +741,7 @@ func Test_ValidateSSVMessage(t *testing.T) { signedSSVMessage := spectestingutils.SignQBFTMsg(ks.OperatorKeys[1], 5, qbftMessage) signedSSVMessage.FullData = spectestingutils.TestingQBFTFullData - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorContains(t, err, ErrSignerNotInCommittee.Error()) @@ -753,12 +751,12 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("partial zero signer ID", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) msg := spectestingutils.SignPartialSigSSVMessage(ks, spectestingutils.SSVMsgAggregator(nil, spectestingutils.PostConsensusAggregatorMsg(ks.Shares[1], 1, spec.DataVersionPhase0))) msg.OperatorIDs = []spectypes.OperatorID{0} - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(msg.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(msg, topicID, peerID, receivedAt) require.ErrorIs(t, err, ErrZeroSigner) @@ -768,14 +766,14 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("partial inconsistent signer ID", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) ssvMessage := spectestingutils.SSVMsgAggregator(nil, spectestingutils.PostConsensusAggregatorMsg(ks.Shares[1], 1, spec.DataVersionPhase0)) ssvMessage.MsgID = committeeIdentifier partialSigSSVMessage := spectestingutils.SignPartialSigSSVMessage(ks, ssvMessage) partialSigSSVMessage.OperatorIDs = []spectypes.OperatorID{2} - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(committeeID)[0] _, err = validator.handleSignedSSVMessage(partialSigSSVMessage, topicID, peerID, receivedAt) expectedErr := ErrInconsistentSigners @@ -788,7 +786,7 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("no partial signature messages", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) messages := spectestingutils.PostConsensusAggregatorMsg(ks.Shares[1], 1, spec.DataVersionPhase0) messages.Messages = nil @@ -796,7 +794,7 @@ func Test_ValidateSSVMessage(t *testing.T) { ssvMessage.MsgID = committeeIdentifier signedSSVMessage := spectestingutils.SignedSSVMessageWithSigner(1, ks.OperatorKeys[1], ssvMessage) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(committeeID)[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorIs(t, err, ErrNoPartialSignatureMessages) @@ -806,12 +804,12 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("partial wrong RSA signature size", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) partialSigSSVMessage := spectestingutils.SignPartialSigSSVMessage(ks, spectestingutils.SSVMsgAggregator(nil, spectestingutils.PostConsensusAggregatorMsg(ks.Shares[1], 1, spec.DataVersionPhase0))) partialSigSSVMessage.Signatures = [][]byte{{1}} - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(partialSigSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(partialSigSSVMessage, topicID, peerID, receivedAt) require.ErrorContains(t, err, ErrWrongRSASignatureSize.Error()) @@ -863,7 +861,7 @@ func Test_ValidateSSVMessage(t *testing.T) { signedSSVMessage := spectestingutils.SignedSSVMessageWithSigner(1, ks.OperatorKeys[1], ssvMessage) - receivedAt := netCfg.Beacon.GetSlotStartTime(spectestingutils.TestingDutySlot) + receivedAt := netCfg.GetSlotStartTime(spectestingutils.TestingDutySlot) topicID := commons.CommitteeTopicID(committeeID)[0] @@ -892,7 +890,7 @@ func Test_ValidateSSVMessage(t *testing.T) { signedSSVMessage := spectestingutils.SignedSSVMessageWithSigner(1, ks.OperatorKeys[1], ssvMessage) - receivedAt := netCfg.Beacon.GetSlotStartTime(spectestingutils.TestingDutySlot) + receivedAt := netCfg.GetSlotStartTime(spectestingutils.TestingDutySlot) topicID := commons.CommitteeTopicID(committeeID)[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorContains(t, err, ErrInvalidPartialSignatureType.Error()) @@ -941,7 +939,7 @@ func Test_ValidateSSVMessage(t *testing.T) { signedSSVMessage := spectestingutils.SignedSSVMessageWithSigner(1, ks.OperatorKeys[1], ssvMessage) - receivedAt := netCfg.Beacon.GetSlotStartTime(spectestingutils.TestingDutySlot) + receivedAt := netCfg.GetSlotStartTime(spectestingutils.TestingDutySlot) topicID := commons.CommitteeTopicID(committeeID)[0] t.Log(signedSSVMessage.SSVMessage.MsgID.GetDomain()) _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -956,12 +954,12 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("invalid QBFT message type", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot, func(message *specqbft.Message) { message.MsgType = math.MaxUint64 }) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) expectedErr := ErrUnknownQBFTMessageType @@ -972,11 +970,11 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("wrong signature size", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) signedSSVMessage.Signatures = [][]byte{{0x1}} - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorContains(t, err, ErrWrongRSASignatureSize.Error()) @@ -986,11 +984,11 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("no signers", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) signedSSVMessage.OperatorIDs = nil - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorIs(t, err, ErrNoSigners) @@ -1001,7 +999,7 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("more signers than committee size", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) signedSSVMessage.OperatorIDs = []spectypes.OperatorID{1, 2, 3, 4, 5} signedSSVMessage.Signatures = [][]byte{ @@ -1012,7 +1010,7 @@ func Test_ValidateSSVMessage(t *testing.T) { signedSSVMessage.Signatures[0], } - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorContains(t, err, ErrSignerNotInCommittee.Error()) @@ -1022,11 +1020,11 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("consensus zero signer", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) signedSSVMessage.OperatorIDs = []spectypes.OperatorID{0} - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorIs(t, err, ErrZeroSigner) @@ -1036,11 +1034,11 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("non unique signer", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateMultiSignedMessage(ks, committeeIdentifier, slot) signedSSVMessage.OperatorIDs = []spectypes.OperatorID{1, 2, 2} - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorIs(t, err, ErrDuplicatedSigner) @@ -1140,11 +1138,11 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("signers not sorted", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateMultiSignedMessage(ks, committeeIdentifier, slot) signedSSVMessage.OperatorIDs = []spectypes.OperatorID{3, 2, 1} - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorIs(t, err, ErrSignersNotSorted) @@ -1154,11 +1152,11 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("wrong signers/signatures length", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateMultiSignedMessage(ks, committeeIdentifier, slot) signedSSVMessage.OperatorIDs = committee - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -1169,12 +1167,12 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("decided too few signers", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateMultiSignedMessage(ks, committeeIdentifier, slot) signedSSVMessage.OperatorIDs = []spectypes.OperatorID{1, 2} signedSSVMessage.Signatures = signedSSVMessage.Signatures[:2] - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -1185,12 +1183,12 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("non decided with multiple signers", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateMultiSignedMessage(ks, committeeIdentifier, slot, func(message *specqbft.Message) { message.MsgType = specqbft.ProposalMsgType }) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -1202,7 +1200,7 @@ func Test_ValidateSSVMessage(t *testing.T) { // Send late message for all roles and receive late message error t.Run("late message", func(t *testing.T) { const epoch = 1 - slot := netCfg.Beacon.FirstSlotAtEpoch(epoch) + slot := netCfg.FirstSlotAtEpoch(epoch) ds := dutystore.New() ds.Proposer.Set(epoch, []dutystore.StoreDuty[eth2apiv1.ProposerDuty]{ @@ -1215,10 +1213,10 @@ func Test_ValidateSSVMessage(t *testing.T) { validator := New(netCfg, validatorStore, operators, ds, signatureVerifier, phase0.Epoch(0)).(*messageValidator) tests := map[spectypes.RunnerRole]time.Time{ - spectypes.RoleCommittee: netCfg.Beacon.GetSlotStartTime(slot + 35), - spectypes.RoleAggregator: netCfg.Beacon.GetSlotStartTime(slot + 35), - spectypes.RoleProposer: netCfg.Beacon.GetSlotStartTime(slot + 4), - spectypes.RoleSyncCommitteeContribution: netCfg.Beacon.GetSlotStartTime(slot + 4), + spectypes.RoleCommittee: netCfg.GetSlotStartTime(slot + 35), + spectypes.RoleAggregator: netCfg.GetSlotStartTime(slot + 35), + spectypes.RoleProposer: netCfg.GetSlotStartTime(slot + 4), + spectypes.RoleSyncCommitteeContribution: netCfg.GetSlotStartTime(slot + 4), } for role, receivedAt := range tests { @@ -1242,10 +1240,10 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("early message", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot - 1) + receivedAt := netCfg.GetSlotStartTime(slot - 1) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -1256,11 +1254,11 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("not a leader", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) signedSSVMessage.OperatorIDs = []spectypes.OperatorID{2} - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorContains(t, err, ErrSignerNotLeader.Error()) @@ -1270,13 +1268,13 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("malformed prepare justification", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot, func(message *specqbft.Message) { message.PrepareJustification = [][]byte{{1}} }) signedSSVMessage.OperatorIDs = []spectypes.OperatorID{2} - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -1287,7 +1285,7 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("non-proposal with prepare justification", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot, func(message *specqbft.Message) { message.PrepareJustification = spectestingutils.MarshalJustifications([]*spectypes.SignedSSVMessage{ @@ -1299,7 +1297,7 @@ func Test_ValidateSSVMessage(t *testing.T) { }) signedSSVMessage.FullData = nil - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -1310,7 +1308,7 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("non-proposal with round change justification", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot, func(message *specqbft.Message) { message.RoundChangeJustification = spectestingutils.MarshalJustifications([]*spectypes.SignedSSVMessage{ @@ -1322,7 +1320,7 @@ func Test_ValidateSSVMessage(t *testing.T) { }) signedSSVMessage.FullData = nil - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -1333,14 +1331,14 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("malformed round change justification", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot, func(message *specqbft.Message) { message.RoundChangeJustification = [][]byte{{1}} }) signedSSVMessage.FullData = nil - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -1351,12 +1349,12 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("wrong root hash", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) signedSSVMessage.FullData = []byte{1} - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -1368,11 +1366,11 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("double proposal with different data", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.NoError(t, err) @@ -1393,7 +1391,7 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("double prepare", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) identifier := spectypes.NewMsgID(netCfg.DomainType, ks.ValidatorPK.Serialize(), spectypes.RoleProposer) signedSSVMessage := generateSignedMessage(ks, identifier, slot, func(message *specqbft.Message) { @@ -1401,7 +1399,7 @@ func Test_ValidateSSVMessage(t *testing.T) { }) signedSSVMessage.FullData = nil - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(committeeID)[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.NoError(t, err) @@ -1416,14 +1414,14 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("double commit", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot, func(message *specqbft.Message) { message.MsgType = specqbft.CommitMsgType }) signedSSVMessage.FullData = nil - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.NoError(t, err) @@ -1438,14 +1436,14 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("double round change", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot, func(message *specqbft.Message) { message.MsgType = specqbft.RoundChangeMsgType }) signedSSVMessage.FullData = nil - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.NoError(t, err) @@ -1460,14 +1458,14 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("decided with same signers", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateMultiSignedMessage(ks, committeeIdentifier, slot, func(message *specqbft.Message) { message.MsgType = specqbft.CommitMsgType }) signedSSVMessage.FullData = nil - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -1481,13 +1479,13 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("slot already advanced", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, nonCommitteeIdentifier, slot, func(message *specqbft.Message) { message.Height = 8 }) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(committeeID)[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.NoError(t, err) @@ -1504,13 +1502,13 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("round already advanced", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot, func(message *specqbft.Message) { message.Round = 5 }) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(5 * roundtimer.QuickTimeout) + receivedAt := netCfg.GetSlotStartTime(slot).Add(5 * roundtimer.QuickTimeout) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.NoError(t, err) @@ -1526,7 +1524,7 @@ func Test_ValidateSSVMessage(t *testing.T) { // Receive message from a round that is too high for that epoch should receive an error t.Run("round too high", func(t *testing.T) { const epoch = 1 - slot := netCfg.Beacon.FirstSlotAtEpoch(epoch) + slot := netCfg.FirstSlotAtEpoch(epoch) ds := dutystore.New() ds.Proposer.Set(epoch, []dutystore.StoreDuty[eth2apiv1.ProposerDuty]{ @@ -1571,7 +1569,7 @@ func Test_ValidateSSVMessage(t *testing.T) { sinceSlotStart += roundtimer.QuickTimeout } - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(sinceSlotStart) + receivedAt := netCfg.GetSlotStartTime(slot).Add(sinceSlotStart) _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorContains(t, err, ErrRoundTooHigh.Error()) }) @@ -1582,12 +1580,12 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("event message", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) signedSSVMessage.SSVMessage.MsgType = message.SSVEventMsgType - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -1598,13 +1596,13 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("unknown type message", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) unknownType := spectypes.MsgType(12345) signedSSVMessage.SSVMessage.MsgType = unknownType - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -1615,11 +1613,11 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("wrong signature", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, wrongSignatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -1630,11 +1628,11 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("incorrect topic", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := "incorrect" _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) @@ -1645,9 +1643,9 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("nil signed ssv message", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) _, err = validator.handleSignedSSVMessage(nil, "", peerID, receivedAt) require.ErrorContains(t, err, ErrNilSignedSSVMessage.Error()) @@ -1657,12 +1655,12 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("nil ssv message", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) signedSSVMessage.SSVMessage = nil - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) _, err = validator.handleSignedSSVMessage(signedSSVMessage, "", peerID, receivedAt) require.ErrorContains(t, err, ErrNilSSVMessage.Error()) @@ -1672,13 +1670,13 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("zero round", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot, func(message *specqbft.Message) { message.Round = specqbft.NoRound }) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorContains(t, err, ErrZeroRound.Error()) @@ -1688,12 +1686,12 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("no signatures", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot) signedSSVMessage.Signatures = [][]byte{} - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorContains(t, err, ErrNoSignatures.Error()) @@ -1703,7 +1701,7 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("mismatched identifier", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot, func(message *specqbft.Message) { wrongID := spectypes.NewMsgID(netCfg.DomainType, encodedCommitteeID[:], nonCommitteeRole) @@ -1711,7 +1709,7 @@ func Test_ValidateSSVMessage(t *testing.T) { }) signedSSVMessage.SSVMessage.MsgID = committeeIdentifier - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorContains(t, err, ErrMismatchedIdentifier.Error()) @@ -1721,13 +1719,13 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("prepare/commit with FullData", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot, func(message *specqbft.Message) { message.MsgType = specqbft.PrepareMsgType }) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorContains(t, err, ErrPrepareOrCommitWithFullData.Error()) @@ -1743,14 +1741,14 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("non-consensus with FullData", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) ssvMessage := spectestingutils.SSVMsgAggregator(nil, spectestingutils.PostConsensusAggregatorMsg(ks.Shares[1], 1, spec.DataVersionPhase0)) ssvMessage.MsgID = committeeIdentifier signedSSVMessage := spectestingutils.SignPartialSigSSVMessage(ks, ssvMessage) signedSSVMessage.FullData = []byte{1} - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(committeeID)[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorIs(t, err, ErrFullDataNotInConsensusMessage) @@ -1760,7 +1758,7 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("partial signature with multiple signers", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) ssvMessage := spectestingutils.SSVMsgAggregator(nil, spectestingutils.PostConsensusAggregatorMsg(ks.Shares[1], 1, spec.DataVersionPhase0)) ssvMessage.MsgID = committeeIdentifier @@ -1768,7 +1766,7 @@ func Test_ValidateSSVMessage(t *testing.T) { signedSSVMessage.OperatorIDs = []spectypes.OperatorID{1, 2} signedSSVMessage.Signatures = append(signedSSVMessage.Signatures, signedSSVMessage.Signatures[0]) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(committeeID)[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorIs(t, err, ErrPartialSigOneSigner) @@ -1778,7 +1776,7 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("partial signature with too many messages", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) messages := spectestingutils.PostConsensusAggregatorMsg(ks.Shares[1], 1, spec.DataVersionPhase0) for i := 0; i < 12; i++ { @@ -1796,7 +1794,7 @@ func Test_ValidateSSVMessage(t *testing.T) { signedSSVMessage := spectestingutils.SignPartialSigSSVMessage(ks, ssvMessage) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorContains(t, err, ErrTooManyPartialSignatureMessages.Error()) @@ -1806,7 +1804,7 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("partial signature with triple validator index", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) messages := spectestingutils.PostConsensusAggregatorMsg(ks.Shares[1], 1, spec.DataVersionPhase0) for i := 0; i < 3; i++ { @@ -1824,7 +1822,7 @@ func Test_ValidateSSVMessage(t *testing.T) { signedSSVMessage := spectestingutils.SignPartialSigSSVMessage(ks, ssvMessage) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorContains(t, err, ErrTripleValidatorIndexInPartialSignatures.Error()) @@ -1834,7 +1832,7 @@ func Test_ValidateSSVMessage(t *testing.T) { t.Run("partial signature with validator index mismatch", func(t *testing.T) { validator := New(netCfg, validatorStore, operators, dutyStore, signatureVerifier, phase0.Epoch(0)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) + slot := netCfg.FirstSlotAtEpoch(1) messages := spectestingutils.PostConsensusAggregatorMsg(ks.Shares[1], 1, spec.DataVersionPhase0) messages.Messages[0].ValidatorIndex = math.MaxUint64 @@ -1850,7 +1848,7 @@ func Test_ValidateSSVMessage(t *testing.T) { signedSSVMessage := spectestingutils.SignPartialSigSSVMessage(ks, ssvMessage) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) + receivedAt := netCfg.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(committeeID)[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, peerID, receivedAt) require.ErrorContains(t, err, ErrValidatorIndexMismatch.Error()) @@ -1912,8 +1910,8 @@ func generateShares(t *testing.T, ks *spectestingutils.TestKeySet, ns storage.St copy(inactiveShare.ValidatorPubKey[:], inactiveSK.PublicKey().Marshal()) require.NoError(t, ns.Shares().Save(nil, inactiveShare)) - slot := netCfg.Beacon.EstimatedCurrentSlot() - activationEpoch := netCfg.Beacon.EstimatedEpochAtSlot(slot) + slot := netCfg.EstimatedCurrentSlot() + activationEpoch := netCfg.EstimatedEpochAtSlot(slot) exitEpoch := goclient.FarFutureEpoch nonUpdatedMetadataShare := &ssvtypes.SSVShare{ diff --git a/migrations/migration_4_configlock_add_alan_fork_to_network_name.go b/migrations/migration_4_configlock_add_alan_fork_to_network_name.go index 6a49712c01..03e99cc5f9 100644 --- a/migrations/migration_4_configlock_add_alan_fork_to_network_name.go +++ b/migrations/migration_4_configlock_add_alan_fork_to_network_name.go @@ -6,7 +6,6 @@ import ( "go.uber.org/zap" - "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/storage/basedb" ) @@ -27,12 +26,6 @@ var migration_4_configlock_add_alan_fork_to_network_name = Migration{ // If config is not found, it means the node is not initialized yet if found { - networkConfig, err := networkconfig.GetNetworkConfigByName(config.NetworkName) - if err != nil { - return fmt.Errorf("failed to get network config by name: %w", err) - } - - config.NetworkName = networkConfig.NetworkName() if err := nodeStorage.SaveConfig(txn, config); err != nil { return fmt.Errorf("failed to save config: %w", err) } diff --git a/migrations/migrations.go b/migrations/migrations.go index 6324b43167..921b7c8ed3 100644 --- a/migrations/migrations.go +++ b/migrations/migrations.go @@ -53,20 +53,20 @@ type Migrations []Migration // Options is the options for running migrations. type Options struct { - Db basedb.Database - NodeStorage operatorstorage.Storage - DbPath string - NetworkConfig networkconfig.NetworkConfig + Db basedb.Database + NodeStorage operatorstorage.Storage + DbPath string + BeaconConfig networkconfig.Beacon } // nolint func (o Options) nodeStorage(logger *zap.Logger) (operatorstorage.Storage, error) { - return operatorstorage.NewNodeStorage(o.NetworkConfig, logger, o.Db) + return operatorstorage.NewNodeStorage(o.BeaconConfig, logger, o.Db) } // nolint func (o Options) signerStorage(logger *zap.Logger) ekm.Storage { - return ekm.NewSignerStorage(o.Db, o.NetworkConfig.Beacon, logger) + return ekm.NewSignerStorage(o.Db, o.BeaconConfig, logger) } // Run executes the migrations. diff --git a/network/discovery/dv5_bootnode.go b/network/discovery/dv5_bootnode.go index 3831b71299..cc58a03940 100644 --- a/network/discovery/dv5_bootnode.go +++ b/network/discovery/dv5_bootnode.go @@ -28,9 +28,9 @@ type Bootnode struct { } // NewBootnode creates a new bootnode -func NewBootnode(pctx context.Context, logger *zap.Logger, networkCfg networkconfig.NetworkConfig, opts *BootnodeOptions) (*Bootnode, error) { +func NewBootnode(pctx context.Context, logger *zap.Logger, ssvConfig networkconfig.SSVConfig, opts *BootnodeOptions) (*Bootnode, error) { ctx, cancel := context.WithCancel(pctx) - disc, err := createBootnodeDiscovery(ctx, logger, networkCfg, opts) + disc, err := createBootnodeDiscovery(ctx, logger, ssvConfig, opts) if err != nil { cancel() return nil, err @@ -52,13 +52,13 @@ func (b *Bootnode) Close() error { return nil } -func createBootnodeDiscovery(ctx context.Context, logger *zap.Logger, networkCfg networkconfig.NetworkConfig, opts *BootnodeOptions) (Service, error) { +func createBootnodeDiscovery(ctx context.Context, logger *zap.Logger, ssvConfig networkconfig.SSVConfig, opts *BootnodeOptions) (Service, error) { privKey, err := utils.ECDSAPrivateKey(logger.Named(logging.NameBootNode), opts.PrivateKey) if err != nil { return nil, err } discOpts := &Options{ - NetworkConfig: networkCfg, + SSVConfig: ssvConfig, DiscV5Opts: &DiscV5Options{ IP: opts.ExternalIP, BindIP: "", // net.IPv4zero.String() diff --git a/network/discovery/dv5_service.go b/network/discovery/dv5_service.go index 0b3685a1e8..3ff6f6552b 100644 --- a/network/discovery/dv5_service.go +++ b/network/discovery/dv5_service.go @@ -72,8 +72,8 @@ type DiscV5Service struct { conn *net.UDPConn sharedConn *SharedUDPConn - networkConfig networkconfig.NetworkConfig - subnets commons.Subnets + ssvConfig networkconfig.SSVConfig + subnets commons.Subnets publishLock chan struct{} } @@ -86,7 +86,7 @@ func newDiscV5Service(pctx context.Context, logger *zap.Logger, opts *Options) ( cancel: cancel, conns: opts.ConnIndex, subnetsIdx: opts.SubnetsIdx, - networkConfig: opts.NetworkConfig, + ssvConfig: opts.SSVConfig, subnets: opts.DiscV5Opts.Subnets, publishLock: make(chan struct{}, 1), discoveredPeersPool: opts.DiscoveredPeersPool, @@ -196,9 +196,9 @@ func (dvs *DiscV5Service) checkPeer(ctx context.Context, e PeerEvent) error { if err != nil { return errors.Wrap(err, "could not read domain type") } - if dvs.networkConfig.DomainType != nodeDomainType { + if dvs.ssvConfig.DomainType != nodeDomainType { recordPeerSkipped(ctx, skipReasonDomainTypeMismatch) - return fmt.Errorf("domain type %x doesn't match %x", nodeDomainType, dvs.networkConfig.DomainType) + return fmt.Errorf("domain type %x doesn't match %x", nodeDomainType, dvs.ssvConfig.DomainType) } // Get the peer's subnets, skipping if it has none. @@ -253,7 +253,7 @@ func (dvs *DiscV5Service) initDiscV5Listener(discOpts *Options) error { } // Get the protocol ID, or set to default if not provided - protocolID := dvs.networkConfig.DiscoveryProtocolID + protocolID := dvs.ssvConfig.DiscoveryProtocolID emptyProtocolID := [6]byte{} if protocolID == emptyProtocolID { protocolID = DefaultSSVProtocolID @@ -278,7 +278,7 @@ func (dvs *DiscV5Service) initDiscV5Listener(discOpts *Options) error { fields.BindIP(bindIP), zap.Uint16("UdpPort", opts.Port), fields.ENRLocalNode(localNode), - fields.Domain(discOpts.NetworkConfig.DomainType), + fields.Domain(discOpts.SSVConfig.DomainType), fields.ProtocolID(protocolID), ) @@ -297,7 +297,7 @@ func (dvs *DiscV5Service) initDiscV5Listener(discOpts *Options) error { fields.BindIP(bindIP), zap.Uint16("UdpPort", opts.Port), fields.ENRLocalNode(localNode), - fields.Domain(discOpts.NetworkConfig.DomainType), + fields.Domain(discOpts.SSVConfig.DomainType), ) dvs.dv5Listener = NewForkingDV5Listener(dvs.logger, dv5PreForkListener, dv5PostForkListener, 5*time.Second) @@ -388,12 +388,12 @@ func (dvs *DiscV5Service) DeregisterSubnets(subnets ...uint64) (updated bool, er // PublishENR publishes the ENR with the current domain type across the network func (dvs *DiscV5Service) PublishENR() { // Update own node record. - err := records.SetDomainTypeEntry(dvs.dv5Listener.LocalNode(), records.KeyDomainType, dvs.networkConfig.DomainType) + err := records.SetDomainTypeEntry(dvs.dv5Listener.LocalNode(), records.KeyDomainType, dvs.ssvConfig.DomainType) if err != nil { dvs.logger.Error("could not set domain type", zap.Error(err)) return } - err = records.SetDomainTypeEntry(dvs.dv5Listener.LocalNode(), records.KeyNextDomainType, dvs.networkConfig.DomainType) + err = records.SetDomainTypeEntry(dvs.dv5Listener.LocalNode(), records.KeyNextDomainType, dvs.ssvConfig.DomainType) if err != nil { dvs.logger.Error("could not set next domain type", zap.Error(err)) return @@ -458,8 +458,8 @@ func (dvs *DiscV5Service) createLocalNode(discOpts *Options, ipAddr net.IP) (*en localNode, // Satisfy decorations of forks supported by this node. - DecorateWithDomainType(records.KeyDomainType, dvs.networkConfig.DomainType), - DecorateWithDomainType(records.KeyNextDomainType, dvs.networkConfig.DomainType), + DecorateWithDomainType(records.KeyDomainType, dvs.ssvConfig.DomainType), + DecorateWithDomainType(records.KeyNextDomainType, dvs.ssvConfig.DomainType), DecorateWithSubnets(opts.Subnets), ) if err != nil { @@ -468,7 +468,7 @@ func (dvs *DiscV5Service) createLocalNode(discOpts *Options, ipAddr net.IP) (*en logFields := []zapcore.Field{ fields.ENRLocalNode(localNode), - fields.Domain(dvs.networkConfig.DomainType), + fields.Domain(dvs.ssvConfig.DomainType), } if opts.Subnets.HasActive() { diff --git a/network/discovery/dv5_service_test.go b/network/discovery/dv5_service_test.go index 3fa7e80a85..f9992a8beb 100644 --- a/network/discovery/dv5_service_test.go +++ b/network/discovery/dv5_service_test.go @@ -19,7 +19,6 @@ import ( "github.com/ssvlabs/ssv/network/peers/connections/mock" "github.com/ssvlabs/ssv/network/records" "github.com/ssvlabs/ssv/networkconfig" - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" "github.com/ssvlabs/ssv/utils" "github.com/ssvlabs/ssv/utils/ttl" ) @@ -83,13 +82,8 @@ func TestCheckPeer(t *testing.T) { } ) - var checkPeerTestNetwork = networkconfig.NetworkConfig{ - BeaconConfig: networkconfig.BeaconConfig{ - Beacon: beacon.NewNetwork(spectypes.BeaconTestNetwork), - }, - SSVConfig: networkconfig.SSVConfig{ - DomainType: spectypes.DomainType{0x1, 0x2, 0x3, 0x4}, - }, + var checkPeerTestSSVConfig = networkconfig.SSVConfig{ + DomainType: spectypes.DomainType{0x1, 0x2, 0x3, 0x4}, } // Create the LocalNode instances for the tests. @@ -125,7 +119,7 @@ func TestCheckPeer(t *testing.T) { ctx: ctx, conns: &mock.MockConnectionIndex{LimitValue: false}, subnetsIdx: subnetIndex, - networkConfig: checkPeerTestNetwork, + ssvConfig: checkPeerTestSSVConfig, subnets: mySubnets, discoveredPeersPool: ttl.New[peer.ID, DiscoveredPeer](time.Hour, time.Hour), trimmedRecently: ttl.New[peer.ID, struct{}](time.Hour, time.Hour), diff --git a/network/discovery/service.go b/network/discovery/service.go index be69188b4d..92ac437979 100644 --- a/network/discovery/service.go +++ b/network/discovery/service.go @@ -42,7 +42,7 @@ type Options struct { SubnetsIdx peers.SubnetsIndex HostAddress string HostDNS string - NetworkConfig networkconfig.NetworkConfig + SSVConfig networkconfig.SSVConfig DiscoveredPeersPool *ttl.Map[peer.ID, DiscoveredPeer] TrimmedRecently *ttl.Map[peer.ID, struct{}] } diff --git a/network/discovery/service_test.go b/network/discovery/service_test.go index 1f670291b0..4249e342bb 100644 --- a/network/discovery/service_test.go +++ b/network/discovery/service_test.go @@ -34,7 +34,7 @@ func TestNewDiscV5Service(t *testing.T) { assert.NotNil(t, dvs.dv5Listener) assert.NotNil(t, dvs.conns) assert.NotNil(t, dvs.subnetsIdx) - assert.NotNil(t, dvs.networkConfig) + assert.NotNil(t, dvs.ssvConfig) // Check bootnodes CheckBootnodes(t, dvs, testNetConfig) @@ -144,7 +144,7 @@ func TestDiscV5Service_PublishENR(t *testing.T) { ctx, cancel := context.WithTimeout(t.Context(), 2*time.Second) defer cancel() - opts := testingDiscoveryOptions(t, testNetConfig) + opts := testingDiscoveryOptions(t, testNetConfig.SSVConfig) dvs, err := newDiscV5Service(ctx, testLogger, opts) require.NoError(t, err) @@ -158,19 +158,19 @@ func TestDiscV5Service_PublishENR(t *testing.T) { checkLocalNodeDomainTypeAlignment(t, localNode, testNetConfig) // Change network config - dvs.networkConfig = networkconfig.HoleskyStage + dvs.ssvConfig = networkconfig.TestNetwork.SSVConfig // Test PublishENR method dvs.PublishENR() // Check LocalNode has been updated - checkLocalNodeDomainTypeAlignment(t, localNode, networkconfig.HoleskyStage) + checkLocalNodeDomainTypeAlignment(t, localNode, networkconfig.TestNetwork) } func TestDiscV5Service_Bootstrap(t *testing.T) { ctx, cancel := context.WithTimeout(t.Context(), 2*time.Second) defer cancel() - opts := testingDiscoveryOptions(t, testNetConfig) + opts := testingDiscoveryOptions(t, testNetConfig.SSVConfig) dvs, err := newDiscV5Service(t.Context(), testLogger, opts) require.NoError(t, err) @@ -365,7 +365,7 @@ func TestServiceAddressConfiguration(t *testing.T) { defer cancel() // create options with unique ports for parallel testing - opts := testingDiscoveryOptions(t, testNetConfig) + opts := testingDiscoveryOptions(t, testNetConfig.SSVConfig) opts.DiscV5Opts.Port = uint16(13000 + i*10) opts.DiscV5Opts.TCPPort = uint16(14000 + i*10) opts.HostAddress = tc.hostAddress diff --git a/network/discovery/util_test.go b/network/discovery/util_test.go index b435207fcf..47237be3cf 100644 --- a/network/discovery/util_test.go +++ b/network/discovery/util_test.go @@ -40,7 +40,7 @@ var ( ) // Options for the discovery service -func testingDiscoveryOptions(t *testing.T, networkConfig networkconfig.NetworkConfig) *Options { +func testingDiscoveryOptions(t *testing.T, ssvConfig networkconfig.SSVConfig) *Options { // Generate key privKey, err := crypto.GenerateKey() require.NoError(t, err) @@ -54,7 +54,7 @@ func testingDiscoveryOptions(t *testing.T, networkConfig networkconfig.NetworkCo Port: testPort, TCPPort: testTCPPort, NetworkKey: privKey, - Bootnodes: networkConfig.Bootnodes, + Bootnodes: ssvConfig.Bootnodes, Subnets: mockSubnets(1), EnableLogging: false, } @@ -67,15 +67,15 @@ func testingDiscoveryOptions(t *testing.T, networkConfig networkconfig.NetworkCo DiscV5Opts: discV5Opts, ConnIndex: connectionIndex, SubnetsIdx: subnetsIndex, - NetworkConfig: networkConfig, + SSVConfig: ssvConfig, DiscoveredPeersPool: ttl.New[peer.ID, DiscoveredPeer](time.Hour, time.Hour), TrimmedRecently: ttl.New[peer.ID, struct{}](time.Hour, time.Hour), } } // Testing discovery with a given NetworkConfig -func testingDiscoveryWithNetworkConfig(t *testing.T, netConfig networkconfig.NetworkConfig) *DiscV5Service { - opts := testingDiscoveryOptions(t, netConfig) +func testingDiscoveryWithNetworkConfig(t *testing.T, ssvConfig networkconfig.SSVConfig) *DiscV5Service { + opts := testingDiscoveryOptions(t, ssvConfig) dvs, err := newDiscV5Service(t.Context(), testLogger, opts) require.NoError(t, err) require.NotNil(t, dvs) @@ -84,7 +84,7 @@ func testingDiscoveryWithNetworkConfig(t *testing.T, netConfig networkconfig.Net // Testing discovery service func testingDiscovery(t *testing.T) *DiscV5Service { - return testingDiscoveryWithNetworkConfig(t, testNetConfig) + return testingDiscoveryWithNetworkConfig(t, testNetConfig.SSVConfig) } // Testing LocalNode diff --git a/network/p2p/config.go b/network/p2p/config.go index a9c47f408b..0f6869431d 100644 --- a/network/p2p/config.go +++ b/network/p2p/config.go @@ -73,8 +73,8 @@ type Config struct { UserAgent string // NodeStorage is used to get operator metadata. NodeStorage storage.Storage - // Network defines a network configuration. - Network networkconfig.NetworkConfig + // NetworkConfig defines a network configuration. + NetworkConfig networkconfig.NetworkConfig // MessageValidator validates incoming messages. MessageValidator validation.MessageValidator @@ -183,12 +183,12 @@ func (c *Config) configureAddrs(logger *zap.Logger, opts []libp2p.Option) ([]lib func (c *Config) TransformBootnodes() []string { if c.Bootnodes == "" { - return c.Network.Bootnodes + return c.NetworkConfig.Bootnodes } // extend additional bootnodes from config extraBootnodes := strings.Split(c.Bootnodes, ";") - return append(extraBootnodes, c.Network.Bootnodes...) + return append(extraBootnodes, c.NetworkConfig.Bootnodes...) } func userAgent(fromCfg string) string { diff --git a/network/p2p/p2p.go b/network/p2p/p2p.go index ea62da1c16..3cca377a18 100644 --- a/network/p2p/p2p.go +++ b/network/p2p/p2p.go @@ -555,9 +555,9 @@ func (n *p2pNetwork) UpdateScoreParams() { // function to get the starting time of the next epoch nextEpochStartingTime := func() time.Time { - currEpoch := n.cfg.Network.Beacon.EstimatedCurrentEpoch() + currEpoch := n.cfg.NetworkConfig.EstimatedCurrentEpoch() nextEpoch := currEpoch + 1 - return n.cfg.Network.Beacon.EpochStartTime(nextEpoch) + return n.cfg.NetworkConfig.EpochStartTime(nextEpoch) } // Create timer that triggers on the beginning of the next epoch diff --git a/network/p2p/p2p_setup.go b/network/p2p/p2p_setup.go index 1c1f527cf4..1db12a4aca 100644 --- a/network/p2p/p2p_setup.go +++ b/network/p2p/p2p_setup.go @@ -186,7 +186,7 @@ func (n *p2pNetwork) setupPeerServices() error { if err != nil { return err } - d := n.cfg.Network.DomainType + d := n.cfg.NetworkConfig.DomainType domain := "0x" + hex.EncodeToString(d[:]) self := records.NewNodeInfo(domain) self.Metadata = &records.NodeMetadata{ @@ -215,7 +215,7 @@ func (n *p2pNetwork) setupPeerServices() error { // Handshake filters filters := func() []connections.HandshakeFilter { - newDomain := n.cfg.Network.DomainType + newDomain := n.cfg.NetworkConfig.DomainType newDomainString := "0x" + hex.EncodeToString(newDomain[:]) return []connections.HandshakeFilter{ connections.NetworkIDFilter(newDomainString), @@ -234,7 +234,7 @@ func (n *p2pNetwork) setupPeerServices() error { SubnetsIdx: n.idx, IDService: ids, Network: n.host.Network(), - DomainType: n.cfg.Network.DomainType, + DomainType: n.cfg.NetworkConfig.DomainType, SubnetsProvider: n.ActiveSubnets, }, filters) @@ -291,7 +291,7 @@ func (n *p2pNetwork) setupDiscovery() error { SubnetsIdx: n.idx, HostAddress: n.cfg.HostAddress, HostDNS: n.cfg.HostDNS, - NetworkConfig: n.cfg.Network, + SSVConfig: n.cfg.NetworkConfig.SSVConfig, DiscoveredPeersPool: n.discoveredPeersPool, TrimmedRecently: n.trimmedRecently, } @@ -308,7 +308,7 @@ func (n *p2pNetwork) setupDiscovery() error { func (n *p2pNetwork) setupPubsub() (topics.Controller, error) { cfg := &topics.PubSubConfig{ - NetworkConfig: n.cfg.Network, + NetworkConfig: n.cfg.NetworkConfig, Host: n.host, TraceLog: n.cfg.PubSubTrace, MsgValidator: n.msgValidator, @@ -332,7 +332,7 @@ func (n *p2pNetwork) setupPubsub() (topics.Controller, error) { cfg.ScoreIndex = nil } - midHandler := topics.NewMsgIDHandler(n.ctx, n.cfg.Network, time.Minute*2) + midHandler := topics.NewMsgIDHandler(n.ctx, time.Minute*2) n.msgResolver = midHandler cfg.MsgIDHandler = midHandler go cfg.MsgIDHandler.Start() diff --git a/network/p2p/p2p_test.go b/network/p2p/p2p_test.go index 9f56e53c2d..286985fca1 100644 --- a/network/p2p/p2p_test.go +++ b/network/p2p/p2p_test.go @@ -141,7 +141,7 @@ func generateValidatorMsg(ks *spectestingutils.TestKeySet, round specqbft.Round, panic("committee role shouldn't be used here") } netCfg := networkconfig.TestNetwork - height := specqbft.Height(netCfg.Beacon.EstimatedCurrentSlot()) + height := specqbft.Height(netCfg.EstimatedCurrentSlot()) fullData := spectestingutils.TestingQBFTFullData @@ -167,7 +167,7 @@ func generateValidatorMsg(ks *spectestingutils.TestKeySet, round specqbft.Round, func generateCommitteeMsg(ks *spectestingutils.TestKeySet, round specqbft.Round) *spectypes.SignedSSVMessage { netCfg := networkconfig.TestNetwork - height := specqbft.Height(netCfg.Beacon.EstimatedCurrentSlot()) + height := specqbft.Height(netCfg.EstimatedCurrentSlot()) share := &ssvtypes.SSVShare{ Share: *spectestingutils.TestingShare(ks, spectestingutils.TestingValidatorIndex), diff --git a/network/p2p/test_utils.go b/network/p2p/test_utils.go index 8fb6c3c5d0..adca8984c1 100644 --- a/network/p2p/test_utils.go +++ b/network/p2p/test_utils.go @@ -55,7 +55,7 @@ func (ln *LocalNet) WithBootnode(ctx context.Context, logger *zap.Logger) error if err != nil { return err } - bn, err := discovery.NewBootnode(ctx, logger, networkconfig.TestNetwork, &discovery.BootnodeOptions{ + bn, err := discovery.NewBootnode(ctx, logger, networkconfig.TestNetwork.SSVConfig, &discovery.BootnodeOptions{ PrivateKey: hex.EncodeToString(b), ExternalIP: "127.0.0.1", Port: ln.udpRand.Next(13001, 13999), @@ -185,7 +185,7 @@ func (ln *LocalNet) NewTestP2pNetwork(ctx context.Context, nodeIndex uint64, key signatureVerifier, phase0.Epoch(0), ) - cfg.Network = networkconfig.TestNetwork + cfg.NetworkConfig = networkconfig.TestNetwork if options.TotalValidators > 0 { cfg.GetValidatorStats = func() (uint64, uint64, uint64, error) { return options.TotalValidators, options.ActiveValidators, options.MyValidators, nil diff --git a/network/topics/controller_test.go b/network/topics/controller_test.go index 6e03c5b23d..6957bfa327 100644 --- a/network/topics/controller_test.go +++ b/network/topics/controller_test.go @@ -385,7 +385,7 @@ func newPeer(ctx context.Context, logger *zap.Logger, t *testing.T, msgValidator var p *P var midHandler topics.MsgIDHandler if msgID { - midHandler = topics.NewMsgIDHandler(ctx, networkconfig.TestNetwork, 2*time.Minute) + midHandler = topics.NewMsgIDHandler(ctx, 2*time.Minute) go midHandler.Start() } cfg := &topics.PubSubConfig{ @@ -399,7 +399,6 @@ func newPeer(ctx context.Context, logger *zap.Logger, t *testing.T, msgValidator Scoring: &topics.ScoringConfig{ IPWhitelist: nil, IPColocationWeight: 0, - OneEpochDuration: time.Minute, }, MsgValidator: msgValidator, ScoreInspector: scoreInspector, diff --git a/network/topics/msg_id.go b/network/topics/msg_id.go index 0931d9cbf8..faa55fb499 100644 --- a/network/topics/msg_id.go +++ b/network/topics/msg_id.go @@ -13,7 +13,6 @@ import ( "go.uber.org/zap" "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/networkconfig" ) const ( @@ -55,23 +54,21 @@ type msgIDEntry struct { // msgIDHandler implements MsgIDHandler type msgIDHandler struct { - networkConfig networkconfig.NetworkConfig - ctx context.Context - added chan addedEvent - ids map[string]*msgIDEntry - locker sync.Locker - ttl time.Duration + ctx context.Context + added chan addedEvent + ids map[string]*msgIDEntry + locker sync.Locker + ttl time.Duration } // NewMsgIDHandler creates a new MsgIDHandler -func NewMsgIDHandler(ctx context.Context, networkConfig networkconfig.NetworkConfig, ttl time.Duration) MsgIDHandler { +func NewMsgIDHandler(ctx context.Context, ttl time.Duration) MsgIDHandler { handler := &msgIDHandler{ - networkConfig: networkConfig, - ctx: ctx, - added: make(chan addedEvent, msgIDHandlerBufferSize), - ids: make(map[string]*msgIDEntry), - locker: &sync.Mutex{}, - ttl: ttl, + ctx: ctx, + added: make(chan addedEvent, msgIDHandlerBufferSize), + ids: make(map[string]*msgIDEntry), + locker: &sync.Mutex{}, + ttl: ttl, } return handler } diff --git a/network/topics/msg_validator_test.go b/network/topics/msg_validator_test.go index 700bf091ae..45775e50a4 100644 --- a/network/topics/msg_validator_test.go +++ b/network/topics/msg_validator_test.go @@ -60,7 +60,7 @@ func TestMsgValidator(t *testing.T) { require.NotNil(t, mv) - slot := networkconfig.TestNetwork.Beacon.GetBeaconNetwork().EstimatedCurrentSlot() + slot := networkconfig.TestNetwork.EstimatedCurrentSlot() operatorID := uint64(1) operatorPrivateKey := ks.OperatorKeys[operatorID] diff --git a/network/topics/params/helpers.go b/network/topics/params/helpers.go index cbbd47f502..e6efe59f65 100644 --- a/network/topics/params/helpers.go +++ b/network/topics/params/helpers.go @@ -7,10 +7,6 @@ import ( "github.com/pkg/errors" ) -const ( - oneEpochDuration = (12 * time.Second) * 32 -) - // scoreDecay determines the decay rate from the provided time period till // the decayToZero value. Ex: ( 1 -> 0.01) func scoreDecay(totalDecayDuration time.Duration, decayIntervalDuration time.Duration) float64 { diff --git a/network/topics/params/message_rate.go b/network/topics/params/message_rate.go index a64f4a07d8..66e3d53b0d 100644 --- a/network/topics/params/message_rate.go +++ b/network/topics/params/message_rate.go @@ -2,49 +2,79 @@ package params import ( "math" + "time" + "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/registry/storage" ) -// Ethereum parameters +// Threshold and limit parameters const ( - EthereumValidators = 1000000.0 // TODO: get from network? - SyncCommitteeSize = 512.0 // TODO: get from network? - EstimatedAttestationCommitteeSize = EthereumValidators / 2048.0 - AggregatorProbability = 16.0 / EstimatedAttestationCommitteeSize - ProposalProbability = 1.0 / EthereumValidators - SyncCommitteeProbability = SyncCommitteeSize / EthereumValidators - SyncCommitteeAggProb = SyncCommitteeProbability * 16.0 / (SyncCommitteeSize / 4.0) - MaxValidatorsPerCommittee = 560.0 - SlotsPerEpoch = 32.0 // TODO: get from network? - MaxAttestationDutiesPerEpochForCommittee = SlotsPerEpoch - SingleSCDutiesLimit = 0 + // SingleSCDutiesLimit represents the limit of the number of committee duties in an epoch + // with only sync committee beacon duties (no attestation) taken for a very big number of validators. + // To help reasoning it, note that for a very big number of validators all slots in the epoch + // will have an attestation with high probability and, thus, + // the committee duties with only sync committee beacon duties tends to 0. + SingleSCDutiesLimit = 0 + // MaxValidatorsPerCommitteeListCut serves as a threshold size for creating a cache + // that computes the expected number of duties given a committee size. + // For each committee size, we can compute the precise expected number of duties. + // However, for big enough committees (considered as bigger than the following constant), + // results are pretty much the same. So we create a list of const values only up to the following value. + // For values that exceed it, the function shall return a default limit answer + // (e.g. number of committees duties per epoch -> 32). + // TODO: It depends on duties per epoch, 32 duties per epoch maps to MaxValidatorsPerCommitteeListCut=560. If the value of duties per epoch changes, this value needs to be adjusted (need to run Monte Carlo simulation for that number). + MaxValidatorsPerCommitteeListCut = 560 ) -// Expected number of messages per duty step - -func consensusMessages(n int) int { - return 1 + n + n + 2 // 1 Proposal + n Prepares + n Commits + 2 Decideds (average) +type rateCalculator struct { + netCfg networkconfig.NetworkConfig + generatedExpectedNumberOfCommitteeDutiesPerEpochDueToAttestation []float64 + generatedExpectedSingleSCCommitteeDutiesPerEpoch []float64 } -func partialSignatureMessages(n int) int { - return n -} +func newRateCalculator(netCfg networkconfig.NetworkConfig) *rateCalculator { + rc := &rateCalculator{ + netCfg: netCfg, + generatedExpectedNumberOfCommitteeDutiesPerEpochDueToAttestation: []float64{}, + generatedExpectedSingleSCCommitteeDutiesPerEpoch: []float64{}, + } -func dutyWithPreConsensus(n int) int { - // Pre-Consensus + Consensus + Post-Consensus - return partialSignatureMessages(n) + consensusMessages(n) + partialSignatureMessages(n) + rc.generateCachedValues() + + return rc } -func dutyWithoutPreConsensus(n int) int { - // Consensus + Post-Consensus - return consensusMessages(n) + partialSignatureMessages(n) +// Calculates the message rate for a topic given its committees' configurations (number of operators and number of validators) +func (rc *rateCalculator) calculateMessageRateForTopic(committees []*storage.Committee) float64 { + if len(committees) == 0 { + return 0 + } + + totalMsgRate := 0.0 + + for _, committee := range committees { + committeeSize := len(committee.Operators) + numValidators := len(committee.Validators) + + totalMsgRate += rc.expectedNumberOfCommitteeDutiesPerEpochDueToAttestationCached(numValidators) * float64(dutyWithoutPreConsensus(committeeSize)) + totalMsgRate += rc.expectedSingleSCCommitteeDutiesPerEpochCached(numValidators) * float64(dutyWithoutPreConsensus(committeeSize)) + totalMsgRate += float64(numValidators) * rc.AggregatorProbability() * float64(dutyWithPreConsensus(committeeSize)) + totalMsgRate += float64(numValidators) * float64(rc.netCfg.GetSlotsPerEpoch()) * rc.ProposalProbability() * float64(dutyWithPreConsensus(committeeSize)) + totalMsgRate += float64(numValidators) * float64(rc.netCfg.GetSlotsPerEpoch()) * rc.SyncCommitteeAggProb() * float64(dutyWithPreConsensus(committeeSize)) + } + + // Convert rate to seconds + totalEpochSeconds := float64(rc.netCfg.EpochDuration() / time.Second) + totalMsgRate = totalMsgRate / totalEpochSeconds + + return totalMsgRate } // Expected number of committee duties per epoch due to attestations -func expectedNumberOfCommitteeDutiesPerEpochDueToAttestation(numValidators int) float64 { +func (rc *rateCalculator) calcExpectedNumberOfCommitteeDutiesPerEpochDueToAttestation(numValidators int) float64 { k := float64(numValidators) - n := SlotsPerEpoch + n := float64(rc.netCfg.GetSlotsPerEpoch()) // Probability that all validators are not assigned to slot i probabilityAllNotOnSlotI := math.Pow((n-1)/n, k) @@ -59,77 +89,94 @@ func expectedNumberOfCommitteeDutiesPerEpochDueToAttestation(numValidators int) } // Expected committee duties per epoch that are due to only sync committee beacon duties -func expectedSingleSCCommitteeDutiesPerEpoch(numValidators int) float64 { +func (rc *rateCalculator) calcExpectedSingleSCCommitteeDutiesPerEpoch(numValidators int) float64 { // Probability that a validator is not in sync committee - chanceOfNotBeingInSyncCommittee := 1.0 - SyncCommitteeProbability + chanceOfNotBeingInSyncCommittee := 1.0 - rc.SyncCommitteeProbability() // Probability that all validators are not in sync committee chanceThatAllValidatorsAreNotInSyncCommittee := math.Pow(chanceOfNotBeingInSyncCommittee, float64(numValidators)) // Probability that at least one validator is in sync committee chanceOfAtLeastOneValidatorBeingInSyncCommittee := 1.0 - chanceThatAllValidatorsAreNotInSyncCommittee // Expected number of slots with no attestation duty - expectedSlotsWithNoDuty := 32.0 - expectedNumberOfCommitteeDutiesPerEpochDueToAttestationCached(numValidators) + expectedSlotsWithNoDuty := 32.0 - rc.calcExpectedNumberOfCommitteeDutiesPerEpochDueToAttestation(numValidators) // Expected number of committee duties per epoch created due to only sync committee duties return chanceOfAtLeastOneValidatorBeingInSyncCommittee * expectedSlotsWithNoDuty } -// Cache costly calculations +func (rc *rateCalculator) generateCachedValues() { + // Cache costly calculations -func generateCachedValues(generator func(int) float64, threshold int) []float64 { - results := make([]float64, 0, threshold) + expectedCommNumber := make([]float64, 0, MaxValidatorsPerCommitteeListCut) + expectedSingleSCC := make([]float64, 0, MaxValidatorsPerCommitteeListCut) - for i := 0; i < threshold; i++ { - results = append(results, generator(i)) + for i := 0; i < MaxValidatorsPerCommitteeListCut; i++ { + expectedCommNumber = append(expectedCommNumber, rc.calcExpectedNumberOfCommitteeDutiesPerEpochDueToAttestation(i)) + expectedSingleSCC = append(expectedSingleSCC, rc.calcExpectedSingleSCCommitteeDutiesPerEpoch(i)) } - return results + rc.generatedExpectedNumberOfCommitteeDutiesPerEpochDueToAttestation = expectedCommNumber + rc.generatedExpectedSingleSCCommitteeDutiesPerEpoch = expectedSingleSCC } -var generatedExpectedNumberOfCommitteeDutiesPerEpochDueToAttestation = generateCachedValues(expectedNumberOfCommitteeDutiesPerEpochDueToAttestation, MaxValidatorsPerCommittee) - -func expectedNumberOfCommitteeDutiesPerEpochDueToAttestationCached(numValidators int) float64 { +func (rc *rateCalculator) expectedNumberOfCommitteeDutiesPerEpochDueToAttestationCached(numValidators int) float64 { // If the committee has more validators than our computed cache, we return the limit value - if numValidators >= MaxValidatorsPerCommittee { - return MaxAttestationDutiesPerEpochForCommittee + if numValidators >= MaxValidatorsPerCommitteeListCut { + return float64(rc.MaxAttestationDutiesPerEpochForCommittee()) } - return generatedExpectedNumberOfCommitteeDutiesPerEpochDueToAttestation[numValidators] + return rc.generatedExpectedNumberOfCommitteeDutiesPerEpochDueToAttestation[numValidators] } -var generatedExpectedSingleSCCommitteeDutiesPerEpoch = generateCachedValues(expectedSingleSCCommitteeDutiesPerEpoch, MaxValidatorsPerCommittee) - -func expectedSingleSCCommitteeDutiesPerEpochCached(numValidators int) float64 { +func (rc *rateCalculator) expectedSingleSCCommitteeDutiesPerEpochCached(numValidators int) float64 { // If the committee has more validators than our computed cache, we return the limit value - if numValidators >= MaxValidatorsPerCommittee { + if numValidators >= MaxValidatorsPerCommitteeListCut { return SingleSCDutiesLimit } - return generatedExpectedSingleSCCommitteeDutiesPerEpoch[numValidators] + return rc.generatedExpectedSingleSCCommitteeDutiesPerEpoch[numValidators] } -// Calculates the message rate for a topic given its committees' configurations (number of operators and number of validators) -func calculateMessageRateForTopic(committees []*storage.Committee) float64 { - if len(committees) == 0 { - return 0 - } +func (rc *rateCalculator) AggregatorProbability() float64 { + return 16.0 / rc.EstimatedAttestationCommitteeSize() +} - totalMsgRate := 0.0 +func (rc *rateCalculator) ProposalProbability() float64 { + return 1.0 / float64(rc.netCfg.TotalEthereumValidators) +} - for _, committee := range committees { - committeeSize := len(committee.Operators) - numValidators := len(committee.Validators) +func (rc *rateCalculator) SyncCommitteeProbability() float64 { + return float64(rc.netCfg.GetSyncCommitteeSize()) / float64(rc.netCfg.TotalEthereumValidators) +} - totalMsgRate += expectedNumberOfCommitteeDutiesPerEpochDueToAttestationCached(numValidators) * float64(dutyWithoutPreConsensus(committeeSize)) - totalMsgRate += expectedSingleSCCommitteeDutiesPerEpochCached(numValidators) * float64(dutyWithoutPreConsensus(committeeSize)) - totalMsgRate += float64(numValidators) * AggregatorProbability * float64(dutyWithPreConsensus(committeeSize)) - totalMsgRate += float64(numValidators) * SlotsPerEpoch * ProposalProbability * float64(dutyWithPreConsensus(committeeSize)) - totalMsgRate += float64(numValidators) * SlotsPerEpoch * SyncCommitteeAggProb * float64(dutyWithPreConsensus(committeeSize)) - } +func (rc *rateCalculator) SyncCommitteeAggProb() float64 { + return rc.SyncCommitteeProbability() * 16.0 / (float64(rc.netCfg.GetSyncCommitteeSize()) / 4.0) +} - // Convert rate to seconds - totalEpochSeconds := float64(SlotsPerEpoch * 12) - totalMsgRate = totalMsgRate / totalEpochSeconds +func (rc *rateCalculator) MaxAttestationDutiesPerEpochForCommittee() uint64 { + return rc.netCfg.GetSlotsPerEpoch() +} - return totalMsgRate +func (rc *rateCalculator) EstimatedAttestationCommitteeSize() float64 { + return float64(rc.netCfg.TotalEthereumValidators) / 2048.0 +} + +// Expected number of messages per duty step + +func consensusMessages(n int) int { + return 1 + n + n + 2 // 1 Proposal + n Prepares + n Commits + 2 Decideds (average) +} + +func partialSignatureMessages(n int) int { + return n +} + +func dutyWithPreConsensus(n int) int { + // Pre-Consensus + Consensus + Post-Consensus + return partialSignatureMessages(n) + consensusMessages(n) + partialSignatureMessages(n) +} + +func dutyWithoutPreConsensus(n int) int { + // Consensus + Post-Consensus + return consensusMessages(n) + partialSignatureMessages(n) } diff --git a/network/topics/params/message_rate_test.go b/network/topics/params/message_rate_test.go index 64aeae1956..dd571603ad 100644 --- a/network/topics/params/message_rate_test.go +++ b/network/topics/params/message_rate_test.go @@ -7,6 +7,7 @@ import ( spectypes "github.com/ssvlabs/ssv-spec/types" "github.com/stretchr/testify/require" + "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/protocol/v2/types" "github.com/ssvlabs/ssv/registry/storage" ) @@ -78,7 +79,8 @@ func TestCalculateMessageRateForTopic(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - msgRate := calculateMessageRateForTopic(tt.args.committees) + rc := newRateCalculator(networkconfig.TestNetwork) + msgRate := rc.calculateMessageRateForTopic(tt.args.committees) require.InDelta(t, tt.want, msgRate, tt.want*0.001) }) } diff --git a/network/topics/params/peer_score.go b/network/topics/params/peer_score.go index 323e53c6a7..7b0d063f16 100644 --- a/network/topics/params/peer_score.go +++ b/network/topics/params/peer_score.go @@ -6,6 +6,8 @@ import ( pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/peer" + + "github.com/ssvlabs/ssv/networkconfig" ) const ( @@ -17,10 +19,9 @@ const ( opportunisticGraftThreshold = 5 // Overall parameters - topicScoreCap = 32.72 - decayInterval = 32 * (time.Second * 12) // One epoch - decayToZero = 0.01 - retainScore = 100 * 32 * 12 * time.Second + topicScoreCap = 32.72 + decayToZero = 0.01 + retainScoreEpochMultiplier = 100 // P5 appSpecificWeight = 0 @@ -45,13 +46,9 @@ func PeerScoreThresholds() *pubsub.PeerScoreThresholds { } // PeerScoreParams returns peer score params according to the given options -func PeerScoreParams(oneEpoch, msgIDCacheTTL time.Duration, disableColocation bool, ipWhilelist ...*net.IPNet) *pubsub.PeerScoreParams { - if oneEpoch == 0 { - oneEpoch = oneEpochDuration - } - +func PeerScoreParams(netCfg networkconfig.NetworkConfig, msgIDCacheTTL time.Duration, disableColocation bool, ipWhitelist ...*net.IPNet) *pubsub.PeerScoreParams { // P7 calculation - behaviourPenaltyDecay := scoreDecay(oneEpoch*10, decayInterval) + behaviourPenaltyDecay := scoreDecay(netCfg.EpochDuration()*10, netCfg.EpochDuration()) maxAllowedRatePerDecayInterval := 10.0 targetVal, _ := decayConvergence(behaviourPenaltyDecay, maxAllowedRatePerDecayInterval) targetVal = targetVal - behaviourPenaltyThreshold @@ -66,9 +63,9 @@ func PeerScoreParams(oneEpoch, msgIDCacheTTL time.Duration, disableColocation bo Topics: make(map[string]*pubsub.TopicScoreParams), // Overall parameters TopicScoreCap: topicScoreCap, - DecayInterval: decayInterval, + DecayInterval: netCfg.EpochDuration(), DecayToZero: decayToZero, - RetainScore: retainScore, + RetainScore: retainScoreEpochMultiplier * netCfg.EpochDuration(), SeenMsgTTL: msgIDCacheTTL, // P5 @@ -80,7 +77,7 @@ func PeerScoreParams(oneEpoch, msgIDCacheTTL time.Duration, disableColocation bo // P6 IPColocationFactorWeight: finalIPColocationFactorWeight, IPColocationFactorThreshold: ipColocationFactorThreshold, - IPColocationFactorWhitelist: ipWhilelist, + IPColocationFactorWhitelist: ipWhitelist, // P7 BehaviourPenaltyWeight: behaviourPenaltyWeight, diff --git a/network/topics/params/scores_test.go b/network/topics/params/scores_test.go index 245943dfd5..324c4d5606 100644 --- a/network/topics/params/scores_test.go +++ b/network/topics/params/scores_test.go @@ -9,6 +9,7 @@ import ( pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/stretchr/testify/require" + "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/registry/storage" ) @@ -22,7 +23,7 @@ func TestTopicScoreParams(t *testing.T) { "subnet topic 0 validators", func() *Options { validators := uint64(0) - opts := NewSubnetTopicOpts(validators, 128, []*storage.Committee{}) + opts := NewSubnetTopicOpts(networkconfig.TestNetwork, validators, 128, []*storage.Committee{}) return opts }, nil, @@ -31,7 +32,7 @@ func TestTopicScoreParams(t *testing.T) { "subnet topic 1k validators", func() *Options { validators := uint64(1000) - opts := NewSubnetTopicOpts(validators, 128, createTestingSingleCommittees(validators)) + opts := NewSubnetTopicOpts(networkconfig.TestNetwork, validators, 128, createTestingSingleCommittees(validators)) return opts }, nil, @@ -40,7 +41,7 @@ func TestTopicScoreParams(t *testing.T) { "subnet topic 10k validators", func() *Options { validators := uint64(10_000) - opts := NewSubnetTopicOpts(validators, 128, createTestingSingleCommittees(validators)) + opts := NewSubnetTopicOpts(networkconfig.TestNetwork, validators, 128, createTestingSingleCommittees(validators)) return opts }, nil, @@ -49,7 +50,7 @@ func TestTopicScoreParams(t *testing.T) { "subnet topic 51k validators", func() *Options { validators := uint64(51_000) - opts := NewSubnetTopicOpts(validators, 128, createTestingSingleCommittees(validators)) + opts := NewSubnetTopicOpts(networkconfig.TestNetwork, validators, 128, createTestingSingleCommittees(validators)) return opts }, nil, @@ -76,7 +77,7 @@ func TestTopicScoreParams(t *testing.T) { } func TestPeerScoreParams(t *testing.T) { - peerScoreParams := PeerScoreParams(oneEpochDuration, 550*(time.Millisecond*700), false) + peerScoreParams := PeerScoreParams(networkconfig.TestNetwork, 550*(time.Millisecond*700), false) raw, err := peerScoreParamsString(peerScoreParams) require.NoError(t, err) require.NotNil(t, raw) diff --git a/network/topics/params/topic_score.go b/network/topics/params/topic_score.go index d8366ad288..fb9a8efad8 100644 --- a/network/topics/params/topic_score.go +++ b/network/topics/params/topic_score.go @@ -7,6 +7,7 @@ import ( pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/pkg/errors" + "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/registry/storage" ) @@ -96,7 +97,7 @@ type Options struct { func (o *Options) defaults() { // Network if o.Network.OneEpochDuration == 0 { - o.Network.OneEpochDuration = oneEpochDuration + o.Network.OneEpochDuration = 12 * time.Second * 32 } if o.Network.TotalTopicsWeight == 0 { o.Network.TotalTopicsWeight = totalTopicsWeight @@ -150,35 +151,38 @@ func (o *Options) maxScore() float64 { } // NewOpts creates new TopicOpts instance -func NewOpts(activeValidators uint64, subnets int) *Options { +func NewOpts(epochDuration time.Duration, activeValidators uint64, subnets int) *Options { return &Options{ Network: NetworkOpts{ ActiveValidators: activeValidators, Subnets: subnets, + OneEpochDuration: epochDuration, }, Topic: TopicOpts{}, } } // NewSubnetTopicOpts creates new TopicOpts for a subnet topic -func NewSubnetTopicOpts(activeValidators uint64, subnets int, committees []*storage.Committee) *Options { +func NewSubnetTopicOpts(netCfg networkconfig.NetworkConfig, activeValidators uint64, subnets int, committees []*storage.Committee) *Options { // Create options with default values - opts := NewOpts(activeValidators, subnets) + opts := NewOpts(netCfg.EpochDuration(), activeValidators, subnets) opts.defaults() // Set topic weight with equal weights opts.Topic.TopicWeight = opts.Network.TotalTopicsWeight / float64(opts.Network.Subnets) + rc := newRateCalculator(netCfg) + // Set the expected message rate for the topic - opts.Topic.ExpectedMsgRate = calculateMessageRateForTopic(committees) + opts.Topic.ExpectedMsgRate = rc.calculateMessageRateForTopic(committees) return opts } // NewSubnetTopicOpts creates new TopicOpts for a subnet topic -func NewSubnetTopicOptsValidators(activeValidators uint64, subnets int) *Options { +func NewSubnetTopicOptsValidators(netCfg networkconfig.NetworkConfig, activeValidators uint64, subnets int) *Options { // Create options with default values - opts := NewOpts(activeValidators, subnets) + opts := NewOpts(netCfg.EpochDuration(), activeValidators, subnets) opts.defaults() // Set topic weight with equal weights @@ -201,6 +205,7 @@ func TopicParams(opts *Options) (*pubsub.TopicScoreParams, error) { // Set to default if not set opts.defaults() + decayInterval := opts.Network.OneEpochDuration expectedMessagesPerDecayInterval := opts.Topic.ExpectedMsgRate * decayInterval.Seconds() // P1 diff --git a/network/topics/pubsub.go b/network/topics/pubsub.go index 5154309918..7fc8c9a271 100644 --- a/network/topics/pubsub.go +++ b/network/topics/pubsub.go @@ -47,11 +47,10 @@ const ( // PubSubConfig is the needed config to instantiate pubsub type PubSubConfig struct { NetworkConfig networkconfig.NetworkConfig - - Host host.Host - TraceLog bool - StaticPeers []peer.AddrInfo - MsgHandler PubsubMessageHandler + Host host.Host + TraceLog bool + StaticPeers []peer.AddrInfo + MsgHandler PubsubMessageHandler // MsgValidator accepts the topic name and returns the corresponding msg validator // in case we need different validators for specific topics, // this should be the place to map a validator to topic @@ -76,7 +75,6 @@ type PubSubConfig struct { type ScoringConfig struct { IPWhitelist []*net.IPNet IPColocationWeight float64 - OneEpochDuration time.Duration } // PubsubBundle includes the pubsub router, plus involved components @@ -117,7 +115,13 @@ type CommitteesProvider interface { } // NewPubSub creates a new pubsub router and the necessary components -func NewPubSub(ctx context.Context, logger *zap.Logger, cfg *PubSubConfig, committeesProvider CommitteesProvider, gossipScoreIndex peers.GossipScoreIndex) (*pubsub.PubSub, Controller, error) { +func NewPubSub( + ctx context.Context, + logger *zap.Logger, + cfg *PubSubConfig, + committeesProvider CommitteesProvider, + gossipScoreIndex peers.GossipScoreIndex, +) (*pubsub.PubSub, Controller, error) { if err := cfg.init(); err != nil { return nil, nil, err } @@ -167,7 +171,7 @@ func NewPubSub(ctx context.Context, logger *zap.Logger, cfg *PubSubConfig, commi } // Get overall score params - peerScoreParams := params.PeerScoreParams(cfg.Scoring.OneEpochDuration, cfg.MsgIDCacheTTL, cfg.DisableIPRateLimit, cfg.Scoring.IPWhitelist...) + peerScoreParams := params.PeerScoreParams(cfg.NetworkConfig, cfg.MsgIDCacheTTL, cfg.DisableIPRateLimit, cfg.Scoring.IPWhitelist...) // Define score inspector if inspector == nil { diff --git a/network/topics/scoring.go b/network/topics/scoring.go index 64e6a4cca8..272fd7fc27 100644 --- a/network/topics/scoring.go +++ b/network/topics/scoring.go @@ -5,7 +5,6 @@ import ( "math" "strconv" "strings" - "time" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/peer" @@ -22,7 +21,6 @@ import ( func DefaultScoringConfig() *ScoringConfig { return &ScoringConfig{ IPColocationWeight: -35.11, - OneEpochDuration: (12 * time.Second) * 32, } } @@ -207,7 +205,7 @@ func topicScoreParams(logger *zap.Logger, cfg *PubSubConfig, committeesProvider logger.Debug("got filtered committees for score params") // Create topic options - opts := params.NewSubnetTopicOpts(totalValidators, commons.SubnetsCount, topicCommittees) + opts := params.NewSubnetTopicOpts(cfg.NetworkConfig, totalValidators, commons.SubnetsCount, topicCommittees) // Generate topic parameters tp, err := params.TopicParams(opts) diff --git a/networkconfig/beacon.go b/networkconfig/beacon.go index 6dffbe5737..67eed08955 100644 --- a/networkconfig/beacon.go +++ b/networkconfig/beacon.go @@ -1,9 +1,266 @@ package networkconfig import ( - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" + "encoding/json" + "fmt" + "maps" + "math" + "time" + + "github.com/attestantio/go-eth2-client/spec" + "github.com/attestantio/go-eth2-client/spec/phase0" ) +//go:generate go tool -modfile=../tool.mod mockgen -package=networkconfig -destination=./beacon_mock.go -source=./beacon.go + +type Beacon interface { + GetSlotStartTime(slot phase0.Slot) time.Time + GetSlotEndTime(slot phase0.Slot) time.Time + EstimatedCurrentSlot() phase0.Slot + EstimatedSlotAtTime(time time.Time) phase0.Slot + EstimatedCurrentEpoch() phase0.Epoch + EstimatedEpochAtSlot(slot phase0.Slot) phase0.Epoch + IsFirstSlotOfEpoch(slot phase0.Slot) bool + GetEpochFirstSlot(epoch phase0.Epoch) phase0.Slot + GetEpochsPerSyncCommitteePeriod() uint64 + EstimatedSyncCommitteePeriodAtEpoch(epoch phase0.Epoch) uint64 + FirstEpochOfSyncPeriod(period uint64) phase0.Epoch + LastSlotOfSyncPeriod(period uint64) phase0.Slot + FirstSlotAtEpoch(epoch phase0.Epoch) phase0.Slot + EpochStartTime(epoch phase0.Epoch) time.Time + EstimatedTimeAtSlot(slot phase0.Slot) time.Time + IntervalDuration() time.Duration + EpochDuration() time.Duration + GetSlotDuration() time.Duration + GetSlotsPerEpoch() uint64 + GetGenesisTime() time.Time + GetSyncCommitteeSize() uint64 + GetGenesisValidatorsRoot() phase0.Root + GetNetworkName() string + ForkAtEpoch(epoch phase0.Epoch) (spec.DataVersion, *phase0.Fork) +} + type BeaconConfig struct { - Beacon beacon.BeaconNetwork + NetworkName string + SlotDuration time.Duration + SlotsPerEpoch uint64 + EpochsPerSyncCommitteePeriod uint64 + SyncCommitteeSize uint64 + SyncCommitteeSubnetCount uint64 + TargetAggregatorsPerSyncSubcommittee uint64 + TargetAggregatorsPerCommittee uint64 + IntervalsPerSlot uint64 + GenesisForkVersion phase0.Version + GenesisTime time.Time + GenesisValidatorsRoot phase0.Root + Forks map[spec.DataVersion]phase0.Fork +} + +func (b BeaconConfig) String() string { + marshaled, err := json.Marshal(b) + if err != nil { + panic(err) + } + + return string(marshaled) +} + +// GetSlotStartTime returns the start time for the given slot +func (b BeaconConfig) GetSlotStartTime(slot phase0.Slot) time.Time { + if slot > math.MaxInt64 { + panic(fmt.Sprintf("slot %d out of range", slot)) + } + durationSinceGenesisStart := time.Duration(slot) * b.SlotDuration // #nosec G115: slot cannot exceed math.MaxInt64 + start := b.GenesisTime.Add(durationSinceGenesisStart) + return start +} + +// GetSlotEndTime returns the end time for the given slot +func (b BeaconConfig) GetSlotEndTime(slot phase0.Slot) time.Time { + return b.GetSlotStartTime(slot + 1) +} + +// EstimatedCurrentSlot returns the estimation of the current slot +func (b BeaconConfig) EstimatedCurrentSlot() phase0.Slot { + return b.EstimatedSlotAtTime(time.Now()) +} + +// EstimatedSlotAtTime estimates slot at the given time +func (b BeaconConfig) EstimatedSlotAtTime(time time.Time) phase0.Slot { + if time.Before(b.GenesisTime) { + panic(fmt.Sprintf("time %v is before genesis time %v", time, b.GenesisTime)) + } + timeAfterGenesis := time.Sub(b.GenesisTime) + return phase0.Slot(timeAfterGenesis / b.SlotDuration) // #nosec G115: genesis can't be negative +} + +// EstimatedCurrentEpoch estimates the current epoch +// https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#compute_start_slot_at_epoch +func (b BeaconConfig) EstimatedCurrentEpoch() phase0.Epoch { + return b.EstimatedEpochAtSlot(b.EstimatedCurrentSlot()) +} + +// EstimatedEpochAtSlot estimates epoch at the given slot +func (b BeaconConfig) EstimatedEpochAtSlot(slot phase0.Slot) phase0.Epoch { + return phase0.Epoch(uint64(slot) / b.SlotsPerEpoch) +} + +// IsFirstSlotOfEpoch estimates epoch at the given slot +func (b BeaconConfig) IsFirstSlotOfEpoch(slot phase0.Slot) bool { + return uint64(slot)%b.SlotsPerEpoch == 0 +} + +// GetEpochFirstSlot returns the beacon node first slot in epoch +func (b BeaconConfig) GetEpochFirstSlot(epoch phase0.Epoch) phase0.Slot { + return phase0.Slot(uint64(epoch) * b.SlotsPerEpoch) +} + +// GetEpochsPerSyncCommitteePeriod returns the number of epochs per sync committee period. +func (b BeaconConfig) GetEpochsPerSyncCommitteePeriod() uint64 { + return b.EpochsPerSyncCommitteePeriod +} + +// EstimatedSyncCommitteePeriodAtEpoch estimates the current sync committee period at the given Epoch +func (b BeaconConfig) EstimatedSyncCommitteePeriodAtEpoch(epoch phase0.Epoch) uint64 { + return uint64(epoch) / b.GetEpochsPerSyncCommitteePeriod() +} + +// FirstEpochOfSyncPeriod calculates the first epoch of the given sync period. +func (b BeaconConfig) FirstEpochOfSyncPeriod(period uint64) phase0.Epoch { + return phase0.Epoch(period * b.GetEpochsPerSyncCommitteePeriod()) +} + +// LastSlotOfSyncPeriod calculates the first epoch of the given sync period. +func (b BeaconConfig) LastSlotOfSyncPeriod(period uint64) phase0.Slot { + lastEpoch := b.FirstEpochOfSyncPeriod(period+1) - 1 + // If we are in the sync committee that ends at slot x we do not generate a message during slot x-1 + // as it will never be included, hence -1. + return b.GetEpochFirstSlot(lastEpoch+1) - 2 +} + +func (b BeaconConfig) FirstSlotAtEpoch(epoch phase0.Epoch) phase0.Slot { + return phase0.Slot(uint64(epoch) * b.SlotsPerEpoch) +} + +func (b BeaconConfig) EpochStartTime(epoch phase0.Epoch) time.Time { + firstSlot := b.FirstSlotAtEpoch(epoch) + t := b.EstimatedTimeAtSlot(firstSlot) + return t +} + +func (b BeaconConfig) EstimatedTimeAtSlot(slot phase0.Slot) time.Time { + if slot > math.MaxInt64 { + panic(fmt.Sprintf("slot %d out of range", slot)) + } + d := time.Duration(slot) * b.SlotDuration // #nosec G115: slot cannot exceed math.MaxInt64 + return b.GenesisTime.Add(d) +} + +func (b BeaconConfig) IntervalDuration() time.Duration { + if b.IntervalsPerSlot > math.MaxInt64 { + panic("intervals per slot out of range") + } + return b.SlotDuration / time.Duration(b.IntervalsPerSlot) // #nosec G115: intervals per slot cannot exceed math.MaxInt64 +} + +func (b BeaconConfig) EpochDuration() time.Duration { + if b.SlotsPerEpoch > math.MaxInt64 { + panic("slots per epoch out of range") + } + return b.SlotDuration * time.Duration(b.SlotsPerEpoch) // #nosec G115: slot cannot exceed math.MaxInt64 +} + +func (b BeaconConfig) GetSlotDuration() time.Duration { + return b.SlotDuration +} + +func (b BeaconConfig) GetSlotsPerEpoch() uint64 { + return b.SlotsPerEpoch +} + +func (b BeaconConfig) GetGenesisTime() time.Time { + return b.GenesisTime +} + +func (b BeaconConfig) GetSyncCommitteeSize() uint64 { + return b.SyncCommitteeSize +} + +func (b BeaconConfig) GetGenesisValidatorsRoot() phase0.Root { + return b.GenesisValidatorsRoot +} + +func (b BeaconConfig) GetNetworkName() string { + return b.NetworkName +} + +func (b BeaconConfig) ForkAtEpoch(epoch phase0.Epoch) (spec.DataVersion, *phase0.Fork) { + versions := []spec.DataVersion{ + spec.DataVersionPhase0, + spec.DataVersionAltair, + spec.DataVersionBellatrix, + spec.DataVersionCapella, + spec.DataVersionDeneb, + spec.DataVersionElectra, + } + + for i, v := range versions { + if epoch < b.Forks[v].Epoch { + if i == 0 { + panic("epoch before genesis") + } + + version := versions[i-1] + fork := b.Forks[version] + return version, &fork + } + } + + version := versions[len(versions)-1] + fork := b.Forks[version] + return version, &fork +} + +func (b BeaconConfig) AssertSame(other BeaconConfig) error { + if b.NetworkName != other.NetworkName { + return fmt.Errorf("different NetworkName") + } + if b.SlotDuration != other.SlotDuration { + return fmt.Errorf("different SlotDuration") + } + if b.SlotsPerEpoch != other.SlotsPerEpoch { + return fmt.Errorf("different SlotsPerEpoch") + } + if b.EpochsPerSyncCommitteePeriod != other.EpochsPerSyncCommitteePeriod { + return fmt.Errorf("different EpochsPerSyncCommitteePeriod") + } + if b.SyncCommitteeSize != other.SyncCommitteeSize { + return fmt.Errorf("different SyncCommitteeSize") + } + if b.SyncCommitteeSubnetCount != other.SyncCommitteeSubnetCount { + return fmt.Errorf("different SyncCommitteeSubnetCount") + } + if b.TargetAggregatorsPerSyncSubcommittee != other.TargetAggregatorsPerSyncSubcommittee { + return fmt.Errorf("different TargetAggregatorsPerSyncSubcommittee") + } + if b.TargetAggregatorsPerCommittee != other.TargetAggregatorsPerCommittee { + return fmt.Errorf("different TargetAggregatorsPerCommittee") + } + if b.IntervalsPerSlot != other.IntervalsPerSlot { + return fmt.Errorf("different IntervalsPerSlot") + } + if b.GenesisForkVersion != other.GenesisForkVersion { + return fmt.Errorf("different GenesisForkVersion") + } + if b.GenesisTime != other.GenesisTime { + return fmt.Errorf("different GenesisTime") + } + if b.GenesisValidatorsRoot != other.GenesisValidatorsRoot { + return fmt.Errorf("different GenesisValidatorsRoot") + } + + if !maps.Equal(b.Forks, other.Forks) { + return fmt.Errorf("different Forks") + } + return nil } diff --git a/networkconfig/beacon_mock.go b/networkconfig/beacon_mock.go new file mode 100644 index 0000000000..8b829ba136 --- /dev/null +++ b/networkconfig/beacon_mock.go @@ -0,0 +1,380 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./beacon.go +// +// Generated by this command: +// +// mockgen -package=networkconfig -destination=./beacon_mock.go -source=./beacon.go +// + +// Package networkconfig is a generated GoMock package. +package networkconfig + +import ( + reflect "reflect" + time "time" + + spec "github.com/attestantio/go-eth2-client/spec" + phase0 "github.com/attestantio/go-eth2-client/spec/phase0" + gomock "go.uber.org/mock/gomock" +) + +// MockBeacon is a mock of Beacon interface. +type MockBeacon struct { + ctrl *gomock.Controller + recorder *MockBeaconMockRecorder + isgomock struct{} +} + +// MockBeaconMockRecorder is the mock recorder for MockBeacon. +type MockBeaconMockRecorder struct { + mock *MockBeacon +} + +// NewMockBeacon creates a new mock instance. +func NewMockBeacon(ctrl *gomock.Controller) *MockBeacon { + mock := &MockBeacon{ctrl: ctrl} + mock.recorder = &MockBeaconMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBeacon) EXPECT() *MockBeaconMockRecorder { + return m.recorder +} + +// EpochDuration mocks base method. +func (m *MockBeacon) EpochDuration() time.Duration { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EpochDuration") + ret0, _ := ret[0].(time.Duration) + return ret0 +} + +// EpochDuration indicates an expected call of EpochDuration. +func (mr *MockBeaconMockRecorder) EpochDuration() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EpochDuration", reflect.TypeOf((*MockBeacon)(nil).EpochDuration)) +} + +// EpochStartTime mocks base method. +func (m *MockBeacon) EpochStartTime(epoch phase0.Epoch) time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EpochStartTime", epoch) + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// EpochStartTime indicates an expected call of EpochStartTime. +func (mr *MockBeaconMockRecorder) EpochStartTime(epoch any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EpochStartTime", reflect.TypeOf((*MockBeacon)(nil).EpochStartTime), epoch) +} + +// EstimatedCurrentEpoch mocks base method. +func (m *MockBeacon) EstimatedCurrentEpoch() phase0.Epoch { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EstimatedCurrentEpoch") + ret0, _ := ret[0].(phase0.Epoch) + return ret0 +} + +// EstimatedCurrentEpoch indicates an expected call of EstimatedCurrentEpoch. +func (mr *MockBeaconMockRecorder) EstimatedCurrentEpoch() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimatedCurrentEpoch", reflect.TypeOf((*MockBeacon)(nil).EstimatedCurrentEpoch)) +} + +// EstimatedCurrentSlot mocks base method. +func (m *MockBeacon) EstimatedCurrentSlot() phase0.Slot { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EstimatedCurrentSlot") + ret0, _ := ret[0].(phase0.Slot) + return ret0 +} + +// EstimatedCurrentSlot indicates an expected call of EstimatedCurrentSlot. +func (mr *MockBeaconMockRecorder) EstimatedCurrentSlot() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimatedCurrentSlot", reflect.TypeOf((*MockBeacon)(nil).EstimatedCurrentSlot)) +} + +// EstimatedEpochAtSlot mocks base method. +func (m *MockBeacon) EstimatedEpochAtSlot(slot phase0.Slot) phase0.Epoch { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EstimatedEpochAtSlot", slot) + ret0, _ := ret[0].(phase0.Epoch) + return ret0 +} + +// EstimatedEpochAtSlot indicates an expected call of EstimatedEpochAtSlot. +func (mr *MockBeaconMockRecorder) EstimatedEpochAtSlot(slot any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimatedEpochAtSlot", reflect.TypeOf((*MockBeacon)(nil).EstimatedEpochAtSlot), slot) +} + +// EstimatedSlotAtTime mocks base method. +func (m *MockBeacon) EstimatedSlotAtTime(arg0 time.Time) phase0.Slot { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EstimatedSlotAtTime", arg0) + ret0, _ := ret[0].(phase0.Slot) + return ret0 +} + +// EstimatedSlotAtTime indicates an expected call of EstimatedSlotAtTime. +func (mr *MockBeaconMockRecorder) EstimatedSlotAtTime(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimatedSlotAtTime", reflect.TypeOf((*MockBeacon)(nil).EstimatedSlotAtTime), arg0) +} + +// EstimatedSyncCommitteePeriodAtEpoch mocks base method. +func (m *MockBeacon) EstimatedSyncCommitteePeriodAtEpoch(epoch phase0.Epoch) uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EstimatedSyncCommitteePeriodAtEpoch", epoch) + ret0, _ := ret[0].(uint64) + return ret0 +} + +// EstimatedSyncCommitteePeriodAtEpoch indicates an expected call of EstimatedSyncCommitteePeriodAtEpoch. +func (mr *MockBeaconMockRecorder) EstimatedSyncCommitteePeriodAtEpoch(epoch any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimatedSyncCommitteePeriodAtEpoch", reflect.TypeOf((*MockBeacon)(nil).EstimatedSyncCommitteePeriodAtEpoch), epoch) +} + +// EstimatedTimeAtSlot mocks base method. +func (m *MockBeacon) EstimatedTimeAtSlot(slot phase0.Slot) time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EstimatedTimeAtSlot", slot) + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// EstimatedTimeAtSlot indicates an expected call of EstimatedTimeAtSlot. +func (mr *MockBeaconMockRecorder) EstimatedTimeAtSlot(slot any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimatedTimeAtSlot", reflect.TypeOf((*MockBeacon)(nil).EstimatedTimeAtSlot), slot) +} + +// FirstEpochOfSyncPeriod mocks base method. +func (m *MockBeacon) FirstEpochOfSyncPeriod(period uint64) phase0.Epoch { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FirstEpochOfSyncPeriod", period) + ret0, _ := ret[0].(phase0.Epoch) + return ret0 +} + +// FirstEpochOfSyncPeriod indicates an expected call of FirstEpochOfSyncPeriod. +func (mr *MockBeaconMockRecorder) FirstEpochOfSyncPeriod(period any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FirstEpochOfSyncPeriod", reflect.TypeOf((*MockBeacon)(nil).FirstEpochOfSyncPeriod), period) +} + +// FirstSlotAtEpoch mocks base method. +func (m *MockBeacon) FirstSlotAtEpoch(epoch phase0.Epoch) phase0.Slot { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FirstSlotAtEpoch", epoch) + ret0, _ := ret[0].(phase0.Slot) + return ret0 +} + +// FirstSlotAtEpoch indicates an expected call of FirstSlotAtEpoch. +func (mr *MockBeaconMockRecorder) FirstSlotAtEpoch(epoch any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FirstSlotAtEpoch", reflect.TypeOf((*MockBeacon)(nil).FirstSlotAtEpoch), epoch) +} + +// ForkAtEpoch mocks base method. +func (m *MockBeacon) ForkAtEpoch(epoch phase0.Epoch) (spec.DataVersion, *phase0.Fork) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ForkAtEpoch", epoch) + ret0, _ := ret[0].(spec.DataVersion) + ret1, _ := ret[1].(*phase0.Fork) + return ret0, ret1 +} + +// ForkAtEpoch indicates an expected call of ForkAtEpoch. +func (mr *MockBeaconMockRecorder) ForkAtEpoch(epoch any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ForkAtEpoch", reflect.TypeOf((*MockBeacon)(nil).ForkAtEpoch), epoch) +} + +// GetEpochFirstSlot mocks base method. +func (m *MockBeacon) GetEpochFirstSlot(epoch phase0.Epoch) phase0.Slot { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEpochFirstSlot", epoch) + ret0, _ := ret[0].(phase0.Slot) + return ret0 +} + +// GetEpochFirstSlot indicates an expected call of GetEpochFirstSlot. +func (mr *MockBeaconMockRecorder) GetEpochFirstSlot(epoch any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEpochFirstSlot", reflect.TypeOf((*MockBeacon)(nil).GetEpochFirstSlot), epoch) +} + +// GetEpochsPerSyncCommitteePeriod mocks base method. +func (m *MockBeacon) GetEpochsPerSyncCommitteePeriod() uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEpochsPerSyncCommitteePeriod") + ret0, _ := ret[0].(uint64) + return ret0 +} + +// GetEpochsPerSyncCommitteePeriod indicates an expected call of GetEpochsPerSyncCommitteePeriod. +func (mr *MockBeaconMockRecorder) GetEpochsPerSyncCommitteePeriod() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEpochsPerSyncCommitteePeriod", reflect.TypeOf((*MockBeacon)(nil).GetEpochsPerSyncCommitteePeriod)) +} + +// GetGenesisTime mocks base method. +func (m *MockBeacon) GetGenesisTime() time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetGenesisTime") + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// GetGenesisTime indicates an expected call of GetGenesisTime. +func (mr *MockBeaconMockRecorder) GetGenesisTime() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGenesisTime", reflect.TypeOf((*MockBeacon)(nil).GetGenesisTime)) +} + +// GetGenesisValidatorsRoot mocks base method. +func (m *MockBeacon) GetGenesisValidatorsRoot() phase0.Root { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetGenesisValidatorsRoot") + ret0, _ := ret[0].(phase0.Root) + return ret0 +} + +// GetGenesisValidatorsRoot indicates an expected call of GetGenesisValidatorsRoot. +func (mr *MockBeaconMockRecorder) GetGenesisValidatorsRoot() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGenesisValidatorsRoot", reflect.TypeOf((*MockBeacon)(nil).GetGenesisValidatorsRoot)) +} + +// GetNetworkName mocks base method. +func (m *MockBeacon) GetNetworkName() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNetworkName") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetNetworkName indicates an expected call of GetNetworkName. +func (mr *MockBeaconMockRecorder) GetNetworkName() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetworkName", reflect.TypeOf((*MockBeacon)(nil).GetNetworkName)) +} + +// GetSlotDuration mocks base method. +func (m *MockBeacon) GetSlotDuration() time.Duration { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSlotDuration") + ret0, _ := ret[0].(time.Duration) + return ret0 +} + +// GetSlotDuration indicates an expected call of GetSlotDuration. +func (mr *MockBeaconMockRecorder) GetSlotDuration() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSlotDuration", reflect.TypeOf((*MockBeacon)(nil).GetSlotDuration)) +} + +// GetSlotEndTime mocks base method. +func (m *MockBeacon) GetSlotEndTime(slot phase0.Slot) time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSlotEndTime", slot) + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// GetSlotEndTime indicates an expected call of GetSlotEndTime. +func (mr *MockBeaconMockRecorder) GetSlotEndTime(slot any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSlotEndTime", reflect.TypeOf((*MockBeacon)(nil).GetSlotEndTime), slot) +} + +// GetSlotStartTime mocks base method. +func (m *MockBeacon) GetSlotStartTime(slot phase0.Slot) time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSlotStartTime", slot) + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// GetSlotStartTime indicates an expected call of GetSlotStartTime. +func (mr *MockBeaconMockRecorder) GetSlotStartTime(slot any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSlotStartTime", reflect.TypeOf((*MockBeacon)(nil).GetSlotStartTime), slot) +} + +// GetSlotsPerEpoch mocks base method. +func (m *MockBeacon) GetSlotsPerEpoch() uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSlotsPerEpoch") + ret0, _ := ret[0].(uint64) + return ret0 +} + +// GetSlotsPerEpoch indicates an expected call of GetSlotsPerEpoch. +func (mr *MockBeaconMockRecorder) GetSlotsPerEpoch() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSlotsPerEpoch", reflect.TypeOf((*MockBeacon)(nil).GetSlotsPerEpoch)) +} + +// GetSyncCommitteeSize mocks base method. +func (m *MockBeacon) GetSyncCommitteeSize() uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSyncCommitteeSize") + ret0, _ := ret[0].(uint64) + return ret0 +} + +// GetSyncCommitteeSize indicates an expected call of GetSyncCommitteeSize. +func (mr *MockBeaconMockRecorder) GetSyncCommitteeSize() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSyncCommitteeSize", reflect.TypeOf((*MockBeacon)(nil).GetSyncCommitteeSize)) +} + +// IntervalDuration mocks base method. +func (m *MockBeacon) IntervalDuration() time.Duration { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IntervalDuration") + ret0, _ := ret[0].(time.Duration) + return ret0 +} + +// IntervalDuration indicates an expected call of IntervalDuration. +func (mr *MockBeaconMockRecorder) IntervalDuration() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IntervalDuration", reflect.TypeOf((*MockBeacon)(nil).IntervalDuration)) +} + +// IsFirstSlotOfEpoch mocks base method. +func (m *MockBeacon) IsFirstSlotOfEpoch(slot phase0.Slot) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsFirstSlotOfEpoch", slot) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsFirstSlotOfEpoch indicates an expected call of IsFirstSlotOfEpoch. +func (mr *MockBeaconMockRecorder) IsFirstSlotOfEpoch(slot any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsFirstSlotOfEpoch", reflect.TypeOf((*MockBeacon)(nil).IsFirstSlotOfEpoch), slot) +} + +// LastSlotOfSyncPeriod mocks base method. +func (m *MockBeacon) LastSlotOfSyncPeriod(period uint64) phase0.Slot { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LastSlotOfSyncPeriod", period) + ret0, _ := ret[0].(phase0.Slot) + return ret0 +} + +// LastSlotOfSyncPeriod indicates an expected call of LastSlotOfSyncPeriod. +func (mr *MockBeaconMockRecorder) LastSlotOfSyncPeriod(period any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LastSlotOfSyncPeriod", reflect.TypeOf((*MockBeacon)(nil).LastSlotOfSyncPeriod), period) +} diff --git a/networkconfig/beacon_test.go b/networkconfig/beacon_test.go new file mode 100644 index 0000000000..d15469b15b --- /dev/null +++ b/networkconfig/beacon_test.go @@ -0,0 +1,121 @@ +package networkconfig + +import ( + "testing" + + "github.com/attestantio/go-eth2-client/spec" + "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/stretchr/testify/require" +) + +// TestForkAtEpoch verifies that ForkAtEpoch returns the correct version and fork data based on fork epochs. +func TestForkAtEpoch(t *testing.T) { + config := &BeaconConfig{ + Forks: map[spec.DataVersion]phase0.Fork{ + spec.DataVersionPhase0: { + Epoch: phase0.Epoch(0), + PreviousVersion: phase0.Version{0}, + CurrentVersion: phase0.Version{0}, + }, + spec.DataVersionAltair: { + Epoch: phase0.Epoch(10), + PreviousVersion: phase0.Version{0}, + CurrentVersion: phase0.Version{1}, + }, + spec.DataVersionBellatrix: { + Epoch: phase0.Epoch(20), + PreviousVersion: phase0.Version{1}, + CurrentVersion: phase0.Version{2}, + }, + spec.DataVersionCapella: { + Epoch: phase0.Epoch(30), + PreviousVersion: phase0.Version{2}, + CurrentVersion: phase0.Version{3}, + }, + spec.DataVersionDeneb: { + Epoch: phase0.Epoch(40), + PreviousVersion: phase0.Version{3}, + CurrentVersion: phase0.Version{4}, + }, + spec.DataVersionElectra: { + Epoch: phase0.Epoch(50), + PreviousVersion: phase0.Version{4}, + CurrentVersion: phase0.Version{5}, + }, + }, + } + + tests := []struct { + epoch phase0.Epoch + version spec.DataVersion + fork phase0.Fork + }{ + {epoch: 0, version: spec.DataVersionPhase0, fork: phase0.Fork{ + PreviousVersion: phase0.Version{0}, + CurrentVersion: phase0.Version{0}, + Epoch: 0, + }}, + {epoch: 9, version: spec.DataVersionPhase0, fork: phase0.Fork{ + PreviousVersion: phase0.Version{0}, + CurrentVersion: phase0.Version{0}, + Epoch: 0, + }}, + {epoch: 10, version: spec.DataVersionAltair, fork: phase0.Fork{ + Epoch: phase0.Epoch(10), + PreviousVersion: phase0.Version{0}, + CurrentVersion: phase0.Version{1}, + }}, + {epoch: 15, version: spec.DataVersionAltair, fork: phase0.Fork{ + Epoch: phase0.Epoch(10), + PreviousVersion: phase0.Version{0}, + CurrentVersion: phase0.Version{1}, + }}, + {epoch: 20, version: spec.DataVersionBellatrix, fork: phase0.Fork{ + Epoch: phase0.Epoch(20), + PreviousVersion: phase0.Version{1}, + CurrentVersion: phase0.Version{2}, + }}, + {epoch: 25, version: spec.DataVersionBellatrix, fork: phase0.Fork{ + Epoch: phase0.Epoch(20), + PreviousVersion: phase0.Version{1}, + CurrentVersion: phase0.Version{2}, + }}, + {epoch: 30, version: spec.DataVersionCapella, fork: phase0.Fork{ + Epoch: phase0.Epoch(30), + PreviousVersion: phase0.Version{2}, + CurrentVersion: phase0.Version{3}, + }}, + {epoch: 35, version: spec.DataVersionCapella, fork: phase0.Fork{ + Epoch: phase0.Epoch(30), + PreviousVersion: phase0.Version{2}, + CurrentVersion: phase0.Version{3}, + }}, + {epoch: 40, version: spec.DataVersionDeneb, fork: phase0.Fork{ + Epoch: phase0.Epoch(40), + PreviousVersion: phase0.Version{3}, + CurrentVersion: phase0.Version{4}, + }}, + {epoch: 45, version: spec.DataVersionDeneb, fork: phase0.Fork{ + Epoch: phase0.Epoch(40), + PreviousVersion: phase0.Version{3}, + CurrentVersion: phase0.Version{4}, + }}, + {epoch: 50, version: spec.DataVersionElectra, fork: phase0.Fork{ + Epoch: phase0.Epoch(50), + PreviousVersion: phase0.Version{4}, + CurrentVersion: phase0.Version{5}, + }}, + {epoch: 55, version: spec.DataVersionElectra, fork: phase0.Fork{ + Epoch: phase0.Epoch(50), + PreviousVersion: phase0.Version{4}, + CurrentVersion: phase0.Version{5}, + }}, + } + + for _, tc := range tests { + version, fork := config.ForkAtEpoch(tc.epoch) + require.Equal(t, tc.version, version, "Wrong version") + require.NotNil(t, tc.fork, fork, "Nil fork") + require.Equal(t, tc.fork, *fork, "Wrong fork") + } +} diff --git a/networkconfig/config.go b/networkconfig/config.go deleted file mode 100644 index 6d290ed244..0000000000 --- a/networkconfig/config.go +++ /dev/null @@ -1,67 +0,0 @@ -package networkconfig - -import ( - "encoding/json" - "fmt" - "time" -) - -var SupportedConfigs = map[string]NetworkConfig{ - Mainnet.Name: Mainnet, - Holesky.Name: Holesky, - HoleskyStage.Name: HoleskyStage, - LocalTestnet.Name: LocalTestnet, - HoleskyE2E.Name: HoleskyE2E, - Hoodi.Name: Hoodi, - HoodiStage.Name: HoodiStage, - Sepolia.Name: Sepolia, -} - -const forkName = "alan" - -func GetNetworkConfigByName(name string) (NetworkConfig, error) { - if network, ok := SupportedConfigs[name]; ok { - return network, nil - } - - return NetworkConfig{}, fmt.Errorf("network not supported: %v", name) -} - -type NetworkConfig struct { - Name string - BeaconConfig - SSVConfig -} - -func (n NetworkConfig) String() string { - b, err := json.MarshalIndent(n, "", "\t") - if err != nil { - return "" - } - - return string(b) -} - -func (n NetworkConfig) NetworkName() string { - return fmt.Sprintf("%s:%s", n.Name, forkName) -} - -// ForkVersion returns the fork version of the network. -func (n NetworkConfig) ForkVersion() [4]byte { - return n.Beacon.ForkVersion() -} - -// SlotDurationSec returns slot duration -func (n NetworkConfig) SlotDurationSec() time.Duration { - return n.Beacon.SlotDurationSec() -} - -// SlotsPerEpoch returns number of slots per one epoch -func (n NetworkConfig) SlotsPerEpoch() uint64 { - return n.Beacon.SlotsPerEpoch() -} - -// GetGenesisTime returns the genesis time in unix time. -func (n NetworkConfig) GetGenesisTime() time.Time { - return time.Unix(n.Beacon.MinGenesisTime(), 0) -} diff --git a/networkconfig/holesky-e2e.go b/networkconfig/holesky-e2e.go index 0e8e060261..32efa10ce7 100644 --- a/networkconfig/holesky-e2e.go +++ b/networkconfig/holesky-e2e.go @@ -3,20 +3,16 @@ package networkconfig import ( "math/big" + ethcommon "github.com/ethereum/go-ethereum/common" spectypes "github.com/ssvlabs/ssv-spec/types" - - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" ) -var HoleskyE2E = NetworkConfig{ - Name: "holesky-e2e", - BeaconConfig: BeaconConfig{ - Beacon: beacon.NewNetwork(spectypes.HoleskyNetwork), - }, - SSVConfig: SSVConfig{ - DomainType: spectypes.DomainType{0x0, 0x0, 0xee, 0x1}, - RegistryContractAddr: "0x58410bef803ecd7e63b23664c586a6db72daf59c", - RegistrySyncOffset: big.NewInt(405579), - Bootnodes: []string{}, - }, +const HoleskyE2EName = "holesky-e2e" + +var HoleskyE2ESSV = SSVConfig{ + DomainType: spectypes.DomainType{0x0, 0x0, 0xee, 0x1}, + RegistryContractAddr: ethcommon.HexToAddress("0x58410bef803ecd7e63b23664c586a6db72daf59c"), + RegistrySyncOffset: big.NewInt(405579), + Bootnodes: []string{}, + TotalEthereumValidators: HoleskySSV.TotalEthereumValidators, } diff --git a/networkconfig/holesky-stage.go b/networkconfig/holesky-stage.go index 682feb604d..e15185afee 100644 --- a/networkconfig/holesky-stage.go +++ b/networkconfig/holesky-stage.go @@ -3,27 +3,22 @@ package networkconfig import ( "math/big" - spectypes "github.com/ssvlabs/ssv-spec/types" - - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" + ethcommon "github.com/ethereum/go-ethereum/common" ) -var HoleskyStage = NetworkConfig{ - Name: "holesky-stage", - BeaconConfig: BeaconConfig{ - Beacon: beacon.NewNetwork(spectypes.HoleskyNetwork), - }, - SSVConfig: SSVConfig{ - DomainType: [4]byte{0x00, 0x00, 0x31, 0x13}, - RegistrySyncOffset: new(big.Int).SetInt64(84599), - RegistryContractAddr: "0x0d33801785340072C452b994496B19f196b7eE15", - DiscoveryProtocolID: [6]byte{'s', 's', 'v', 'd', 'v', '5'}, - Bootnodes: []string{ - // Public bootnode: - // "enr:-Ja4QDYHVgUs9NvlMqq93ot6VNqbmrIlMrwKnq4X3DPRgyUNB4ospDp8ubMvsf-KsgqY8rzpZKy4GbE1DLphabpRBc-GAY_diLjngmlkgnY0gmlwhDQrLYqJc2VjcDI1NmsxoQKnAiuSlgSR8asjCH0aYoVKM8uPbi4noFuFHZHaAHqknYNzc3YBg3RjcIITiYN1ZHCCD6E", +const HoleskyStageName = "holesky-stage" + +var HoleskyStageSSV = SSVConfig{ + DomainType: [4]byte{0x00, 0x00, 0x31, 0x13}, + RegistrySyncOffset: new(big.Int).SetInt64(84599), + RegistryContractAddr: ethcommon.HexToAddress("0x0d33801785340072C452b994496B19f196b7eE15"), + DiscoveryProtocolID: [6]byte{'s', 's', 'v', 'd', 'v', '5'}, + Bootnodes: []string{ + // Public bootnode: + // "enr:-Ja4QDYHVgUs9NvlMqq93ot6VNqbmrIlMrwKnq4X3DPRgyUNB4ospDp8ubMvsf-KsgqY8rzpZKy4GbE1DLphabpRBc-GAY_diLjngmlkgnY0gmlwhDQrLYqJc2VjcDI1NmsxoQKnAiuSlgSR8asjCH0aYoVKM8uPbi4noFuFHZHaAHqknYNzc3YBg3RjcIITiYN1ZHCCD6E", - // Private bootnode: - "enr:-Ja4QDRUBjWOvVfGxpxvv3FqaCy3psm7IsKu5ETb1GXiexGYDFppD33t7AHRfmQddoAkBiyb7pt4t7ZN0sNB9CsW4I-GAZGOmChMgmlkgnY0gmlwhAorXxuJc2VjcDI1NmsxoQP_bBE-ZYvaXKBR3dRYMN5K_lZP-q-YsBzDZEtxH_4T_YNzc3YBg3RjcIITioN1ZHCCD6I", - }, + // Private bootnode: + "enr:-Ja4QDRUBjWOvVfGxpxvv3FqaCy3psm7IsKu5ETb1GXiexGYDFppD33t7AHRfmQddoAkBiyb7pt4t7ZN0sNB9CsW4I-GAZGOmChMgmlkgnY0gmlwhAorXxuJc2VjcDI1NmsxoQP_bBE-ZYvaXKBR3dRYMN5K_lZP-q-YsBzDZEtxH_4T_YNzc3YBg3RjcIITioN1ZHCCD6I", }, + TotalEthereumValidators: HoleskySSV.TotalEthereumValidators, } diff --git a/networkconfig/holesky.go b/networkconfig/holesky.go index 7934182742..5cabf6562a 100644 --- a/networkconfig/holesky.go +++ b/networkconfig/holesky.go @@ -3,24 +3,20 @@ package networkconfig import ( "math/big" + ethcommon "github.com/ethereum/go-ethereum/common" spectypes "github.com/ssvlabs/ssv-spec/types" - - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" ) -var Holesky = NetworkConfig{ - Name: "holesky", - BeaconConfig: BeaconConfig{ - Beacon: beacon.NewNetwork(spectypes.HoleskyNetwork), - }, - SSVConfig: SSVConfig{ - DomainType: spectypes.DomainType{0x0, 0x0, 0x5, 0x2}, - RegistrySyncOffset: new(big.Int).SetInt64(181612), - RegistryContractAddr: "0x38A4794cCEd47d3baf7370CcC43B560D3a1beEFA", - DiscoveryProtocolID: [6]byte{'s', 's', 'v', 'd', 'v', '5'}, - Bootnodes: []string{ - // SSV Labs - "enr:-Ja4QKFD3u5tZob7xukp-JKX9QJMFqqI68cItsE4tBbhsOyDR0M_1UUjb35hbrqvTP3bnXO_LnKh-jNLTeaUqN4xiduGAZKaP_sagmlkgnY0gmlwhDb0fh6Jc2VjcDI1NmsxoQMw_H2anuiqP9NmEaZwbUfdvPFog7PvcKmoVByDa576SINzc3YBg3RjcIITioN1ZHCCD6I", - }, +const HoleskyName = "holesky" + +var HoleskySSV = SSVConfig{ + DomainType: spectypes.DomainType{0x0, 0x0, 0x5, 0x2}, + RegistrySyncOffset: new(big.Int).SetInt64(181612), + RegistryContractAddr: ethcommon.HexToAddress("0x38A4794cCEd47d3baf7370CcC43B560D3a1beEFA"), + DiscoveryProtocolID: [6]byte{'s', 's', 'v', 'd', 'v', '5'}, + Bootnodes: []string{ + // SSV Labs + "enr:-Ja4QKFD3u5tZob7xukp-JKX9QJMFqqI68cItsE4tBbhsOyDR0M_1UUjb35hbrqvTP3bnXO_LnKh-jNLTeaUqN4xiduGAZKaP_sagmlkgnY0gmlwhDb0fh6Jc2VjcDI1NmsxoQMw_H2anuiqP9NmEaZwbUfdvPFog7PvcKmoVByDa576SINzc3YBg3RjcIITioN1ZHCCD6I", }, + TotalEthereumValidators: 1757795, // active_validators from https://holesky.beaconcha.in/index/data on Nov 20, 2024 } diff --git a/networkconfig/hoodi-stage.go b/networkconfig/hoodi-stage.go index b013d11c2c..b78942dc05 100644 --- a/networkconfig/hoodi-stage.go +++ b/networkconfig/hoodi-stage.go @@ -3,24 +3,19 @@ package networkconfig import ( "math/big" - spectypes "github.com/ssvlabs/ssv-spec/types" - - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" + ethcommon "github.com/ethereum/go-ethereum/common" ) -var HoodiStage = NetworkConfig{ - Name: "hoodi-stage", - BeaconConfig: BeaconConfig{ - Beacon: beacon.NewNetwork(spectypes.HoodiNetwork), - }, - SSVConfig: SSVConfig{ - DomainType: [4]byte{0x00, 0x00, 0x31, 0x14}, - RegistrySyncOffset: new(big.Int).SetInt64(1004), - RegistryContractAddr: "0x0aaace4e8affc47c6834171c88d342a4abd8f105", - DiscoveryProtocolID: [6]byte{'s', 's', 'v', 'd', 'v', '5'}, - Bootnodes: []string{ - // SSV Labs - "enr:-Ja4QJZcaYfS0GpX-5xREVBa26a-E-QHMFek-EndsJdgM6loIM7pfbJwPDCNK1VzPkUhMjwcTTuNASiHU6X-sjsrxFmGAZWjNu06gmlkgnY0gmlwhErcGnyJc2VjcDI1NmsxoQP_bBE-ZYvaXKBR3dRYMN5K_lZP-q-YsBzDZEtxH_4T_YNzc3YBg3RjcIITioN1ZHCCD6I", - }, +const HoodiStageName = "hoodi-stage" + +var HoodiStageSSV = SSVConfig{ + DomainType: [4]byte{0x00, 0x00, 0x31, 0x14}, + RegistrySyncOffset: new(big.Int).SetInt64(1004), + RegistryContractAddr: ethcommon.HexToAddress("0x0aaace4e8affc47c6834171c88d342a4abd8f105"), + DiscoveryProtocolID: [6]byte{'s', 's', 'v', 'd', 'v', '5'}, + Bootnodes: []string{ + // SSV Labs + "enr:-Ja4QJZcaYfS0GpX-5xREVBa26a-E-QHMFek-EndsJdgM6loIM7pfbJwPDCNK1VzPkUhMjwcTTuNASiHU6X-sjsrxFmGAZWjNu06gmlkgnY0gmlwhErcGnyJc2VjcDI1NmsxoQP_bBE-ZYvaXKBR3dRYMN5K_lZP-q-YsBzDZEtxH_4T_YNzc3YBg3RjcIITioN1ZHCCD6I", }, + TotalEthereumValidators: HoodiSSV.TotalEthereumValidators, } diff --git a/networkconfig/hoodi.go b/networkconfig/hoodi.go index 24292d2251..67a50e834c 100644 --- a/networkconfig/hoodi.go +++ b/networkconfig/hoodi.go @@ -3,24 +3,20 @@ package networkconfig import ( "math/big" + ethcommon "github.com/ethereum/go-ethereum/common" spectypes "github.com/ssvlabs/ssv-spec/types" - - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" ) -var Hoodi = NetworkConfig{ - Name: "hoodi", - BeaconConfig: BeaconConfig{ - Beacon: beacon.NewNetwork(spectypes.HoodiNetwork), - }, - SSVConfig: SSVConfig{ - DomainType: spectypes.DomainType{0x0, 0x0, 0x5, 0x3}, - RegistrySyncOffset: new(big.Int).SetInt64(1065), - RegistryContractAddr: "0x58410Bef803ECd7E63B23664C586A6DB72DAf59c", - DiscoveryProtocolID: [6]byte{'s', 's', 'v', 'd', 'v', '5'}, - Bootnodes: []string{ - // SSV Labs - "enr:-Ja4QIKlyNFuFtTOnVoavqwmpgSJXfhSmhpdSDOUhf5-FBr7bBxQRvG6VrpUvlkr8MtpNNuMAkM33AseduSaOhd9IeWGAZWjRbnvgmlkgnY0gmlwhCNVVTCJc2VjcDI1NmsxoQNTTyiJPoZh502xOZpHSHAfR-94NaXLvi5J4CNHMh2tjoNzc3YBg3RjcIITioN1ZHCCD6I", - }, +const HoodiName = "hoodi" + +var HoodiSSV = SSVConfig{ + DomainType: spectypes.DomainType{0x0, 0x0, 0x5, 0x3}, + RegistrySyncOffset: new(big.Int).SetInt64(1065), + RegistryContractAddr: ethcommon.HexToAddress("0x58410Bef803ECd7E63B23664C586A6DB72DAf59c"), + DiscoveryProtocolID: [6]byte{'s', 's', 'v', 'd', 'v', '5'}, + Bootnodes: []string{ + // SSV Labs + "enr:-Ja4QIKlyNFuFtTOnVoavqwmpgSJXfhSmhpdSDOUhf5-FBr7bBxQRvG6VrpUvlkr8MtpNNuMAkM33AseduSaOhd9IeWGAZWjRbnvgmlkgnY0gmlwhCNVVTCJc2VjcDI1NmsxoQNTTyiJPoZh502xOZpHSHAfR-94NaXLvi5J4CNHMh2tjoNzc3YBg3RjcIITioN1ZHCCD6I", }, + TotalEthereumValidators: 1107955, // active_validators from https://hoodi.beaconcha.in/index/data on Apr 18, 2025 } diff --git a/networkconfig/local-testnet.go b/networkconfig/local-testnet.go index 6318ab4ac4..8372ca1c5c 100644 --- a/networkconfig/local-testnet.go +++ b/networkconfig/local-testnet.go @@ -1,21 +1,19 @@ package networkconfig import ( - spectypes "github.com/ssvlabs/ssv-spec/types" + "math/big" - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" + ethcommon "github.com/ethereum/go-ethereum/common" + spectypes "github.com/ssvlabs/ssv-spec/types" ) -var LocalTestnet = NetworkConfig{ - Name: "local-testnet", - BeaconConfig: BeaconConfig{ - Beacon: beacon.NewLocalTestNetwork(spectypes.PraterNetwork), - }, - SSVConfig: SSVConfig{ - DomainType: spectypes.DomainType{0x0, 0x0, spectypes.JatoV2NetworkID.Byte(), 0x2}, - RegistryContractAddr: "0xC3CD9A0aE89Fff83b71b58b6512D43F8a41f363D", - Bootnodes: []string{ - "enr:-Li4QLR4Y1VbwiqFYKy6m-WFHRNDjhMDZ_qJwIABu2PY9BHjIYwCKpTvvkVmZhu43Q6zVA29sEUhtz10rQjDJkK3Hd-GAYiGrW2Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhCLdu_SJc2VjcDI1NmsxoQJTcI7GHPw-ZqIflPZYYDK_guurp_gsAFF5Erns3-PAvIN0Y3CCE4mDdWRwgg-h", - }, - }, +const LocalTestnetName = "local-testnet" + +var LocalTestnetSSV = SSVConfig{ + DomainType: spectypes.DomainType{0x0, 0x0, spectypes.JatoV2NetworkID.Byte(), 0x2}, + RegistrySyncOffset: new(big.Int).SetInt64(0), RegistryContractAddr: ethcommon.HexToAddress("0xC3CD9A0aE89Fff83b71b58b6512D43F8a41f363D"), + Bootnodes: []string{ + "enr:-Li4QLR4Y1VbwiqFYKy6m-WFHRNDjhMDZ_qJwIABu2PY9BHjIYwCKpTvvkVmZhu43Q6zVA29sEUhtz10rQjDJkK3Hd-GAYiGrW2Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhCLdu_SJc2VjcDI1NmsxoQJTcI7GHPw-ZqIflPZYYDK_guurp_gsAFF5Erns3-PAvIN0Y3CCE4mDdWRwgg-h", + }, DiscoveryProtocolID: [6]byte{'s', 's', 'v', 'd', 'v', '5'}, + TotalEthereumValidators: TestNetwork.TotalEthereumValidators, } diff --git a/networkconfig/mainnet.go b/networkconfig/mainnet.go index e8ce291a42..c96426d8c4 100644 --- a/networkconfig/mainnet.go +++ b/networkconfig/mainnet.go @@ -3,33 +3,29 @@ package networkconfig import ( "math/big" + ethcommon "github.com/ethereum/go-ethereum/common" spectypes "github.com/ssvlabs/ssv-spec/types" - - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" ) -var Mainnet = NetworkConfig{ - Name: "mainnet", - BeaconConfig: BeaconConfig{ - Beacon: beacon.NewNetwork(spectypes.MainNetwork), - }, - SSVConfig: SSVConfig{ - DomainType: spectypes.AlanMainnet, - RegistrySyncOffset: new(big.Int).SetInt64(17507487), - RegistryContractAddr: "0xDD9BC35aE942eF0cFa76930954a156B3fF30a4E1", - DiscoveryProtocolID: [6]byte{'s', 's', 'v', 'd', 'v', '5'}, - Bootnodes: []string{ - // SSV Labs - "enr:-Ja4QAbDe5XANqJUDyJU1GmtS01qqMwDYx9JNZgymjBb55fMaha80E2HznRYoUGy6NFVSvs1u1cFqSM0MgJI-h1QKLeGAZKaTo7LgmlkgnY0gmlwhDQrfraJc2VjcDI1NmsxoQNEj0Pgq9-VxfeX83LPDOUPyWiTVzdI-DnfMdO1n468u4Nzc3YBg3RjcIITioN1ZHCCD6I", +const MainnetName = "mainnet" + +var MainnetSSV = SSVConfig{ + DomainType: spectypes.AlanMainnet, + RegistrySyncOffset: new(big.Int).SetInt64(17507487), + RegistryContractAddr: ethcommon.HexToAddress("0xDD9BC35aE942eF0cFa76930954a156B3fF30a4E1"), + DiscoveryProtocolID: [6]byte{'s', 's', 'v', 'd', 'v', '5'}, + Bootnodes: []string{ + // SSV Labs + "enr:-Ja4QAbDe5XANqJUDyJU1GmtS01qqMwDYx9JNZgymjBb55fMaha80E2HznRYoUGy6NFVSvs1u1cFqSM0MgJI-h1QKLeGAZKaTo7LgmlkgnY0gmlwhDQrfraJc2VjcDI1NmsxoQNEj0Pgq9-VxfeX83LPDOUPyWiTVzdI-DnfMdO1n468u4Nzc3YBg3RjcIITioN1ZHCCD6I", - // 0NEinfra bootnode - "enr:-Li4QDwrOuhEq5gBJBzFUPkezoYiy56SXZUwkSD7bxYo8RAhPnHyS0de0nOQrzl-cL47RY9Jg8k6Y_MgaUd9a5baYXeGAYnfZE76h2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhDaTS0mJc2VjcDI1NmsxoQMZzUHaN3eClRgF9NAqRNc-ilGpJDDJxdenfo4j-zWKKYN0Y3CCE4iDdWRwgg-g", + // 0NEinfra bootnode + "enr:-Li4QDwrOuhEq5gBJBzFUPkezoYiy56SXZUwkSD7bxYo8RAhPnHyS0de0nOQrzl-cL47RY9Jg8k6Y_MgaUd9a5baYXeGAYnfZE76h2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhDaTS0mJc2VjcDI1NmsxoQMZzUHaN3eClRgF9NAqRNc-ilGpJDDJxdenfo4j-zWKKYN0Y3CCE4iDdWRwgg-g", - // Eridian (eridianalpha.com) - "enr:-Li4QIzHQ2H82twhvsu8EePZ6CA1gl0_B0WWsKaT07245TkHUqXay-MXEgObJB7BxMFl8TylFxfnKNxQyGTXh-2nAlOGAYuraxUEh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBKCzUSJc2VjcDI1NmsxoQNKskkQ6-mBdBWr_ORJfyHai5uD0vL6Fuw90X0sPwmRsoN0Y3CCE4iDdWRwgg-g", + // Eridian (eridianalpha.com) + "enr:-Li4QIzHQ2H82twhvsu8EePZ6CA1gl0_B0WWsKaT07245TkHUqXay-MXEgObJB7BxMFl8TylFxfnKNxQyGTXh-2nAlOGAYuraxUEh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBKCzUSJc2VjcDI1NmsxoQNKskkQ6-mBdBWr_ORJfyHai5uD0vL6Fuw90X0sPwmRsoN0Y3CCE4iDdWRwgg-g", - // CryptoManufaktur - "enr:-Li4QH7FwJcL8gJj0zHAITXqghMkG-A5bfWh2-3Q7vosy9D1BS8HZk-1ITuhK_rfzG3v_UtBDI6uNJZWpdcWfrQFCxKGAYnQ1DRCh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLb3g2Jc2VjcDI1NmsxoQKeSDcZWSaY9FC723E9yYX1Li18bswhLNlxBZdLfgOKp4N0Y3CCE4mDdWRwgg-h", - }, + // CryptoManufaktur + "enr:-Li4QH7FwJcL8gJj0zHAITXqghMkG-A5bfWh2-3Q7vosy9D1BS8HZk-1ITuhK_rfzG3v_UtBDI6uNJZWpdcWfrQFCxKGAYnQ1DRCh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLb3g2Jc2VjcDI1NmsxoQKeSDcZWSaY9FC723E9yYX1Li18bswhLNlxBZdLfgOKp4N0Y3CCE4mDdWRwgg-h", }, + TotalEthereumValidators: 1064860, // active_validators from https://mainnet.beaconcha.in/index/data on Apr 18, 2025 } diff --git a/networkconfig/network.go b/networkconfig/network.go new file mode 100644 index 0000000000..cafc335ea1 --- /dev/null +++ b/networkconfig/network.go @@ -0,0 +1,35 @@ +package networkconfig + +import ( + "encoding/json" + "fmt" +) + +//go:generate go tool -modfile=../tool.mod mockgen -package=networkconfig -destination=./network_mock.go -source=./network.go + +const forkName = "alan" + +type Network interface { + NetworkName() string + Beacon + SSV +} + +type NetworkConfig struct { + Name string + BeaconConfig + SSVConfig +} + +func (n NetworkConfig) String() string { + jsonBytes, err := json.Marshal(n) + if err != nil { + panic(err) + } + + return string(jsonBytes) +} + +func (n NetworkConfig) NetworkName() string { + return fmt.Sprintf("%s:%s", n.Name, forkName) +} diff --git a/networkconfig/network_mock.go b/networkconfig/network_mock.go new file mode 100644 index 0000000000..b311116b57 --- /dev/null +++ b/networkconfig/network_mock.go @@ -0,0 +1,409 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./network.go +// +// Generated by this command: +// +// mockgen -package=networkconfig -destination=./network_mock.go -source=./network.go +// + +// Package networkconfig is a generated GoMock package. +package networkconfig + +import ( + reflect "reflect" + time "time" + + spec "github.com/attestantio/go-eth2-client/spec" + phase0 "github.com/attestantio/go-eth2-client/spec/phase0" + types "github.com/ssvlabs/ssv-spec/types" + gomock "go.uber.org/mock/gomock" +) + +// MockNetwork is a mock of Network interface. +type MockNetwork struct { + ctrl *gomock.Controller + recorder *MockNetworkMockRecorder + isgomock struct{} +} + +// MockNetworkMockRecorder is the mock recorder for MockNetwork. +type MockNetworkMockRecorder struct { + mock *MockNetwork +} + +// NewMockNetwork creates a new mock instance. +func NewMockNetwork(ctrl *gomock.Controller) *MockNetwork { + mock := &MockNetwork{ctrl: ctrl} + mock.recorder = &MockNetworkMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockNetwork) EXPECT() *MockNetworkMockRecorder { + return m.recorder +} + +// EpochDuration mocks base method. +func (m *MockNetwork) EpochDuration() time.Duration { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EpochDuration") + ret0, _ := ret[0].(time.Duration) + return ret0 +} + +// EpochDuration indicates an expected call of EpochDuration. +func (mr *MockNetworkMockRecorder) EpochDuration() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EpochDuration", reflect.TypeOf((*MockNetwork)(nil).EpochDuration)) +} + +// EpochStartTime mocks base method. +func (m *MockNetwork) EpochStartTime(epoch phase0.Epoch) time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EpochStartTime", epoch) + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// EpochStartTime indicates an expected call of EpochStartTime. +func (mr *MockNetworkMockRecorder) EpochStartTime(epoch any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EpochStartTime", reflect.TypeOf((*MockNetwork)(nil).EpochStartTime), epoch) +} + +// EstimatedCurrentEpoch mocks base method. +func (m *MockNetwork) EstimatedCurrentEpoch() phase0.Epoch { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EstimatedCurrentEpoch") + ret0, _ := ret[0].(phase0.Epoch) + return ret0 +} + +// EstimatedCurrentEpoch indicates an expected call of EstimatedCurrentEpoch. +func (mr *MockNetworkMockRecorder) EstimatedCurrentEpoch() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimatedCurrentEpoch", reflect.TypeOf((*MockNetwork)(nil).EstimatedCurrentEpoch)) +} + +// EstimatedCurrentSlot mocks base method. +func (m *MockNetwork) EstimatedCurrentSlot() phase0.Slot { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EstimatedCurrentSlot") + ret0, _ := ret[0].(phase0.Slot) + return ret0 +} + +// EstimatedCurrentSlot indicates an expected call of EstimatedCurrentSlot. +func (mr *MockNetworkMockRecorder) EstimatedCurrentSlot() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimatedCurrentSlot", reflect.TypeOf((*MockNetwork)(nil).EstimatedCurrentSlot)) +} + +// EstimatedEpochAtSlot mocks base method. +func (m *MockNetwork) EstimatedEpochAtSlot(slot phase0.Slot) phase0.Epoch { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EstimatedEpochAtSlot", slot) + ret0, _ := ret[0].(phase0.Epoch) + return ret0 +} + +// EstimatedEpochAtSlot indicates an expected call of EstimatedEpochAtSlot. +func (mr *MockNetworkMockRecorder) EstimatedEpochAtSlot(slot any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimatedEpochAtSlot", reflect.TypeOf((*MockNetwork)(nil).EstimatedEpochAtSlot), slot) +} + +// EstimatedSlotAtTime mocks base method. +func (m *MockNetwork) EstimatedSlotAtTime(arg0 time.Time) phase0.Slot { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EstimatedSlotAtTime", arg0) + ret0, _ := ret[0].(phase0.Slot) + return ret0 +} + +// EstimatedSlotAtTime indicates an expected call of EstimatedSlotAtTime. +func (mr *MockNetworkMockRecorder) EstimatedSlotAtTime(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimatedSlotAtTime", reflect.TypeOf((*MockNetwork)(nil).EstimatedSlotAtTime), arg0) +} + +// EstimatedSyncCommitteePeriodAtEpoch mocks base method. +func (m *MockNetwork) EstimatedSyncCommitteePeriodAtEpoch(epoch phase0.Epoch) uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EstimatedSyncCommitteePeriodAtEpoch", epoch) + ret0, _ := ret[0].(uint64) + return ret0 +} + +// EstimatedSyncCommitteePeriodAtEpoch indicates an expected call of EstimatedSyncCommitteePeriodAtEpoch. +func (mr *MockNetworkMockRecorder) EstimatedSyncCommitteePeriodAtEpoch(epoch any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimatedSyncCommitteePeriodAtEpoch", reflect.TypeOf((*MockNetwork)(nil).EstimatedSyncCommitteePeriodAtEpoch), epoch) +} + +// EstimatedTimeAtSlot mocks base method. +func (m *MockNetwork) EstimatedTimeAtSlot(slot phase0.Slot) time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EstimatedTimeAtSlot", slot) + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// EstimatedTimeAtSlot indicates an expected call of EstimatedTimeAtSlot. +func (mr *MockNetworkMockRecorder) EstimatedTimeAtSlot(slot any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimatedTimeAtSlot", reflect.TypeOf((*MockNetwork)(nil).EstimatedTimeAtSlot), slot) +} + +// FirstEpochOfSyncPeriod mocks base method. +func (m *MockNetwork) FirstEpochOfSyncPeriod(period uint64) phase0.Epoch { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FirstEpochOfSyncPeriod", period) + ret0, _ := ret[0].(phase0.Epoch) + return ret0 +} + +// FirstEpochOfSyncPeriod indicates an expected call of FirstEpochOfSyncPeriod. +func (mr *MockNetworkMockRecorder) FirstEpochOfSyncPeriod(period any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FirstEpochOfSyncPeriod", reflect.TypeOf((*MockNetwork)(nil).FirstEpochOfSyncPeriod), period) +} + +// FirstSlotAtEpoch mocks base method. +func (m *MockNetwork) FirstSlotAtEpoch(epoch phase0.Epoch) phase0.Slot { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FirstSlotAtEpoch", epoch) + ret0, _ := ret[0].(phase0.Slot) + return ret0 +} + +// FirstSlotAtEpoch indicates an expected call of FirstSlotAtEpoch. +func (mr *MockNetworkMockRecorder) FirstSlotAtEpoch(epoch any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FirstSlotAtEpoch", reflect.TypeOf((*MockNetwork)(nil).FirstSlotAtEpoch), epoch) +} + +// ForkAtEpoch mocks base method. +func (m *MockNetwork) ForkAtEpoch(epoch phase0.Epoch) (spec.DataVersion, *phase0.Fork) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ForkAtEpoch", epoch) + ret0, _ := ret[0].(spec.DataVersion) + ret1, _ := ret[1].(*phase0.Fork) + return ret0, ret1 +} + +// ForkAtEpoch indicates an expected call of ForkAtEpoch. +func (mr *MockNetworkMockRecorder) ForkAtEpoch(epoch any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ForkAtEpoch", reflect.TypeOf((*MockNetwork)(nil).ForkAtEpoch), epoch) +} + +// GetDomainType mocks base method. +func (m *MockNetwork) GetDomainType() types.DomainType { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDomainType") + ret0, _ := ret[0].(types.DomainType) + return ret0 +} + +// GetDomainType indicates an expected call of GetDomainType. +func (mr *MockNetworkMockRecorder) GetDomainType() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainType", reflect.TypeOf((*MockNetwork)(nil).GetDomainType)) +} + +// GetEpochFirstSlot mocks base method. +func (m *MockNetwork) GetEpochFirstSlot(epoch phase0.Epoch) phase0.Slot { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEpochFirstSlot", epoch) + ret0, _ := ret[0].(phase0.Slot) + return ret0 +} + +// GetEpochFirstSlot indicates an expected call of GetEpochFirstSlot. +func (mr *MockNetworkMockRecorder) GetEpochFirstSlot(epoch any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEpochFirstSlot", reflect.TypeOf((*MockNetwork)(nil).GetEpochFirstSlot), epoch) +} + +// GetEpochsPerSyncCommitteePeriod mocks base method. +func (m *MockNetwork) GetEpochsPerSyncCommitteePeriod() uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEpochsPerSyncCommitteePeriod") + ret0, _ := ret[0].(uint64) + return ret0 +} + +// GetEpochsPerSyncCommitteePeriod indicates an expected call of GetEpochsPerSyncCommitteePeriod. +func (mr *MockNetworkMockRecorder) GetEpochsPerSyncCommitteePeriod() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEpochsPerSyncCommitteePeriod", reflect.TypeOf((*MockNetwork)(nil).GetEpochsPerSyncCommitteePeriod)) +} + +// GetGenesisTime mocks base method. +func (m *MockNetwork) GetGenesisTime() time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetGenesisTime") + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// GetGenesisTime indicates an expected call of GetGenesisTime. +func (mr *MockNetworkMockRecorder) GetGenesisTime() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGenesisTime", reflect.TypeOf((*MockNetwork)(nil).GetGenesisTime)) +} + +// GetGenesisValidatorsRoot mocks base method. +func (m *MockNetwork) GetGenesisValidatorsRoot() phase0.Root { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetGenesisValidatorsRoot") + ret0, _ := ret[0].(phase0.Root) + return ret0 +} + +// GetGenesisValidatorsRoot indicates an expected call of GetGenesisValidatorsRoot. +func (mr *MockNetworkMockRecorder) GetGenesisValidatorsRoot() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGenesisValidatorsRoot", reflect.TypeOf((*MockNetwork)(nil).GetGenesisValidatorsRoot)) +} + +// GetNetworkName mocks base method. +func (m *MockNetwork) GetNetworkName() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNetworkName") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetNetworkName indicates an expected call of GetNetworkName. +func (mr *MockNetworkMockRecorder) GetNetworkName() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetworkName", reflect.TypeOf((*MockNetwork)(nil).GetNetworkName)) +} + +// GetSlotDuration mocks base method. +func (m *MockNetwork) GetSlotDuration() time.Duration { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSlotDuration") + ret0, _ := ret[0].(time.Duration) + return ret0 +} + +// GetSlotDuration indicates an expected call of GetSlotDuration. +func (mr *MockNetworkMockRecorder) GetSlotDuration() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSlotDuration", reflect.TypeOf((*MockNetwork)(nil).GetSlotDuration)) +} + +// GetSlotEndTime mocks base method. +func (m *MockNetwork) GetSlotEndTime(slot phase0.Slot) time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSlotEndTime", slot) + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// GetSlotEndTime indicates an expected call of GetSlotEndTime. +func (mr *MockNetworkMockRecorder) GetSlotEndTime(slot any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSlotEndTime", reflect.TypeOf((*MockNetwork)(nil).GetSlotEndTime), slot) +} + +// GetSlotStartTime mocks base method. +func (m *MockNetwork) GetSlotStartTime(slot phase0.Slot) time.Time { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSlotStartTime", slot) + ret0, _ := ret[0].(time.Time) + return ret0 +} + +// GetSlotStartTime indicates an expected call of GetSlotStartTime. +func (mr *MockNetworkMockRecorder) GetSlotStartTime(slot any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSlotStartTime", reflect.TypeOf((*MockNetwork)(nil).GetSlotStartTime), slot) +} + +// GetSlotsPerEpoch mocks base method. +func (m *MockNetwork) GetSlotsPerEpoch() uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSlotsPerEpoch") + ret0, _ := ret[0].(uint64) + return ret0 +} + +// GetSlotsPerEpoch indicates an expected call of GetSlotsPerEpoch. +func (mr *MockNetworkMockRecorder) GetSlotsPerEpoch() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSlotsPerEpoch", reflect.TypeOf((*MockNetwork)(nil).GetSlotsPerEpoch)) +} + +// GetSyncCommitteeSize mocks base method. +func (m *MockNetwork) GetSyncCommitteeSize() uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSyncCommitteeSize") + ret0, _ := ret[0].(uint64) + return ret0 +} + +// GetSyncCommitteeSize indicates an expected call of GetSyncCommitteeSize. +func (mr *MockNetworkMockRecorder) GetSyncCommitteeSize() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSyncCommitteeSize", reflect.TypeOf((*MockNetwork)(nil).GetSyncCommitteeSize)) +} + +// IntervalDuration mocks base method. +func (m *MockNetwork) IntervalDuration() time.Duration { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IntervalDuration") + ret0, _ := ret[0].(time.Duration) + return ret0 +} + +// IntervalDuration indicates an expected call of IntervalDuration. +func (mr *MockNetworkMockRecorder) IntervalDuration() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IntervalDuration", reflect.TypeOf((*MockNetwork)(nil).IntervalDuration)) +} + +// IsFirstSlotOfEpoch mocks base method. +func (m *MockNetwork) IsFirstSlotOfEpoch(slot phase0.Slot) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsFirstSlotOfEpoch", slot) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsFirstSlotOfEpoch indicates an expected call of IsFirstSlotOfEpoch. +func (mr *MockNetworkMockRecorder) IsFirstSlotOfEpoch(slot any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsFirstSlotOfEpoch", reflect.TypeOf((*MockNetwork)(nil).IsFirstSlotOfEpoch), slot) +} + +// LastSlotOfSyncPeriod mocks base method. +func (m *MockNetwork) LastSlotOfSyncPeriod(period uint64) phase0.Slot { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LastSlotOfSyncPeriod", period) + ret0, _ := ret[0].(phase0.Slot) + return ret0 +} + +// LastSlotOfSyncPeriod indicates an expected call of LastSlotOfSyncPeriod. +func (mr *MockNetworkMockRecorder) LastSlotOfSyncPeriod(period any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LastSlotOfSyncPeriod", reflect.TypeOf((*MockNetwork)(nil).LastSlotOfSyncPeriod), period) +} + +// NetworkName mocks base method. +func (m *MockNetwork) NetworkName() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NetworkName") + ret0, _ := ret[0].(string) + return ret0 +} + +// NetworkName indicates an expected call of NetworkName. +func (mr *MockNetworkMockRecorder) NetworkName() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkName", reflect.TypeOf((*MockNetwork)(nil).NetworkName)) +} diff --git a/networkconfig/sepolia.go b/networkconfig/sepolia.go index cc05bfb9d4..8ec1899e81 100644 --- a/networkconfig/sepolia.go +++ b/networkconfig/sepolia.go @@ -3,24 +3,20 @@ package networkconfig import ( "math/big" + ethcommon "github.com/ethereum/go-ethereum/common" spectypes "github.com/ssvlabs/ssv-spec/types" - - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" ) -var Sepolia = NetworkConfig{ - Name: "sepolia", - BeaconConfig: BeaconConfig{ - Beacon: beacon.NewNetwork(spectypes.SepoliaNetwork), - }, - SSVConfig: SSVConfig{ - DomainType: spectypes.DomainType{0x0, 0x0, 0x5, 0x69}, - RegistrySyncOffset: new(big.Int).SetInt64(7795814), - RegistryContractAddr: "0x261419B48F36EdF420743E9f91bABF4856e76f99", - DiscoveryProtocolID: [6]byte{'s', 's', 'v', 'd', 'v', '5'}, - Bootnodes: []string{ - // SSV Labs - "enr:-Ja4QIE0Ml0a8Pq9zD-0g9KYGN3jAMPJ0CAP0i16fK-PSHfLeORl-Z5p8odoP1oS5S2E8IsF5jNG7gqTKhjVsHR-Z_CGAZXrnTJrgmlkgnY0gmlwhCOjXGWJc2VjcDI1NmsxoQKCRDQsIdFsJDmu_ZU2H6b2_HRJbuUneDXHLfFkSQH9O4Nzc3YBg3RjcIITioN1ZHCCD6I", - }, +const SepoliaName = "sepolia" + +var SepoliaSSV = SSVConfig{ + DomainType: spectypes.DomainType{0x0, 0x0, 0x5, 0x69}, + RegistrySyncOffset: new(big.Int).SetInt64(7795814), + RegistryContractAddr: ethcommon.HexToAddress("0x261419B48F36EdF420743E9f91bABF4856e76f99"), + DiscoveryProtocolID: [6]byte{'s', 's', 'v', 'd', 'v', '5'}, + Bootnodes: []string{ + // SSV Labs + "enr:-Ja4QIE0Ml0a8Pq9zD-0g9KYGN3jAMPJ0CAP0i16fK-PSHfLeORl-Z5p8odoP1oS5S2E8IsF5jNG7gqTKhjVsHR-Z_CGAZXrnTJrgmlkgnY0gmlwhCOjXGWJc2VjcDI1NmsxoQKCRDQsIdFsJDmu_ZU2H6b2_HRJbuUneDXHLfFkSQH9O4Nzc3YBg3RjcIITioN1ZHCCD6I", }, + TotalEthereumValidators: 1781, // active_validators from https://sepolia.beaconcha.in/index/data on Mar 20, 2025 } diff --git a/networkconfig/ssv.go b/networkconfig/ssv.go index 6ae33b78e9..5547c60587 100644 --- a/networkconfig/ssv.go +++ b/networkconfig/ssv.go @@ -1,15 +1,129 @@ package networkconfig import ( + "encoding/json" + "fmt" "math/big" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" spectypes "github.com/ssvlabs/ssv-spec/types" ) +//go:generate go tool -modfile=../tool.mod mockgen -package=networkconfig -destination=./ssv_mock.go -source=./ssv.go + +var supportedSSVConfigs = map[string]SSVConfig{ + MainnetName: MainnetSSV, + HoleskyName: HoleskySSV, + HoleskyStageName: HoleskyStageSSV, + LocalTestnetName: LocalTestnetSSV, + HoleskyE2EName: HoleskyE2ESSV, + HoodiName: HoodiSSV, + HoodiStageName: HoodiStageSSV, + SepoliaName: SepoliaSSV, +} + +func GetSSVConfigByName(name string) (SSVConfig, error) { + if network, ok := supportedSSVConfigs[name]; ok { + return network, nil + } + + return SSVConfig{}, fmt.Errorf("network not supported: %v", name) +} + +type SSV interface { + GetDomainType() spectypes.DomainType +} + type SSVConfig struct { - DomainType spectypes.DomainType - RegistrySyncOffset *big.Int - RegistryContractAddr string // TODO: ethcommon.Address - Bootnodes []string - DiscoveryProtocolID [6]byte + DomainType spectypes.DomainType + RegistrySyncOffset *big.Int + RegistryContractAddr ethcommon.Address + Bootnodes []string + DiscoveryProtocolID [6]byte + TotalEthereumValidators int // value needs to be maintained — consider getting it from external API with default or per-network value(s) as fallback +} + +func (s SSVConfig) String() string { + marshaled, err := json.Marshal(s) + if err != nil { + panic(err) + } + + return string(marshaled) +} + +type marshaledConfig struct { + DomainType hexutil.Bytes `json:"DomainType,omitempty" yaml:"DomainType,omitempty"` + RegistrySyncOffset *big.Int `json:"RegistrySyncOffset,omitempty" yaml:"RegistrySyncOffset,omitempty"` + RegistryContractAddr ethcommon.Address `json:"RegistryContractAddr,omitempty" yaml:"RegistryContractAddr,omitempty"` + Bootnodes []string `json:"Bootnodes,omitempty" yaml:"Bootnodes,omitempty"` + DiscoveryProtocolID hexutil.Bytes `json:"DiscoveryProtocolID,omitempty" yaml:"DiscoveryProtocolID,omitempty"` + TotalEthereumValidators int `json:"TotalEthereumValidators,omitempty" yaml:"TotalEthereumValidators,omitempty"` +} + +// Helper method to avoid duplication between MarshalJSON and MarshalYAML +func (s SSVConfig) marshal() marshaledConfig { + aux := marshaledConfig{ + DomainType: s.DomainType[:], + RegistrySyncOffset: s.RegistrySyncOffset, + RegistryContractAddr: s.RegistryContractAddr, + Bootnodes: s.Bootnodes, + DiscoveryProtocolID: s.DiscoveryProtocolID[:], + TotalEthereumValidators: s.TotalEthereumValidators, + } + + return aux +} + +func (s SSVConfig) MarshalJSON() ([]byte, error) { + return json.Marshal(s.marshal()) +} + +func (s SSVConfig) MarshalYAML() (interface{}, error) { + return s.marshal(), nil +} + +// Helper method to avoid duplication between UnmarshalJSON and UnmarshalYAML +func (s *SSVConfig) unmarshalFromConfig(aux marshaledConfig) error { + if len(aux.DomainType) != 4 { + return fmt.Errorf("invalid domain type length: expected 4 bytes, got %d", len(aux.DomainType)) + } + + if len(aux.DiscoveryProtocolID) != 6 { + return fmt.Errorf("invalid discovery protocol ID length: expected 6 bytes, got %d", len(aux.DiscoveryProtocolID)) + } + + *s = SSVConfig{ + DomainType: spectypes.DomainType(aux.DomainType), + RegistrySyncOffset: aux.RegistrySyncOffset, + RegistryContractAddr: aux.RegistryContractAddr, + Bootnodes: aux.Bootnodes, + DiscoveryProtocolID: [6]byte(aux.DiscoveryProtocolID), + TotalEthereumValidators: aux.TotalEthereumValidators, + } + + return nil +} + +func (s *SSVConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + var aux marshaledConfig + if err := unmarshal(&aux); err != nil { + return err + } + + return s.unmarshalFromConfig(aux) +} + +func (s *SSVConfig) UnmarshalJSON(data []byte) error { + var aux marshaledConfig + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + return s.unmarshalFromConfig(aux) +} + +func (s SSVConfig) GetDomainType() spectypes.DomainType { + return s.DomainType } diff --git a/networkconfig/ssv_mock.go b/networkconfig/ssv_mock.go new file mode 100644 index 0000000000..8bccd1f876 --- /dev/null +++ b/networkconfig/ssv_mock.go @@ -0,0 +1,55 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./ssv.go +// +// Generated by this command: +// +// mockgen -package=networkconfig -destination=./ssv_mock.go -source=./ssv.go +// + +// Package networkconfig is a generated GoMock package. +package networkconfig + +import ( + reflect "reflect" + + types "github.com/ssvlabs/ssv-spec/types" + gomock "go.uber.org/mock/gomock" +) + +// MockSSV is a mock of SSV interface. +type MockSSV struct { + ctrl *gomock.Controller + recorder *MockSSVMockRecorder + isgomock struct{} +} + +// MockSSVMockRecorder is the mock recorder for MockSSV. +type MockSSVMockRecorder struct { + mock *MockSSV +} + +// NewMockSSV creates a new mock instance. +func NewMockSSV(ctrl *gomock.Controller) *MockSSV { + mock := &MockSSV{ctrl: ctrl} + mock.recorder = &MockSSVMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSSV) EXPECT() *MockSSVMockRecorder { + return m.recorder +} + +// GetDomainType mocks base method. +func (m *MockSSV) GetDomainType() types.DomainType { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDomainType") + ret0, _ := ret[0].(types.DomainType) + return ret0 +} + +// GetDomainType indicates an expected call of GetDomainType. +func (mr *MockSSVMockRecorder) GetDomainType() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainType", reflect.TypeOf((*MockSSV)(nil).GetDomainType)) +} diff --git a/networkconfig/ssv_test.go b/networkconfig/ssv_test.go new file mode 100644 index 0000000000..8872d6b4a3 --- /dev/null +++ b/networkconfig/ssv_test.go @@ -0,0 +1,232 @@ +package networkconfig + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "math/big" + "reflect" + "sort" + "testing" + + ethcommon "github.com/ethereum/go-ethereum/common" + spectypes "github.com/ssvlabs/ssv-spec/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +func TestSSVConfig_MarshalUnmarshalJSON(t *testing.T) { + // Create a sample SSVConfig + originalConfig := SSVConfig{ + DomainType: spectypes.DomainType{0x01, 0x02, 0x03, 0x04}, + RegistrySyncOffset: big.NewInt(123456), + RegistryContractAddr: ethcommon.HexToAddress("0x123456789abcdef0123456789abcdef012345678"), + Bootnodes: []string{"bootnode1", "bootnode2"}, + DiscoveryProtocolID: [6]byte{0x05, 0x06, 0x07, 0x08, 0x09, 0x0a}, + } + + // Marshal to JSON + jsonBytes, err := json.Marshal(originalConfig) + require.NoError(t, err) + + // Unmarshal from JSON + var unmarshaledConfig SSVConfig + err = json.Unmarshal(jsonBytes, &unmarshaledConfig) + require.NoError(t, err) + + // Marshal again after unmarshaling + remarshaledBytes, err := json.Marshal(unmarshaledConfig) + require.NoError(t, err) + + // Compare the original and remarshaled JSON bytes + assert.JSONEq(t, string(jsonBytes), string(remarshaledBytes)) + + // Compare the original and unmarshaled structs + assert.Equal(t, originalConfig.DomainType, unmarshaledConfig.DomainType) + assert.Equal(t, originalConfig.RegistrySyncOffset.Int64(), unmarshaledConfig.RegistrySyncOffset.Int64()) + assert.Equal(t, originalConfig.RegistryContractAddr, unmarshaledConfig.RegistryContractAddr) + assert.Equal(t, originalConfig.Bootnodes, unmarshaledConfig.Bootnodes) + assert.Equal(t, originalConfig.DiscoveryProtocolID, unmarshaledConfig.DiscoveryProtocolID) +} + +func TestSSVConfig_MarshalUnmarshalYAML(t *testing.T) { + // Create a sample SSVConfig + originalConfig := SSVConfig{ + DomainType: spectypes.DomainType{0x01, 0x02, 0x03, 0x04}, + RegistrySyncOffset: big.NewInt(123456), + RegistryContractAddr: ethcommon.HexToAddress("0x123456789abcdef0123456789abcdef012345678"), + Bootnodes: []string{"bootnode1", "bootnode2"}, + DiscoveryProtocolID: [6]byte{0x05, 0x06, 0x07, 0x08, 0x09, 0x0a}, + } + + // Marshal to YAML + yamlBytes, err := yaml.Marshal(originalConfig) + require.NoError(t, err) + + // Unmarshal from YAML + var unmarshaledConfig SSVConfig + err = yaml.Unmarshal(yamlBytes, &unmarshaledConfig) + require.NoError(t, err) + + // Marshal again after unmarshaling + remarshaledBytes, err := yaml.Marshal(unmarshaledConfig) + require.NoError(t, err) + + // Compare the original and unmarshaled structs + assert.Equal(t, originalConfig.DomainType, unmarshaledConfig.DomainType) + assert.Equal(t, originalConfig.RegistrySyncOffset.Int64(), unmarshaledConfig.RegistrySyncOffset.Int64()) + assert.Equal(t, originalConfig.RegistryContractAddr, unmarshaledConfig.RegistryContractAddr) + assert.Equal(t, originalConfig.Bootnodes, unmarshaledConfig.Bootnodes) + assert.Equal(t, originalConfig.DiscoveryProtocolID, unmarshaledConfig.DiscoveryProtocolID) + + // Compare the original and remarshaled YAML bytes + // YAML doesn't preserve order by default, so we need to compare the unmarshaled content + var originalYAMLMap map[string]interface{} + var remarshaledYAMLMap map[string]interface{} + + err = yaml.Unmarshal(yamlBytes, &originalYAMLMap) + require.NoError(t, err) + + err = yaml.Unmarshal(remarshaledBytes, &remarshaledYAMLMap) + require.NoError(t, err) + + assert.Equal(t, originalYAMLMap, remarshaledYAMLMap) +} + +// hashStructJSON creates a deterministic hash of a struct by marshaling to sorted JSON +func hashStructJSON(v interface{}) (string, error) { + // Create a JSON encoder that sorts map keys + buffer := &bytes.Buffer{} + encoder := json.NewEncoder(buffer) + encoder.SetIndent("", "") + encoder.SetEscapeHTML(false) + + if err := encoder.Encode(v); err != nil { + return "", err + } + + // Compute SHA-256 hash + hasher := sha256.New() + hasher.Write(buffer.Bytes()) + return hex.EncodeToString(hasher.Sum(nil)), nil +} + +// TestFieldPreservation ensures that all fields are properly preserved during +// marshal/unmarshal operations and that we can detect changes to the struct +func TestFieldPreservation(t *testing.T) { + t.Run("test all fields are present after marshaling", func(t *testing.T) { + // Get all field names from SSVConfig + configType := reflect.TypeOf(SSVConfig{}) + marshaledType := reflect.TypeOf(marshaledConfig{}) + + var configFields, marshaledFields []string + + for i := 0; i < configType.NumField(); i++ { + configFields = append(configFields, configType.Field(i).Name) + } + + for i := 0; i < marshaledType.NumField(); i++ { + marshaledFields = append(marshaledFields, marshaledType.Field(i).Name) + } + + // Sort fields for deterministic comparison + sort.Strings(configFields) + sort.Strings(marshaledFields) + + // Ensure the same fields exist in both structs + assert.Equal(t, configFields, marshaledFields, "SSVConfig and marshaledConfig should have the same fields") + }) + + t.Run("hash comparison JSON", func(t *testing.T) { + // Create a sample config + config := SSVConfig{ + DomainType: spectypes.DomainType{0x01, 0x02, 0x03, 0x04}, + RegistrySyncOffset: big.NewInt(123456), + RegistryContractAddr: ethcommon.HexToAddress("0x123456789abcdef0123456789abcdef012345678"), + Bootnodes: []string{"bootnode1", "bootnode2"}, + DiscoveryProtocolID: [6]byte{0x05, 0x06, 0x07, 0x08, 0x09, 0x0a}, + } + + // Marshal and unmarshal to test preservation + jsonBytes, err := json.Marshal(config) + require.NoError(t, err) + + var unmarshaled SSVConfig + err = json.Unmarshal(jsonBytes, &unmarshaled) + require.NoError(t, err) + + // Hash the original and unmarshaled struct + originalHash, err := hashStructJSON(config) + require.NoError(t, err) + + unmarshaledHash, err := hashStructJSON(unmarshaled) + require.NoError(t, err) + + // The hashes should match if all fields are preserved + assert.Equal(t, originalHash, unmarshaledHash, "Hash mismatch indicates fields weren't properly preserved in JSON") + + // Store the expected hash - this will fail if a new field is added without updating the tests + expectedJSONHash := "3afe88f355185266dfd842df18a096ea8f40dd28f8b022710aedca1d09d59cef" + assert.Equal(t, expectedJSONHash, originalHash, + "Hash has changed. If you've added a new field, please update the expected hash in this test.") + }) + + t.Run("hash comparison YAML", func(t *testing.T) { + // Create a sample config + config := SSVConfig{ + DomainType: spectypes.DomainType{0x01, 0x02, 0x03, 0x04}, + RegistrySyncOffset: big.NewInt(123456), + RegistryContractAddr: ethcommon.HexToAddress("0x123456789abcdef0123456789abcdef012345678"), + Bootnodes: []string{"bootnode1", "bootnode2"}, + DiscoveryProtocolID: [6]byte{0x05, 0x06, 0x07, 0x08, 0x09, 0x0a}, + } + + // Marshal and unmarshal to test preservation + yamlBytes, err := yaml.Marshal(config) + require.NoError(t, err) + + var unmarshaled SSVConfig + err = yaml.Unmarshal(yamlBytes, &unmarshaled) + require.NoError(t, err) + + // For YAML, convert to JSON for consistent hashing + originalHash, err := hashStructJSON(config) + require.NoError(t, err) + + unmarshaledHash, err := hashStructJSON(unmarshaled) + require.NoError(t, err) + + // The hashes should match if all fields are preserved + assert.Equal(t, originalHash, unmarshaledHash, "Hash mismatch indicates fields weren't properly preserved in YAML") + }) +} + +// TestExistingNetworkConfigs validates that all predefined network configs +// can be marshaled and unmarshaled correctly +func TestExistingNetworkConfigs(t *testing.T) { + for networkName, config := range supportedSSVConfigs { + t.Run(networkName, func(t *testing.T) { + // JSON test + jsonBytes, err := json.Marshal(config) + require.NoError(t, err) + + var jsonUnmarshaled SSVConfig + err = json.Unmarshal(jsonBytes, &jsonUnmarshaled) + require.NoError(t, err) + + assert.Equal(t, config.DomainType, jsonUnmarshaled.DomainType) + + // YAML test + yamlBytes, err := yaml.Marshal(config) + require.NoError(t, err) + + var yamlUnmarshaled SSVConfig + err = yaml.Unmarshal(yamlBytes, &yamlUnmarshaled) + require.NoError(t, err) + + assert.Equal(t, config.DomainType, yamlUnmarshaled.DomainType) + }) + } +} diff --git a/networkconfig/test-network.go b/networkconfig/test-network.go index bed1b4e5f5..4cd6fc6e04 100644 --- a/networkconfig/test-network.go +++ b/networkconfig/test-network.go @@ -2,23 +2,72 @@ package networkconfig import ( "math/big" + "time" + "github.com/attestantio/go-eth2-client/spec" + "github.com/attestantio/go-eth2-client/spec/phase0" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" spectypes "github.com/ssvlabs/ssv-spec/types" - - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" ) +// Not exporting any name as it's used for tests and not supposed to be passed in a config. + var TestNetwork = NetworkConfig{ Name: "testnet", BeaconConfig: BeaconConfig{ - Beacon: beacon.NewNetwork(spectypes.BeaconTestNetwork), + NetworkName: string(spectypes.BeaconTestNetwork), + SlotDuration: spectypes.BeaconTestNetwork.SlotDurationSec(), + SlotsPerEpoch: spectypes.BeaconTestNetwork.SlotsPerEpoch(), + EpochsPerSyncCommitteePeriod: 256, + SyncCommitteeSize: 512, + SyncCommitteeSubnetCount: 4, + TargetAggregatorsPerSyncSubcommittee: 16, + TargetAggregatorsPerCommittee: 16, + IntervalsPerSlot: 3, + GenesisForkVersion: spectypes.BeaconTestNetwork.ForkVersion(), + GenesisTime: time.Unix(int64(spectypes.BeaconTestNetwork.MinGenesisTime()), 0), // #nosec G115 -- time should not exceed int64 + GenesisValidatorsRoot: phase0.Root(hexutil.MustDecode("0x043db0d9a83813551ee2f33450d23797757d430911a9320530ad8a0eabc43efb")), + Forks: map[spec.DataVersion]phase0.Fork{ + spec.DataVersionPhase0: { + Epoch: phase0.Epoch(0), + PreviousVersion: phase0.Version{0, 0, 0, 0}, + CurrentVersion: phase0.Version{0, 0, 0, 0}, + }, + spec.DataVersionAltair: { + Epoch: phase0.Epoch(1), + PreviousVersion: phase0.Version{0, 0, 0, 0}, + CurrentVersion: phase0.Version{1, 0, 0, 0}, + }, + spec.DataVersionBellatrix: { + Epoch: phase0.Epoch(2), + PreviousVersion: phase0.Version{1, 0, 0, 0}, + CurrentVersion: phase0.Version{2, 0, 0, 0}, + }, + spec.DataVersionCapella: { + Epoch: phase0.Epoch(3), + PreviousVersion: phase0.Version{2, 0, 0, 0}, + CurrentVersion: phase0.Version{3, 0, 0, 0}, + }, + spec.DataVersionDeneb: { + Epoch: phase0.Epoch(4), + PreviousVersion: phase0.Version{3, 0, 0, 0}, + CurrentVersion: phase0.Version{4, 0, 0, 0}, + }, + spec.DataVersionElectra: { + Epoch: phase0.Epoch(5), + PreviousVersion: phase0.Version{4, 0, 0, 0}, + CurrentVersion: phase0.Version{5, 0, 0, 0}, + }, + }, }, SSVConfig: SSVConfig{ DomainType: spectypes.DomainType{0x0, 0x0, spectypes.JatoNetworkID.Byte(), 0x2}, RegistrySyncOffset: new(big.Int).SetInt64(9015219), - RegistryContractAddr: "0x4B133c68A084B8A88f72eDCd7944B69c8D545f03", + RegistryContractAddr: ethcommon.HexToAddress("0x4B133c68A084B8A88f72eDCd7944B69c8D545f03"), Bootnodes: []string{ "enr:-Li4QFIQzamdvTxGJhvcXG_DFmCeyggSffDnllY5DiU47pd_K_1MRnSaJimWtfKJ-MD46jUX9TwgW5Jqe0t4pH41RYWGAYuFnlyth2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhCLdu_SJc2VjcDI1NmsxoQN4v-N9zFYwEqzGPBBX37q24QPFvAVUtokIo1fblIsmTIN0Y3CCE4uDdWRwgg-j", }, + TotalEthereumValidators: 1_000_000, // just some high enough value, so we never accidentally reach the message-limits derived from it while testing something with local testnet }, } diff --git a/operator/duties/attester.go b/operator/duties/attester.go index d4c5e2af63..25012f376b 100644 --- a/operator/duties/attester.go +++ b/operator/duties/attester.go @@ -75,14 +75,14 @@ func (h *AttesterHandler) HandleDuties(ctx context.Context) { case <-next: slot := h.ticker.Slot() next = h.ticker.Next() - currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(slot) + currentEpoch := h.beaconConfig.EstimatedEpochAtSlot(slot) buildStr := fmt.Sprintf("e%v-s%v-#%v", currentEpoch, slot, slot%32+1) h.logger.Debug("🛠 ticker event", zap.String("epoch_slot_pos", buildStr)) h.processExecution(ctx, currentEpoch, slot) h.processFetching(ctx, currentEpoch, slot) - slotsPerEpoch := h.network.Beacon.SlotsPerEpoch() + slotsPerEpoch := h.beaconConfig.GetSlotsPerEpoch() // If we have reached the mid-point of the epoch, fetch the duties for the next epoch in the next slot. // This allows us to set them up at a time when the beacon node should be less busy. @@ -96,7 +96,7 @@ func (h *AttesterHandler) HandleDuties(ctx context.Context) { } case reorgEvent := <-h.reorg: - currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(reorgEvent.Slot) + currentEpoch := h.beaconConfig.EstimatedEpochAtSlot(reorgEvent.Slot) buildStr := fmt.Sprintf("e%v-s%v-#%v", currentEpoch, reorgEvent.Slot, reorgEvent.Slot%32+1) h.logger.Info("🔀 reorg event received", zap.String("epoch_slot_pos", buildStr), zap.Any("event", reorgEvent)) @@ -120,8 +120,8 @@ func (h *AttesterHandler) HandleDuties(ctx context.Context) { } case <-h.indicesChange: - slot := h.network.Beacon.EstimatedCurrentSlot() - currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(slot) + slot := h.beaconConfig.EstimatedCurrentSlot() + currentEpoch := h.beaconConfig.EstimatedEpochAtSlot(slot) buildStr := fmt.Sprintf("e%v-s%v-#%v", currentEpoch, slot, slot%32+1) h.logger.Info("🔁 indices change received", zap.String("epoch_slot_pos", buildStr)) @@ -136,16 +136,16 @@ func (h *AttesterHandler) HandleDuties(ctx context.Context) { } func (h *AttesterHandler) HandleInitialDuties(ctx context.Context) { - ctx, cancel := context.WithTimeout(ctx, h.network.Beacon.SlotDurationSec()/2) + ctx, cancel := context.WithTimeout(ctx, h.beaconConfig.GetSlotDuration()/2) defer cancel() - slot := h.network.Beacon.EstimatedCurrentSlot() - epoch := h.network.Beacon.EstimatedEpochAtSlot(slot) + slot := h.beaconConfig.EstimatedCurrentSlot() + epoch := h.beaconConfig.EstimatedEpochAtSlot(slot) h.processFetching(ctx, epoch, slot) } func (h *AttesterHandler) processFetching(ctx context.Context, epoch phase0.Epoch, slot phase0.Slot) { - ctx, cancel := context.WithDeadline(ctx, h.network.Beacon.GetSlotStartTime(slot+1).Add(100*time.Millisecond)) + ctx, cancel := context.WithDeadline(ctx, h.beaconConfig.GetSlotStartTime(slot+1).Add(100*time.Millisecond)) defer cancel() if h.fetchCurrentEpoch { @@ -256,8 +256,8 @@ func (h *AttesterHandler) toSpecDuty(duty *eth2apiv1.AttesterDuty, role spectype } func (h *AttesterHandler) shouldExecute(duty *eth2apiv1.AttesterDuty) bool { - currentSlot := h.network.Beacon.EstimatedCurrentSlot() - currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(currentSlot) + currentSlot := h.beaconConfig.EstimatedCurrentSlot() + currentEpoch := h.beaconConfig.EstimatedEpochAtSlot(currentSlot) v, exists := h.validatorProvider.Validator(duty.PubKey[:]) if !exists { @@ -275,8 +275,8 @@ func (h *AttesterHandler) shouldExecute(duty *eth2apiv1.AttesterDuty) bool { } // execute task if slot already began and not pass 1 epoch - var attestationPropagationSlotRange = phase0.Slot(h.network.Beacon.SlotsPerEpoch()) - if currentSlot >= duty.Slot && currentSlot-duty.Slot <= attestationPropagationSlotRange { + var attestationPropagationSlotRange = h.beaconConfig.GetSlotsPerEpoch() + if currentSlot >= duty.Slot && uint64(currentSlot-duty.Slot) <= attestationPropagationSlotRange { return true } if currentSlot+1 == duty.Slot { @@ -312,5 +312,6 @@ func toBeaconCommitteeSubscription(duty *eth2apiv1.AttesterDuty, role spectypes. } func (h *AttesterHandler) shouldFetchNexEpoch(slot phase0.Slot) bool { - return uint64(slot)%h.network.Beacon.SlotsPerEpoch() > h.network.Beacon.SlotsPerEpoch()/2-2 + slotsPerEpoch := h.beaconConfig.GetSlotsPerEpoch() + return uint64(slot)%slotsPerEpoch > slotsPerEpoch/2-2 } diff --git a/operator/duties/attester_test.go b/operator/duties/attester_test.go index 5f891e84a6..63a95bd7eb 100644 --- a/operator/duties/attester_test.go +++ b/operator/duties/attester_test.go @@ -894,7 +894,7 @@ func TestScheduler_Attester_Early_Block(t *testing.T) { } scheduler.HandleHeadEvent()(e.Data.(*eth2apiv1.HeadEvent)) waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - require.Less(t, time.Since(startTime), scheduler.network.Beacon.SlotDurationSec()/3) + require.Less(t, time.Since(startTime), scheduler.beaconConfig.GetSlotDuration()/3) // Stop scheduler & wait for graceful exit. cancel() diff --git a/operator/duties/base_handler.go b/operator/duties/base_handler.go index bde0f436ac..bfbee074b6 100644 --- a/operator/duties/base_handler.go +++ b/operator/duties/base_handler.go @@ -17,7 +17,7 @@ type dutyHandler interface { logger *zap.Logger, beaconNode BeaconNode, executionClient ExecutionClient, - network networkconfig.NetworkConfig, + beaconConfig networkconfig.Beacon, validatorProvider ValidatorProvider, validatorController ValidatorController, dutiesExecutor DutiesExecutor, @@ -34,7 +34,7 @@ type baseHandler struct { logger *zap.Logger beaconNode BeaconNode executionClient ExecutionClient - network networkconfig.NetworkConfig + beaconConfig networkconfig.Beacon validatorProvider ValidatorProvider validatorController ValidatorController dutiesExecutor DutiesExecutor @@ -51,7 +51,7 @@ func (h *baseHandler) Setup( logger *zap.Logger, beaconNode BeaconNode, executionClient ExecutionClient, - network networkconfig.NetworkConfig, + beaconConfig networkconfig.Beacon, validatorProvider ValidatorProvider, validatorController ValidatorController, dutiesExecutor DutiesExecutor, @@ -62,7 +62,7 @@ func (h *baseHandler) Setup( h.logger = logger.With(zap.String("handler", name)) h.beaconNode = beaconNode h.executionClient = executionClient - h.network = network + h.beaconConfig = beaconConfig h.validatorProvider = validatorProvider h.validatorController = validatorController h.dutiesExecutor = dutiesExecutor diff --git a/operator/duties/base_handler_mock.go b/operator/duties/base_handler_mock.go index 3c21bc63ef..56b9037bab 100644 --- a/operator/duties/base_handler_mock.go +++ b/operator/duties/base_handler_mock.go @@ -82,13 +82,13 @@ func (mr *MockdutyHandlerMockRecorder) Name() *gomock.Call { } // Setup mocks base method. -func (m *MockdutyHandler) Setup(name string, logger *zap.Logger, beaconNode BeaconNode, executionClient ExecutionClient, network networkconfig.NetworkConfig, validatorProvider ValidatorProvider, validatorController ValidatorController, dutiesExecutor DutiesExecutor, slotTickerProvider slotticker.Provider, reorgEvents chan ReorgEvent, indicesChange chan struct{}) { +func (m *MockdutyHandler) Setup(name string, logger *zap.Logger, beaconNode BeaconNode, executionClient ExecutionClient, beaconConfig networkconfig.Beacon, validatorProvider ValidatorProvider, validatorController ValidatorController, dutiesExecutor DutiesExecutor, slotTickerProvider slotticker.Provider, reorgEvents chan ReorgEvent, indicesChange chan struct{}) { m.ctrl.T.Helper() - m.ctrl.Call(m, "Setup", name, logger, beaconNode, executionClient, network, validatorProvider, validatorController, dutiesExecutor, slotTickerProvider, reorgEvents, indicesChange) + m.ctrl.Call(m, "Setup", name, logger, beaconNode, executionClient, beaconConfig, validatorProvider, validatorController, dutiesExecutor, slotTickerProvider, reorgEvents, indicesChange) } // Setup indicates an expected call of Setup. -func (mr *MockdutyHandlerMockRecorder) Setup(name, logger, beaconNode, executionClient, network, validatorProvider, validatorController, dutiesExecutor, slotTickerProvider, reorgEvents, indicesChange any) *gomock.Call { +func (mr *MockdutyHandlerMockRecorder) Setup(name, logger, beaconNode, executionClient, beaconConfig, validatorProvider, validatorController, dutiesExecutor, slotTickerProvider, reorgEvents, indicesChange any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Setup", reflect.TypeOf((*MockdutyHandler)(nil).Setup), name, logger, beaconNode, executionClient, network, validatorProvider, validatorController, dutiesExecutor, slotTickerProvider, reorgEvents, indicesChange) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Setup", reflect.TypeOf((*MockdutyHandler)(nil).Setup), name, logger, beaconNode, executionClient, beaconConfig, validatorProvider, validatorController, dutiesExecutor, slotTickerProvider, reorgEvents, indicesChange) } diff --git a/operator/duties/committee.go b/operator/duties/committee.go index 428020eba1..8cf955f50b 100644 --- a/operator/duties/committee.go +++ b/operator/duties/committee.go @@ -55,8 +55,8 @@ func (h *CommitteeHandler) HandleDuties(ctx context.Context) { case <-next: slot := h.ticker.Slot() next = h.ticker.Next() - epoch := h.network.Beacon.EstimatedEpochAtSlot(slot) - period := h.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) + epoch := h.beaconConfig.EstimatedEpochAtSlot(slot) + period := h.beaconConfig.EstimatedSyncCommitteePeriodAtEpoch(epoch) buildStr := fmt.Sprintf("p%v-e%v-s%v-#%v", period, epoch, slot, slot%32+1) h.logger.Debug("🛠 ticker event", zap.String("period_epoch_slot_pos", buildStr)) @@ -182,15 +182,15 @@ func (h *CommitteeHandler) shouldExecuteAtt(duty *eth2apiv1.AttesterDuty, epoch return false } - currentSlot := h.network.Beacon.EstimatedCurrentSlot() + currentSlot := h.beaconConfig.EstimatedCurrentSlot() if participates := h.canParticipate(share, currentSlot); !participates { return false } // execute task if slot already began and not pass 1 epoch - var attestationPropagationSlotRange = phase0.Slot(h.network.Beacon.SlotsPerEpoch()) - if currentSlot >= duty.Slot && currentSlot-duty.Slot <= attestationPropagationSlotRange { + var attestationPropagationSlotRange = h.beaconConfig.GetSlotsPerEpoch() + if currentSlot >= duty.Slot && uint64(currentSlot-duty.Slot) <= attestationPropagationSlotRange { return true } if currentSlot+1 == duty.Slot { @@ -203,11 +203,11 @@ func (h *CommitteeHandler) shouldExecuteAtt(duty *eth2apiv1.AttesterDuty, epoch func (h *CommitteeHandler) shouldExecuteSync(duty *eth2apiv1.SyncCommitteeDuty, slot phase0.Slot, epoch phase0.Epoch) bool { share, found := h.validatorProvider.Validator(duty.PubKey[:]) - if !found || !share.IsParticipating(h.network, epoch) { + if !found || !share.IsParticipating(h.beaconConfig, epoch) { return false } - currentSlot := h.network.Beacon.EstimatedCurrentSlot() + currentSlot := h.beaconConfig.EstimatedCurrentSlot() if participates := h.canParticipate(share, currentSlot); !participates { return false @@ -226,7 +226,7 @@ func (h *CommitteeHandler) shouldExecuteSync(duty *eth2apiv1.SyncCommitteeDuty, } func (h *CommitteeHandler) canParticipate(share *types.SSVShare, currentSlot phase0.Slot) bool { - currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(currentSlot) + currentEpoch := h.beaconConfig.EstimatedEpochAtSlot(currentSlot) if share.MinParticipationEpoch() > currentEpoch { h.logger.Debug("validator not yet participating", diff --git a/operator/duties/committee_test.go b/operator/duties/committee_test.go index 288279e3de..238c1a5d6b 100644 --- a/operator/duties/committee_test.go +++ b/operator/duties/committee_test.go @@ -12,8 +12,8 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" + "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/operator/duties/dutystore" - mocknetwork "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon/mocks" ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types" "github.com/ssvlabs/ssv/utils/hashmap" ) @@ -28,33 +28,35 @@ func setupCommitteeDutiesMock( fetchDutiesCall := make(chan struct{}) executeDutiesCall := make(chan committeeDutiesMap) - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().EstimatedSyncCommitteePeriodAtEpoch(gomock.Any()).DoAndReturn( + s.beaconConfig.(*networkconfig.MockBeacon).EXPECT().EstimatedSyncCommitteePeriodAtEpoch(gomock.Any()).DoAndReturn( func(epoch phase0.Epoch) uint64 { - return uint64(epoch) / s.network.Beacon.EpochsPerSyncCommitteePeriod() + return uint64(epoch) / s.beaconConfig.GetEpochsPerSyncCommitteePeriod() }, ).AnyTimes() - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().FirstEpochOfSyncPeriod(gomock.Any()).DoAndReturn( + s.beaconConfig.(*networkconfig.MockBeacon).EXPECT().FirstEpochOfSyncPeriod(gomock.Any()).DoAndReturn( func(period uint64) phase0.Epoch { - return phase0.Epoch(period * s.network.Beacon.EpochsPerSyncCommitteePeriod()) + return phase0.Epoch(period * s.beaconConfig.GetEpochsPerSyncCommitteePeriod()) }, ).AnyTimes() - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().GetEpochFirstSlot(gomock.Any()).DoAndReturn( + s.beaconConfig.(*networkconfig.MockBeacon).EXPECT().GetEpochFirstSlot(gomock.Any()).DoAndReturn( func(epoch phase0.Epoch) phase0.Slot { - return phase0.Slot(uint64(epoch) * s.network.Beacon.SlotsPerEpoch()) + return phase0.Slot(uint64(epoch) * s.beaconConfig.GetSlotsPerEpoch()) }, ).AnyTimes() - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().LastSlotOfSyncPeriod(gomock.Any()).DoAndReturn( + s.beaconConfig.(*networkconfig.MockBeacon).EXPECT().LastSlotOfSyncPeriod(gomock.Any()).DoAndReturn( func(period uint64) phase0.Slot { - lastEpoch := s.network.Beacon.FirstEpochOfSyncPeriod(period+1) - 1 + lastEpoch := s.beaconConfig.FirstEpochOfSyncPeriod(period+1) - 1 // If we are in the sync committee that ends at slot x we do not generate a message during slot x-1 // as it will never be included, hence -1. - return s.network.Beacon.GetEpochFirstSlot(lastEpoch+1) - 2 + return s.beaconConfig.GetEpochFirstSlot(lastEpoch+1) - 2 }, ).AnyTimes() + s.beaconConfig.(*networkconfig.MockBeacon).EXPECT().IntervalDuration().Return(s.beaconConfig.GetSlotDuration() / 3).AnyTimes() + s.beaconNode.(*MockBeaconNode).EXPECT().AttesterDuties(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( func(ctx context.Context, epoch phase0.Epoch, indices []phase0.ValidatorIndex) ([]*eth2apiv1.AttesterDuty, error) { if waitForDuties.Get() { @@ -69,7 +71,7 @@ func setupCommitteeDutiesMock( if waitForDuties.Get() { fetchDutiesCall <- struct{}{} } - period := s.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) + period := s.beaconConfig.EstimatedSyncCommitteePeriodAtEpoch(epoch) duties, _ := syncDuties.Get(period) return duties, nil }).AnyTimes() @@ -110,7 +112,7 @@ func setupCommitteeDutiesMock( }, Status: eth2apiv1.ValidatorStateActiveOngoing, } - firstEpoch := s.network.Beacon.FirstEpochOfSyncPeriod(period) + firstEpoch := s.beaconConfig.FirstEpochOfSyncPeriod(period) if firstEpoch < minEpoch { minEpoch = firstEpoch ssvShare.SetMinParticipationEpoch(firstEpoch) @@ -178,7 +180,7 @@ func TestScheduler_Committee_Same_Slot_Attester_Only(t *testing.T) { waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) // validate the 1/3 of the slot waiting time - require.Less(t, scheduler.network.Beacon.SlotDurationSec()/3, time.Since(startTime)) + require.Less(t, scheduler.beaconConfig.GetSlotDuration()/3, time.Since(startTime)) // Stop scheduler & wait for graceful exit. cancel() @@ -221,7 +223,7 @@ func TestScheduler_Committee_Same_Slot_SyncCommittee_Only(t *testing.T) { waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) // validate the 1/3 of the slot waiting time - require.Less(t, scheduler.network.Beacon.SlotDurationSec()/3, time.Since(startTime)) + require.Less(t, scheduler.beaconConfig.GetSlotDuration()/3, time.Since(startTime)) // Stop scheduler & wait for graceful exit. cancel() @@ -272,7 +274,7 @@ func TestScheduler_Committee_Same_Slot(t *testing.T) { waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) // validate the 1/3 of the slot waiting time - require.Less(t, scheduler.network.Beacon.SlotDurationSec()/3, time.Since(startTime)) + require.Less(t, scheduler.beaconConfig.GetSlotDuration()/3, time.Since(startTime)) // Stop scheduler & wait for graceful exit. cancel() @@ -321,7 +323,7 @@ func TestScheduler_Committee_Diff_Slot_Attester_Only(t *testing.T) { waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) // validate the 1/3 of the slot waiting time - require.Less(t, scheduler.network.Beacon.SlotDurationSec()/3, time.Since(startTime)) + require.Less(t, scheduler.beaconConfig.GetSlotDuration()/3, time.Since(startTime)) // Stop scheduler & wait for graceful exit. cancel() @@ -398,7 +400,7 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only(t *testing.T) { waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) // validate the 1/3 of the slot waiting time - require.Less(t, scheduler.network.Beacon.SlotDurationSec()/3, time.Since(startTime)) + require.Less(t, scheduler.beaconConfig.GetSlotDuration()/3, time.Since(startTime)) // Stop scheduler & wait for graceful exit. cancel() @@ -475,7 +477,7 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_2(t *testing.T) { waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) // validate the 1/3 of the slot waiting time - require.Less(t, scheduler.network.Beacon.SlotDurationSec()/3, time.Since(startTime)) + require.Less(t, scheduler.beaconConfig.GetSlotDuration()/3, time.Since(startTime)) // Stop scheduler & wait for graceful exit. cancel() @@ -550,7 +552,7 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_3(t *testing.T) { waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) // validate the 1/3 of the slot waiting time - require.Less(t, scheduler.network.Beacon.SlotDurationSec()/3, time.Since(startTime)) + require.Less(t, scheduler.beaconConfig.GetSlotDuration()/3, time.Since(startTime)) // Stop scheduler & wait for graceful exit. cancel() @@ -885,7 +887,7 @@ func TestScheduler_Committee_Early_Block_Attester_Only(t *testing.T) { } scheduler.HandleHeadEvent()(e.Data.(*eth2apiv1.HeadEvent)) waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) - require.Less(t, time.Since(startTime), scheduler.network.Beacon.SlotDurationSec()/3) + require.Less(t, time.Since(startTime), scheduler.beaconConfig.GetSlotDuration()/3) // Stop scheduler & wait for graceful exit. cancel() @@ -934,7 +936,7 @@ func TestScheduler_Committee_Early_Block(t *testing.T) { waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) // validate the 1/3 of the slot waiting time - require.Less(t, scheduler.network.Beacon.SlotDurationSec()/3, time.Since(startTime)) + require.Less(t, scheduler.beaconConfig.GetSlotDuration()/3, time.Since(startTime)) // STEP 3: wait for attester duties to be executed faster than 1/3 of the slot duration when // Beacon head event is observed (block arrival) @@ -952,7 +954,7 @@ func TestScheduler_Committee_Early_Block(t *testing.T) { } scheduler.HandleHeadEvent()(e.Data.(*eth2apiv1.HeadEvent)) waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) - require.Less(t, time.Since(startTime), scheduler.network.Beacon.SlotDurationSec()/3) + require.Less(t, time.Since(startTime), scheduler.beaconConfig.GetSlotDuration()/3) // Stop scheduler & wait for graceful exit. cancel() diff --git a/operator/duties/proposer.go b/operator/duties/proposer.go index 6a0fca30ce..71af520802 100644 --- a/operator/duties/proposer.go +++ b/operator/duties/proposer.go @@ -63,11 +63,11 @@ func (h *ProposerHandler) HandleDuties(ctx context.Context) { case <-next: slot := h.ticker.Slot() next = h.ticker.Next() - currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(slot) + currentEpoch := h.beaconConfig.EstimatedEpochAtSlot(slot) buildStr := fmt.Sprintf("e%v-s%v-#%v", currentEpoch, slot, slot%32+1) h.logger.Debug("🛠 ticker event", zap.String("epoch_slot_pos", buildStr)) - ctx, cancel := context.WithDeadline(ctx, h.network.Beacon.GetSlotStartTime(slot+1).Add(100*time.Millisecond)) + ctx, cancel := context.WithDeadline(ctx, h.beaconConfig.GetSlotStartTime(slot+1).Add(100*time.Millisecond)) if h.fetchFirst { h.fetchFirst = false h.indicesChanged = false @@ -83,13 +83,13 @@ func (h *ProposerHandler) HandleDuties(ctx context.Context) { cancel() // last slot of epoch - if uint64(slot)%h.network.Beacon.SlotsPerEpoch() == h.network.Beacon.SlotsPerEpoch()-1 { + if uint64(slot)%h.beaconConfig.GetSlotsPerEpoch() == h.beaconConfig.GetSlotsPerEpoch()-1 { h.duties.ResetEpoch(currentEpoch - 1) h.fetchFirst = true } case reorgEvent := <-h.reorg: - currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(reorgEvent.Slot) + currentEpoch := h.beaconConfig.EstimatedEpochAtSlot(reorgEvent.Slot) buildStr := fmt.Sprintf("e%v-s%v-#%v", currentEpoch, reorgEvent.Slot, reorgEvent.Slot%32+1) h.logger.Info("🔀 reorg event received", zap.String("epoch_slot_pos", buildStr), zap.Any("event", reorgEvent)) @@ -100,8 +100,8 @@ func (h *ProposerHandler) HandleDuties(ctx context.Context) { } case <-h.indicesChange: - slot := h.network.Beacon.EstimatedCurrentSlot() - currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(slot) + slot := h.beaconConfig.EstimatedCurrentSlot() + currentEpoch := h.beaconConfig.EstimatedEpochAtSlot(slot) buildStr := fmt.Sprintf("e%v-s%v-#%v", currentEpoch, slot, slot%32+1) h.logger.Info("🔁 indices change received", zap.String("epoch_slot_pos", buildStr)) @@ -111,10 +111,10 @@ func (h *ProposerHandler) HandleDuties(ctx context.Context) { } func (h *ProposerHandler) HandleInitialDuties(ctx context.Context) { - ctx, cancel := context.WithTimeout(ctx, h.network.Beacon.SlotDurationSec()/2) + ctx, cancel := context.WithTimeout(ctx, h.beaconConfig.GetSlotDuration()/2) defer cancel() - epoch := h.network.Beacon.EstimatedCurrentEpoch() + epoch := h.beaconConfig.EstimatedCurrentEpoch() h.processFetching(ctx, epoch) } @@ -206,7 +206,7 @@ func (h *ProposerHandler) toSpecDuty(duty *eth2apiv1.ProposerDuty, role spectype } func (h *ProposerHandler) shouldExecute(duty *eth2apiv1.ProposerDuty) bool { - currentSlot := h.network.Beacon.EstimatedCurrentSlot() + currentSlot := h.beaconConfig.EstimatedCurrentSlot() // execute task if slot already began and not pass 1 slot if currentSlot == duty.Slot { return true diff --git a/operator/duties/scheduler.go b/operator/duties/scheduler.go index bd969f0d66..c71a74420f 100644 --- a/operator/duties/scheduler.go +++ b/operator/duties/scheduler.go @@ -16,7 +16,6 @@ import ( spectypes "github.com/ssvlabs/ssv-spec/types" "go.uber.org/zap" - "github.com/ssvlabs/ssv/beacon/goclient" "github.com/ssvlabs/ssv/logging" "github.com/ssvlabs/ssv/logging/fields" "github.com/ssvlabs/ssv/network" @@ -24,7 +23,6 @@ import ( "github.com/ssvlabs/ssv/operator/duties/dutystore" "github.com/ssvlabs/ssv/operator/slotticker" "github.com/ssvlabs/ssv/protocol/v2/types" - "github.com/ssvlabs/ssv/utils/casts" ) //go:generate go tool -modfile=../../tool.mod mockgen -package=duties -destination=./scheduler_mock.go -source=./scheduler.go @@ -77,7 +75,7 @@ type SchedulerOptions struct { Ctx context.Context BeaconNode BeaconNode ExecutionClient ExecutionClient - Network networkconfig.NetworkConfig + BeaconConfig networkconfig.Beacon ValidatorProvider ValidatorProvider ValidatorController ValidatorController DutyExecutor DutyExecutor @@ -92,7 +90,7 @@ type Scheduler struct { logger *zap.Logger beaconNode BeaconNode executionClient ExecutionClient - network networkconfig.NetworkConfig + beaconConfig networkconfig.Beacon validatorProvider ValidatorProvider validatorController ValidatorController slotTickerProvider slotticker.Provider @@ -125,7 +123,7 @@ func NewScheduler(logger *zap.Logger, opts *SchedulerOptions) *Scheduler { logger: logger.Named(logging.NameDutyScheduler), beaconNode: opts.BeaconNode, executionClient: opts.ExecutionClient, - network: opts.Network, + beaconConfig: opts.BeaconConfig, slotTickerProvider: opts.SlotTickerProvider, dutyExecutor: opts.DutyExecutor, validatorProvider: opts.ValidatorProvider, @@ -183,7 +181,7 @@ func (s *Scheduler) Start(ctx context.Context) error { s.logger, s.beaconNode, s.executionClient, - s.network, + s.beaconConfig, s.validatorProvider, s.validatorController, s, @@ -292,8 +290,8 @@ func (s *Scheduler) SlotTicker(ctx context.Context) { case <-s.ticker.Next(): slot := s.ticker.Slot() - delayThirdOfSlot := s.network.SlotDurationSec() / casts.DurationFromUint64(goclient.IntervalsPerSlot) - finalTime := s.network.Beacon.GetSlotStartTime(slot).Add(delayThirdOfSlot) + delay := s.beaconConfig.IntervalDuration() + finalTime := s.beaconConfig.GetSlotStartTime(slot).Add(delay) waitDuration := time.Until(finalTime) if waitDuration > 0 { time.Sleep(waitDuration) @@ -309,13 +307,13 @@ func (s *Scheduler) HandleHeadEvent() func(event *eth2apiv1.HeadEvent) { return func(event *eth2apiv1.HeadEvent) { var zeroRoot phase0.Root - if event.Slot != s.network.Beacon.EstimatedCurrentSlot() { + if event.Slot != s.beaconConfig.EstimatedCurrentSlot() { // No need to process outdated events here. return } // check for reorg - epoch := s.network.Beacon.EstimatedEpochAtSlot(event.Slot) + epoch := s.beaconConfig.EstimatedEpochAtSlot(event.Slot) buildStr := fmt.Sprintf("e%v-s%v-#%v", epoch, event.Slot, event.Slot%32+1) logger := s.logger.With(zap.String("epoch_slot_pos", buildStr)) if s.lastBlockEpoch != 0 { @@ -368,8 +366,8 @@ func (s *Scheduler) HandleHeadEvent() func(event *eth2apiv1.HeadEvent) { s.currentDutyDependentRoot = event.CurrentDutyDependentRoot currentTime := time.Now() - delay := s.network.SlotDurationSec() / casts.DurationFromUint64(goclient.IntervalsPerSlot) /* a third of the slot duration */ - slotStartTimeWithDelay := s.network.Beacon.GetSlotStartTime(event.Slot).Add(delay) + delay := s.beaconConfig.IntervalDuration() + slotStartTimeWithDelay := s.beaconConfig.GetSlotStartTime(event.Slot).Add(delay) if currentTime.Before(slotStartTimeWithDelay) { logger.Debug("🏁 Head event: Block arrived before 1/3 slot", zap.Duration("time_saved", slotStartTimeWithDelay.Sub(currentTime))) @@ -386,7 +384,7 @@ func (s *Scheduler) HandleHeadEvent() func(event *eth2apiv1.HeadEvent) { func (s *Scheduler) ExecuteDuties(ctx context.Context, duties []*spectypes.ValidatorDuty) { for _, duty := range duties { logger := s.loggerWithDutyContext(duty) - slotDelay := time.Since(s.network.Beacon.GetSlotStartTime(duty.Slot)) + slotDelay := time.Since(s.beaconConfig.GetSlotStartTime(duty.Slot)) if slotDelay >= 100*time.Millisecond { logger.Debug("⚠️ late duty execution", zap.Int64("slot_delay", slotDelay.Milliseconds())) } @@ -406,10 +404,10 @@ func (s *Scheduler) ExecuteCommitteeDuties(ctx context.Context, duties committee for _, committee := range duties { duty := committee.duty logger := s.loggerWithCommitteeDutyContext(committee) // TODO: extract this in dutyExecutor (validator controller), don't pass logger to ExecuteCommitteeDuty - dutyEpoch := s.network.Beacon.EstimatedEpochAtSlot(duty.Slot) + dutyEpoch := s.beaconConfig.EstimatedEpochAtSlot(duty.Slot) logger.Debug("🔧 executing committee duty", fields.Duties(dutyEpoch, duty.ValidatorDuties)) - slotDelay := time.Since(s.network.Beacon.GetSlotStartTime(duty.Slot)) + slotDelay := time.Since(s.beaconConfig.GetSlotStartTime(duty.Slot)) if slotDelay >= 100*time.Millisecond { logger.Debug("⚠️ late duty execution", zap.Int64("slot_delay", slotDelay.Milliseconds())) } @@ -427,27 +425,27 @@ func (s *Scheduler) loggerWithDutyContext(duty *spectypes.ValidatorDuty) *zap.Lo return s.logger. With(fields.BeaconRole(duty.Type)). With(zap.Uint64("committee_index", uint64(duty.CommitteeIndex))). - With(fields.CurrentSlot(s.network.Beacon.EstimatedCurrentSlot())). + With(fields.CurrentSlot(s.beaconConfig.EstimatedCurrentSlot())). With(fields.Slot(duty.Slot)). - With(fields.Epoch(s.network.Beacon.EstimatedEpochAtSlot(duty.Slot))). + With(fields.Epoch(s.beaconConfig.EstimatedEpochAtSlot(duty.Slot))). With(fields.PubKey(duty.PubKey[:])). - With(fields.StartTimeUnixMilli(s.network.Beacon.GetSlotStartTime(duty.Slot))) + With(fields.StartTimeUnixMilli(s.beaconConfig.GetSlotStartTime(duty.Slot))) } // loggerWithCommitteeDutyContext returns an instance of logger with the given committee duty's information func (s *Scheduler) loggerWithCommitteeDutyContext(committeeDuty *committeeDuty) *zap.Logger { duty := committeeDuty.duty - dutyEpoch := s.network.Beacon.EstimatedEpochAtSlot(duty.Slot) + dutyEpoch := s.beaconConfig.EstimatedEpochAtSlot(duty.Slot) committeeDutyID := fields.FormatCommitteeDutyID(committeeDuty.operatorIDs, dutyEpoch, duty.Slot) return s.logger. With(fields.CommitteeID(committeeDuty.id)). With(fields.DutyID(committeeDutyID)). With(fields.Role(duty.RunnerRole())). - With(fields.CurrentSlot(s.network.Beacon.EstimatedCurrentSlot())). + With(fields.CurrentSlot(s.beaconConfig.EstimatedCurrentSlot())). With(fields.Slot(duty.Slot)). With(fields.Epoch(dutyEpoch)). - With(fields.StartTimeUnixMilli(s.network.Beacon.GetSlotStartTime(duty.Slot))) + With(fields.StartTimeUnixMilli(s.beaconConfig.GetSlotStartTime(duty.Slot))) } // advanceHeadSlot will set s.headSlot to the provided slot (but only if the provided slot is higher, diff --git a/operator/duties/scheduler_test.go b/operator/duties/scheduler_test.go index e9efd1156b..60d22843b6 100644 --- a/operator/duties/scheduler_test.go +++ b/operator/duties/scheduler_test.go @@ -18,7 +18,6 @@ import ( "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/operator/slotticker" mockslotticker "github.com/ssvlabs/ssv/operator/slotticker/mocks" - mocknetwork "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon/mocks" ) type MockSlotTicker interface { @@ -86,14 +85,13 @@ func setupSchedulerAndMocks(t *testing.T, handlers []dutyHandler, currentSlot *S mockValidatorController := NewMockValidatorController(ctrl) mockDutyExecutor := NewMockDutyExecutor(ctrl) mockSlotService := &mockSlotTickerService{} - mockNetworkConfig := networkconfig.NetworkConfig{} - mockNetworkConfig.Beacon = mocknetwork.NewMockBeaconNetwork(ctrl) + mockBeaconConfig := networkconfig.NewMockBeacon(ctrl) opts := &SchedulerOptions{ Ctx: ctx, BeaconNode: mockBeaconNode, ExecutionClient: mockExecutionClient, - Network: mockNetworkConfig, + BeaconConfig: mockBeaconConfig, ValidatorProvider: mockValidatorProvider, ValidatorController: mockValidatorController, DutyExecutor: mockDutyExecutor, @@ -111,33 +109,34 @@ func setupSchedulerAndMocks(t *testing.T, handlers []dutyHandler, currentSlot *S mockBeaconNode.EXPECT().SubscribeToHeadEvents(ctx, "duty_scheduler", gomock.Any()).Return(nil) - mockNetworkConfig.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().MinGenesisTime().Return(int64(0)).AnyTimes() - mockNetworkConfig.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().SlotDurationSec().Return(150 * time.Millisecond).AnyTimes() - mockNetworkConfig.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().SlotsPerEpoch().Return(uint64(32)).AnyTimes() - mockNetworkConfig.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().GetSlotStartTime(gomock.Any()).DoAndReturn( + s.beaconConfig.(*networkconfig.MockBeacon).EXPECT().GetSlotDuration().Return(150 * time.Millisecond).AnyTimes() + s.beaconConfig.(*networkconfig.MockBeacon).EXPECT().GetSlotsPerEpoch().Return(uint64(32)).AnyTimes() + s.beaconConfig.(*networkconfig.MockBeacon).EXPECT().GetSlotStartTime(gomock.Any()).DoAndReturn( func(slot phase0.Slot) time.Time { return time.Now() }, ).AnyTimes() - mockNetworkConfig.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().EstimatedEpochAtSlot(gomock.Any()).DoAndReturn( + s.beaconConfig.(*networkconfig.MockBeacon).EXPECT().EstimatedEpochAtSlot(gomock.Any()).DoAndReturn( func(slot phase0.Slot) phase0.Epoch { - return phase0.Epoch(uint64(slot) / s.network.SlotsPerEpoch()) + return phase0.Epoch(uint64(slot) / s.beaconConfig.GetSlotsPerEpoch()) }, ).AnyTimes() - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().EstimatedCurrentSlot().DoAndReturn( + s.beaconConfig.(*networkconfig.MockBeacon).EXPECT().EstimatedCurrentSlot().DoAndReturn( func() phase0.Slot { return currentSlot.Get() }, ).AnyTimes() - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().EstimatedCurrentEpoch().DoAndReturn( + s.beaconConfig.(*networkconfig.MockBeacon).EXPECT().EstimatedCurrentEpoch().DoAndReturn( func() phase0.Epoch { - return phase0.Epoch(uint64(currentSlot.Get()) / s.network.SlotsPerEpoch()) + return phase0.Epoch(uint64(currentSlot.Get()) / s.beaconConfig.GetSlotsPerEpoch()) }, ).AnyTimes() - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().EpochsPerSyncCommitteePeriod().Return(uint64(256)).AnyTimes() + s.beaconConfig.(*networkconfig.MockBeacon).EXPECT().GetEpochsPerSyncCommitteePeriod().Return(uint64(256)).AnyTimes() + + s.beaconConfig.(*networkconfig.MockBeacon).EXPECT().IntervalDuration().Return(s.beaconConfig.GetSlotDuration() / 3).AnyTimes() // Create a pool to wait for the scheduler to finish. schedulerPool := pool.New().WithErrors().WithContext(ctx) @@ -351,7 +350,7 @@ func TestScheduler_Run(t *testing.T) { opts := &SchedulerOptions{ Ctx: ctx, BeaconNode: mockBeaconNode, - Network: networkconfig.TestNetwork, + BeaconConfig: networkconfig.TestNetwork.BeaconConfig, ValidatorProvider: mockValidatorProvider, SlotTickerProvider: func() slotticker.SlotTicker { return mockTicker @@ -399,7 +398,7 @@ func TestScheduler_Regression_IndicesChangeStuck(t *testing.T) { opts := &SchedulerOptions{ Ctx: ctx, BeaconNode: mockBeaconNode, - Network: networkconfig.TestNetwork, + BeaconConfig: networkconfig.TestNetwork.BeaconConfig, ValidatorProvider: mockValidatorProvider, SlotTickerProvider: func() slotticker.SlotTicker { return mockTicker diff --git a/operator/duties/sync_committee.go b/operator/duties/sync_committee.go index 3d301f0fc3..a6dbc06e12 100644 --- a/operator/duties/sync_committee.go +++ b/operator/duties/sync_committee.go @@ -66,9 +66,9 @@ func (h *SyncCommitteeHandler) HandleDuties(ctx context.Context) { // Prepare relevant duties 1.5 epochs (48 slots) ahead of the sync committee period change. // The 1.5 epochs timing helps ensure setup occurs when the beacon node is likely less busy. - h.preparationSlots = h.network.Beacon.SlotsPerEpoch() * 3 / 2 + h.preparationSlots = h.beaconConfig.GetSlotsPerEpoch() * 3 / 2 - if h.shouldFetchNextPeriod(h.network.Beacon.EstimatedCurrentSlot()) { + if h.shouldFetchNextPeriod(h.beaconConfig.EstimatedCurrentSlot()) { h.fetchNextPeriod = true } @@ -81,12 +81,12 @@ func (h *SyncCommitteeHandler) HandleDuties(ctx context.Context) { case <-next: slot := h.ticker.Slot() next = h.ticker.Next() - epoch := h.network.Beacon.EstimatedEpochAtSlot(slot) - period := h.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) + epoch := h.beaconConfig.EstimatedEpochAtSlot(slot) + period := h.beaconConfig.EstimatedSyncCommitteePeriodAtEpoch(epoch) buildStr := fmt.Sprintf("p%v-e%v-s%v-#%v", period, epoch, slot, slot%32+1) h.logger.Debug("🛠 ticker event", zap.String("period_epoch_slot_pos", buildStr)) - ctx, cancel := context.WithDeadline(ctx, h.network.Beacon.GetSlotStartTime(slot+1).Add(100*time.Millisecond)) + ctx, cancel := context.WithDeadline(ctx, h.beaconConfig.GetSlotStartTime(slot+1).Add(100*time.Millisecond)) h.processExecution(ctx, period, slot) h.processFetching(ctx, epoch, period, true) cancel() @@ -98,13 +98,13 @@ func (h *SyncCommitteeHandler) HandleDuties(ctx context.Context) { } // last slot of period - if slot == h.network.Beacon.LastSlotOfSyncPeriod(period) { + if slot == h.beaconConfig.LastSlotOfSyncPeriod(period) { h.duties.Reset(period - 1) } case reorgEvent := <-h.reorg: - epoch := h.network.Beacon.EstimatedEpochAtSlot(reorgEvent.Slot) - period := h.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) + epoch := h.beaconConfig.EstimatedEpochAtSlot(reorgEvent.Slot) + period := h.beaconConfig.EstimatedSyncCommitteePeriodAtEpoch(epoch) buildStr := fmt.Sprintf("p%v-e%v-s%v-#%v", period, epoch, reorgEvent.Slot, reorgEvent.Slot%32+1) h.logger.Info("🔀 reorg event received", zap.String("period_epoch_slot_pos", buildStr), zap.Any("event", reorgEvent)) @@ -116,9 +116,9 @@ func (h *SyncCommitteeHandler) HandleDuties(ctx context.Context) { } case <-h.indicesChange: - slot := h.network.Beacon.EstimatedCurrentSlot() - epoch := h.network.Beacon.EstimatedEpochAtSlot(slot) - period := h.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) + slot := h.beaconConfig.EstimatedCurrentSlot() + epoch := h.beaconConfig.EstimatedEpochAtSlot(slot) + period := h.beaconConfig.EstimatedSyncCommitteePeriodAtEpoch(epoch) buildStr := fmt.Sprintf("p%v-e%v-s%v-#%v", period, epoch, slot, slot%32+1) h.logger.Info("🔁 indices change received", zap.String("period_epoch_slot_pos", buildStr)) @@ -133,11 +133,11 @@ func (h *SyncCommitteeHandler) HandleDuties(ctx context.Context) { } func (h *SyncCommitteeHandler) HandleInitialDuties(ctx context.Context) { - ctx, cancel := context.WithTimeout(ctx, h.network.Beacon.SlotDurationSec()/2) + ctx, cancel := context.WithTimeout(ctx, h.beaconConfig.GetSlotDuration()/2) defer cancel() - epoch := h.network.Beacon.EstimatedCurrentEpoch() - period := h.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) + epoch := h.beaconConfig.EstimatedCurrentEpoch() + period := h.beaconConfig.EstimatedSyncCommitteePeriodAtEpoch(epoch) h.processFetching(ctx, epoch, period, false) } @@ -186,12 +186,12 @@ func (h *SyncCommitteeHandler) processExecution(ctx context.Context, period uint func (h *SyncCommitteeHandler) fetchAndProcessDuties(ctx context.Context, epoch phase0.Epoch, period uint64, waitForInitial bool) error { start := time.Now() - if period > h.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) { - epoch = h.network.Beacon.FirstEpochOfSyncPeriod(period) + if period > h.beaconConfig.EstimatedSyncCommitteePeriodAtEpoch(epoch) { + epoch = h.beaconConfig.FirstEpochOfSyncPeriod(period) } eligibleIndices := h.validatorController.FilterIndices(waitForInitial, func(s *types.SSVShare) bool { - return s.IsParticipating(h.network, epoch) + return s.IsParticipating(h.beaconConfig, epoch) }) if len(eligibleIndices) == 0 { @@ -224,7 +224,7 @@ func (h *SyncCommitteeHandler) fetchAndProcessDuties(ctx context.Context, epoch h.prepareDutiesResultLog(period, duties, start) // lastEpoch + 1 due to the fact that we need to subscribe "until" the end of the period - lastEpoch := h.network.Beacon.FirstEpochOfSyncPeriod(period+1) - 1 + lastEpoch := h.beaconConfig.FirstEpochOfSyncPeriod(period+1) - 1 subscriptions := calculateSubscriptions(lastEpoch+1, duties) if len(subscriptions) > 0 { if deadline, ok := ctx.Deadline(); ok { @@ -275,8 +275,8 @@ func (h *SyncCommitteeHandler) toSpecDuty(duty *eth2apiv1.SyncCommitteeDuty, slo } func (h *SyncCommitteeHandler) shouldExecute(duty *eth2apiv1.SyncCommitteeDuty, slot phase0.Slot) bool { - currentSlot := h.network.Beacon.EstimatedCurrentSlot() - currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(currentSlot) + currentSlot := h.beaconConfig.EstimatedCurrentSlot() + currentEpoch := h.beaconConfig.EstimatedEpochAtSlot(currentSlot) v, exists := h.validatorProvider.Validator(duty.PubKey[:]) if !exists { @@ -324,5 +324,5 @@ func (h *SyncCommitteeHandler) shouldFetchNextPeriod(slot phase0.Slot) bool { } func (h *SyncCommitteeHandler) slotsPerPeriod() uint64 { - return h.network.Beacon.EpochsPerSyncCommitteePeriod() * h.network.Beacon.SlotsPerEpoch() + return h.beaconConfig.GetEpochsPerSyncCommitteePeriod() * h.beaconConfig.GetSlotsPerEpoch() } diff --git a/operator/duties/sync_committee_test.go b/operator/duties/sync_committee_test.go index 956f909a55..5d7af60c72 100644 --- a/operator/duties/sync_committee_test.go +++ b/operator/duties/sync_committee_test.go @@ -12,8 +12,8 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" + "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/operator/duties/dutystore" - mocknetwork "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon/mocks" ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types" "github.com/ssvlabs/ssv/utils/hashmap" ) @@ -27,30 +27,30 @@ func setupSyncCommitteeDutiesMock( fetchDutiesCall := make(chan struct{}) executeDutiesCall := make(chan []*spectypes.ValidatorDuty) - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().EstimatedSyncCommitteePeriodAtEpoch(gomock.Any()).DoAndReturn( + s.beaconConfig.(*networkconfig.MockBeacon).EXPECT().EstimatedSyncCommitteePeriodAtEpoch(gomock.Any()).DoAndReturn( func(epoch phase0.Epoch) uint64 { - return uint64(epoch) / s.network.Beacon.EpochsPerSyncCommitteePeriod() + return uint64(epoch) / s.beaconConfig.GetEpochsPerSyncCommitteePeriod() }, ).AnyTimes() - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().FirstEpochOfSyncPeriod(gomock.Any()).DoAndReturn( + s.beaconConfig.(*networkconfig.MockBeacon).EXPECT().FirstEpochOfSyncPeriod(gomock.Any()).DoAndReturn( func(period uint64) phase0.Epoch { - return phase0.Epoch(period * s.network.Beacon.EpochsPerSyncCommitteePeriod()) + return phase0.Epoch(period * s.beaconConfig.GetEpochsPerSyncCommitteePeriod()) }, ).AnyTimes() - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().LastSlotOfSyncPeriod(gomock.Any()).DoAndReturn( + s.beaconConfig.(*networkconfig.MockBeacon).EXPECT().LastSlotOfSyncPeriod(gomock.Any()).DoAndReturn( func(period uint64) phase0.Slot { - lastEpoch := s.network.Beacon.FirstEpochOfSyncPeriod(period+1) - 1 + lastEpoch := s.beaconConfig.FirstEpochOfSyncPeriod(period+1) - 1 // If we are in the sync committee that ends at slot x we do not generate a message during slot x-1 // as it will never be included, hence -1. - return s.network.Beacon.GetEpochFirstSlot(lastEpoch+1) - 2 + return s.beaconConfig.GetEpochFirstSlot(lastEpoch+1) - 2 }, ).AnyTimes() - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().GetEpochFirstSlot(gomock.Any()).DoAndReturn( + s.beaconConfig.(*networkconfig.MockBeacon).EXPECT().GetEpochFirstSlot(gomock.Any()).DoAndReturn( func(epoch phase0.Epoch) phase0.Slot { - return phase0.Slot(uint64(epoch) * s.network.Beacon.SlotsPerEpoch()) + return phase0.Slot(uint64(epoch) * s.beaconConfig.GetSlotsPerEpoch()) }, ).AnyTimes() @@ -59,7 +59,7 @@ func setupSyncCommitteeDutiesMock( if waitForDuties.Get() { fetchDutiesCall <- struct{}{} } - period := s.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) + period := s.beaconConfig.EstimatedSyncCommitteePeriodAtEpoch(epoch) duties, _ := dutiesMap.Get(period) return duties, nil }).AnyTimes() @@ -77,7 +77,7 @@ func setupSyncCommitteeDutiesMock( ValidatorIndex: duty.ValidatorIndex, }, } - firstEpoch := s.network.Beacon.FirstEpochOfSyncPeriod(period) + firstEpoch := s.beaconConfig.FirstEpochOfSyncPeriod(period) if firstEpoch < minEpoch { minEpoch = firstEpoch ssvShare.SetMinParticipationEpoch(firstEpoch) @@ -159,7 +159,7 @@ func TestScheduler_SyncCommittee_Same_Period(t *testing.T) { waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // STEP 3: expect sync committee duties to be executed at the last slot of the period - currentSlot.Set(scheduler.network.Beacon.LastSlotOfSyncPeriod(0)) + currentSlot.Set(scheduler.beaconConfig.LastSlotOfSyncPeriod(0)) duties, _ = dutiesMap.Get(0) expected = expectedExecutedSyncCommitteeDuties(handler, duties, currentSlot.Get()) setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) @@ -168,7 +168,7 @@ func TestScheduler_SyncCommittee_Same_Period(t *testing.T) { waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) // STEP 4: expect no action to be taken as we are in the next period - firstSlotOfNextPeriod := scheduler.network.Beacon.GetEpochFirstSlot(scheduler.network.Beacon.FirstEpochOfSyncPeriod(1)) + firstSlotOfNextPeriod := scheduler.beaconConfig.GetEpochFirstSlot(scheduler.beaconConfig.FirstEpochOfSyncPeriod(1)) currentSlot.Set(firstSlotOfNextPeriod) ticker.Send(currentSlot.Get()) waitForNoAction(t, logger, fetchDutiesCall, executeDutiesCall, timeout) @@ -583,7 +583,7 @@ func TestScheduler_SyncCommittee_Early_Block(t *testing.T) { } scheduler.HandleHeadEvent()(e.Data.(*v1.HeadEvent)) waitForDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - require.Greater(t, time.Since(startTime), time.Duration(float64(scheduler.network.Beacon.SlotDurationSec()/3)*0.90)) // 10% margin due to flakiness of the test + require.Greater(t, time.Since(startTime), time.Duration(float64(scheduler.beaconConfig.GetSlotDuration()/3)*0.90)) // 10% margin due to flakiness of the test // Stop scheduler & wait for graceful exit. cancel() diff --git a/operator/duties/validatorregistration.go b/operator/duties/validatorregistration.go index 882c1be550..cbfc301902 100644 --- a/operator/duties/validatorregistration.go +++ b/operator/duties/validatorregistration.go @@ -10,7 +10,7 @@ import ( ) // frequencyEpochs defines how frequently we want to submit validator-registrations. -const frequencyEpochs = uint64(10) +const frequencyEpochs = 10 type ValidatorRegistrationHandler struct { baseHandler @@ -38,7 +38,7 @@ func (h *ValidatorRegistrationHandler) HandleDuties(ctx context.Context) { defer h.logger.Info("duty handler exited") // validator should be registered within frequencyEpochs epochs time in a corresponding slot - registrationSlots := h.network.SlotsPerEpoch() * frequencyEpochs + registrationSlots := h.beaconConfig.GetSlotsPerEpoch() * frequencyEpochs next := h.ticker.Next() for { @@ -49,7 +49,7 @@ func (h *ValidatorRegistrationHandler) HandleDuties(ctx context.Context) { case <-next: slot := h.ticker.Slot() next = h.ticker.Next() - epoch := h.network.Beacon.EstimatedEpochAtSlot(slot) + epoch := h.beaconConfig.EstimatedEpochAtSlot(slot) shares := h.validatorProvider.SelfValidators() var vrs []ValidatorRegistration diff --git a/operator/duties/voluntary_exit.go b/operator/duties/voluntary_exit.go index 6b2c27adfa..e8041bd8f5 100644 --- a/operator/duties/voluntary_exit.go +++ b/operator/duties/voluntary_exit.go @@ -3,6 +3,7 @@ package duties import ( "context" "math/big" + "time" "github.com/attestantio/go-eth2-client/spec/phase0" spectypes "github.com/ssvlabs/ssv-spec/types" @@ -114,7 +115,7 @@ func (h *VoluntaryExitHandler) processExecution(ctx context.Context, slot phase0 } h.dutyQueue = pendingDuties - h.duties.RemoveSlot(slot - phase0.Slot(h.network.SlotsPerEpoch())) + h.duties.RemoveSlot(slot - phase0.Slot(h.beaconConfig.GetSlotsPerEpoch())) if dutyCount := len(dutiesForExecution); dutyCount != 0 { h.dutiesExecutor.ExecuteDuties(ctx, dutiesForExecution) @@ -137,7 +138,7 @@ func (h *VoluntaryExitHandler) blockSlot(ctx context.Context, blockNumber uint64 return 0, err } - blockSlot = h.network.Beacon.EstimatedSlotAtTime(int64(block.Time())) // #nosec G115 + blockSlot = h.beaconConfig.EstimatedSlotAtTime(time.Unix(int64(block.Time()), 0)) // #nosec G115 h.blockSlots[blockNumber] = blockSlot for k, v := range h.blockSlots { diff --git a/operator/duties/voluntary_exit_test.go b/operator/duties/voluntary_exit_test.go index 5edebdd773..5420c4b517 100644 --- a/operator/duties/voluntary_exit_test.go +++ b/operator/duties/voluntary_exit_test.go @@ -5,6 +5,7 @@ import ( "math/big" "sync/atomic" "testing" + "time" "github.com/attestantio/go-eth2-client/spec/phase0" ethtypes "github.com/ethereum/go-ethereum/core/types" @@ -13,8 +14,8 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" + "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/operator/duties/dutystore" - mocknetwork "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon/mocks" ) func TestVoluntaryExitHandler_HandleDuties(t *testing.T) { @@ -150,9 +151,9 @@ func create1to1BlockSlotMapping(scheduler *Scheduler) *atomic.Uint64 { return expectedBlock, nil }, ).AnyTimes() - scheduler.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().EstimatedSlotAtTime(gomock.Any()).DoAndReturn( - func(time int64) phase0.Slot { - return phase0.Slot(time) + scheduler.beaconConfig.(*networkconfig.MockBeacon).EXPECT().EstimatedSlotAtTime(gomock.Any()).DoAndReturn( + func(time time.Time) phase0.Slot { + return phase0.Slot(time.Unix()) }, ).AnyTimes() @@ -166,7 +167,7 @@ func assert1to1BlockSlotMapping(t *testing.T, scheduler *Scheduler) { require.NoError(t, err) require.NotNil(t, block) - slot := scheduler.network.Beacon.EstimatedSlotAtTime(int64(block.Time())) + slot := scheduler.beaconConfig.EstimatedSlotAtTime(time.Unix(int64(block.Time()), 0)) require.EqualValues(t, blockNumber, slot) } diff --git a/operator/fee_recipient/controller.go b/operator/fee_recipient/controller.go index 13395cfc93..a8a787959f 100644 --- a/operator/fee_recipient/controller.go +++ b/operator/fee_recipient/controller.go @@ -28,7 +28,7 @@ type RecipientController interface { type ControllerOptions struct { Ctx context.Context BeaconClient beaconprotocol.BeaconNode - Network networkconfig.NetworkConfig + BeaconConfig networkconfig.BeaconConfig ShareStorage storage.Shares RecipientStorage storage.Recipients SlotTickerProvider slotticker.Provider @@ -40,7 +40,7 @@ type recipientController struct { logger *zap.Logger ctx context.Context beaconClient beaconprotocol.BeaconNode - network networkconfig.NetworkConfig + beaconConfig networkconfig.BeaconConfig shareStorage storage.Shares recipientStorage storage.Recipients slotTickerProvider slotticker.Provider @@ -52,7 +52,7 @@ func NewController(logger *zap.Logger, opts *ControllerOptions) *recipientContro logger: logger, ctx: opts.Ctx, beaconClient: opts.BeaconClient, - network: opts.Network, + beaconConfig: opts.BeaconConfig, shareStorage: opts.ShareStorage, recipientStorage: opts.RecipientStorage, slotTickerProvider: opts.SlotTickerProvider, @@ -76,7 +76,7 @@ func (rc *recipientController) listenToTicker(ctx context.Context) { <-ticker.Next() slot := ticker.Slot() // submit if first time or if first slot in epoch - if firstTimeSubmitted && uint64(slot)%rc.network.SlotsPerEpoch() != (rc.network.SlotsPerEpoch()/2) { + if firstTimeSubmitted && uint64(slot)%rc.beaconConfig.SlotsPerEpoch != (rc.beaconConfig.SlotsPerEpoch/2) { continue } firstTimeSubmitted = true diff --git a/operator/fee_recipient/controller_test.go b/operator/fee_recipient/controller_test.go index 714b9e5d9b..d7cef406ed 100644 --- a/operator/fee_recipient/controller_test.go +++ b/operator/fee_recipient/controller_test.go @@ -41,12 +41,13 @@ func TestSubmitProposal(t *testing.T) { db, shareStorage, recipientStorage := createStorage(t) defer db.Close() - network := networkconfig.TestNetwork + + beaconConfig := networkconfig.TestNetwork.BeaconConfig populateStorage(t, shareStorage, operatorData) frCtrl := NewController(logger, &ControllerOptions{ Ctx: context.TODO(), - Network: network, + BeaconConfig: beaconConfig, ShareStorage: shareStorage, RecipientStorage: recipientStorage, OperatorDataStore: operatorDataStore, @@ -79,11 +80,11 @@ func TestSubmitProposal(t *testing.T) { go frCtrl.Start(t.Context()) slots := []phase0.Slot{ - 1, // first time - 2, // should not call submit - 20, // should not call submit - phase0.Slot(network.SlotsPerEpoch()) / 2, // halfway through epoch - 63, // should not call submit + 1, // first time + 2, // should not call submit + 20, // should not call submit + phase0.Slot(beaconConfig.SlotsPerEpoch / 2), // halfway through epoch + 63, // should not call submit } for _, s := range slots { diff --git a/operator/node.go b/operator/node.go index 3b868d7af2..278e812c73 100644 --- a/operator/node.go +++ b/operator/node.go @@ -26,10 +26,10 @@ import ( // Options contains options to create the node type Options struct { - // NetworkName is the network name of this node - NetworkName string `yaml:"Network" env:"NETWORK" env-default:"mainnet" env-description:"Ethereum network to connect to (mainnet, holesky, sepolia, etc.)"` - CustomDomainType string `yaml:"CustomDomainType" env:"CUSTOM_DOMAIN_TYPE" env-default:"" env-description:"Override SSV domain type for network isolation. Warning: Please modify only if you are certain of the implications. This would be incremented by 1 after Alan fork (e.g., 0x01020304 → 0x01020305 post-fork)"` - Network networkconfig.NetworkConfig + NetworkName string `yaml:"Network" env:"NETWORK" env-default:"mainnet" env-description:"Ethereum network to connect to (mainnet, holesky, sepolia, etc.). For backwards compatibility it's ignored if CustomNetwork is set"` + CustomNetwork *networkconfig.SSVConfig `yaml:"CustomNetwork" env:"CUSTOM_NETWORK" env-description:"Custom SSV network configuration"` + CustomDomainType string `yaml:"CustomDomainType" env:"CUSTOM_DOMAIN_TYPE" env-default:"" env-description:"Override SSV domain type for network isolation. Warning: Please modify only if you are certain of the implications. This would be incremented by 1 after Alan fork (e.g., 0x01020304 → 0x01020305 post-fork)"` // DEPRECATED: use CustomNetwork instead. + NetworkConfig networkconfig.NetworkConfig BeaconNode beaconprotocol.BeaconNode // TODO: consider renaming to ConsensusClient ExecutionClient executionclient.Provider P2PNetwork network.P2PNetwork @@ -68,7 +68,7 @@ func New(logger *zap.Logger, opts Options, slotTickerProvider slotticker.Provide context: opts.Context, validatorsCtrl: opts.ValidatorController, validatorOptions: opts.ValidatorOptions, - network: opts.Network, + network: opts.NetworkConfig, consensusClient: opts.BeaconNode, executionClient: opts.ExecutionClient, net: opts.P2PNetwork, @@ -78,7 +78,7 @@ func New(logger *zap.Logger, opts Options, slotTickerProvider slotticker.Provide Ctx: opts.Context, BeaconNode: opts.BeaconNode, ExecutionClient: opts.ExecutionClient, - Network: opts.Network, + BeaconConfig: opts.NetworkConfig, ValidatorProvider: opts.ValidatorStore.WithOperatorID(opts.ValidatorOptions.OperatorDataStore.GetOperatorID), ValidatorController: opts.ValidatorController, DutyExecutor: opts.ValidatorController, @@ -91,7 +91,7 @@ func New(logger *zap.Logger, opts Options, slotTickerProvider slotticker.Provide feeRecipientCtrl: fee_recipient.NewController(logger, &fee_recipient.ControllerOptions{ Ctx: opts.Context, BeaconClient: opts.BeaconNode, - Network: opts.Network, + BeaconConfig: opts.NetworkConfig.BeaconConfig, ShareStorage: opts.ValidatorOptions.RegistryStorage.Shares(), RecipientStorage: opts.ValidatorOptions.RegistryStorage, OperatorDataStore: opts.ValidatorOptions.OperatorDataStore, diff --git a/operator/storage/storage.go b/operator/storage/storage.go index 1a67c69727..d540b6b14a 100644 --- a/operator/storage/storage.go +++ b/operator/storage/storage.go @@ -64,7 +64,7 @@ type storage struct { } // NewNodeStorage creates a new instance of Storage -func NewNodeStorage(networkConfig networkconfig.NetworkConfig, logger *zap.Logger, db basedb.Database) (Storage, error) { +func NewNodeStorage(beaconCfg networkconfig.Beacon, logger *zap.Logger, db basedb.Database) (Storage, error) { stg := &storage{ logger: logger, db: db, @@ -74,7 +74,7 @@ func NewNodeStorage(networkConfig networkconfig.NetworkConfig, logger *zap.Logge var err error - stg.shareStore, stg.validatorStore, err = registrystorage.NewSharesStorage(networkConfig, db, OperatorStoragePrefix) + stg.shareStore, stg.validatorStore, err = registrystorage.NewSharesStorage(beaconCfg, db, OperatorStoragePrefix) if err != nil { return nil, err } diff --git a/operator/validator/controller.go b/operator/validator/controller.go index 5812d1507f..58492a3dbe 100644 --- a/operator/validator/controller.go +++ b/operator/validator/controller.go @@ -85,7 +85,7 @@ type ControllerOptions struct { MessageValidator validation.MessageValidator ValidatorsMap *validators.ValidatorsMap DoppelgangerHandler doppelganger.Provider - NetworkConfig networkconfig.NetworkConfig + NetworkConfig networkconfig.Network ValidatorSyncer *metadata.Syncer Graffiti []byte @@ -151,7 +151,7 @@ type controller struct { logger *zap.Logger - networkConfig networkconfig.NetworkConfig + networkConfig networkconfig.Network sharesStorage SharesStorage operatorsStorage registrystorage.Operators recipientsStorage Recipients @@ -233,8 +233,7 @@ func NewController(logger *zap.Logger, options ControllerOptions) Controller { } } - beaconNetwork := options.NetworkConfig.Beacon - cacheTTL := beaconNetwork.SlotDurationSec() * time.Duration(beaconNetwork.SlotsPerEpoch()*2) // #nosec G115 + cacheTTL := 2 * options.NetworkConfig.EpochDuration() // #nosec G115 ctrl := controller{ logger: logger.Named(logging.NameController), @@ -310,7 +309,7 @@ func (c *controller) GetValidatorStats() (uint64, uint64, uint64, error) { if ok := s.BelongsToOperator(c.operatorDataStore.GetOperatorID()); ok { operatorShares++ } - if s.IsParticipating(c.networkConfig, c.beacon.GetBeaconNetwork().EstimatedCurrentEpoch()) { + if s.IsParticipating(c.networkConfig, c.networkConfig.EstimatedCurrentEpoch()) { active++ } } @@ -376,7 +375,7 @@ func (c *controller) handleWorkerMessages(ctx context.Context, msg network.Decod if item == nil { committeeObserverOptions := validator.CommitteeObserverOptions{ Logger: c.logger, - NetworkConfig: c.networkConfig, + BeaconConfig: c.networkConfig, ValidatorStore: c.validatorStore, Network: c.validatorOptions.Network, Storage: c.validatorOptions.Storage, @@ -396,7 +395,7 @@ func (c *controller) handleWorkerMessages(ctx context.Context, msg network.Decod c.committeesObservers.Set( ssvMsg.GetID(), ncv, - time.Duration(ttlSlots)*c.beacon.GetBeaconNetwork().SlotDurationSec(), + time.Duration(ttlSlots)*c.networkConfig.GetSlotDuration(), ) } else { ncv = item @@ -614,7 +613,7 @@ func (c *controller) ExecuteDuty(ctx context.Context, logger *zap.Logger, duty * copy(pk, duty.PubKey[:]) if v, ok := c.GetValidator(spectypes.ValidatorPK(pk)); ok { - ssvMsg, err := CreateDutyExecuteMsg(duty, pk, c.networkConfig.DomainType) + ssvMsg, err := CreateDutyExecuteMsg(duty, pk, c.networkConfig.GetDomainType()) if err != nil { logger.Error("could not create duty execute msg", zap.Error(err)) return @@ -635,7 +634,7 @@ func (c *controller) ExecuteDuty(ctx context.Context, logger *zap.Logger, duty * func (c *controller) ExecuteCommitteeDuty(ctx context.Context, logger *zap.Logger, committeeID spectypes.CommitteeID, duty *spectypes.CommitteeDuty) { if cm, ok := c.validatorsMap.GetCommittee(committeeID); ok { - ssvMsg, err := CreateCommitteeDutyExecuteMsg(duty, committeeID, c.networkConfig.DomainType) + ssvMsg, err := CreateCommitteeDutyExecuteMsg(duty, committeeID, c.networkConfig.GetDomainType()) if err != nil { logger.Error("could not create duty execute msg", zap.Error(err)) return @@ -804,7 +803,7 @@ func (c *controller) onShareInit(share *ssvtypes.SSVShare) (*validator.Validator ctx, cancel, logger, - c.beacon.GetBeaconNetwork(), + c.networkConfig, operator, committeeRunnerFunc, nil, @@ -954,7 +953,7 @@ func (c *controller) handleMetadataUpdate(ctx context.Context, syncBatch metadat zap.Int("started_validators", startedValidators), ) // Refresh duties if there are any new active validators. - if !c.reportIndicesChange(ctx, 2*c.beacon.GetBeaconNetwork().SlotDurationSec()) { + if !c.reportIndicesChange(ctx, 2*c.networkConfig.GetSlotDuration()) { c.logger.Warn("timed out while notifying DutyScheduler of new validators") } } @@ -989,7 +988,7 @@ func (c *controller) ReportValidatorStatuses(ctx context.Context) { validatorsPerStatus := make(map[validatorStatus]uint32) for _, share := range c.validatorStore.OperatorValidators(c.operatorDataStore.GetOperatorID()) { - if share.IsParticipating(c.networkConfig, c.beacon.GetBeaconNetwork().EstimatedCurrentEpoch()) { + if share.IsParticipating(c.networkConfig, c.networkConfig.EstimatedCurrentEpoch()) { validatorsPerStatus[statusParticipating]++ } if !share.HasBeaconMetadata() { @@ -1046,18 +1045,18 @@ func SetupCommitteeRunners( buildController := func(role spectypes.RunnerRole, valueCheckF specqbft.ProposedValueCheckF) *qbftcontroller.Controller { config := &qbft.Config{ BeaconSigner: options.Signer, - Domain: options.NetworkConfig.DomainType, + Domain: options.NetworkConfig.GetDomainType(), ValueCheckF: valueCheckF, ProposerF: func(state *specqbft.State, round specqbft.Round) spectypes.OperatorID { leader := qbft.RoundRobinProposer(state, round) return leader }, Network: options.Network, - Timer: roundtimer.New(ctx, options.NetworkConfig.Beacon, role, nil), + Timer: roundtimer.New(ctx, options.NetworkConfig, role, nil), CutOffRound: roundtimer.CutOffRound, } - identifier := spectypes.NewMsgID(options.NetworkConfig.DomainType, options.Operator.CommitteeID[:], role) + identifier := spectypes.NewMsgID(options.NetworkConfig.GetDomainType(), options.Operator.CommitteeID[:], role) qbftCtrl := qbftcontroller.NewController(identifier[:], options.Operator, config, options.OperatorSigner, options.FullNode) return qbftCtrl } @@ -1069,7 +1068,7 @@ func SetupCommitteeRunners( dutyGuard runner.CommitteeDutyGuard, ) (*runner.CommitteeRunner, error) { // Create a committee runner. - epoch := options.NetworkConfig.Beacon.GetBeaconNetwork().EstimatedEpochAtSlot(slot) + epoch := options.NetworkConfig.EstimatedEpochAtSlot(slot) valCheck := ssv.BeaconVoteValueCheckF(options.Signer, slot, attestingValidators, epoch) crunner, err := runner.NewCommitteeRunner( options.NetworkConfig, @@ -1114,7 +1113,7 @@ func SetupRunners( buildController := func(role spectypes.RunnerRole, valueCheckF specqbft.ProposedValueCheckF) *qbftcontroller.Controller { config := &qbft.Config{ BeaconSigner: options.Signer, - Domain: options.NetworkConfig.DomainType, + Domain: options.NetworkConfig.GetDomainType(), ValueCheckF: nil, // sets per role type ProposerF: func(state *specqbft.State, round specqbft.Round) spectypes.OperatorID { leader := qbft.RoundRobinProposer(state, round) @@ -1122,12 +1121,12 @@ func SetupRunners( return leader }, Network: options.Network, - Timer: roundtimer.New(ctx, options.NetworkConfig.Beacon, role, nil), + Timer: roundtimer.New(ctx, options.NetworkConfig, role, nil), CutOffRound: roundtimer.CutOffRound, } config.ValueCheckF = valueCheckF - identifier := spectypes.NewMsgID(options.NetworkConfig.DomainType, options.SSVShare.ValidatorPubKey[:], role) + identifier := spectypes.NewMsgID(options.NetworkConfig.GetDomainType(), options.SSVShare.ValidatorPubKey[:], role) qbftCtrl := qbftcontroller.NewController(identifier[:], options.Operator, config, options.OperatorSigner, options.FullNode) return qbftCtrl } @@ -1136,26 +1135,25 @@ func SetupRunners( shareMap[options.SSVShare.ValidatorIndex] = &options.SSVShare.Share runners := runner.ValidatorDutyRunners{} - domainType := options.NetworkConfig.DomainType var err error for _, role := range runnersType { switch role { case spectypes.RoleProposer: - proposedValueCheck := ssv.ProposerValueCheckF(options.Signer, options.NetworkConfig.Beacon.GetBeaconNetwork(), options.SSVShare.ValidatorPubKey, options.SSVShare.ValidatorIndex, phase0.BLSPubKey(options.SSVShare.SharePubKey)) + proposedValueCheck := ssv.ProposerValueCheckF(options.Signer, options.NetworkConfig, options.SSVShare.ValidatorPubKey, options.SSVShare.ValidatorIndex, phase0.BLSPubKey(options.SSVShare.SharePubKey)) qbftCtrl := buildController(spectypes.RoleProposer, proposedValueCheck) - runners[role], err = runner.NewProposerRunner(domainType, options.NetworkConfig.Beacon.GetBeaconNetwork(), shareMap, qbftCtrl, options.Beacon, options.Network, options.Signer, options.OperatorSigner, options.DoppelgangerHandler, proposedValueCheck, 0, options.Graffiti) + runners[role], err = runner.NewProposerRunner(options.NetworkConfig, shareMap, qbftCtrl, options.Beacon, options.Network, options.Signer, options.OperatorSigner, options.DoppelgangerHandler, proposedValueCheck, 0, options.Graffiti) case spectypes.RoleAggregator: - aggregatorValueCheckF := ssv.AggregatorValueCheckF(options.Signer, options.NetworkConfig.Beacon.GetBeaconNetwork(), options.SSVShare.ValidatorPubKey, options.SSVShare.ValidatorIndex) + aggregatorValueCheckF := ssv.AggregatorValueCheckF(options.Signer, options.NetworkConfig, options.SSVShare.ValidatorPubKey, options.SSVShare.ValidatorIndex) qbftCtrl := buildController(spectypes.RoleAggregator, aggregatorValueCheckF) - runners[role], err = runner.NewAggregatorRunner(domainType, options.NetworkConfig.Beacon.GetBeaconNetwork(), shareMap, qbftCtrl, options.Beacon, options.Network, options.Signer, options.OperatorSigner, aggregatorValueCheckF, 0) + runners[role], err = runner.NewAggregatorRunner(options.NetworkConfig, shareMap, qbftCtrl, options.Beacon, options.Network, options.Signer, options.OperatorSigner, aggregatorValueCheckF, 0) case spectypes.RoleSyncCommitteeContribution: - syncCommitteeContributionValueCheckF := ssv.SyncCommitteeContributionValueCheckF(options.Signer, options.NetworkConfig.Beacon.GetBeaconNetwork(), options.SSVShare.ValidatorPubKey, options.SSVShare.ValidatorIndex) + syncCommitteeContributionValueCheckF := ssv.SyncCommitteeContributionValueCheckF(options.Signer, options.NetworkConfig, options.SSVShare.ValidatorPubKey, options.SSVShare.ValidatorIndex) qbftCtrl := buildController(spectypes.RoleSyncCommitteeContribution, syncCommitteeContributionValueCheckF) - runners[role], err = runner.NewSyncCommitteeAggregatorRunner(domainType, options.NetworkConfig.Beacon.GetBeaconNetwork(), shareMap, qbftCtrl, options.Beacon, options.Network, options.Signer, options.OperatorSigner, syncCommitteeContributionValueCheckF, 0) + runners[role], err = runner.NewSyncCommitteeAggregatorRunner(options.NetworkConfig, shareMap, qbftCtrl, options.Beacon, options.Network, options.Signer, options.OperatorSigner, syncCommitteeContributionValueCheckF, 0) case spectypes.RoleValidatorRegistration: - runners[role], err = runner.NewValidatorRegistrationRunner(domainType, options.NetworkConfig.Beacon.GetBeaconNetwork(), shareMap, options.Beacon, options.Network, options.Signer, options.OperatorSigner, options.GasLimit) + runners[role], err = runner.NewValidatorRegistrationRunner(options.NetworkConfig, shareMap, options.Beacon, options.Network, options.Signer, options.OperatorSigner, options.GasLimit) case spectypes.RoleVoluntaryExit: - runners[role], err = runner.NewVoluntaryExitRunner(domainType, options.NetworkConfig.Beacon.GetBeaconNetwork(), shareMap, options.Beacon, options.Network, options.Signer, options.OperatorSigner) + runners[role], err = runner.NewVoluntaryExitRunner(options.NetworkConfig, shareMap, options.Beacon, options.Network, options.Signer, options.OperatorSigner) } if err != nil { return nil, errors.Wrap(err, "could not create duty runner") diff --git a/operator/validator/controller_test.go b/operator/validator/controller_test.go index 289bbab54e..b7b2aaadf5 100644 --- a/operator/validator/controller_test.go +++ b/operator/validator/controller_test.go @@ -526,8 +526,6 @@ func TestSetupValidators(t *testing.T) { committeeMap := make(map[spectypes.CommitteeID]*validator.Committee) mockValidatorsMap := validators.New(context.TODO(), validators.WithInitialState(testValidatorsMap, committeeMap)) - bc.EXPECT().GetBeaconNetwork().Return(networkconfig.TestNetwork.Beacon.GetBeaconNetwork()).AnyTimes() - // Set up the controller with mock data controllerOptions := MockControllerOptions{ beacon: bc, @@ -592,9 +590,6 @@ func TestGetValidatorStats(t *testing.T) { bc := beacon.NewMockBeaconNode(ctrl) activationEpoch, exitEpoch := phase0.Epoch(1), goclient.FarFutureEpoch - netCfg := networkconfig.TestNetwork - bc.EXPECT().GetBeaconNetwork().Return(netCfg.Beacon.GetBeaconNetwork()).AnyTimes() - t.Run("Test with multiple operators", func(t *testing.T) { // Setup for this subtest operatorIds := []uint64{1, 2, 3} diff --git a/operator/validator/metadata/syncer.go b/operator/validator/metadata/syncer.go index fd339f59b8..768c562942 100644 --- a/operator/validator/metadata/syncer.go +++ b/operator/validator/metadata/syncer.go @@ -32,7 +32,7 @@ type Syncer struct { logger *zap.Logger shareStorage shareStorage validatorStore selfValidatorStore - networkConfig networkconfig.NetworkConfig + beaconConfig networkconfig.BeaconConfig beaconNode beacon.BeaconNode fixedSubnets networkcommons.Subnets syncInterval time.Duration @@ -54,7 +54,7 @@ func NewSyncer( logger *zap.Logger, shareStorage shareStorage, validatorStore selfValidatorStore, - networkConfig networkconfig.NetworkConfig, + beaconConfig networkconfig.BeaconConfig, beaconNode beacon.BeaconNode, fixedSubnets networkcommons.Subnets, opts ...Option, @@ -63,7 +63,7 @@ func NewSyncer( logger: logger, shareStorage: shareStorage, validatorStore: validatorStore, - networkConfig: networkConfig, + beaconConfig: beaconConfig, beaconNode: beaconNode, fixedSubnets: fixedSubnets, syncInterval: defaultSyncInterval, @@ -253,14 +253,14 @@ func (s *Syncer) syncNextBatch(ctx context.Context, subnetsBuf *big.Int) (SyncBa pubKeys[i] = share.ValidatorPubKey } - indicesBefore := s.allActiveIndices(ctx, s.networkConfig.Beacon.GetBeaconNetwork().EstimatedCurrentEpoch()) + indicesBefore := s.allActiveIndices(ctx, s.beaconConfig.EstimatedCurrentEpoch()) validators, err := s.Sync(ctx, pubKeys) if err != nil { return SyncBatch{}, false, fmt.Errorf("sync: %w", err) } - indicesAfter := s.allActiveIndices(ctx, s.networkConfig.Beacon.GetBeaconNetwork().EstimatedCurrentEpoch()) + indicesAfter := s.allActiveIndices(ctx, s.beaconConfig.EstimatedCurrentEpoch()) update := SyncBatch{ IndicesBefore: indicesBefore, @@ -324,7 +324,7 @@ func (s *Syncer) allActiveIndices(_ context.Context, epoch phase0.Epoch) []phase // TODO: use context, return if it's done s.shareStorage.Range(nil, func(share *ssvtypes.SSVShare) bool { - if share.IsParticipating(s.networkConfig, epoch) { + if share.IsParticipating(s.beaconConfig, epoch) { indices = append(indices, share.ValidatorIndex) } return true diff --git a/operator/validator/metadata/syncer_test.go b/operator/validator/metadata/syncer_test.go index 04d5cf50b7..df853c23f8 100644 --- a/operator/validator/metadata/syncer_test.go +++ b/operator/validator/metadata/syncer_test.go @@ -95,7 +95,7 @@ func TestUpdateValidatorMetadata(t *testing.T) { return result, nil }).AnyTimes() - syncer := NewSyncer(logger, sharesStorage, validatorStore, networkconfig.TestNetwork, beaconNode, commons.ZeroSubnets) + syncer := NewSyncer(logger, sharesStorage, validatorStore, networkconfig.TestNetwork.BeaconConfig, beaconNode, commons.ZeroSubnets) _, err := syncer.Sync(context.TODO(), []spectypes.ValidatorPK{tc.testPublicKey}) if tc.sharesStorageErr != nil { require.ErrorIs(t, err, tc.sharesStorageErr) @@ -422,7 +422,7 @@ func TestSyncer_Stream(t *testing.T) { shareStorage: mockShareStorage, validatorStore: mockValidatorStore, beaconNode: defaultMockBeaconNode, - networkConfig: networkconfig.TestNetwork, + beaconConfig: networkconfig.TestNetwork.BeaconConfig, syncInterval: testSyncInterval, streamInterval: testStreamInterval, updateSendTimeout: testUpdateSendTimeout, @@ -521,7 +521,7 @@ func TestSyncer_Stream(t *testing.T) { shareStorage: mockShareStorage, validatorStore: mockValidatorStore, beaconNode: errMockBeaconNode, - networkConfig: networkconfig.TestNetwork, + beaconConfig: networkconfig.TestNetwork.BeaconConfig, syncInterval: testSyncInterval, streamInterval: testStreamInterval, updateSendTimeout: testUpdateSendTimeout, @@ -595,7 +595,7 @@ func TestSyncer_Stream(t *testing.T) { shareStorage: mockShareStorage, validatorStore: mockValidatorStore, beaconNode: defaultMockBeaconNode, - networkConfig: networkconfig.TestNetwork, + beaconConfig: networkconfig.TestNetwork.BeaconConfig, syncInterval: testSyncInterval, streamInterval: testStreamInterval, updateSendTimeout: testUpdateSendTimeout, @@ -667,7 +667,7 @@ func TestWithUpdateInterval(t *testing.T) { logger, mockShareStorage, mockValidatorStore, - networkconfig.TestNetwork, + networkconfig.TestNetwork.BeaconConfig, mockBeaconNode, commons.ZeroSubnets, WithSyncInterval(interval), diff --git a/operator/validator/task_executor.go b/operator/validator/task_executor.go index 2ab9d52567..9b18db7e4c 100644 --- a/operator/validator/task_executor.go +++ b/operator/validator/task_executor.go @@ -62,7 +62,7 @@ func (c *controller) ReactivateCluster(owner common.Address, operatorIDs []spect // Notify DutyScheduler about the changes in validator indices without blocking. go func() { ctx := context.Background() // TODO: pass context - if !c.reportIndicesChange(ctx, 2*c.beacon.GetBeaconNetwork().SlotDurationSec()) { + if !c.reportIndicesChange(ctx, 2*c.networkConfig.GetSlotDuration()) { logger.Error("failed to notify indices change") } }() @@ -109,7 +109,7 @@ func (c *controller) ExitValidator(pubKey phase0.BLSPubKey, blockNumber uint64, select { case c.validatorExitCh <- exitDesc: logger.Debug("added voluntary exit task to pipeline") - case <-time.After(2 * c.beacon.GetBeaconNetwork().SlotDurationSec()): + case <-time.After(2 * c.networkConfig.GetSlotDuration()): logger.Error("failed to schedule ExitValidator duty!") } }() diff --git a/operator/validator/task_executor_test.go b/operator/validator/task_executor_test.go index 3be64fb207..07c0da2e64 100644 --- a/operator/validator/task_executor_test.go +++ b/operator/validator/task_executor_test.go @@ -258,8 +258,6 @@ func TestController_ReactivateCluster(t *testing.T) { recipientData := buildFeeRecipient("67Ce5c69260bd819B4e0AD13f4b873074D479811", "45E668aba4b7fc8761331EC3CE77584B7A99A51A") recipientStorage.EXPECT().GetRecipientData(gomock.Any(), gomock.Any()).AnyTimes().Return(recipientData, true, nil) - bc.EXPECT().GetBeaconNetwork().AnyTimes().Return(testingBC.GetBeaconNetwork()) - indiciesUpdate := make(chan struct{}) go func() { <-ctr.indicesChange diff --git a/protocol/v2/blockchain/beacon/client.go b/protocol/v2/blockchain/beacon/client.go index 41276a6352..4ab619d44b 100644 --- a/protocol/v2/blockchain/beacon/client.go +++ b/protocol/v2/blockchain/beacon/client.go @@ -2,6 +2,7 @@ package beacon import ( "context" + "time" "github.com/attestantio/go-eth2-client/api" eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" @@ -10,7 +11,6 @@ import ( "github.com/attestantio/go-eth2-client/spec/bellatrix" "github.com/attestantio/go-eth2-client/spec/phase0" ssz "github.com/ferranbt/fastssz" - "github.com/ssvlabs/ssv-spec/types" ) // TODO: add missing tests @@ -79,12 +79,6 @@ type DomainCalls interface { DomainData(ctx context.Context, epoch phase0.Epoch, domain phase0.DomainType) (phase0.Domain, error) } -type VersionCalls interface { - // DataVersion returns a data version for the given epoch. - // In practice, for performance, responses can be cached in order not to always trigger an API call. - DataVersion(epoch phase0.Epoch) spec.DataVersion -} - // beaconDuties interface serves all duty related calls type beaconDuties interface { AttesterDuties(ctx context.Context, epoch phase0.Epoch, validatorIndices []phase0.ValidatorIndex) ([]*eth2apiv1.AttesterDuty, error) @@ -120,7 +114,6 @@ type signer interface { // BeaconNode interface for all beacon duty calls type BeaconNode interface { - GetBeaconNetwork() types.BeaconNetwork AttesterCalls ProposerCalls AggregatorCalls @@ -129,7 +122,6 @@ type BeaconNode interface { ValidatorRegistrationCalls VoluntaryExitCalls DomainCalls - VersionCalls beaconDuties beaconSubscriber @@ -137,3 +129,14 @@ type BeaconNode interface { signer // TODO need to handle differently proposer } + +// Options for controller struct creation +type Options struct { + Context context.Context + BeaconNodeAddr string `yaml:"BeaconNodeAddr" env:"BEACON_NODE_ADDR" env-required:"true" env-description:"Beacon node URL(s). Multiple nodes are supported via semicolon-separated URLs (e.g. 'http://localhost:5052;http://localhost:5053')"` + SyncDistanceTolerance uint64 `yaml:"SyncDistanceTolerance" env:"BEACON_SYNC_DISTANCE_TOLERANCE" env-default:"4" env-description:"Maximum number of slots behind head considered in-sync"` + WithWeightedAttestationData bool `yaml:"WithWeightedAttestationData" env:"WITH_WEIGHTED_ATTESTATION_DATA" env-default:"false" env-description:"Enable attestation data scoring across multiple beacon nodes"` + + CommonTimeout time.Duration // Optional. + LongTimeout time.Duration // Optional. +} diff --git a/protocol/v2/blockchain/beacon/mock_client.go b/protocol/v2/blockchain/beacon/mock_client.go index f8375b0cd7..5742f52083 100644 --- a/protocol/v2/blockchain/beacon/mock_client.go +++ b/protocol/v2/blockchain/beacon/mock_client.go @@ -20,7 +20,6 @@ import ( bellatrix "github.com/attestantio/go-eth2-client/spec/bellatrix" phase0 "github.com/attestantio/go-eth2-client/spec/phase0" ssz "github.com/ferranbt/fastssz" - types "github.com/ssvlabs/ssv-spec/types" gomock "go.uber.org/mock/gomock" ) @@ -451,44 +450,6 @@ func (mr *MockDomainCallsMockRecorder) DomainData(ctx, epoch, domain any) *gomoc return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DomainData", reflect.TypeOf((*MockDomainCalls)(nil).DomainData), ctx, epoch, domain) } -// MockVersionCalls is a mock of VersionCalls interface. -type MockVersionCalls struct { - ctrl *gomock.Controller - recorder *MockVersionCallsMockRecorder - isgomock struct{} -} - -// MockVersionCallsMockRecorder is the mock recorder for MockVersionCalls. -type MockVersionCallsMockRecorder struct { - mock *MockVersionCalls -} - -// NewMockVersionCalls creates a new mock instance. -func NewMockVersionCalls(ctrl *gomock.Controller) *MockVersionCalls { - mock := &MockVersionCalls{ctrl: ctrl} - mock.recorder = &MockVersionCallsMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockVersionCalls) EXPECT() *MockVersionCallsMockRecorder { - return m.recorder -} - -// DataVersion mocks base method. -func (m *MockVersionCalls) DataVersion(epoch phase0.Epoch) spec.DataVersion { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DataVersion", epoch) - ret0, _ := ret[0].(spec.DataVersion) - return ret0 -} - -// DataVersion indicates an expected call of DataVersion. -func (mr *MockVersionCallsMockRecorder) DataVersion(epoch any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DataVersion", reflect.TypeOf((*MockVersionCalls)(nil).DataVersion), epoch) -} - // MockbeaconDuties is a mock of beaconDuties interface. type MockbeaconDuties struct { ctrl *gomock.Controller @@ -855,20 +816,6 @@ func (mr *MockBeaconNodeMockRecorder) GetBeaconBlock(ctx, slot, graffiti, randao return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBeaconBlock", reflect.TypeOf((*MockBeaconNode)(nil).GetBeaconBlock), ctx, slot, graffiti, randao) } -// GetBeaconNetwork mocks base method. -func (m *MockBeaconNode) GetBeaconNetwork() types.BeaconNetwork { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetBeaconNetwork") - ret0, _ := ret[0].(types.BeaconNetwork) - return ret0 -} - -// GetBeaconNetwork indicates an expected call of GetBeaconNetwork. -func (mr *MockBeaconNodeMockRecorder) GetBeaconNetwork() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBeaconNetwork", reflect.TypeOf((*MockBeaconNode)(nil).GetBeaconNetwork)) -} - // GetSyncCommitteeContribution mocks base method. func (m *MockBeaconNode) GetSyncCommitteeContribution(ctx context.Context, slot phase0.Slot, selectionProofs []phase0.BLSSignature, subnetIDs []uint64) (ssz.Marshaler, spec.DataVersion, error) { m.ctrl.T.Helper() diff --git a/protocol/v2/blockchain/beacon/mocks/network.go b/protocol/v2/blockchain/beacon/mocks/network.go deleted file mode 100644 index 7fdaea7afb..0000000000 --- a/protocol/v2/blockchain/beacon/mocks/network.go +++ /dev/null @@ -1,337 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./network.go -// -// Generated by this command: -// -// mockgen -package=mocks -destination=./mocks/network.go -source=./network.go -// - -// Package mocks is a generated GoMock package. -package mocks - -import ( - reflect "reflect" - time "time" - - phase0 "github.com/attestantio/go-eth2-client/spec/phase0" - types "github.com/ssvlabs/ssv-spec/types" - beacon "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" - gomock "go.uber.org/mock/gomock" -) - -// MockBeaconNetwork is a mock of BeaconNetwork interface. -type MockBeaconNetwork struct { - ctrl *gomock.Controller - recorder *MockBeaconNetworkMockRecorder -} - -// MockBeaconNetworkMockRecorder is the mock recorder for MockBeaconNetwork. -type MockBeaconNetworkMockRecorder struct { - mock *MockBeaconNetwork -} - -// NewMockBeaconNetwork creates a new mock instance. -func NewMockBeaconNetwork(ctrl *gomock.Controller) *MockBeaconNetwork { - mock := &MockBeaconNetwork{ctrl: ctrl} - mock.recorder = &MockBeaconNetworkMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockBeaconNetwork) EXPECT() *MockBeaconNetworkMockRecorder { - return m.recorder -} - -// EpochStartTime mocks base method. -func (m *MockBeaconNetwork) EpochStartTime(epoch phase0.Epoch) time.Time { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EpochStartTime", epoch) - ret0, _ := ret[0].(time.Time) - return ret0 -} - -// EpochStartTime indicates an expected call of EpochStartTime. -func (mr *MockBeaconNetworkMockRecorder) EpochStartTime(epoch any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EpochStartTime", reflect.TypeOf((*MockBeaconNetwork)(nil).EpochStartTime), epoch) -} - -// EpochsPerSyncCommitteePeriod mocks base method. -func (m *MockBeaconNetwork) EpochsPerSyncCommitteePeriod() uint64 { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EpochsPerSyncCommitteePeriod") - ret0, _ := ret[0].(uint64) - return ret0 -} - -// EpochsPerSyncCommitteePeriod indicates an expected call of EpochsPerSyncCommitteePeriod. -func (mr *MockBeaconNetworkMockRecorder) EpochsPerSyncCommitteePeriod() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EpochsPerSyncCommitteePeriod", reflect.TypeOf((*MockBeaconNetwork)(nil).EpochsPerSyncCommitteePeriod)) -} - -// EstimatedCurrentEpoch mocks base method. -func (m *MockBeaconNetwork) EstimatedCurrentEpoch() phase0.Epoch { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EstimatedCurrentEpoch") - ret0, _ := ret[0].(phase0.Epoch) - return ret0 -} - -// EstimatedCurrentEpoch indicates an expected call of EstimatedCurrentEpoch. -func (mr *MockBeaconNetworkMockRecorder) EstimatedCurrentEpoch() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimatedCurrentEpoch", reflect.TypeOf((*MockBeaconNetwork)(nil).EstimatedCurrentEpoch)) -} - -// EstimatedCurrentSlot mocks base method. -func (m *MockBeaconNetwork) EstimatedCurrentSlot() phase0.Slot { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EstimatedCurrentSlot") - ret0, _ := ret[0].(phase0.Slot) - return ret0 -} - -// EstimatedCurrentSlot indicates an expected call of EstimatedCurrentSlot. -func (mr *MockBeaconNetworkMockRecorder) EstimatedCurrentSlot() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimatedCurrentSlot", reflect.TypeOf((*MockBeaconNetwork)(nil).EstimatedCurrentSlot)) -} - -// EstimatedEpochAtSlot mocks base method. -func (m *MockBeaconNetwork) EstimatedEpochAtSlot(slot phase0.Slot) phase0.Epoch { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EstimatedEpochAtSlot", slot) - ret0, _ := ret[0].(phase0.Epoch) - return ret0 -} - -// EstimatedEpochAtSlot indicates an expected call of EstimatedEpochAtSlot. -func (mr *MockBeaconNetworkMockRecorder) EstimatedEpochAtSlot(slot any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimatedEpochAtSlot", reflect.TypeOf((*MockBeaconNetwork)(nil).EstimatedEpochAtSlot), slot) -} - -// EstimatedSlotAtTime mocks base method. -func (m *MockBeaconNetwork) EstimatedSlotAtTime(time int64) phase0.Slot { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EstimatedSlotAtTime", time) - ret0, _ := ret[0].(phase0.Slot) - return ret0 -} - -// EstimatedSlotAtTime indicates an expected call of EstimatedSlotAtTime. -func (mr *MockBeaconNetworkMockRecorder) EstimatedSlotAtTime(time any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimatedSlotAtTime", reflect.TypeOf((*MockBeaconNetwork)(nil).EstimatedSlotAtTime), time) -} - -// EstimatedSyncCommitteePeriodAtEpoch mocks base method. -func (m *MockBeaconNetwork) EstimatedSyncCommitteePeriodAtEpoch(epoch phase0.Epoch) uint64 { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EstimatedSyncCommitteePeriodAtEpoch", epoch) - ret0, _ := ret[0].(uint64) - return ret0 -} - -// EstimatedSyncCommitteePeriodAtEpoch indicates an expected call of EstimatedSyncCommitteePeriodAtEpoch. -func (mr *MockBeaconNetworkMockRecorder) EstimatedSyncCommitteePeriodAtEpoch(epoch any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimatedSyncCommitteePeriodAtEpoch", reflect.TypeOf((*MockBeaconNetwork)(nil).EstimatedSyncCommitteePeriodAtEpoch), epoch) -} - -// EstimatedTimeAtSlot mocks base method. -func (m *MockBeaconNetwork) EstimatedTimeAtSlot(slot phase0.Slot) int64 { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EstimatedTimeAtSlot", slot) - ret0, _ := ret[0].(int64) - return ret0 -} - -// EstimatedTimeAtSlot indicates an expected call of EstimatedTimeAtSlot. -func (mr *MockBeaconNetworkMockRecorder) EstimatedTimeAtSlot(slot any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EstimatedTimeAtSlot", reflect.TypeOf((*MockBeaconNetwork)(nil).EstimatedTimeAtSlot), slot) -} - -// FirstEpochOfSyncPeriod mocks base method. -func (m *MockBeaconNetwork) FirstEpochOfSyncPeriod(period uint64) phase0.Epoch { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FirstEpochOfSyncPeriod", period) - ret0, _ := ret[0].(phase0.Epoch) - return ret0 -} - -// FirstEpochOfSyncPeriod indicates an expected call of FirstEpochOfSyncPeriod. -func (mr *MockBeaconNetworkMockRecorder) FirstEpochOfSyncPeriod(period any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FirstEpochOfSyncPeriod", reflect.TypeOf((*MockBeaconNetwork)(nil).FirstEpochOfSyncPeriod), period) -} - -// FirstSlotAtEpoch mocks base method. -func (m *MockBeaconNetwork) FirstSlotAtEpoch(epoch phase0.Epoch) phase0.Slot { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FirstSlotAtEpoch", epoch) - ret0, _ := ret[0].(phase0.Slot) - return ret0 -} - -// FirstSlotAtEpoch indicates an expected call of FirstSlotAtEpoch. -func (mr *MockBeaconNetworkMockRecorder) FirstSlotAtEpoch(epoch any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FirstSlotAtEpoch", reflect.TypeOf((*MockBeaconNetwork)(nil).FirstSlotAtEpoch), epoch) -} - -// ForkVersion mocks base method. -func (m *MockBeaconNetwork) ForkVersion() [4]byte { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ForkVersion") - ret0, _ := ret[0].([4]byte) - return ret0 -} - -// ForkVersion indicates an expected call of ForkVersion. -func (mr *MockBeaconNetworkMockRecorder) ForkVersion() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ForkVersion", reflect.TypeOf((*MockBeaconNetwork)(nil).ForkVersion)) -} - -// GetBeaconNetwork mocks base method. -func (m *MockBeaconNetwork) GetBeaconNetwork() types.BeaconNetwork { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetBeaconNetwork") - ret0, _ := ret[0].(types.BeaconNetwork) - return ret0 -} - -// GetBeaconNetwork indicates an expected call of GetBeaconNetwork. -func (mr *MockBeaconNetworkMockRecorder) GetBeaconNetwork() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBeaconNetwork", reflect.TypeOf((*MockBeaconNetwork)(nil).GetBeaconNetwork)) -} - -// GetEpochFirstSlot mocks base method. -func (m *MockBeaconNetwork) GetEpochFirstSlot(epoch phase0.Epoch) phase0.Slot { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetEpochFirstSlot", epoch) - ret0, _ := ret[0].(phase0.Slot) - return ret0 -} - -// GetEpochFirstSlot indicates an expected call of GetEpochFirstSlot. -func (mr *MockBeaconNetworkMockRecorder) GetEpochFirstSlot(epoch any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEpochFirstSlot", reflect.TypeOf((*MockBeaconNetwork)(nil).GetEpochFirstSlot), epoch) -} - -// GetNetwork mocks base method. -func (m *MockBeaconNetwork) GetNetwork() beacon.Network { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetNetwork") - ret0, _ := ret[0].(beacon.Network) - return ret0 -} - -// GetNetwork indicates an expected call of GetNetwork. -func (mr *MockBeaconNetworkMockRecorder) GetNetwork() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetwork", reflect.TypeOf((*MockBeaconNetwork)(nil).GetNetwork)) -} - -// GetSlotEndTime mocks base method. -func (m *MockBeaconNetwork) GetSlotEndTime(slot phase0.Slot) time.Time { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSlotEndTime", slot) - ret0, _ := ret[0].(time.Time) - return ret0 -} - -// GetSlotEndTime indicates an expected call of GetSlotEndTime. -func (mr *MockBeaconNetworkMockRecorder) GetSlotEndTime(slot any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSlotEndTime", reflect.TypeOf((*MockBeaconNetwork)(nil).GetSlotEndTime), slot) -} - -// GetSlotStartTime mocks base method. -func (m *MockBeaconNetwork) GetSlotStartTime(slot phase0.Slot) time.Time { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSlotStartTime", slot) - ret0, _ := ret[0].(time.Time) - return ret0 -} - -// GetSlotStartTime indicates an expected call of GetSlotStartTime. -func (mr *MockBeaconNetworkMockRecorder) GetSlotStartTime(slot any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSlotStartTime", reflect.TypeOf((*MockBeaconNetwork)(nil).GetSlotStartTime), slot) -} - -// IsFirstSlotOfEpoch mocks base method. -func (m *MockBeaconNetwork) IsFirstSlotOfEpoch(slot phase0.Slot) bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsFirstSlotOfEpoch", slot) - ret0, _ := ret[0].(bool) - return ret0 -} - -// IsFirstSlotOfEpoch indicates an expected call of IsFirstSlotOfEpoch. -func (mr *MockBeaconNetworkMockRecorder) IsFirstSlotOfEpoch(slot any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsFirstSlotOfEpoch", reflect.TypeOf((*MockBeaconNetwork)(nil).IsFirstSlotOfEpoch), slot) -} - -// LastSlotOfSyncPeriod mocks base method. -func (m *MockBeaconNetwork) LastSlotOfSyncPeriod(period uint64) phase0.Slot { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LastSlotOfSyncPeriod", period) - ret0, _ := ret[0].(phase0.Slot) - return ret0 -} - -// LastSlotOfSyncPeriod indicates an expected call of LastSlotOfSyncPeriod. -func (mr *MockBeaconNetworkMockRecorder) LastSlotOfSyncPeriod(period any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LastSlotOfSyncPeriod", reflect.TypeOf((*MockBeaconNetwork)(nil).LastSlotOfSyncPeriod), period) -} - -// MinGenesisTime mocks base method. -func (m *MockBeaconNetwork) MinGenesisTime() int64 { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MinGenesisTime") - ret0, _ := ret[0].(int64) - return ret0 -} - -// MinGenesisTime indicates an expected call of MinGenesisTime. -func (mr *MockBeaconNetworkMockRecorder) MinGenesisTime() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MinGenesisTime", reflect.TypeOf((*MockBeaconNetwork)(nil).MinGenesisTime)) -} - -// SlotDurationSec mocks base method. -func (m *MockBeaconNetwork) SlotDurationSec() time.Duration { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SlotDurationSec") - ret0, _ := ret[0].(time.Duration) - return ret0 -} - -// SlotDurationSec indicates an expected call of SlotDurationSec. -func (mr *MockBeaconNetworkMockRecorder) SlotDurationSec() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SlotDurationSec", reflect.TypeOf((*MockBeaconNetwork)(nil).SlotDurationSec)) -} - -// SlotsPerEpoch mocks base method. -func (m *MockBeaconNetwork) SlotsPerEpoch() uint64 { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SlotsPerEpoch") - ret0, _ := ret[0].(uint64) - return ret0 -} - -// SlotsPerEpoch indicates an expected call of SlotsPerEpoch. -func (mr *MockBeaconNetworkMockRecorder) SlotsPerEpoch() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SlotsPerEpoch", reflect.TypeOf((*MockBeaconNetwork)(nil).SlotsPerEpoch)) -} diff --git a/protocol/v2/blockchain/beacon/network.go b/protocol/v2/blockchain/beacon/network.go deleted file mode 100644 index 25d049ca96..0000000000 --- a/protocol/v2/blockchain/beacon/network.go +++ /dev/null @@ -1,163 +0,0 @@ -package beacon - -import ( - "time" - - "github.com/attestantio/go-eth2-client/spec/phase0" - spectypes "github.com/ssvlabs/ssv-spec/types" -) - -//go:generate go tool -modfile=../../../../tool.mod mockgen -package=mocks -destination=./mocks/network.go -source=./network.go - -// Network is a beacon chain network. -type Network struct { - spectypes.BeaconNetwork - LocalTestNet bool -} - -type BeaconNetwork interface { - ForkVersion() [4]byte - MinGenesisTime() int64 - SlotDurationSec() time.Duration - SlotsPerEpoch() uint64 - EstimatedCurrentSlot() phase0.Slot - EstimatedSlotAtTime(time int64) phase0.Slot - EstimatedTimeAtSlot(slot phase0.Slot) int64 - EstimatedCurrentEpoch() phase0.Epoch - EstimatedEpochAtSlot(slot phase0.Slot) phase0.Epoch - FirstSlotAtEpoch(epoch phase0.Epoch) phase0.Slot - EpochStartTime(epoch phase0.Epoch) time.Time - - GetSlotStartTime(slot phase0.Slot) time.Time - GetSlotEndTime(slot phase0.Slot) time.Time - IsFirstSlotOfEpoch(slot phase0.Slot) bool - GetEpochFirstSlot(epoch phase0.Epoch) phase0.Slot - - EpochsPerSyncCommitteePeriod() uint64 - EstimatedSyncCommitteePeriodAtEpoch(epoch phase0.Epoch) uint64 - FirstEpochOfSyncPeriod(period uint64) phase0.Epoch - LastSlotOfSyncPeriod(period uint64) phase0.Slot - - GetNetwork() Network - GetBeaconNetwork() spectypes.BeaconNetwork -} - -// NewNetwork creates a new beacon chain network. -func NewNetwork(network spectypes.BeaconNetwork) Network { - return Network{ - BeaconNetwork: network, - LocalTestNet: false, - } -} - -// NewLocalTestNetwork creates a new local beacon chain network. -func NewLocalTestNetwork(network spectypes.BeaconNetwork) Network { - return Network{ - BeaconNetwork: network, - LocalTestNet: true, - } -} - -// MinGenesisTime returns min genesis time value -func (n Network) MinGenesisTime() int64 { - if n.LocalTestNet { - return 1689072978 - } - return int64(n.BeaconNetwork.MinGenesisTime()) // #nosec G115 -} - -// GetNetwork returns the network -func (n Network) GetNetwork() Network { - return n -} - -// GetBeaconNetwork returns the beacon network the node is on -func (n Network) GetBeaconNetwork() spectypes.BeaconNetwork { - return n.BeaconNetwork -} - -// GetSlotStartTime returns the start time for the given slot -func (n Network) GetSlotStartTime(slot phase0.Slot) time.Time { - timeSinceGenesisStart := int64(uint64(slot) * uint64(n.SlotDurationSec().Seconds())) // #nosec G115 - start := time.Unix(n.MinGenesisTime()+timeSinceGenesisStart, 0) - return start -} - -// GetSlotEndTime returns the end time for the given slot -func (n Network) GetSlotEndTime(slot phase0.Slot) time.Time { - return n.GetSlotStartTime(slot + 1) -} - -// EstimatedCurrentSlot returns the estimation of the current slot -func (n Network) EstimatedCurrentSlot() phase0.Slot { - return n.EstimatedSlotAtTime(time.Now().Unix()) -} - -// EstimatedSlotAtTime estimates slot at the given time -func (n Network) EstimatedSlotAtTime(time int64) phase0.Slot { - genesis := n.MinGenesisTime() - if time < genesis { - return 0 - } - return phase0.Slot(uint64(time-genesis) / uint64(n.SlotDurationSec().Seconds())) //#nosec G115 -} - -// EstimatedCurrentEpoch estimates the current epoch -// https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#compute_start_slot_at_epoch -func (n Network) EstimatedCurrentEpoch() phase0.Epoch { - return n.EstimatedEpochAtSlot(n.EstimatedCurrentSlot()) -} - -// EstimatedEpochAtSlot estimates epoch at the given slot -func (n Network) EstimatedEpochAtSlot(slot phase0.Slot) phase0.Epoch { - return phase0.Epoch(slot / phase0.Slot(n.SlotsPerEpoch())) -} - -// IsFirstSlotOfEpoch estimates epoch at the given slot -func (n Network) IsFirstSlotOfEpoch(slot phase0.Slot) bool { - return uint64(slot)%n.SlotsPerEpoch() == 0 -} - -// GetEpochFirstSlot returns the beacon node first slot in epoch -func (n Network) GetEpochFirstSlot(epoch phase0.Epoch) phase0.Slot { - return phase0.Slot(uint64(epoch) * n.SlotsPerEpoch()) -} - -// EpochsPerSyncCommitteePeriod returns the number of epochs per sync committee period. -func (n Network) EpochsPerSyncCommitteePeriod() uint64 { - return 256 -} - -// EstimatedSyncCommitteePeriodAtEpoch estimates the current sync committee period at the given Epoch -func (n Network) EstimatedSyncCommitteePeriodAtEpoch(epoch phase0.Epoch) uint64 { - return uint64(epoch) / n.EpochsPerSyncCommitteePeriod() -} - -// FirstEpochOfSyncPeriod calculates the first epoch of the given sync period. -func (n Network) FirstEpochOfSyncPeriod(period uint64) phase0.Epoch { - return phase0.Epoch(period * n.EpochsPerSyncCommitteePeriod()) -} - -// LastSlotOfSyncPeriod calculates the first epoch of the given sync period. -func (n Network) LastSlotOfSyncPeriod(period uint64) phase0.Slot { - lastEpoch := n.FirstEpochOfSyncPeriod(period+1) - 1 - // If we are in the sync committee that ends at slot x we do not generate a message during slot x-1 - // as it will never be included, hence -1. - return n.GetEpochFirstSlot(lastEpoch+1) - 2 -} - -func (n Network) String() string { - return string(n.BeaconNetwork) -} - -func (n Network) MarshalJSON() ([]byte, error) { - return []byte(`"` + n.BeaconNetwork + `"`), nil -} - -func (n *Network) UnmarshalJSON(b []byte) error { - if len(b) < 2 { - return nil - } - *n = NewNetwork(spectypes.BeaconNetwork(b[1 : len(b)-1])) - return nil -} diff --git a/protocol/v2/blockchain/beacon/network_test.go b/protocol/v2/blockchain/beacon/network_test.go deleted file mode 100644 index 6145dd7667..0000000000 --- a/protocol/v2/blockchain/beacon/network_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package beacon - -import ( - "testing" - - "github.com/attestantio/go-eth2-client/spec/phase0" - spectypes "github.com/ssvlabs/ssv-spec/types" - "github.com/stretchr/testify/require" -) - -func TestNetwork_GetSlotEndTime(t *testing.T) { - slot := phase0.Slot(1) - - n := NewNetwork(spectypes.PraterNetwork) - slotStart := n.GetSlotStartTime(slot) - slotEnd := n.GetSlotEndTime(slot) - - require.Equal(t, n.SlotDurationSec(), slotEnd.Sub(slotStart)) -} diff --git a/protocol/v2/qbft/roundtimer/timer.go b/protocol/v2/qbft/roundtimer/timer.go index 5f8ee3347e..d3e87db242 100644 --- a/protocol/v2/qbft/roundtimer/timer.go +++ b/protocol/v2/qbft/roundtimer/timer.go @@ -10,6 +10,7 @@ import ( specqbft "github.com/ssvlabs/ssv-spec/qbft" spectypes "github.com/ssvlabs/ssv-spec/types" + "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/utils/casts" ) @@ -58,21 +59,21 @@ type RoundTimer struct { timeoutOptions TimeoutOptions // role is the role of the instance role spectypes.RunnerRole - // beaconNetwork is the beacon network - beaconNetwork BeaconNetwork + // beaconConfig is the beacon config + beaconConfig networkconfig.Beacon } // New creates a new instance of RoundTimer. -func New(pctx context.Context, beaconNetwork BeaconNetwork, role spectypes.RunnerRole, done OnRoundTimeoutF) *RoundTimer { +func New(pctx context.Context, beaconConfig networkconfig.Beacon, role spectypes.RunnerRole, done OnRoundTimeoutF) *RoundTimer { ctx, cancelCtx := context.WithCancel(pctx) return &RoundTimer{ - mtx: &sync.RWMutex{}, - ctx: ctx, - cancelCtx: cancelCtx, - timer: nil, - done: done, - role: role, - beaconNetwork: beaconNetwork, + mtx: &sync.RWMutex{}, + ctx: ctx, + cancelCtx: cancelCtx, + timer: nil, + done: done, + role: role, + beaconConfig: beaconConfig, timeoutOptions: TimeoutOptions{ quickThreshold: QuickTimeoutThreshold, quick: QuickTimeout, @@ -110,10 +111,10 @@ func (t *RoundTimer) RoundTimeout(height specqbft.Height, round specqbft.Round) switch t.role { case spectypes.RoleCommittee: // third of the slot time - baseDuration = t.beaconNetwork.SlotDurationSec() / 3 + baseDuration = t.beaconConfig.GetSlotDuration() / 3 case spectypes.RoleAggregator, spectypes.RoleSyncCommitteeContribution: // two-third of the slot time - baseDuration = t.beaconNetwork.SlotDurationSec() / 3 * 2 + baseDuration = t.beaconConfig.GetSlotDuration() / 3 * 2 default: if round <= t.timeoutOptions.quickThreshold { return t.timeoutOptions.quick @@ -135,7 +136,7 @@ func (t *RoundTimer) RoundTimeout(height specqbft.Height, round specqbft.Round) timeoutDuration := baseDuration + additionalTimeout // Get the start time of the duty - dutyStartTime := t.beaconNetwork.GetSlotStartTime(phase0.Slot(height)) + dutyStartTime := t.beaconConfig.GetSlotStartTime(phase0.Slot(height)) // Calculate the time until the duty should start plus the timeout duration return time.Until(dutyStartTime.Add(timeoutDuration)) diff --git a/protocol/v2/qbft/roundtimer/timer_test.go b/protocol/v2/qbft/roundtimer/timer_test.go index a23d0fae2c..efed977d4e 100644 --- a/protocol/v2/qbft/roundtimer/timer_test.go +++ b/protocol/v2/qbft/roundtimer/timer_test.go @@ -7,13 +7,11 @@ import ( "testing" "time" - "github.com/attestantio/go-eth2-client/spec/phase0" specqbft "github.com/ssvlabs/ssv-spec/qbft" spectypes "github.com/ssvlabs/ssv-spec/types" "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" - "github.com/ssvlabs/ssv/protocol/v2/qbft/roundtimer/mocks" + "github.com/ssvlabs/ssv/networkconfig" ) func TestTimeoutForRound(t *testing.T) { @@ -49,27 +47,22 @@ func TestTimeoutForRound(t *testing.T) { } } -func setupMockBeaconNetwork(t *testing.T) *mocks.MockBeaconNetwork { - ctrl := gomock.NewController(t) - mockBeaconNetwork := mocks.NewMockBeaconNetwork(ctrl) - - mockBeaconNetwork.EXPECT().SlotDurationSec().Return(120 * time.Millisecond).AnyTimes() - mockBeaconNetwork.EXPECT().GetSlotStartTime(gomock.Any()).DoAndReturn( - func(slot phase0.Slot) time.Time { - return time.Now() - }, - ).AnyTimes() - return mockBeaconNetwork +func setupMockBeaconConfig() networkconfig.BeaconConfig { + config := networkconfig.TestNetwork.BeaconConfig + config.SlotDuration = 120 * time.Millisecond + config.GenesisTime = time.Now() + + return config } func setupTimer( t *testing.T, - mockBeaconNetwork *mocks.MockBeaconNetwork, + beaconConfig networkconfig.BeaconConfig, onTimeout OnRoundTimeoutF, role spectypes.RunnerRole, round specqbft.Round, ) *RoundTimer { - timer := New(t.Context(), mockBeaconNetwork, role, onTimeout) + timer := New(t.Context(), beaconConfig, role, onTimeout) timer.timeoutOptions = TimeoutOptions{ quickThreshold: round, quick: 100 * time.Millisecond, @@ -80,7 +73,7 @@ func setupTimer( } func testTimeoutForRound(t *testing.T, role spectypes.RunnerRole, threshold specqbft.Round) { - mockBeaconNetwork := setupMockBeaconNetwork(t) + mockBeaconNetwork := setupMockBeaconConfig() count := int32(0) onTimeout := func(round specqbft.Round) { @@ -96,7 +89,7 @@ func testTimeoutForRound(t *testing.T, role spectypes.RunnerRole, threshold spec } func testTimeoutForRoundElapsed(t *testing.T, role spectypes.RunnerRole, threshold specqbft.Round) { - mockBeaconNetwork := setupMockBeaconNetwork(t) + mockBeaconNetwork := setupMockBeaconConfig() count := int32(0) onTimeout := func(round specqbft.Round) { @@ -114,8 +107,7 @@ func testTimeoutForRoundElapsed(t *testing.T, role spectypes.RunnerRole, thresho } func testTimeoutForRoundMulti(t *testing.T, role spectypes.RunnerRole, threshold specqbft.Round) { - ctrl := gomock.NewController(t) - mockBeaconNetwork := mocks.NewMockBeaconNetwork(ctrl) + mockBeaconConfig := setupMockBeaconConfig() var count int32 var timestamps = make([]int64, 4) @@ -128,19 +120,11 @@ func testTimeoutForRoundMulti(t *testing.T, role spectypes.RunnerRole, threshold mu.Unlock() } - timeNow := time.Now() - mockBeaconNetwork.EXPECT().SlotDurationSec().Return(100 * time.Millisecond).AnyTimes() - mockBeaconNetwork.EXPECT().GetSlotStartTime(gomock.Any()).DoAndReturn( - func(slot phase0.Slot) time.Time { - return timeNow - }, - ).AnyTimes() - var wg sync.WaitGroup for i := 0; i < 4; i++ { wg.Add(1) go func(index int) { - timer := New(t.Context(), mockBeaconNetwork, role, func(round specqbft.Round) { onTimeout(index) }) + timer := New(t.Context(), mockBeaconConfig, role, func(round specqbft.Round) { onTimeout(index) }) timer.timeoutOptions = TimeoutOptions{ quickThreshold: threshold, quick: 100 * time.Millisecond, @@ -153,7 +137,7 @@ func testTimeoutForRoundMulti(t *testing.T, role spectypes.RunnerRole, threshold wg.Wait() // Wait for all go-routines to finish - timer := New(t.Context(), mockBeaconNetwork, role, nil) + timer := New(t.Context(), mockBeaconConfig, role, nil) timer.timeoutOptions = TimeoutOptions{ quickThreshold: specqbft.Round(1), quick: 100 * time.Millisecond, diff --git a/protocol/v2/ssv/runner/aggregator.go b/protocol/v2/ssv/runner/aggregator.go index e6ca75d0a5..a04ccfd5b1 100644 --- a/protocol/v2/ssv/runner/aggregator.go +++ b/protocol/v2/ssv/runner/aggregator.go @@ -16,6 +16,7 @@ import ( "go.uber.org/zap" "github.com/ssvlabs/ssv/logging/fields" + "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" "github.com/ssvlabs/ssv/protocol/v2/qbft/controller" ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types" @@ -36,8 +37,7 @@ type AggregatorRunner struct { var _ Runner = &AggregatorRunner{} func NewAggregatorRunner( - domainType spectypes.DomainType, - beaconNetwork spectypes.BeaconNetwork, + networkConfig networkconfig.Network, share map[phase0.ValidatorIndex]*spectypes.Share, qbftController *controller.Controller, beacon beacon.BeaconNode, @@ -54,8 +54,7 @@ func NewAggregatorRunner( return &AggregatorRunner{ BaseRunner: &BaseRunner{ RunnerRoleType: spectypes.RoleAggregator, - DomainType: domainType, - BeaconNetwork: beaconNetwork, + NetworkConfig: networkConfig, Share: share, QBFTController: qbftController, highestDecidedSlot: highestDecidedSlot, @@ -173,7 +172,7 @@ func (r *AggregatorRunner) ProcessConsensus(ctx context.Context, logger *zap.Log Messages: []*spectypes.PartialSignatureMessage{msg}, } - msgID := spectypes.NewMsgID(r.BaseRunner.DomainType, r.GetShare().ValidatorPubKey[:], r.BaseRunner.RunnerRoleType) + msgID := spectypes.NewMsgID(r.BaseRunner.NetworkConfig.GetDomainType(), r.GetShare().ValidatorPubKey[:], r.BaseRunner.RunnerRoleType) encodedMsg, err := postConsensusMsg.Encode() if err != nil { @@ -267,7 +266,7 @@ func (r *AggregatorRunner) ProcessPostConsensus(ctx context.Context, logger *zap recordDutyDuration(ctx, r.measurements.DutyDurationTime(), spectypes.BNRoleAggregator, r.GetState().RunningInstance.State.Round) recordSuccessfulSubmission(ctx, successfullySubmittedAggregates, - r.GetBeaconNode().GetBeaconNetwork().EstimatedEpochAtSlot(r.GetState().StartingDuty.DutySlot()), + r.BaseRunner.NetworkConfig.EstimatedEpochAtSlot(r.GetState().StartingDuty.DutySlot()), spectypes.BNRoleAggregator) return nil @@ -320,7 +319,7 @@ func (r *AggregatorRunner) executeDuty(ctx context.Context, logger *zap.Logger, Messages: []*spectypes.PartialSignatureMessage{msg}, } - msgID := spectypes.NewMsgID(r.BaseRunner.DomainType, r.GetShare().ValidatorPubKey[:], r.BaseRunner.RunnerRoleType) + msgID := spectypes.NewMsgID(r.BaseRunner.NetworkConfig.GetDomainType(), r.GetShare().ValidatorPubKey[:], r.BaseRunner.RunnerRoleType) encodedMsg, err := msgs.Encode() if err != nil { return err diff --git a/protocol/v2/ssv/runner/committee.go b/protocol/v2/ssv/runner/committee.go index b00cd6cb7a..d87a90dbf8 100644 --- a/protocol/v2/ssv/runner/committee.go +++ b/protocol/v2/ssv/runner/committee.go @@ -19,12 +19,13 @@ import ( spectypes "github.com/ssvlabs/ssv-spec/types" "go.uber.org/zap" + "github.com/ssvlabs/ssv/ssvsigner/ekm" + "github.com/ssvlabs/ssv/logging/fields" "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" "github.com/ssvlabs/ssv/protocol/v2/qbft/controller" ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types" - "github.com/ssvlabs/ssv/ssvsigner/ekm" ) var ( @@ -51,7 +52,7 @@ type CommitteeRunner struct { } func NewCommitteeRunner( - networkConfig networkconfig.NetworkConfig, + networkConfig networkconfig.Network, share map[phase0.ValidatorIndex]*spectypes.Share, qbftController *controller.Controller, beacon beacon.BeaconNode, @@ -68,8 +69,7 @@ func NewCommitteeRunner( return &CommitteeRunner{ BaseRunner: &BaseRunner{ RunnerRoleType: spectypes.RoleCommittee, - DomainType: networkConfig.DomainType, - BeaconNetwork: networkConfig.Beacon.GetBeaconNetwork(), + NetworkConfig: networkConfig, Share: share, QBFTController: qbftController, }, @@ -231,8 +231,8 @@ func (cr *CommitteeRunner) ProcessConsensus(ctx context.Context, logger *zap.Log totalSyncCommitteeDuties := 0 blockedAttesterDuties := 0 - epoch := cr.beacon.GetBeaconNetwork().EstimatedEpochAtSlot(duty.DutySlot()) - version := cr.beacon.DataVersion(epoch) + epoch := cr.BaseRunner.NetworkConfig.EstimatedEpochAtSlot(duty.DutySlot()) + version, _ := cr.BaseRunner.NetworkConfig.ForkAtEpoch(epoch) for _, validatorDuty := range duty.(*spectypes.CommitteeDuty).ValidatorDuties { if err := cr.DutyGuard.ValidDuty(validatorDuty.Type, spectypes.ValidatorPK(validatorDuty.PubKey), validatorDuty.DutySlot()); err != nil { @@ -321,7 +321,7 @@ func (cr *CommitteeRunner) ProcessConsensus(ctx context.Context, logger *zap.Log ssvMsg := &spectypes.SSVMessage{ MsgType: spectypes.SSVPartialSignatureMsgType, MsgID: spectypes.NewMsgID( - cr.BaseRunner.DomainType, + cr.BaseRunner.NetworkConfig.GetDomainType(), cr.GetBaseRunner().QBFTController.CommitteeMember.CommitteeID[:], cr.BaseRunner.RunnerRoleType, ), @@ -518,7 +518,7 @@ func (cr *CommitteeRunner) ProcessPostConsensus(ctx context.Context, logger *zap if attestationsCount <= math.MaxUint32 { recordSuccessfulSubmission(ctx, uint32(attestationsCount), - cr.GetBeaconNode().GetBeaconNetwork().EstimatedEpochAtSlot(cr.GetBaseRunner().State.StartingDuty.DutySlot()), + cr.BaseRunner.NetworkConfig.EstimatedEpochAtSlot(cr.GetBaseRunner().State.StartingDuty.DutySlot()), spectypes.BNRoleAttester) } @@ -528,7 +528,7 @@ func (cr *CommitteeRunner) ProcessPostConsensus(ctx context.Context, logger *zap // TODO return error? } logger.Info("✅ successfully submitted attestations", - fields.Epoch(cr.GetBeaconNode().GetBeaconNetwork().EstimatedEpochAtSlot(cr.GetBaseRunner().State.StartingDuty.DutySlot())), + fields.Epoch(cr.BaseRunner.NetworkConfig.EstimatedEpochAtSlot(cr.GetBaseRunner().State.StartingDuty.DutySlot())), fields.Height(cr.BaseRunner.QBFTController.Height), fields.Round(cr.BaseRunner.State.RunningInstance.State.Round), fields.BlockRoot(attData.BeaconBlockRoot), @@ -563,7 +563,7 @@ func (cr *CommitteeRunner) ProcessPostConsensus(ctx context.Context, logger *zap if syncMsgsCount <= math.MaxUint32 { recordSuccessfulSubmission(ctx, uint32(syncMsgsCount), - cr.GetBeaconNode().GetBeaconNetwork().EstimatedEpochAtSlot(cr.GetBaseRunner().State.StartingDuty.DutySlot()), + cr.BaseRunner.NetworkConfig.EstimatedEpochAtSlot(cr.GetBaseRunner().State.StartingDuty.DutySlot()), spectypes.BNRoleSyncCommittee) } @@ -687,9 +687,9 @@ func (cr *CommitteeRunner) expectedPostConsensusRootsAndBeaconObjects(ctx contex } slot := duty.DutySlot() - epoch := cr.GetBaseRunner().BeaconNetwork.EstimatedEpochAtSlot(slot) + epoch := cr.GetBaseRunner().NetworkConfig.EstimatedEpochAtSlot(slot) - dataVersion := cr.beacon.DataVersion(epoch) + dataVersion, _ := cr.GetBaseRunner().NetworkConfig.ForkAtEpoch(epoch) for _, validatorDuty := range duty.(*spectypes.CommitteeDuty).ValidatorDuties { if validatorDuty == nil { @@ -701,7 +701,7 @@ func (cr *CommitteeRunner) expectedPostConsensusRootsAndBeaconObjects(ctx contex } logger := logger.With(fields.Validator(validatorDuty.PubKey[:])) slot := validatorDuty.DutySlot() - epoch := cr.GetBaseRunner().BeaconNetwork.EstimatedEpochAtSlot(slot) + epoch := cr.GetBaseRunner().NetworkConfig.EstimatedEpochAtSlot(slot) switch validatorDuty.Type { case spectypes.BNRoleAttester: // Attestation object diff --git a/protocol/v2/ssv/runner/proposer.go b/protocol/v2/ssv/runner/proposer.go index 4740a17425..4e6f629e92 100644 --- a/protocol/v2/ssv/runner/proposer.go +++ b/protocol/v2/ssv/runner/proposer.go @@ -23,6 +23,7 @@ import ( "go.uber.org/zap" "github.com/ssvlabs/ssv/logging/fields" + "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" "github.com/ssvlabs/ssv/protocol/v2/qbft/controller" ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types" @@ -43,8 +44,7 @@ type ProposerRunner struct { } func NewProposerRunner( - domainType spectypes.DomainType, - beaconNetwork spectypes.BeaconNetwork, + networkConfig networkconfig.Network, share map[phase0.ValidatorIndex]*spectypes.Share, qbftController *controller.Controller, beacon beacon.BeaconNode, @@ -63,8 +63,7 @@ func NewProposerRunner( return &ProposerRunner{ BaseRunner: &BaseRunner{ RunnerRoleType: spectypes.RoleProposer, - DomainType: domainType, - BeaconNetwork: beaconNetwork, + NetworkConfig: networkConfig, Share: share, QBFTController: qbftController, highestDecidedSlot: highestDecidedSlot, @@ -209,7 +208,7 @@ func (r *ProposerRunner) ProcessConsensus(ctx context.Context, logger *zap.Logge Messages: []*spectypes.PartialSignatureMessage{msg}, } - msgID := spectypes.NewMsgID(r.BaseRunner.DomainType, r.GetShare().ValidatorPubKey[:], r.BaseRunner.RunnerRoleType) + msgID := spectypes.NewMsgID(r.BaseRunner.NetworkConfig.GetDomainType(), r.GetShare().ValidatorPubKey[:], r.BaseRunner.RunnerRoleType) encodedMsg, err := postConsensusMsg.Encode() if err != nil { return err @@ -347,7 +346,7 @@ func (r *ProposerRunner) ProcessPostConsensus(ctx context.Context, logger *zap.L recordDutyDuration(ctx, r.measurements.DutyDurationTime(), spectypes.BNRoleProposer, r.GetState().RunningInstance.State.Round) recordSuccessfulSubmission(ctx, uint32(successfullySubmittedProposals), - r.GetBeaconNode().GetBeaconNetwork().EstimatedEpochAtSlot(r.GetState().StartingDuty.DutySlot()), + r.BaseRunner.NetworkConfig.EstimatedEpochAtSlot(r.GetState().StartingDuty.DutySlot()), spectypes.BNRoleProposer) return nil @@ -366,7 +365,7 @@ func (r *ProposerRunner) decidedBlindedBlock() bool { } func (r *ProposerRunner) expectedPreConsensusRootsAndDomain() ([]ssz.HashRoot, phase0.DomainType, error) { - epoch := r.BaseRunner.BeaconNetwork.EstimatedEpochAtSlot(r.GetState().StartingDuty.DutySlot()) + epoch := r.BaseRunner.NetworkConfig.EstimatedEpochAtSlot(r.GetState().StartingDuty.DutySlot()) return []ssz.HashRoot{spectypes.SSZUint64(epoch)}, spectypes.DomainRandao, nil } @@ -410,7 +409,7 @@ func (r *ProposerRunner) executeDuty(ctx context.Context, logger *zap.Logger, du } // sign partial randao - epoch := r.GetBeaconNode().GetBeaconNetwork().EstimatedEpochAtSlot(duty.DutySlot()) + epoch := r.BaseRunner.NetworkConfig.EstimatedEpochAtSlot(duty.DutySlot()) msg, err := r.BaseRunner.signBeaconObject( ctx, r, @@ -428,7 +427,7 @@ func (r *ProposerRunner) executeDuty(ctx context.Context, logger *zap.Logger, du Messages: []*spectypes.PartialSignatureMessage{msg}, } - msgID := spectypes.NewMsgID(r.BaseRunner.DomainType, r.GetShare().ValidatorPubKey[:], r.BaseRunner.RunnerRoleType) + msgID := spectypes.NewMsgID(r.BaseRunner.NetworkConfig.GetDomainType(), r.GetShare().ValidatorPubKey[:], r.BaseRunner.RunnerRoleType) encodedMsg, err := msgs.Encode() if err != nil { return err diff --git a/protocol/v2/ssv/runner/runner.go b/protocol/v2/ssv/runner/runner.go index 3e359a2ee8..632f8827cb 100644 --- a/protocol/v2/ssv/runner/runner.go +++ b/protocol/v2/ssv/runner/runner.go @@ -12,6 +12,7 @@ import ( spectypes "github.com/ssvlabs/ssv-spec/types" "go.uber.org/zap" + "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" "github.com/ssvlabs/ssv/protocol/v2/qbft/controller" "github.com/ssvlabs/ssv/protocol/v2/ssv" @@ -63,8 +64,7 @@ type BaseRunner struct { State *State Share map[phase0.ValidatorIndex]*spectypes.Share QBFTController *controller.Controller - DomainType spectypes.DomainType - BeaconNetwork spectypes.BeaconNetwork + NetworkConfig networkconfig.Network RunnerRoleType spectypes.RunnerRole ssvtypes.OperatorSigner @@ -88,7 +88,7 @@ func (b *BaseRunner) MarshalJSON() ([]byte, error) { State *State Share map[phase0.ValidatorIndex]*spectypes.Share QBFTController *controller.Controller - BeaconNetwork spectypes.BeaconNetwork + BeaconConfig networkconfig.Beacon RunnerRoleType spectypes.RunnerRole highestDecidedSlot phase0.Slot } @@ -98,7 +98,7 @@ func (b *BaseRunner) MarshalJSON() ([]byte, error) { State: b.State, Share: b.Share, QBFTController: b.QBFTController, - BeaconNetwork: b.BeaconNetwork, + BeaconConfig: b.NetworkConfig, RunnerRoleType: b.RunnerRoleType, highestDecidedSlot: b.highestDecidedSlot, } @@ -131,8 +131,7 @@ func NewBaseRunner( state *State, share map[phase0.ValidatorIndex]*spectypes.Share, controller *controller.Controller, - domainType spectypes.DomainType, - beaconNetwork spectypes.BeaconNetwork, + networkConfig networkconfig.NetworkConfig, runnerRoleType spectypes.RunnerRole, highestDecidedSlot phase0.Slot, ) *BaseRunner { @@ -140,8 +139,7 @@ func NewBaseRunner( State: state, Share: share, QBFTController: controller, - BeaconNetwork: beaconNetwork, - DomainType: domainType, + NetworkConfig: networkConfig, RunnerRoleType: runnerRoleType, highestDecidedSlot: highestDecidedSlot, } diff --git a/protocol/v2/ssv/runner/runner_signatures.go b/protocol/v2/ssv/runner/runner_signatures.go index 8c62330b94..3916f09159 100644 --- a/protocol/v2/ssv/runner/runner_signatures.go +++ b/protocol/v2/ssv/runner/runner_signatures.go @@ -22,7 +22,7 @@ func (b *BaseRunner) signBeaconObject( slot spec.Slot, signatureDomain spec.DomainType, ) (*spectypes.PartialSignatureMessage, error) { - epoch := runner.GetBaseRunner().BeaconNetwork.EstimatedEpochAtSlot(slot) + epoch := runner.GetBaseRunner().NetworkConfig.EstimatedEpochAtSlot(slot) domain, err := runner.GetBeaconNode().DomainData(ctx, epoch, signatureDomain) if err != nil { return nil, errors.Wrap(err, "could not get beacon domain") diff --git a/protocol/v2/ssv/runner/runner_validations.go b/protocol/v2/ssv/runner/runner_validations.go index 2e640e6134..b917b05def 100644 --- a/protocol/v2/ssv/runner/runner_validations.go +++ b/protocol/v2/ssv/runner/runner_validations.go @@ -121,7 +121,7 @@ func (b *BaseRunner) verifyExpectedRoot( // convert expected roots to map and mark unique roots when verified sortedExpectedRoots, err := func(expectedRootObjs []ssz.HashRoot) ([][32]byte, error) { - epoch := b.BeaconNetwork.EstimatedEpochAtSlot(b.State.StartingDuty.DutySlot()) + epoch := b.NetworkConfig.EstimatedEpochAtSlot(b.State.StartingDuty.DutySlot()) d, err := runner.GetBeaconNode().DomainData(ctx, epoch, domain) if err != nil { return nil, errors.Wrap(err, "could not get pre consensus root domain") diff --git a/protocol/v2/ssv/runner/sync_committee_aggregator.go b/protocol/v2/ssv/runner/sync_committee_aggregator.go index 5d6ad98954..559347311b 100644 --- a/protocol/v2/ssv/runner/sync_committee_aggregator.go +++ b/protocol/v2/ssv/runner/sync_committee_aggregator.go @@ -16,6 +16,7 @@ import ( "go.uber.org/zap" "github.com/ssvlabs/ssv/logging/fields" + "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" "github.com/ssvlabs/ssv/protocol/v2/qbft/controller" ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types" @@ -34,8 +35,7 @@ type SyncCommitteeAggregatorRunner struct { } func NewSyncCommitteeAggregatorRunner( - domainType spectypes.DomainType, - beaconNetwork spectypes.BeaconNetwork, + networkConfig networkconfig.Network, share map[phase0.ValidatorIndex]*spectypes.Share, qbftController *controller.Controller, beacon beacon.BeaconNode, @@ -52,8 +52,7 @@ func NewSyncCommitteeAggregatorRunner( return &SyncCommitteeAggregatorRunner{ BaseRunner: &BaseRunner{ RunnerRoleType: spectypes.RoleSyncCommitteeContribution, - DomainType: domainType, - BeaconNetwork: beaconNetwork, + NetworkConfig: networkConfig, Share: share, QBFTController: qbftController, highestDecidedSlot: highestDecidedSlot, @@ -206,7 +205,7 @@ func (r *SyncCommitteeAggregatorRunner) ProcessConsensus(ctx context.Context, lo Messages: msgs, } - msgID := spectypes.NewMsgID(r.BaseRunner.DomainType, r.GetShare().ValidatorPubKey[:], r.BaseRunner.RunnerRoleType) + msgID := spectypes.NewMsgID(r.BaseRunner.NetworkConfig.GetDomainType(), r.GetShare().ValidatorPubKey[:], r.BaseRunner.RunnerRoleType) encodedMsg, err := postConsensusMsg.Encode() if err != nil { @@ -318,7 +317,7 @@ func (r *SyncCommitteeAggregatorRunner) ProcessPostConsensus(ctx context.Context recordDutyDuration(ctx, r.measurements.DutyDurationTime(), spectypes.BNRoleSyncCommitteeContribution, r.GetState().RunningInstance.State.Round) recordSuccessfulSubmission(ctx, successfullySubmittedContributions, - r.GetBeaconNode().GetBeaconNetwork().EstimatedEpochAtSlot(r.GetState().StartingDuty.DutySlot()), + r.BaseRunner.NetworkConfig.EstimatedEpochAtSlot(r.GetState().StartingDuty.DutySlot()), spectypes.BNRoleSyncCommitteeContribution) return nil @@ -335,7 +334,7 @@ func (r *SyncCommitteeAggregatorRunner) generateContributionAndProof( SelectionProof: proof, } - epoch := r.BaseRunner.BeaconNetwork.EstimatedEpochAtSlot(r.GetState().StartingDuty.DutySlot()) + epoch := r.BaseRunner.NetworkConfig.EstimatedEpochAtSlot(r.GetState().StartingDuty.DutySlot()) dContribAndProof, err := r.GetBeaconNode().DomainData(ctx, epoch, spectypes.DomainContributionAndProof) if err != nil { return nil, phase0.Root{}, errors.Wrap(err, "could not get domain data") @@ -420,7 +419,7 @@ func (r *SyncCommitteeAggregatorRunner) executeDuty(ctx context.Context, logger msgs.Messages = append(msgs.Messages, msg) } - msgID := spectypes.NewMsgID(r.BaseRunner.DomainType, r.GetShare().ValidatorPubKey[:], r.BaseRunner.RunnerRoleType) + msgID := spectypes.NewMsgID(r.BaseRunner.NetworkConfig.GetDomainType(), r.GetShare().ValidatorPubKey[:], r.BaseRunner.RunnerRoleType) encodedMsg, err := msgs.Encode() if err != nil { return err diff --git a/protocol/v2/ssv/runner/validator_registration.go b/protocol/v2/ssv/runner/validator_registration.go index 766b1750f8..3ad08f81c2 100644 --- a/protocol/v2/ssv/runner/validator_registration.go +++ b/protocol/v2/ssv/runner/validator_registration.go @@ -17,6 +17,7 @@ import ( "go.uber.org/zap" "github.com/ssvlabs/ssv/logging/fields" + "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types" "github.com/ssvlabs/ssv/ssvsigner/ekm" @@ -35,8 +36,7 @@ type ValidatorRegistrationRunner struct { } func NewValidatorRegistrationRunner( - domainType spectypes.DomainType, - beaconNetwork spectypes.BeaconNetwork, + networkConfig networkconfig.Network, share map[phase0.ValidatorIndex]*spectypes.Share, beacon beacon.BeaconNode, network specqbft.Network, @@ -51,8 +51,7 @@ func NewValidatorRegistrationRunner( return &ValidatorRegistrationRunner{ BaseRunner: &BaseRunner{ RunnerRoleType: spectypes.RoleValidatorRegistration, - DomainType: domainType, - BeaconNetwork: beaconNetwork, + NetworkConfig: networkConfig, Share: share, }, @@ -180,7 +179,7 @@ func (r *ValidatorRegistrationRunner) executeDuty(ctx context.Context, logger *z Messages: []*spectypes.PartialSignatureMessage{msg}, } - msgID := spectypes.NewMsgID(r.BaseRunner.DomainType, r.GetShare().ValidatorPubKey[:], r.BaseRunner.RunnerRoleType) + msgID := spectypes.NewMsgID(r.BaseRunner.NetworkConfig.GetDomainType(), r.GetShare().ValidatorPubKey[:], r.BaseRunner.RunnerRoleType) encodedMsg, err := msgs.Encode() if err != nil { return err @@ -223,12 +222,12 @@ func (r *ValidatorRegistrationRunner) calculateValidatorRegistration(slot phase0 pk := phase0.BLSPubKey{} copy(pk[:], share.ValidatorPubKey[:]) - epoch := r.BaseRunner.BeaconNetwork.EstimatedEpochAtSlot(slot) + epoch := r.BaseRunner.NetworkConfig.EstimatedEpochAtSlot(slot) return &v1.ValidatorRegistration{ FeeRecipient: share.FeeRecipientAddress, GasLimit: r.gasLimit, - Timestamp: r.BaseRunner.BeaconNetwork.EpochStartTime(epoch), + Timestamp: r.BaseRunner.NetworkConfig.EpochStartTime(epoch), Pubkey: pk, }, nil } diff --git a/protocol/v2/ssv/runner/voluntary_exit.go b/protocol/v2/ssv/runner/voluntary_exit.go index 8ece527f3b..e6d6cc8421 100644 --- a/protocol/v2/ssv/runner/voluntary_exit.go +++ b/protocol/v2/ssv/runner/voluntary_exit.go @@ -14,6 +14,7 @@ import ( "go.uber.org/zap" "github.com/ssvlabs/ssv/logging/fields" + "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types" "github.com/ssvlabs/ssv/ssvsigner/ekm" @@ -33,8 +34,7 @@ type VoluntaryExitRunner struct { } func NewVoluntaryExitRunner( - domainType spectypes.DomainType, - beaconNetwork spectypes.BeaconNetwork, + networkConfig networkconfig.Network, share map[phase0.ValidatorIndex]*spectypes.Share, beacon beacon.BeaconNode, network specqbft.Network, @@ -49,8 +49,7 @@ func NewVoluntaryExitRunner( return &VoluntaryExitRunner{ BaseRunner: &BaseRunner{ RunnerRoleType: spectypes.RoleVoluntaryExit, - DomainType: domainType, - BeaconNetwork: beaconNetwork, + NetworkConfig: networkConfig, Share: share, }, @@ -163,7 +162,7 @@ func (r *VoluntaryExitRunner) executeDuty(ctx context.Context, logger *zap.Logge Messages: []*spectypes.PartialSignatureMessage{msg}, } - msgID := spectypes.NewMsgID(r.BaseRunner.DomainType, r.GetShare().ValidatorPubKey[:], r.BaseRunner.RunnerRoleType) + msgID := spectypes.NewMsgID(r.BaseRunner.NetworkConfig.GetDomainType(), r.GetShare().ValidatorPubKey[:], r.BaseRunner.RunnerRoleType) encodedMsg, err := msgs.Encode() if err != nil { return err @@ -198,7 +197,7 @@ func (r *VoluntaryExitRunner) executeDuty(ctx context.Context, logger *zap.Logge // Returns *phase0.VoluntaryExit object with current epoch and own validator index func (r *VoluntaryExitRunner) calculateVoluntaryExit() (*phase0.VoluntaryExit, error) { - epoch := r.BaseRunner.BeaconNetwork.EstimatedEpochAtSlot(r.BaseRunner.State.StartingDuty.DutySlot()) + epoch := r.BaseRunner.NetworkConfig.EstimatedEpochAtSlot(r.BaseRunner.State.StartingDuty.DutySlot()) validatorIndex := r.GetState().StartingDuty.(*spectypes.ValidatorDuty).ValidatorIndex return &phase0.VoluntaryExit{ Epoch: epoch, diff --git a/protocol/v2/ssv/spectest/committee_msg_processing_type.go b/protocol/v2/ssv/spectest/committee_msg_processing_type.go index b887263d70..538aeeb688 100644 --- a/protocol/v2/ssv/spectest/committee_msg_processing_type.go +++ b/protocol/v2/ssv/spectest/committee_msg_processing_type.go @@ -19,6 +19,7 @@ import ( "github.com/ssvlabs/ssv/integration/qbft/tests" "github.com/ssvlabs/ssv/logging" + "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/protocol/v2/ssv/queue" "github.com/ssvlabs/ssv/protocol/v2/ssv/validator" protocoltesting "github.com/ssvlabs/ssv/protocol/v2/testing" @@ -186,9 +187,9 @@ func overrideStateComparisonCommitteeSpecTest(t *testing.T, test *CommitteeSpecT committee.Shares = specCommittee.Share committee.CommitteeMember = &specCommittee.CommitteeMember - //for _, r := range committee.Runners { - // r.BaseRunner.BeaconNetwork = spectypes.BeaconTestNetwork - //} + for _, r := range committee.Runners { + r.BaseRunner.NetworkConfig = networkconfig.TestNetwork + } root, err := committee.GetRoot() require.NoError(t, err) diff --git a/protocol/v2/ssv/spectest/msg_processing_type.go b/protocol/v2/ssv/spectest/msg_processing_type.go index f75254adbd..1d9dc9b847 100644 --- a/protocol/v2/ssv/spectest/msg_processing_type.go +++ b/protocol/v2/ssv/spectest/msg_processing_type.go @@ -239,6 +239,8 @@ func overrideStateComparison(t *testing.T, test *MsgProcessingSpecTest, name str r, err = typescomparable.UnmarshalStateComparison(specDir, name, testType, r) require.NoError(t, err) + r.GetBaseRunner().NetworkConfig = networkconfig.TestNetwork + // override test.PostDutyRunnerState = r @@ -294,7 +296,7 @@ var baseCommitteeWithRunnerSample = func( ctx, cancel, logger, - runnerSample.GetBaseRunner().BeaconNetwork, + runnerSample.GetBaseRunner().NetworkConfig, spectestingutils.TestingCommitteeMember(keySetSample), createRunnerF, shareMap, diff --git a/protocol/v2/ssv/spectest/multi_start_new_runner_duty_type.go b/protocol/v2/ssv/spectest/multi_start_new_runner_duty_type.go index 8e301c686d..68919e3740 100644 --- a/protocol/v2/ssv/spectest/multi_start_new_runner_duty_type.go +++ b/protocol/v2/ssv/spectest/multi_start_new_runner_duty_type.go @@ -15,6 +15,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap" + "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/protocol/v2/ssv/runner" protocoltesting "github.com/ssvlabs/ssv/protocol/v2/testing" ) @@ -163,6 +164,8 @@ func overrideStateComparisonForStartNewRunnerDutySpecTest(t *testing.T, test *St r, err = typescomparable.UnmarshalStateComparison(specDir, name, testType, r) require.NoError(t, err) + r.GetBaseRunner().NetworkConfig = networkconfig.TestNetwork + // override test.PostDutyRunnerState = r diff --git a/protocol/v2/ssv/spectest/ssv_mapping_test.go b/protocol/v2/ssv/spectest/ssv_mapping_test.go index e45d766cab..4daeca9988 100644 --- a/protocol/v2/ssv/spectest/ssv_mapping_test.go +++ b/protocol/v2/ssv/spectest/ssv_mapping_test.go @@ -23,7 +23,6 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap" - tests2 "github.com/ssvlabs/ssv/integration/qbft/tests" "github.com/ssvlabs/ssv/logging" "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/protocol/v2/qbft/controller" @@ -365,7 +364,7 @@ func fixRunnerForRun(t *testing.T, runnerMap map[string]interface{}, ks *spectes base := &runner.BaseRunner{} byts, _ := json.Marshal(baseRunnerMap) require.NoError(t, json.Unmarshal(byts, &base)) - base.DomainType = networkconfig.TestNetwork.DomainType + base.NetworkConfig = networkconfig.TestNetwork logger := logging.TestLogger(t) @@ -381,9 +380,7 @@ func fixRunnerForRun(t *testing.T, runnerMap map[string]interface{}, ks *spectes } } - if (ret.GetBaseRunner().DomainType == spectypes.DomainType{}) { - ret.GetBaseRunner().DomainType = networkconfig.TestNetwork.DomainType - } + ret.GetBaseRunner().NetworkConfig = networkconfig.TestNetwork return ret } @@ -554,7 +551,7 @@ func fixCommitteeForRun(t *testing.T, ctx context.Context, logger *zap.Logger, c ctx, cancel, logger, - tests2.NewTestingBeaconNodeWrapped().GetBeaconNetwork(), + networkconfig.TestNetwork, &specCommittee.CommitteeMember, func(slot phase0.Slot, shareMap map[phase0.ValidatorIndex]*spectypes.Share, _ []phase0.BLSPubKey, _ runner.CommitteeDutyGuard) (*runner.CommitteeRunner, error) { r := ssvtesting.CommitteeRunnerWithShareMap(logger, shareMap) diff --git a/protocol/v2/ssv/testing/runner.go b/protocol/v2/ssv/testing/runner.go index d60a7b2435..dd9558f640 100644 --- a/protocol/v2/ssv/testing/runner.go +++ b/protocol/v2/ssv/testing/runner.go @@ -83,13 +83,13 @@ var ConstructBaseRunner = func( valCheck = ssv.BeaconVoteValueCheckF(km, spectestingutils.TestingDutySlot, []phase0.BLSPubKey{phase0.BLSPubKey(share.SharePubKey)}, spectestingutils.TestingDutyEpoch) case spectypes.RoleProposer: - valCheck = ssv.ProposerValueCheckF(km, spectypes.BeaconTestNetwork, + valCheck = ssv.ProposerValueCheckF(km, networkconfig.TestNetwork.BeaconConfig, (spectypes.ValidatorPK)(spectestingutils.TestingValidatorPubKey), spectestingutils.TestingValidatorIndex, phase0.BLSPubKey(share.SharePubKey)) case spectypes.RoleAggregator: - valCheck = ssv.AggregatorValueCheckF(km, spectypes.BeaconTestNetwork, + valCheck = ssv.AggregatorValueCheckF(km, networkconfig.TestNetwork.BeaconConfig, (spectypes.ValidatorPK)(spectestingutils.TestingValidatorPubKey), spectestingutils.TestingValidatorIndex) case spectypes.RoleSyncCommitteeContribution: - valCheck = ssv.SyncCommitteeContributionValueCheckF(km, spectypes.BeaconTestNetwork, + valCheck = ssv.SyncCommitteeContributionValueCheckF(km, networkconfig.TestNetwork.BeaconConfig, (spectypes.ValidatorPK)(spectestingutils.TestingValidatorPubKey), spectestingutils.TestingValidatorIndex) default: valCheck = nil @@ -134,8 +134,7 @@ var ConstructBaseRunner = func( ) case spectypes.RoleAggregator: r, err = runner.NewAggregatorRunner( - networkconfig.TestNetwork.DomainType, - spectypes.BeaconTestNetwork, + networkconfig.TestNetwork, shareMap, contr, tests.NewTestingBeaconNodeWrapped(), @@ -147,8 +146,7 @@ var ConstructBaseRunner = func( ) case spectypes.RoleProposer: r, err = runner.NewProposerRunner( - networkconfig.TestNetwork.DomainType, - spectypes.BeaconTestNetwork, + networkconfig.TestNetwork, shareMap, contr, tests.NewTestingBeaconNodeWrapped(), @@ -162,8 +160,7 @@ var ConstructBaseRunner = func( ) case spectypes.RoleSyncCommitteeContribution: r, err = runner.NewSyncCommitteeAggregatorRunner( - networkconfig.TestNetwork.DomainType, - spectypes.BeaconTestNetwork, + networkconfig.TestNetwork, shareMap, contr, tests.NewTestingBeaconNodeWrapped(), @@ -175,8 +172,7 @@ var ConstructBaseRunner = func( ) case spectypes.RoleValidatorRegistration: r, err = runner.NewValidatorRegistrationRunner( - networkconfig.TestNetwork.DomainType, - spectypes.BeaconTestNetwork, + networkconfig.TestNetwork, shareMap, tests.NewTestingBeaconNodeWrapped(), net, @@ -186,8 +182,7 @@ var ConstructBaseRunner = func( ) case spectypes.RoleVoluntaryExit: r, err = runner.NewVoluntaryExitRunner( - networkconfig.TestNetwork.DomainType, - spectypes.BeaconTestNetwork, + networkconfig.TestNetwork, shareMap, tests.NewTestingBeaconNodeWrapped(), net, @@ -343,13 +338,13 @@ var ConstructBaseRunnerWithShareMap = func( valCheck = ssv.BeaconVoteValueCheckF(km, spectestingutils.TestingDutySlot, sharePubKeys, spectestingutils.TestingDutyEpoch) case spectypes.RoleProposer: - valCheck = ssv.ProposerValueCheckF(km, spectypes.BeaconTestNetwork, + valCheck = ssv.ProposerValueCheckF(km, networkconfig.TestNetwork.BeaconConfig, shareInstance.ValidatorPubKey, shareInstance.ValidatorIndex, phase0.BLSPubKey(shareInstance.SharePubKey)) case spectypes.RoleAggregator: - valCheck = ssv.AggregatorValueCheckF(km, spectypes.BeaconTestNetwork, + valCheck = ssv.AggregatorValueCheckF(km, networkconfig.TestNetwork.BeaconConfig, shareInstance.ValidatorPubKey, shareInstance.ValidatorIndex) case spectypes.RoleSyncCommitteeContribution: - valCheck = ssv.SyncCommitteeContributionValueCheckF(km, spectypes.BeaconTestNetwork, + valCheck = ssv.SyncCommitteeContributionValueCheckF(km, networkconfig.TestNetwork.BeaconConfig, shareInstance.ValidatorPubKey, shareInstance.ValidatorIndex) default: valCheck = nil @@ -389,8 +384,7 @@ var ConstructBaseRunnerWithShareMap = func( ) case spectypes.RoleAggregator: r, err = runner.NewAggregatorRunner( - networkconfig.TestNetwork.DomainType, - spectypes.BeaconTestNetwork, + networkconfig.TestNetwork, shareMap, contr, tests.NewTestingBeaconNodeWrapped(), @@ -402,8 +396,7 @@ var ConstructBaseRunnerWithShareMap = func( ) case spectypes.RoleProposer: r, err = runner.NewProposerRunner( - networkconfig.TestNetwork.DomainType, - spectypes.BeaconTestNetwork, + networkconfig.TestNetwork, shareMap, contr, tests.NewTestingBeaconNodeWrapped(), @@ -417,8 +410,7 @@ var ConstructBaseRunnerWithShareMap = func( ) case spectypes.RoleSyncCommitteeContribution: r, err = runner.NewSyncCommitteeAggregatorRunner( - networkconfig.TestNetwork.DomainType, - spectypes.BeaconTestNetwork, + networkconfig.TestNetwork, shareMap, contr, tests.NewTestingBeaconNodeWrapped(), @@ -430,8 +422,7 @@ var ConstructBaseRunnerWithShareMap = func( ) case spectypes.RoleValidatorRegistration: r, err = runner.NewValidatorRegistrationRunner( - networkconfig.TestNetwork.DomainType, - spectypes.BeaconTestNetwork, + networkconfig.TestNetwork, shareMap, tests.NewTestingBeaconNodeWrapped(), net, @@ -441,8 +432,7 @@ var ConstructBaseRunnerWithShareMap = func( ) case spectypes.RoleVoluntaryExit: r, err = runner.NewVoluntaryExitRunner( - networkconfig.TestNetwork.DomainType, - spectypes.BeaconTestNetwork, + networkconfig.TestNetwork, shareMap, tests.NewTestingBeaconNodeWrapped(), net, diff --git a/protocol/v2/ssv/validator/committee.go b/protocol/v2/ssv/validator/committee.go index b4e11416ff..7f4646a3ee 100644 --- a/protocol/v2/ssv/validator/committee.go +++ b/protocol/v2/ssv/validator/committee.go @@ -14,6 +14,7 @@ import ( "go.uber.org/zap" "github.com/ssvlabs/ssv/logging/fields" + "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/protocol/v2/message" "github.com/ssvlabs/ssv/protocol/v2/ssv/queue" "github.com/ssvlabs/ssv/protocol/v2/ssv/runner" @@ -32,7 +33,7 @@ type Committee struct { ctx context.Context cancel context.CancelFunc - BeaconNetwork spectypes.BeaconNetwork + beaconConfig networkconfig.Beacon // mtx syncs access to Queues, Runners, Shares. mtx sync.RWMutex @@ -51,7 +52,7 @@ func NewCommittee( ctx context.Context, cancel context.CancelFunc, logger *zap.Logger, - beaconNetwork spectypes.BeaconNetwork, + beaconConfig networkconfig.Beacon, committeeMember *spectypes.CommitteeMember, createRunnerFn CommitteeRunnerFunc, shares map[phase0.ValidatorIndex]*spectypes.Share, @@ -62,7 +63,7 @@ func NewCommittee( } return &Committee{ logger: logger, - BeaconNetwork: beaconNetwork, + beaconConfig: beaconConfig, ctx: ctx, cancel: cancel, Queues: make(map[phase0.Slot]queueContainer), @@ -268,7 +269,7 @@ func (c *Committee) unsafePruneExpiredRunners(logger *zap.Logger, currentSlot ph for slot := range c.Runners { if slot <= minValidSlot { opIds := types.OperatorIDsFromOperators(c.CommitteeMember.Committee) - epoch := c.BeaconNetwork.EstimatedEpochAtSlot(slot) + epoch := c.beaconConfig.EstimatedEpochAtSlot(slot) committeeDutyID := fields.FormatCommitteeDutyID(opIds, epoch, slot) logger = logger.With(fields.DutyID(committeeDutyID)) logger.Debug("pruning expired committee runner", zap.Uint64("slot", uint64(slot))) diff --git a/protocol/v2/ssv/validator/committee_queue.go b/protocol/v2/ssv/validator/committee_queue.go index c77afc8022..a64bb2ab01 100644 --- a/protocol/v2/ssv/validator/committee_queue.go +++ b/protocol/v2/ssv/validator/committee_queue.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "time" specqbft "github.com/ssvlabs/ssv-spec/qbft" spectypes "github.com/ssvlabs/ssv-spec/types" @@ -70,7 +69,7 @@ func (c *Committee) StartConsumeQueue(logger *zap.Logger, duty *spectypes.Commit } // required to stop the queue consumer when timeout message is received by handler - queueCtx, cancelF := context.WithDeadline(c.ctx, time.Unix(c.BeaconNetwork.EstimatedTimeAtSlot(duty.Slot+runnerExpirySlots), 0)) + queueCtx, cancelF := context.WithDeadline(c.ctx, c.beaconConfig.EstimatedTimeAtSlot(duty.Slot+runnerExpirySlots)) go func() { defer cancelF() diff --git a/protocol/v2/ssv/validator/non_committee_validator.go b/protocol/v2/ssv/validator/non_committee_validator.go index 671b2ee41f..741b49b58d 100644 --- a/protocol/v2/ssv/validator/non_committee_validator.go +++ b/protocol/v2/ssv/validator/non_committee_validator.go @@ -20,7 +20,6 @@ import ( "github.com/ssvlabs/ssv/logging/fields" "github.com/ssvlabs/ssv/message/validation" "github.com/ssvlabs/ssv/networkconfig" - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" qbftcontroller "github.com/ssvlabs/ssv/protocol/v2/qbft/controller" qbftstorage "github.com/ssvlabs/ssv/protocol/v2/qbft/storage" "github.com/ssvlabs/ssv/protocol/v2/ssv" @@ -33,8 +32,7 @@ type CommitteeObserver struct { msgID spectypes.MessageID logger *zap.Logger Storage *storage.ParticipantStores - beaconNetwork beacon.BeaconNetwork - networkConfig networkconfig.NetworkConfig + beaconConfig networkconfig.Beacon ValidatorStore registrystorage.ValidatorStore newDecidedHandler qbftcontroller.NewDecidedHandler attesterRoots *ttlcache.Cache[phase0.Root, struct{}] @@ -58,7 +56,7 @@ type BeaconVoteCacheKey struct { type CommitteeObserverOptions struct { FullNode bool Logger *zap.Logger - NetworkConfig networkconfig.NetworkConfig + BeaconConfig networkconfig.Beacon Network specqbft.Network Storage *storage.ParticipantStores Operator *spectypes.CommitteeMember @@ -78,8 +76,7 @@ func NewCommitteeObserver(msgID spectypes.MessageID, opts CommitteeObserverOptio msgID: msgID, logger: opts.Logger, Storage: opts.Storage, - beaconNetwork: opts.NetworkConfig.Beacon, - networkConfig: opts.NetworkConfig, + beaconConfig: opts.BeaconConfig, ValidatorStore: opts.ValidatorStore, newDecidedHandler: opts.NewDecidedHandler, attesterRoots: opts.AttesterRoots, @@ -350,7 +347,7 @@ func (ncv *CommitteeObserver) OnProposalMsg(ctx context.Context, msg *queue.SSVM return nil } - epoch := ncv.beaconNetwork.EstimatedEpochAtSlot(phase0.Slot(qbftMsg.Height)) + epoch := ncv.beaconConfig.EstimatedEpochAtSlot(phase0.Slot(qbftMsg.Height)) if err := ncv.saveAttesterRoots(ctx, epoch, beaconVote, qbftMsg); err != nil { return err @@ -408,7 +405,7 @@ func (ncv *CommitteeObserver) saveSyncCommRoots( func (ncv *CommitteeObserver) postConsensusContainerCapacity() int { // #nosec G115 -- slots per epoch must be low epoch not to cause overflow - return int(ncv.networkConfig.SlotsPerEpoch()) + int(validation.LateSlotAllowance) + return int(ncv.beaconConfig.GetSlotsPerEpoch()) + validation.LateSlotAllowance } func constructAttestationData(vote *spectypes.BeaconVote, slot phase0.Slot, committeeIndex phase0.CommitteeIndex) *phase0.AttestationData { diff --git a/protocol/v2/ssv/validator/opts.go b/protocol/v2/ssv/validator/opts.go index b3e7fb38b1..2091df352c 100644 --- a/protocol/v2/ssv/validator/opts.go +++ b/protocol/v2/ssv/validator/opts.go @@ -20,7 +20,7 @@ const ( // Options represents options that should be passed to a new instance of Validator. type Options struct { - NetworkConfig networkconfig.NetworkConfig + NetworkConfig networkconfig.Network Network specqbft.Network Beacon beacon.BeaconNode Storage *storage.ParticipantStores diff --git a/protocol/v2/ssv/validator/startup.go b/protocol/v2/ssv/validator/startup.go index 04be1825b7..fdff755bc2 100644 --- a/protocol/v2/ssv/validator/startup.go +++ b/protocol/v2/ssv/validator/startup.go @@ -40,7 +40,7 @@ func (v *Validator) Start(logger *zap.Logger) (started bool, err error) { continue } - identifier := spectypes.NewMsgID(v.NetworkConfig.DomainType, share.ValidatorPubKey[:], role) + identifier := spectypes.NewMsgID(v.NetworkConfig.GetDomainType(), share.ValidatorPubKey[:], role) // TODO: P2P var valpk spectypes.ValidatorPK diff --git a/protocol/v2/ssv/validator/validator.go b/protocol/v2/ssv/validator/validator.go index 50b97c968f..8544de306b 100644 --- a/protocol/v2/ssv/validator/validator.go +++ b/protocol/v2/ssv/validator/validator.go @@ -30,7 +30,7 @@ type Validator struct { ctx context.Context cancel context.CancelFunc - NetworkConfig networkconfig.NetworkConfig + NetworkConfig networkconfig.Network DutyRunners runner.ValidatorDutyRunners Network specqbft.Network @@ -105,7 +105,7 @@ func (v *Validator) StartDuty(ctx context.Context, logger *zap.Logger, duty spec // Log with duty ID. baseRunner := dutyRunner.GetBaseRunner() - v.dutyIDs.Set(spectypes.MapDutyToRunnerRole(vDuty.Type), fields.FormatDutyID(baseRunner.BeaconNetwork.EstimatedEpochAtSlot(vDuty.Slot), vDuty.Slot, vDuty.Type.String(), vDuty.ValidatorIndex)) + v.dutyIDs.Set(spectypes.MapDutyToRunnerRole(vDuty.Type), fields.FormatDutyID(baseRunner.NetworkConfig.EstimatedEpochAtSlot(vDuty.Slot), vDuty.Slot, vDuty.Type.String(), vDuty.ValidatorIndex)) logger = v.withDutyID(logger, spectypes.MapDutyToRunnerRole(vDuty.Type)) // Log with height. diff --git a/protocol/v2/ssv/value_check.go b/protocol/v2/ssv/value_check.go index 9debd8bc63..2486c49d53 100644 --- a/protocol/v2/ssv/value_check.go +++ b/protocol/v2/ssv/value_check.go @@ -8,18 +8,19 @@ import ( "github.com/pkg/errors" specqbft "github.com/ssvlabs/ssv-spec/qbft" spectypes "github.com/ssvlabs/ssv-spec/types" - "github.com/ssvlabs/ssv/ssvsigner/ekm" + + "github.com/ssvlabs/ssv/networkconfig" ) func dutyValueCheck( duty *spectypes.ValidatorDuty, - network spectypes.BeaconNetwork, + beaconConfig networkconfig.Beacon, expectedType spectypes.BeaconRole, validatorPK spectypes.ValidatorPK, validatorIndex phase0.ValidatorIndex, ) error { - if network.EstimatedEpochAtSlot(duty.Slot) > network.EstimatedCurrentEpoch()+1 { + if beaconConfig.EstimatedEpochAtSlot(duty.Slot) > beaconConfig.EstimatedCurrentEpoch()+1 { return errors.New("duty epoch is into far future") } @@ -80,7 +81,7 @@ func BeaconVoteValueCheckF( func ProposerValueCheckF( signer ekm.BeaconSigner, - network spectypes.BeaconNetwork, + beaconConfig networkconfig.Beacon, validatorPK spectypes.ValidatorPK, validatorIndex phase0.ValidatorIndex, sharePublicKey phase0.BLSPubKey, @@ -94,7 +95,7 @@ func ProposerValueCheckF( return errors.Wrap(err, "invalid value") } - if err := dutyValueCheck(&cd.Duty, network, spectypes.BNRoleProposer, validatorPK, validatorIndex); err != nil { + if err := dutyValueCheck(&cd.Duty, beaconConfig, spectypes.BNRoleProposer, validatorPK, validatorIndex); err != nil { return errors.Wrap(err, "duty invalid") } @@ -119,7 +120,7 @@ func ProposerValueCheckF( func AggregatorValueCheckF( signer ekm.BeaconSigner, - network spectypes.BeaconNetwork, + beaconConfig networkconfig.Beacon, validatorPK spectypes.ValidatorPK, validatorIndex phase0.ValidatorIndex, ) specqbft.ProposedValueCheckF { @@ -132,7 +133,7 @@ func AggregatorValueCheckF( return errors.Wrap(err, "invalid value") } - if err := dutyValueCheck(&cd.Duty, network, spectypes.BNRoleAggregator, validatorPK, validatorIndex); err != nil { + if err := dutyValueCheck(&cd.Duty, beaconConfig, spectypes.BNRoleAggregator, validatorPK, validatorIndex); err != nil { return errors.Wrap(err, "duty invalid") } return nil @@ -141,7 +142,7 @@ func AggregatorValueCheckF( func SyncCommitteeContributionValueCheckF( signer ekm.BeaconSigner, - network spectypes.BeaconNetwork, + beaconConfig networkconfig.Beacon, validatorPK spectypes.ValidatorPK, validatorIndex phase0.ValidatorIndex, ) specqbft.ProposedValueCheckF { @@ -154,7 +155,7 @@ func SyncCommitteeContributionValueCheckF( return errors.Wrap(err, "invalid value") } - if err := dutyValueCheck(&cd.Duty, network, spectypes.BNRoleSyncCommitteeContribution, validatorPK, validatorIndex); err != nil { + if err := dutyValueCheck(&cd.Duty, beaconConfig, spectypes.BNRoleSyncCommitteeContribution, validatorPK, validatorIndex); err != nil { return errors.Wrap(err, "duty invalid") } diff --git a/protocol/v2/types/ssvshare.go b/protocol/v2/types/ssvshare.go index abc8c918cf..0e3d888c32 100644 --- a/protocol/v2/types/ssvshare.go +++ b/protocol/v2/types/ssvshare.go @@ -102,8 +102,8 @@ func (s *SSVShare) Slashed() bool { // IsParticipating returns true if the validator can participate in *any* SSV duty at the given epoch. // Note: the validator may be eligible only for sync committee, but not to attest and propose. See IsParticipatingAndAttesting. // Requirements: not liquidated and attesting or exited in the current or previous sync committee period. -func (s *SSVShare) IsParticipating(cfg networkconfig.NetworkConfig, epoch phase0.Epoch) bool { - return !s.Liquidated && s.IsSyncCommitteeEligible(cfg, epoch) +func (s *SSVShare) IsParticipating(beaconCfg networkconfig.Beacon, epoch phase0.Epoch) bool { + return !s.Liquidated && s.IsSyncCommitteeEligible(beaconCfg, epoch) } // IsParticipatingAndAttesting returns true if the validator can participate in *all* SSV duties at the given epoch. @@ -112,7 +112,7 @@ func (s *SSVShare) IsParticipatingAndAttesting(epoch phase0.Epoch) bool { return !s.Liquidated && s.IsAttesting(epoch) } -func (s *SSVShare) IsSyncCommitteeEligible(cfg networkconfig.NetworkConfig, epoch phase0.Epoch) bool { +func (s *SSVShare) IsSyncCommitteeEligible(beaconCfg networkconfig.Beacon, epoch phase0.Epoch) bool { if s.IsAttesting(epoch) { return true } @@ -120,7 +120,7 @@ func (s *SSVShare) IsSyncCommitteeEligible(cfg networkconfig.NetworkConfig, epoc if s.Status.IsExited() || s.Status == eth2apiv1.ValidatorStateWithdrawalPossible || s.Status == eth2apiv1.ValidatorStateActiveSlashed { // if validator exited within Current Period OR Current Period - 1, then it is eligible // because Sync committees are assigned EPOCHS_PER_SYNC_COMMITTEE_PERIOD in advance - if epoch >= s.ExitEpoch && cfg.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch)-cfg.Beacon.EstimatedSyncCommitteePeriodAtEpoch(s.ExitEpoch) <= 1 { + if epoch >= s.ExitEpoch && beaconCfg.EstimatedSyncCommitteePeriodAtEpoch(epoch)-beaconCfg.EstimatedSyncCommitteePeriodAtEpoch(s.ExitEpoch) <= 1 { return true } } diff --git a/protocol/v2/types/ssvshare_test.go b/protocol/v2/types/ssvshare_test.go index 89b967e0cf..f3d4dc8979 100644 --- a/protocol/v2/types/ssvshare_test.go +++ b/protocol/v2/types/ssvshare_test.go @@ -209,7 +209,7 @@ func TestSSVShare_IsParticipating(t *testing.T) { for _, tc := range tt { t.Run(tc.Name, func(t *testing.T) { - result := tc.Share.IsParticipating(networkconfig.Mainnet, tc.Epoch) + result := tc.Share.IsParticipating(networkconfig.TestNetwork, tc.Epoch) require.Equal(t, tc.Expected, result) }) } diff --git a/registry/storage/shares.go b/registry/storage/shares.go index 3a5911930d..aa494a7939 100644 --- a/registry/storage/shares.go +++ b/registry/storage/shares.go @@ -121,7 +121,7 @@ func (s *Share) Decode(data []byte) error { return nil } -func NewSharesStorage(networkConfig networkconfig.NetworkConfig, db basedb.Database, prefix []byte) (Shares, ValidatorStore, error) { +func NewSharesStorage(beaconCfg networkconfig.Beacon, db basedb.Database, prefix []byte) (Shares, ValidatorStore, error) { storage := &sharesStorage{ shares: make(map[string]*types.SSVShare), db: db, @@ -134,7 +134,7 @@ func NewSharesStorage(networkConfig networkconfig.NetworkConfig, db basedb.Datab storage.validatorStore = newValidatorStore( func() []*types.SSVShare { return storage.List(nil) }, func(pk []byte) (*types.SSVShare, bool) { return storage.Get(nil, pk) }, - networkConfig, + beaconCfg, ) if err := storage.validatorStore.handleSharesAdded(slices.Collect(maps.Values(storage.shares))...); err != nil { return nil, nil, err diff --git a/registry/storage/validatorstore.go b/registry/storage/validatorstore.go index de8782dd1f..51c7e98848 100644 --- a/registry/storage/validatorstore.go +++ b/registry/storage/validatorstore.go @@ -55,9 +55,9 @@ type Committee struct { } // IsParticipating returns whether any validator in the committee should participate in the given epoch. -func (c *Committee) IsParticipating(cfg networkconfig.NetworkConfig, epoch phase0.Epoch) bool { +func (c *Committee) IsParticipating(beaconCfg networkconfig.Beacon, epoch phase0.Epoch) bool { for _, validator := range c.Validators { - if validator.IsParticipating(cfg, epoch) { + if validator.IsParticipating(beaconCfg, epoch) { return true } } @@ -78,7 +78,7 @@ type validatorStore struct { byCommitteeID map[spectypes.CommitteeID]*Committee byOperatorID map[spectypes.OperatorID]*sharesAndCommittees - networkConfig networkconfig.NetworkConfig + beaconCfg networkconfig.Beacon mu sync.RWMutex } @@ -86,7 +86,7 @@ type validatorStore struct { func newValidatorStore( shares func() []*types.SSVShare, shareByPubKey func([]byte) (*types.SSVShare, bool), - networkConfig networkconfig.NetworkConfig, + beaconCfg networkconfig.Beacon, ) *validatorStore { return &validatorStore{ shares: shares, @@ -94,7 +94,7 @@ func newValidatorStore( byValidatorIndex: make(map[phase0.ValidatorIndex]*types.SSVShare), byCommitteeID: make(map[spectypes.CommitteeID]*Committee), byOperatorID: make(map[spectypes.OperatorID]*sharesAndCommittees), - networkConfig: networkConfig, + beaconCfg: beaconCfg, } } @@ -121,7 +121,7 @@ func (c *validatorStore) Validators() []*types.SSVShare { func (c *validatorStore) ParticipatingValidators(epoch phase0.Epoch) []*types.SSVShare { var validators []*types.SSVShare for _, share := range c.shares() { - if share.IsParticipating(c.networkConfig, epoch) { + if share.IsParticipating(c.beaconCfg, epoch) { validators = append(validators, share) } } @@ -163,7 +163,7 @@ func (c *validatorStore) ParticipatingCommittees(epoch phase0.Epoch) []*Committe var committees []*Committee for _, committee := range c.byCommitteeID { - if committee.IsParticipating(c.networkConfig, epoch) { + if committee.IsParticipating(c.beaconCfg, epoch) { committees = append(committees, committee) } } @@ -199,7 +199,7 @@ func (c *validatorStore) SelfParticipatingValidators(epoch phase0.Epoch) []*type shares := c.OperatorValidators(c.operatorID()) var participating []*types.SSVShare for _, share := range shares { - if share.IsParticipating(c.networkConfig, epoch) { + if share.IsParticipating(c.beaconCfg, epoch) { participating = append(participating, share) } } @@ -221,7 +221,7 @@ func (c *validatorStore) SelfParticipatingCommittees(epoch phase0.Epoch) []*Comm committees := c.OperatorCommittees(c.operatorID()) var participating []*Committee for _, committee := range committees { - if committee.IsParticipating(c.networkConfig, epoch) { + if committee.IsParticipating(c.beaconCfg, epoch) { participating = append(participating, committee) } } diff --git a/scripts/spec-alignment/differ.config.yaml b/scripts/spec-alignment/differ.config.yaml index de2a69931f..d66e6a0faf 100644 --- a/scripts/spec-alignment/differ.config.yaml +++ b/scripts/spec-alignment/differ.config.yaml @@ -1,4 +1,4 @@ -ApprovedChanges: ["115ccce93a478b9a", "c8f122c9fb83793e", "220aede0f8fa5a4a", "fb9cab4ffb28869a", "3ca2431a8d79bc3a", "ca49cf46baf0a367", "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", "f9c984e71b685f9b","8c6b4fee5a4c13ce","c0a8d2019a2c30d5", "717bef26105c733f","2f70630c27062353","2f70337ba7566a69","dd607a44e1341e6b","5210501625ac3de5","f786bf475b5085aa","18a66ed6e613d9c1","e8943e7741f6843d","276a489bd5a00032","ba3bba59f10bf6b","3c50ce0c8089d871","89ee72f6c610ab84","c92b95a85da2cb11","927ea6aed3f98f20","9338904026a0ce37","9683cfa19dc544a3","4d3fa2b8dfcb5f5b", "f19e9a2b295bcfb3", "b10199b2de6f03b8", "1afc17e358f9ca79","4b58762c0b433442","d293ec1bc61bb707","3e88c3b49d093605","4890ff80c88cc41d","5227ff3a225dd20d","81a60407a3a0ba80","db2ad807eb66254a","d308bd7c553ccdcf","bdaf172971637cbe","6ade9202843071fe","2fe8e14083997744","19c9a5362d1e1d3a","5956f803d239f178","92c55a4548a8b760","9a95524213bccfff","2f51a7338b86c229","e96966a281d74505","3ee479b9cbbc3a1d","82b392ba39c6c594","b9d2404e5c570019","24f528d85fb021f2","fe9609a785305d81","b0934079dcd986cc","a9c520a19b26049","d19a9403fd732d94","74a928f5dcb2fdd9","cbbfdb5e68cdac80","10e39d2ceda91f34","f99a004cf6697875","8fa5e8ebf7d223ec","6c80c145ba705243","fbabbc90d0b4178a","ab0c7f24e551ca6","af38a11cb8682c75","b110cba51df9f8d2","c4ff2ed3d20dc419","9295a5bb10efcec7","ab56ea44a75f898a","ff51ef26ab53ba58","df3771e2589008f9","106e5689655bcfc6","f90e0fb6883bff93","667656095cec39ee","9a5597af260c748a","9168b9cfb0cb98c8","875393173d59b0f2","a347ee50e92f7334","91073f39440ab37f","1bbcbe4e8194370d","2169fe3af9e88ab9","a85dccf18844fc79","2bf91d3c3920c5c8","f4ce01b385c68a2","4366899a2cb05197","6f0aecc3342b13c0","874b67f800dd74d6","587785bbbcdc2016","d7397265deb360a6","d9fabd130a8ecdfb","707b55fec1513c90","e51b0346b0d4b612","417e005d18c1f7d4","306964e5ede31618","8c7ca4bb2b8abe1e","b878d79c5774d12d","15599c2b2bd60293","a7891090c37daba1","4192f3dd9bdddea8","ba5b31e5a1adb8e2","dadf83703db9234","3fd72034ab5b908","e939b25394581c4","67d24278154582d6","bc6a77eea2a1ba4b","41fa2c6b8e3aed38","1b6e3c30093fcd6e","98377d7e6f0eea0a","267886c6b07733d7","280250b5f6148515","1b79a8c6288d49d8","47c23de3a5c71c7e","de0f0cd6b40ee150","e91f5b3d583f23af","2036a5ff6aed3717","5ed363e465b9c98b","3ed6b5d40fb4a3b0","e7219eac943d306f","186382b7e69faced","943be3ce709a99d3","3a8800d72c312f75","b9bf74de5674c15d","cb9ad84a103b6499","acdcac18a6e34419", "ee323f91ee6f543c","be9935e5c2bedaef","60ac8eb415e6c748","119e95426dc4affb","e918003e25abf4cd","44c0efaa4b189434","de6ef5f1aa07b38d","b71a695e14c7e892","c9b7984a5d2604b5","72e9cd10959558de","82f692ac2f5817f3","3e0d3d7ade790448","52595c2a99ba6031","ea6ad19cdc29b230","695cba9190654139","4418aa6010632480","e27b9c1ea1c64357","fe62097e8814c106","1cfd3a3660a62879","ddf404c2905d3b26","8ac6baea1b755066","2bc023c7e062f24b","8c43f241aeac6d75","832fc9a9a2b71d00","34e8b7999dfb14b5","ccc48c575f7ab4eb","9de0e5d104caf802","e46f98bfca35d4a0","db882c2be6efd7d4","6577bf381d78bae2","f66dbe09241b5a4","48884cab13bd3903","fd6796ea6d131c3","dfde38eed6f3305d","9b895440c1743c4e","783942f921da9985","e0c97cefd6558c77","75c75fa4503282","680487ae23f269a1","9b70726386e38170","ef7b63d9848dc185","3fe59bc388b9ef3","dd93140e21e6f919","143c7821cd65bcac","70d29c9e7a4ef797","b26376133e68b3db","31a354962a9bcfa0","50e62cbde34516ee","9273478f6feccd62","eddf20a67e3fddef","5a44c960cc4178dd","5499865be87b7e00","446545b04b35fba","8887c18020ddb307","3adda38e5fadcfb","9f105a95e82012a4","50cea598241b0bc9","6396e2866ef1b30","1c0da45940bfa76f","22b9026cb0221ce0","2011029b33aad319", "60f1d510640499ac","396137c9cb425893","7fd407d5e8c91bd4","a037c260bd97c7d2","d50f3cbac505793d","b841fc54d2df22d9","389037ae98d33f26", "f265acf7423fa9b1"] +ApprovedChanges: ["34e8b7999dfb14b5","56ceb03cd44ff702","ccc48c575f7ab4eb","262a19d18869cedd","e710070bfb15d3ab","1c0da45940bfa76f","881fc8fbb6a4edb1","31bc6100f3d4db08","c7ce0261a1b6014f","c4c4caa5d0938b85","60f1d510640499ac","91073f39440ab37f","d308bd7c553ccdcf","bdaf172971637cbe","396137c9cb425893","d4fef6512374c1f5","1bbcbe4e8194370d","59b2375130aef5df","1bd46741a98043b0","417e005d18c1f7d4","14dd874e7df81b37","50cea598241b0bc9","3ddc92a39c324d69","b0934079dcd986cc","e27b9c1ea1c64357","fe62097e8814c106","74a928f5dcb2fdd9","cbbfdb5e68cdac80","6577bf381d78bae2","39ea06bfd1477d2d","7e2550bab51f22b2","87ebd29bd49fc52f","ef39dd5223e0d080","aebb8e4348b6d667","6146023d4d5708a2","fe14e7f0503ea188","fb4cac598a68c592","257c7eb81d6eb245","62547d1b32ce44e3","5be5c4ce7d3212da","960a9c64cd4ec93c","df3771e2589008f9","c8f122c9fb83793e","2c6054db7088bcef","5714652b88e2d44f","7a53b3b037c56325","11587bf00d05a4e2","601862214795ec47","b523eed171e5b6bf","5797f56a84b7ebef","81a60407a3a0ba80","38faed802f80914b","bcb381e843ace421","1067c9a5622c3508","4b58762c0b433442","d293ec1bc61bb707","3e88c3b49d093605","4890ff80c88cc41d","9b70726386e38170","8bbf7eba3fa0cf7e","8cc38593ebe049b6","ef7b63d9848dc185","7f816e2f33da3186","2314d7fa0b387cff","267886c6b07733d7","f9480b2e48319466","98c46ae55d1d4e39","edfd442b4d725fbb","122f053573538a32","d720d714a20833e1","a81c092c985de728","143c7821cd65bcac","9e53c73ee60b1cc2","9d265e99dd31d4f5","70d29c9e7a4ef797","f2972b2906912d3a","c411b0f9f069c032","a037c260bd97c7d2","96631f77414215f5","389037ae98d33f26","b3e7d9382b320d70","3426003abbd5ebf0","22b9026cb0221ce0","e07da5b414afe6a6","50e62cbde34516ee","57c1885b43dd8d19","e8a49856a5edd893","9273478f6feccd62","1cfd3a3660a62879","859884a25fccea80","8651bdc20c1946f3","5a44c960cc4178dd","108b9575f7c1d4bc","5499865be87b7e00","159536003eeddac8","a20ea51df8b7d65b","9133b167cc5a601d","a748ad5c74850749","16ebe47404323cc1","48bfe5cf1e578b47","4366899a2cb05197","730c3e5e59393b7d","5b44a4b425ecc397","df5debc50ec8babc","92a41554b2910bb8","c36c680554dde59f","f265acf7423fa9b1","447feaa5cdc1a010","8ac6baea1b755066","2bc023c7e062f24b","274336ec1127e6c0","ddf404c2905d3b26","8c43f241aeac6d75","832fc9a9a2b71d00","8850900b5d9bcc65","cc22f28953b787ea","3bad6ae11596a574","8f84422a240d889c","82b392ba39c6c594","7975821460ebe1e7","173c505e12aabb8f","47ee0d148148a56f","6707ecfefa5fec21","d5a7389d730464f1","8e4ec8debe331b36","875393173d59b0f2","943be3ce709a99d3", "4e22a08543b079b", "f66dbe09241b5a4", "fd6796ea6d131c3", "634abeefa604ee2", "e939b25394581c4", "6ccbcb02e457e66", "e1d82f0619360a5", "5a7ad98296703f6", "f4ce01b385c68a2"] IgnoredIdentifiers: - logger diff --git a/ssvsigner/ekm/local_key_manager.go b/ssvsigner/ekm/local_key_manager.go index 487535a5f7..a87a5985a1 100644 --- a/ssvsigner/ekm/local_key_manager.go +++ b/ssvsigner/ekm/local_key_manager.go @@ -53,7 +53,6 @@ type LocalKeyManager struct { wallet core.Wallet walletLock *sync.RWMutex signer signer.ValidatorSigner - domain spectypes.DomainType operatorDecrypter keys.OperatorDecrypter slashingProtector } @@ -62,10 +61,10 @@ type LocalKeyManager struct { func NewLocalKeyManager( logger *zap.Logger, db basedb.Database, - network networkconfig.NetworkConfig, + beaconConfig networkconfig.Beacon, operatorPrivKey keys.OperatorPrivateKey, ) (*LocalKeyManager, error) { - signerStore := NewSignerStorage(db, network.Beacon, logger) + signerStore := NewSignerStorage(db, beaconConfig, logger) if err := signerStore.SetEncryptionKey(operatorPrivKey.EKMHash()); err != nil { return nil, err } @@ -91,14 +90,13 @@ func NewLocalKeyManager( } } - beaconSigner := signer.NewSimpleSigner(wallet, protection, core.Network(network.Beacon.GetBeaconNetwork())) + beaconSigner := signer.NewSimpleSigner(wallet, protection, core.Network(beaconConfig.GetNetworkName())) return &LocalKeyManager{ wallet: wallet, walletLock: &sync.RWMutex{}, signer: beaconSigner, - domain: network.DomainType, - slashingProtector: NewSlashingProtector(logger, signerStore, protection), + slashingProtector: NewSlashingProtector(logger, beaconConfig, signerStore, protection), operatorDecrypter: operatorPrivKey, }, nil } diff --git a/ssvsigner/ekm/local_key_manager_test.go b/ssvsigner/ekm/local_key_manager_test.go index c583457bbe..a94a3846d2 100644 --- a/ssvsigner/ekm/local_key_manager_test.go +++ b/ssvsigner/ekm/local_key_manager_test.go @@ -34,7 +34,7 @@ const ( pk2Str = "8796fafa576051372030a75c41caafea149e4368aebaca21c9f90d9974b3973d5cee7d7874e4ec9ec59fb2c8945b3e01" ) -func testKeyManager(t *testing.T, network *networkconfig.NetworkConfig, operatorPrivateKey keys.OperatorPrivateKey) (KeyManager, *networkconfig.NetworkConfig) { +func testKeyManager(t *testing.T, network networkconfig.Network, operatorPrivateKey keys.OperatorPrivateKey) (KeyManager, networkconfig.Network) { threshold.Init() logger := logging.TestLogger(t) @@ -43,12 +43,10 @@ func testKeyManager(t *testing.T, network *networkconfig.NetworkConfig, operator require.NoError(t, err) if network == nil { - network = &networkconfig.NetworkConfig{} - network.Beacon = utils.SetupMockBeaconNetwork(t, nil) - network.DomainType = networkconfig.TestNetwork.DomainType + network = utils.SetupMockNetworkConfig(t, networkconfig.TestNetwork.DomainType, nil) } - km, err := NewLocalKeyManager(logger, db, *network, operatorPrivateKey) + km, err := NewLocalKeyManager(logger, db, network, operatorPrivateKey) require.NoError(t, err) sk1 := &bls.SecretKey{} @@ -87,7 +85,7 @@ func TestEncryptedKeyManager(t *testing.T) { db, err := getBaseStorage(logger) require.NoError(t, err) - signerStorage := NewSignerStorage(db, networkconfig.TestNetwork.Beacon.GetNetwork(), logger) + signerStorage := NewSignerStorage(db, networkconfig.TestNetwork, logger) err = signerStorage.SetEncryptionKey(encryptionKey) require.NoError(t, err) @@ -150,7 +148,7 @@ func TestSignBeaconObject(t *testing.T) { require.NoError(t, km.AddShare(t.Context(), encryptedSK1, phase0.BLSPubKey(sk1.GetPublicKey().Serialize()))) - currentSlot := network.Beacon.EstimatedCurrentSlot() + currentSlot := network.EstimatedCurrentSlot() highestProposal := currentSlot + minSPProposalSlotGap + 1 t.Run("Sign Deneb block", func(t *testing.T) { diff --git a/ssvsigner/ekm/mock.go b/ssvsigner/ekm/mock.go index 6ca37fed95..5351761ded 100644 --- a/ssvsigner/ekm/mock.go +++ b/ssvsigner/ekm/mock.go @@ -3,7 +3,6 @@ package ekm import ( "context" - eth2api "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/ssvlabs/eth2-key-manager/core" "github.com/stretchr/testify/mock" @@ -53,26 +52,6 @@ func (m *MockRemoteSigner) OperatorSign(ctx context.Context, payload []byte) ([] return args.Get(0).([]byte), args.Error(1) } -type MockConsensusClient struct { - mock.Mock -} - -func (m *MockConsensusClient) ForkAtEpoch(ctx context.Context, epoch phase0.Epoch) (*phase0.Fork, error) { - args := m.Called(ctx, epoch) - if args.Get(0) == nil { - return nil, args.Error(1) - } - return args.Get(0).(*phase0.Fork), args.Error(1) -} - -func (m *MockConsensusClient) Genesis(ctx context.Context) (*eth2api.Genesis, error) { - args := m.Called(ctx) - if args.Get(0) == nil { - return nil, args.Error(1) - } - return args.Get(0).(*eth2api.Genesis), args.Error(1) -} - type MockBeaconNetwork struct { mock.Mock } diff --git a/ssvsigner/ekm/remote_key_manager.go b/ssvsigner/ekm/remote_key_manager.go index 0b46ddd2e0..f7f7c9d681 100644 --- a/ssvsigner/ekm/remote_key_manager.go +++ b/ssvsigner/ekm/remote_key_manager.go @@ -39,14 +39,13 @@ import ( // // RemoteKeyManager doesn't use operator private key as it's stored externally in the remote signer. type RemoteKeyManager struct { - logger *zap.Logger - netCfg networkconfig.NetworkConfig - signerClient signerClient - consensusClient consensusClient - getOperatorId func() spectypes.OperatorID - operatorPubKey keys.OperatorPublicKey - signLocksMu sync.RWMutex - signLocks map[signKey]*sync.RWMutex + logger *zap.Logger + beaconConfig networkconfig.Beacon + signerClient signerClient + getOperatorId func() spectypes.OperatorID + operatorPubKey keys.OperatorPublicKey + signLocksMu sync.RWMutex + signLocks map[signKey]*sync.RWMutex slashingProtector } @@ -58,25 +57,18 @@ type signerClient interface { OperatorSign(ctx context.Context, payload []byte) ([]byte, error) } -type consensusClient interface { - ForkAtEpoch(ctx context.Context, epoch phase0.Epoch) (*phase0.Fork, error) - Genesis(ctx context.Context) (*eth2apiv1.Genesis, error) -} - // NewRemoteKeyManager returns a RemoteKeyManager that fetches the operator's public // identity from the signerClient, sets up local slashing protection, and uses // the provided consensusClient to get the current fork/genesis for sign requests. func NewRemoteKeyManager( ctx context.Context, logger *zap.Logger, - netCfg networkconfig.NetworkConfig, + beaconConfig networkconfig.Beacon, signerClient signerClient, - consensusClient consensusClient, db basedb.Database, - networkConfig networkconfig.NetworkConfig, getOperatorId func() spectypes.OperatorID, ) (*RemoteKeyManager, error) { - signerStore := NewSignerStorage(db, networkConfig.Beacon, logger) + signerStore := NewSignerStorage(db, beaconConfig, logger) protection := slashingprotection.NewNormalProtection(signerStore) operatorPubKeyString, err := signerClient.OperatorIdentity(ctx) @@ -91,10 +83,9 @@ func NewRemoteKeyManager( return &RemoteKeyManager{ logger: logger, - netCfg: netCfg, + beaconConfig: beaconConfig, signerClient: signerClient, - consensusClient: consensusClient, - slashingProtector: NewSlashingProtector(logger, signerStore, protection), + slashingProtector: NewSlashingProtector(logger, beaconConfig, signerStore, protection), getOperatorId: getOperatorId, operatorPubKey: operatorPubKey, signLocks: map[signKey]*sync.RWMutex{}, @@ -155,15 +146,10 @@ func (km *RemoteKeyManager) SignBeaconObject( slot phase0.Slot, signatureDomain phase0.DomainType, ) (spectypes.Signature, phase0.Root, error) { - epoch := km.netCfg.Beacon.EstimatedEpochAtSlot(slot) - - forkInfo, err := km.getForkInfo(ctx, epoch) - if err != nil { - return spectypes.Signature{}, phase0.Root{}, fmt.Errorf("get fork info: %w", err) - } + epoch := km.beaconConfig.EstimatedEpochAtSlot(slot) req := web3signer.SignRequest{ - ForkInfo: forkInfo, + ForkInfo: km.getForkInfo(epoch), } switch signatureDomain { @@ -317,7 +303,7 @@ func (km *RemoteKeyManager) handleDomainAttester( return nil, errors.New("could not cast obj to AttestationData") } - network := core.Network(km.netCfg.Beacon.GetBeaconNetwork()) + network := core.Network(km.beaconConfig.GetNetworkName()) if !signer.IsValidFarFutureEpoch(network, data.Target.Epoch) { return nil, fmt.Errorf("target epoch too far into the future") } @@ -450,7 +436,7 @@ func (km *RemoteKeyManager) handleDomainProposer( blockSlot := ret.BlockHeader.Slot - network := core.Network(km.netCfg.Beacon.GetBeaconNetwork()) + network := core.Network(km.beaconConfig.GetNetworkName()) if !signer.IsValidFarFutureSlot(network, blockSlot) { return nil, fmt.Errorf("proposed block slot too far into the future") } @@ -466,21 +452,13 @@ func (km *RemoteKeyManager) handleDomainProposer( return ret, nil } -func (km *RemoteKeyManager) getForkInfo(ctx context.Context, epoch phase0.Epoch) (web3signer.ForkInfo, error) { - currentFork, err := km.consensusClient.ForkAtEpoch(ctx, epoch) - if err != nil { - return web3signer.ForkInfo{}, fmt.Errorf("get current fork: %w", err) - } - - genesis, err := km.consensusClient.Genesis(ctx) - if err != nil { - return web3signer.ForkInfo{}, fmt.Errorf("get genesis: %w", err) - } +func (km *RemoteKeyManager) getForkInfo(epoch phase0.Epoch) web3signer.ForkInfo { + _, currentFork := km.beaconConfig.ForkAtEpoch(epoch) return web3signer.ForkInfo{ Fork: currentFork, - GenesisValidatorsRoot: genesis.GenesisValidatorsRoot, - }, nil + GenesisValidatorsRoot: km.beaconConfig.GetGenesisValidatorsRoot(), + } } func (km *RemoteKeyManager) Sign(payload []byte) ([]byte, error) { diff --git a/ssvsigner/ekm/remote_key_manager_test.go b/ssvsigner/ekm/remote_key_manager_test.go index 62dcac7a10..1b09bc4a36 100644 --- a/ssvsigner/ekm/remote_key_manager_test.go +++ b/ssvsigner/ekm/remote_key_manager_test.go @@ -25,24 +25,23 @@ import ( "go.uber.org/zap" "github.com/ssvlabs/ssv/networkconfig" + "github.com/ssvlabs/ssv/ssvsigner" ) -var testNetCfg = networkconfig.HoodiStage // using a real network config because https://github.com/ssvlabs/eth2-key-manager doesn't support min genesis time for networkconfig.TestNetwork +var testNetCfg = networkconfig.TestRealNetwork.BeaconConfig // using a real network config because https://github.com/ssvlabs/eth2-key-manager doesn't support min genesis time for networkconfig.TestNetwork type RemoteKeyManagerTestSuite struct { suite.Suite - client *MockRemoteSigner - consensusClient *MockConsensusClient - db *MockDatabase - txn *MockTxn - readTxn *MockReadTxn - logger *zap.Logger + client *MockRemoteSigner + db *MockDatabase + txn *MockTxn + readTxn *MockReadTxn + logger *zap.Logger } func (s *RemoteKeyManagerTestSuite) SetupTest() { s.client = &MockRemoteSigner{} - s.consensusClient = &MockConsensusClient{} s.db = &MockDatabase{} s.txn = &MockTxn{} s.readTxn = &MockReadTxn{} @@ -56,9 +55,8 @@ func (s *RemoteKeyManagerTestSuite) TestRemoteKeyManagerWithMockedOperatorKey() rm := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: s.client, - consensusClient: s.consensusClient, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: mockSlashingProtector, @@ -87,9 +85,8 @@ func (s *RemoteKeyManagerTestSuite) TestRemoveShareWithMockedOperatorKey() { rm := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: s.client, - consensusClient: s.consensusClient, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: mockSlashingProtector, @@ -112,12 +109,11 @@ func (s *RemoteKeyManagerTestSuite) TestRemoveShareWithMockedOperatorKey() { func (s *RemoteKeyManagerTestSuite) TestSignWithMockedOperatorKey() { rm := &RemoteKeyManager{ - logger: s.logger, - signerClient: s.client, - consensusClient: s.consensusClient, - getOperatorId: func() spectypes.OperatorID { return 1 }, - operatorPubKey: &MockOperatorPublicKey{}, - signLocks: map[signKey]*sync.RWMutex{}, + logger: s.logger, + signerClient: s.client, + getOperatorId: func() spectypes.OperatorID { return 1 }, + operatorPubKey: &MockOperatorPublicKey{}, + signLocks: map[signKey]*sync.RWMutex{}, } payload := []byte("message_to_sign") @@ -139,7 +135,7 @@ func (s *RemoteKeyManagerTestSuite) TestSignError() { rm := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: mockRemoteSigner, slashingProtector: mockSlashingProtector, operatorPubKey: mockOperatorPublicKey, @@ -167,9 +163,8 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectWithMockedOperatorKey() rm := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: s.client, - consensusClient: s.consensusClient, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: mockSlashingProtector, @@ -198,21 +193,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectWithMockedOperatorKey() mockSlashingProtector.On("IsAttestationSlashable", mock.Anything, attestationData).Return(nil) mockSlashingProtector.On("UpdateHighestAttestation", pubKey, attestationData).Return(nil) - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - - genesis := ð2api.Genesis{ - GenesisTime: time.Unix(12345, 0), - GenesisValidatorsRoot: phase0.Root{9, 8, 7}, - GenesisForkVersion: phase0.Version{1, 2, 3, 4}, - } - - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil) - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil) - expectedSignature := phase0.BLSSignature{5, 6, 7} s.client.On("Sign", mock.Anything, pubKey, mock.Anything).Return(expectedSignature, nil) @@ -223,7 +203,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectWithMockedOperatorKey() s.NotEqual([32]byte{}, root) mockSlashingProtector.AssertExpectations(s.T()) s.client.AssertExpectations(s.T()) - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignBeaconBlock (capella)", func() { @@ -265,21 +244,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectWithMockedOperatorKey() mockSlashingProtector.On("IsBeaconBlockSlashable", mock.Anything, blindedBlock.Slot).Return(nil) mockSlashingProtector.On("UpdateHighestProposal", pubKey, blindedBlock.Slot).Return(nil) - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - - genesis := ð2api.Genesis{ - GenesisTime: time.Unix(12345, 0), - GenesisValidatorsRoot: phase0.Root{9, 8, 7}, - GenesisForkVersion: phase0.Version{1, 2, 3, 4}, - } - - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil) - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil) - expectedSignature := phase0.BLSSignature{5, 6, 7} s.client.On("Sign", mock.Anything, pubKey, mock.Anything).Return(expectedSignature, nil) @@ -290,7 +254,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectWithMockedOperatorKey() s.NotEqual([32]byte{}, root) mockSlashingProtector.AssertExpectations(s.T()) s.client.AssertExpectations(s.T()) - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignBeaconBlock (deneb)", func() { @@ -334,21 +297,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectWithMockedOperatorKey() mockSlashingProtector.On("IsBeaconBlockSlashable", mock.Anything, blindedBlock.Slot).Return(nil) mockSlashingProtector.On("UpdateHighestProposal", pubKey, blindedBlock.Slot).Return(nil) - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - - genesis := ð2api.Genesis{ - GenesisTime: time.Unix(12345, 0), - GenesisValidatorsRoot: phase0.Root{9, 8, 7}, - GenesisForkVersion: phase0.Version{1, 2, 3, 4}, - } - - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil) - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil) - expectedSignature := phase0.BLSSignature{5, 6, 7} s.client.On("Sign", mock.Anything, pubKey, mock.Anything).Return(expectedSignature, nil) @@ -359,7 +307,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectWithMockedOperatorKey() s.NotEqual([32]byte{}, root) mockSlashingProtector.AssertExpectations(s.T()) s.client.AssertExpectations(s.T()) - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignBeaconBlock (electra)", func() { @@ -429,21 +376,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectWithMockedOperatorKey() mockSlashingProtector.On("IsBeaconBlockSlashable", mock.Anything, blindedBlock.Slot).Return(nil) mockSlashingProtector.On("UpdateHighestProposal", pubKey, blindedBlock.Slot).Return(nil) - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - - genesis := ð2api.Genesis{ - GenesisTime: time.Unix(12345, 0), - GenesisValidatorsRoot: phase0.Root{9, 8, 7}, - GenesisForkVersion: phase0.Version{1, 2, 3, 4}, - } - - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil) - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil) - expectedSignature := phase0.BLSSignature{5, 6, 7} s.client.On("Sign", mock.Anything, pubKey, mock.Anything).Return(expectedSignature, nil) @@ -454,7 +386,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectWithMockedOperatorKey() s.NotEqual([32]byte{}, root) mockSlashingProtector.AssertExpectations(s.T()) s.client.AssertExpectations(s.T()) - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignBlindedBeaconBlock (capella)", func() { @@ -498,21 +429,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectWithMockedOperatorKey() mockSlashingProtector.On("IsBeaconBlockSlashable", mock.Anything, blindedBlock.Slot).Return(nil) mockSlashingProtector.On("UpdateHighestProposal", pubKey, blindedBlock.Slot).Return(nil) - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - - genesis := ð2api.Genesis{ - GenesisTime: time.Unix(12345, 0), - GenesisValidatorsRoot: phase0.Root{9, 8, 7}, - GenesisForkVersion: phase0.Version{1, 2, 3, 4}, - } - - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil) - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil) - expectedSignature := phase0.BLSSignature{5, 6, 7} s.client.On("Sign", mock.Anything, pubKey, mock.Anything).Return(expectedSignature, nil) @@ -523,7 +439,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectWithMockedOperatorKey() s.NotEqual([32]byte{}, root) mockSlashingProtector.AssertExpectations(s.T()) s.client.AssertExpectations(s.T()) - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignBlindedBeaconBlock (deneb)", func() { @@ -569,21 +484,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectWithMockedOperatorKey() mockSlashingProtector.On("IsBeaconBlockSlashable", mock.Anything, blindedBlock.Slot).Return(nil) mockSlashingProtector.On("UpdateHighestProposal", pubKey, blindedBlock.Slot).Return(nil) - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - - genesis := ð2api.Genesis{ - GenesisTime: time.Unix(12345, 0), - GenesisValidatorsRoot: phase0.Root{9, 8, 7}, - GenesisForkVersion: phase0.Version{1, 2, 3, 4}, - } - - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil) - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil) - expectedSignature := phase0.BLSSignature{5, 6, 7} s.client.On("Sign", mock.Anything, pubKey, mock.Anything).Return(expectedSignature, nil) @@ -594,7 +494,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectWithMockedOperatorKey() s.NotEqual([32]byte{}, root) mockSlashingProtector.AssertExpectations(s.T()) s.client.AssertExpectations(s.T()) - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignBlindedBeaconBlock (electra)", func() { @@ -666,21 +565,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectWithMockedOperatorKey() mockSlashingProtector.On("IsBeaconBlockSlashable", mock.Anything, blindedBlock.Slot).Return(nil) mockSlashingProtector.On("UpdateHighestProposal", pubKey, blindedBlock.Slot).Return(nil) - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - - genesis := ð2api.Genesis{ - GenesisTime: time.Unix(12345, 0), - GenesisValidatorsRoot: phase0.Root{9, 8, 7}, - GenesisForkVersion: phase0.Version{1, 2, 3, 4}, - } - - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil) - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil) - expectedSignature := phase0.BLSSignature{5, 6, 7} s.client.On("Sign", mock.Anything, pubKey, mock.Anything).Return(expectedSignature, nil) @@ -691,113 +575,22 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectWithMockedOperatorKey() s.NotEqual([32]byte{}, root) mockSlashingProtector.AssertExpectations(s.T()) s.client.AssertExpectations(s.T()) - s.consensusClient.AssertExpectations(s.T()) }) } func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { ctx := s.T().Context() - mockSlashingProtector := &MockSlashingProtector{} - - rm := &RemoteKeyManager{ - logger: s.logger, - netCfg: testNetCfg, - signerClient: s.client, - consensusClient: s.consensusClient, - getOperatorId: func() spectypes.OperatorID { return 1 }, - operatorPubKey: &MockOperatorPublicKey{}, - slashingProtector: mockSlashingProtector, - signLocks: map[signKey]*sync.RWMutex{}, - } - slot := phase0.Slot(123) - s.Run("ForkInfoError", func() { - pubKey := phase0.BLSPubKey{1, 2, 3} - domain := phase0.Domain{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32} - attestationData := &phase0.AttestationData{ - Slot: 123, - Index: 1, - BeaconBlockRoot: phase0.Root{1, 2, 3}, - Source: &phase0.Checkpoint{ - Epoch: 10, - Root: phase0.Root{4, 5, 6}, - }, - Target: &phase0.Checkpoint{ - Epoch: 11, - Root: phase0.Root{7, 8, 9}, - }, - } - - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(nil, errors.New("fork info error")) - - signature, root, err := rm.SignBeaconObject(ctx, attestationData, domain, pubKey, slot, spectypes.DomainAttester) - - s.ErrorContains(err, "get fork info") - s.Equal(spectypes.Signature{}, signature) - s.Equal(phase0.Root{}, root) - s.consensusClient.AssertExpectations(s.T()) - }) - - s.Run("SlashingProtectionError", func() { - clientMock := new(MockRemoteSigner) - consensusMock := new(MockConsensusClient) - slashingMock := new(MockSlashingProtector) - - rmTest := &RemoteKeyManager{ - logger: s.logger, - netCfg: testNetCfg, - signerClient: clientMock, - consensusClient: consensusMock, - getOperatorId: func() spectypes.OperatorID { return 1 }, - operatorPubKey: &MockOperatorPublicKey{}, - slashingProtector: slashingMock, - signLocks: map[signKey]*sync.RWMutex{}, - } - - pubKey := phase0.BLSPubKey{1, 2, 3} - domain := phase0.Domain{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32} - attestationData := &phase0.AttestationData{ - Slot: 123, - Index: 1, - BeaconBlockRoot: phase0.Root{1, 2, 3}, - Source: &phase0.Checkpoint{ - Epoch: 10, - Root: phase0.Root{4, 5, 6}, - }, - Target: &phase0.Checkpoint{ - Epoch: 11, - Root: phase0.Root{7, 8, 9}, - }, - } - - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - consensusMock.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - consensusMock.On("Genesis", mock.Anything).Return(nil, errors.New("genesis error")).Once() - - signature, root, err := rmTest.SignBeaconObject(ctx, attestationData, domain, pubKey, slot, spectypes.DomainAttester) - - s.ErrorContains(err, "get fork info: get genesis") - s.Equal(spectypes.Signature{}, signature) - s.Equal(phase0.Root{}, root) - consensusMock.AssertExpectations(s.T()) - }) - s.Run("RemoteSignerError", func() { clientMock := new(MockRemoteSigner) - consensusMock := new(MockConsensusClient) slashingMock := new(MockSlashingProtector) rmTest := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: clientMock, - consensusClient: consensusMock, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: slashingMock, @@ -820,19 +613,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { }, } - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - genesis := ð2api.Genesis{ - GenesisTime: time.Time{}, - GenesisValidatorsRoot: phase0.Root{}, - GenesisForkVersion: phase0.Version{}, - } - - consensusMock.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - consensusMock.On("Genesis", mock.Anything).Return(genesis, nil).Once() slashingMock.On("IsAttestationSlashable", mock.Anything, mock.Anything).Return(nil).Once() slashingMock.On("UpdateHighestAttestation", mock.Anything, mock.Anything).Return(nil).Once() clientMock.On("Sign", mock.Anything, mock.Anything, mock.Anything).Return(phase0.BLSSignature{}, errors.New("sign error")).Once() @@ -842,19 +622,16 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { s.ErrorContains(err, "remote signer") s.Equal(spectypes.Signature{}, signature) s.Equal(phase0.Root{}, root) - consensusMock.AssertExpectations(s.T()) }) s.Run("IsAttestationSlashableError", func() { clientMock := new(MockRemoteSigner) - consensusMock := new(MockConsensusClient) slashingMock := new(MockSlashingProtector) rmTest := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: clientMock, - consensusClient: consensusMock, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: slashingMock, @@ -877,19 +654,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { }, } - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - genesis := ð2api.Genesis{ - GenesisTime: time.Time{}, - GenesisValidatorsRoot: phase0.Root{}, - GenesisForkVersion: phase0.Version{}, - } - - consensusMock.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - consensusMock.On("Genesis", mock.Anything).Return(genesis, nil).Once() slashingMock.On("IsAttestationSlashable", mock.Anything, mock.Anything).Return(errors.New("test error (IsAttestationSlashable)")).Once() slashingMock.On("UpdateHighestAttestation", mock.Anything, mock.Anything).Return(nil).Once() clientMock.On("Sign", mock.Anything, mock.Anything, mock.Anything).Return([]byte{1, 2, 3}, nil).Once() @@ -899,19 +663,16 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { s.ErrorContains(err, "test error (IsAttestationSlashable)") s.Empty(signature) s.Equal(phase0.Root{}, root) - consensusMock.AssertExpectations(s.T()) }) s.Run("UpdateHighestAttestationError", func() { clientMock := new(MockRemoteSigner) - consensusMock := new(MockConsensusClient) slashingMock := new(MockSlashingProtector) rmTest := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: clientMock, - consensusClient: consensusMock, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: slashingMock, @@ -934,19 +695,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { }, } - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - genesis := ð2api.Genesis{ - GenesisTime: time.Time{}, - GenesisValidatorsRoot: phase0.Root{}, - GenesisForkVersion: phase0.Version{}, - } - - consensusMock.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - consensusMock.On("Genesis", mock.Anything).Return(genesis, nil).Once() slashingMock.On("IsAttestationSlashable", mock.Anything, mock.Anything).Return(nil).Once() slashingMock.On("UpdateHighestAttestation", mock.Anything, mock.Anything).Return(errors.New("test error (UpdateHighestAttestation)")).Once() clientMock.On("Sign", mock.Anything, mock.Anything, mock.Anything).Return([]byte{1, 2, 3}, nil).Once() @@ -956,19 +704,16 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { s.ErrorContains(err, "test error (UpdateHighestAttestation)") s.Empty(signature) s.Equal(phase0.Root{}, root) - consensusMock.AssertExpectations(s.T()) }) s.Run("IsBeaconBlockSlashable_Capella", func() { clientMock := new(MockRemoteSigner) - consensusMock := new(MockConsensusClient) slashingMock := new(MockSlashingProtector) rmTest := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: clientMock, - consensusClient: consensusMock, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: slashingMock, @@ -1012,19 +757,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { }, } - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - genesis := ð2api.Genesis{ - GenesisTime: time.Time{}, - GenesisValidatorsRoot: phase0.Root{}, - GenesisForkVersion: phase0.Version{}, - } - - consensusMock.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - consensusMock.On("Genesis", mock.Anything).Return(genesis, nil).Once() slashingMock.On("IsBeaconBlockSlashable", mock.Anything, mock.Anything).Return(errors.New("test error (IsBeaconBlockSlashable)")).Once() slashingMock.On("UpdateHighestProposal", mock.Anything, mock.Anything).Return(nil).Once() clientMock.On("Sign", mock.Anything, mock.Anything, mock.Anything).Return([]byte{1, 2, 3}, nil).Once() @@ -1034,19 +766,16 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { s.ErrorContains(err, "test error (IsBeaconBlockSlashable)") s.Empty(signature) s.Equal(phase0.Root{}, root) - consensusMock.AssertExpectations(s.T()) }) s.Run("UpdateHighestProposal_Capella", func() { clientMock := new(MockRemoteSigner) - consensusMock := new(MockConsensusClient) slashingMock := new(MockSlashingProtector) rmTest := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: clientMock, - consensusClient: consensusMock, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: slashingMock, @@ -1090,19 +819,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { }, } - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - genesis := ð2api.Genesis{ - GenesisTime: time.Time{}, - GenesisValidatorsRoot: phase0.Root{}, - GenesisForkVersion: phase0.Version{}, - } - - consensusMock.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - consensusMock.On("Genesis", mock.Anything).Return(genesis, nil).Once() slashingMock.On("IsBeaconBlockSlashable", mock.Anything, mock.Anything).Return(nil).Once() slashingMock.On("UpdateHighestProposal", mock.Anything, mock.Anything).Return(errors.New("test error (UpdateHighestProposal)")).Once() clientMock.On("Sign", mock.Anything, mock.Anything, mock.Anything).Return([]byte{1, 2, 3}, nil).Once() @@ -1112,19 +828,16 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { s.ErrorContains(err, "test error (UpdateHighestProposal)") s.Empty(signature) s.Equal(phase0.Root{}, root) - consensusMock.AssertExpectations(s.T()) }) s.Run("IsBeaconBlockSlashable_Deneb", func() { clientMock := new(MockRemoteSigner) - consensusMock := new(MockConsensusClient) slashingMock := new(MockSlashingProtector) rmTest := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: clientMock, - consensusClient: consensusMock, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: slashingMock, @@ -1170,19 +883,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { }, } - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - genesis := ð2api.Genesis{ - GenesisTime: time.Time{}, - GenesisValidatorsRoot: phase0.Root{}, - GenesisForkVersion: phase0.Version{}, - } - - consensusMock.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - consensusMock.On("Genesis", mock.Anything).Return(genesis, nil).Once() slashingMock.On("IsBeaconBlockSlashable", mock.Anything, mock.Anything).Return(errors.New("test error (IsBeaconBlockSlashable)")).Once() slashingMock.On("UpdateHighestProposal", mock.Anything, mock.Anything).Return(nil).Once() clientMock.On("Sign", mock.Anything, mock.Anything, mock.Anything).Return([]byte{1, 2, 3}, nil).Once() @@ -1192,19 +892,16 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { s.ErrorContains(err, "test error (IsBeaconBlockSlashable)") s.Empty(signature) s.Equal(phase0.Root{}, root) - consensusMock.AssertExpectations(s.T()) }) s.Run("UpdateHighestProposal_Deneb", func() { clientMock := new(MockRemoteSigner) - consensusMock := new(MockConsensusClient) slashingMock := new(MockSlashingProtector) rmTest := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: clientMock, - consensusClient: consensusMock, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: slashingMock, @@ -1250,19 +947,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { }, } - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - genesis := ð2api.Genesis{ - GenesisTime: time.Time{}, - GenesisValidatorsRoot: phase0.Root{}, - GenesisForkVersion: phase0.Version{}, - } - - consensusMock.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - consensusMock.On("Genesis", mock.Anything).Return(genesis, nil).Once() slashingMock.On("IsBeaconBlockSlashable", mock.Anything, mock.Anything).Return(nil).Once() slashingMock.On("UpdateHighestProposal", mock.Anything, mock.Anything).Return(errors.New("test error (UpdateHighestProposal)")).Once() clientMock.On("Sign", mock.Anything, mock.Anything, mock.Anything).Return([]byte{1, 2, 3}, nil).Once() @@ -1272,19 +956,16 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { s.ErrorContains(err, "test error (UpdateHighestProposal)") s.Empty(signature) s.Equal(phase0.Root{}, root) - consensusMock.AssertExpectations(s.T()) }) s.Run("IsBeaconBlockSlashable_Electra", func() { clientMock := new(MockRemoteSigner) - consensusMock := new(MockConsensusClient) slashingMock := new(MockSlashingProtector) rmTest := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: clientMock, - consensusClient: consensusMock, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: slashingMock, @@ -1355,19 +1036,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { }, } - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - genesis := ð2api.Genesis{ - GenesisTime: time.Time{}, - GenesisValidatorsRoot: phase0.Root{}, - GenesisForkVersion: phase0.Version{}, - } - - consensusMock.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - consensusMock.On("Genesis", mock.Anything).Return(genesis, nil).Once() slashingMock.On("IsBeaconBlockSlashable", mock.Anything, mock.Anything).Return(errors.New("test error (IsBeaconBlockSlashable)")).Once() slashingMock.On("UpdateHighestProposal", mock.Anything, mock.Anything).Return(nil).Once() clientMock.On("Sign", mock.Anything, mock.Anything, mock.Anything).Return([]byte{1, 2, 3}, nil).Once() @@ -1377,19 +1045,16 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { s.ErrorContains(err, "test error (IsBeaconBlockSlashable)") s.Empty(signature) s.Equal(phase0.Root{}, root) - consensusMock.AssertExpectations(s.T()) }) s.Run("UpdateHighestProposal_Electra", func() { clientMock := new(MockRemoteSigner) - consensusMock := new(MockConsensusClient) slashingMock := new(MockSlashingProtector) rmTest := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: clientMock, - consensusClient: consensusMock, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: slashingMock, @@ -1460,19 +1125,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { }, } - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - genesis := ð2api.Genesis{ - GenesisTime: time.Time{}, - GenesisValidatorsRoot: phase0.Root{}, - GenesisForkVersion: phase0.Version{}, - } - - consensusMock.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - consensusMock.On("Genesis", mock.Anything).Return(genesis, nil).Once() slashingMock.On("IsBeaconBlockSlashable", mock.Anything, mock.Anything).Return(nil).Once() slashingMock.On("UpdateHighestProposal", mock.Anything, mock.Anything).Return(errors.New("test error (UpdateHighestProposal)")).Once() clientMock.On("Sign", mock.Anything, mock.Anything, mock.Anything).Return([]byte{1, 2, 3}, nil).Once() @@ -1482,7 +1134,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectErrorCases() { s.ErrorContains(err, "test error (UpdateHighestProposal)") s.Empty(signature) s.Equal(phase0.Root{}, root) - consensusMock.AssertExpectations(s.T()) }) } @@ -1494,9 +1145,8 @@ func (s *RemoteKeyManagerTestSuite) TestAddShareErrorCases() { rmTest := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: clientMock, - consensusClient: s.consensusClient, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: mockSlashingProtector, @@ -1523,9 +1173,8 @@ func (s *RemoteKeyManagerTestSuite) TestAddShareErrorCases() { rmTest := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: clientMock, - consensusClient: s.consensusClient, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: slashingMock, @@ -1555,9 +1204,8 @@ func (s *RemoteKeyManagerTestSuite) TestRemoveShareErrorCases() { rm := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: s.client, - consensusClient: s.consensusClient, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: mockSlashingProtector, @@ -1580,9 +1228,8 @@ func (s *RemoteKeyManagerTestSuite) TestRemoveShareErrorCases() { rmTest := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: clientMock, - consensusClient: s.consensusClient, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: slashingMock, @@ -1608,9 +1255,8 @@ func (s *RemoteKeyManagerTestSuite) TestRemoveShareErrorCases() { rmTest := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: clientMock, - consensusClient: s.consensusClient, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: slashingMock, @@ -1657,7 +1303,7 @@ func (s *RemoteKeyManagerTestSuite) TestSignSSVMessage() { rm := &RemoteKeyManager{ logger: zap.NewNop(), - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: mockRemoteSigner, getOperatorId: func() spectypes.OperatorID { return 1 }, signLocks: map[signKey]*sync.RWMutex{}, @@ -1715,27 +1361,14 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectAdditionalDomains() { rm := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: s.client, - consensusClient: s.consensusClient, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: mockSlashingProtector, signLocks: map[signKey]*sync.RWMutex{}, } - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - - genesis := ð2api.Genesis{ - GenesisTime: time.Unix(12345, 0), - GenesisValidatorsRoot: phase0.Root{9, 8, 7}, - GenesisForkVersion: phase0.Version{1, 2, 3, 4}, - } - pubKey := phase0.BLSPubKey{1, 2, 3} domain := phase0.Domain{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32} expectedSignature := phase0.BLSSignature{5, 6, 7} @@ -1747,8 +1380,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectAdditionalDomains() { ValidatorIndex: 456, } - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() s.client.On("Sign", mock.Anything, pubKey, mock.Anything).Return(expectedSignature, nil).Once() signature, root, err := rm.SignBeaconObject(ctx, voluntaryExit, domain, pubKey, slot, spectypes.DomainVoluntaryExit) @@ -1757,14 +1388,11 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectAdditionalDomains() { s.EqualValues(expectedSignature[:], signature) s.NotEqual([32]byte{}, root) s.client.AssertExpectations(s.T()) - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignSelectionProof", func() { signedSlot := spectypes.SSZUint64(123) - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() s.client.On("Sign", mock.Anything, pubKey, mock.Anything).Return(expectedSignature, nil).Once() signature, root, err := rm.SignBeaconObject(ctx, signedSlot, domain, pubKey, slot, spectypes.DomainSelectionProof) @@ -1773,14 +1401,11 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectAdditionalDomains() { s.EqualValues(expectedSignature[:], signature) s.NotEqual([32]byte{}, root) s.client.AssertExpectations(s.T()) - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignSyncCommittee", func() { blockRoot := phase0.Root{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32} - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() s.client.On("Sign", mock.Anything, pubKey, mock.Anything).Return(expectedSignature, nil).Once() signature, root, err := rm.SignBeaconObject(ctx, spectypes.SSZBytes(blockRoot[:]), domain, pubKey, slot, spectypes.DomainSyncCommittee) @@ -1789,7 +1414,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectAdditionalDomains() { s.EqualValues(expectedSignature[:], signature) s.NotEqual([32]byte{}, root) s.client.AssertExpectations(s.T()) - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignSyncCommitteeSelectionProof", func() { @@ -1798,8 +1422,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectAdditionalDomains() { SubcommitteeIndex: 456, } - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() s.client.On("Sign", mock.Anything, pubKey, mock.Anything).Return(expectedSignature, nil).Once() signature, root, err := rm.SignBeaconObject(ctx, selectionData, domain, pubKey, slot, spectypes.DomainSyncCommitteeSelectionProof) @@ -1808,22 +1430,17 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectAdditionalDomains() { s.EqualValues(expectedSignature[:], signature) s.NotEqual([32]byte{}, root) s.client.AssertExpectations(s.T()) - s.consensusClient.AssertExpectations(s.T()) }) s.Run("InvalidDomainType", func() { signedSlot := spectypes.SSZUint64(123) unknownDomain := phase0.DomainType{255, 255, 255, 255} - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() - signature, root, err := rm.SignBeaconObject(ctx, signedSlot, domain, pubKey, slot, unknownDomain) s.ErrorContains(err, "domain unknown") s.Nil(signature) s.Equal(phase0.Root{}, root) - s.consensusClient.AssertExpectations(s.T()) }) } @@ -1834,27 +1451,14 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectMoreDomains() { rm := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: s.client, - consensusClient: s.consensusClient, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: mockSlashingProtector, signLocks: map[signKey]*sync.RWMutex{}, } - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - - genesis := ð2api.Genesis{ - GenesisTime: time.Unix(12345, 0), - GenesisValidatorsRoot: phase0.Root{9, 8, 7}, - GenesisForkVersion: phase0.Version{1, 2, 3, 4}, - } - pubKey := phase0.BLSPubKey{1, 2, 3} domain := phase0.Domain{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32} expectedSignature := phase0.BLSSignature{5, 6, 7} @@ -1887,8 +1491,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectMoreDomains() { Aggregate: attestation, } - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() s.client.On("Sign", mock.Anything, pubKey, mock.Anything).Return(expectedSignature, nil).Once() signature, root, err := rm.SignBeaconObject(ctx, aggregateAndProof, domain, pubKey, slot, spectypes.DomainAggregateAndProof) @@ -1897,7 +1499,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectMoreDomains() { s.EqualValues(expectedSignature[:], signature) s.NotEqual([32]byte{}, root) s.client.AssertExpectations(s.T()) - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignAggregateAndProofElectra", func() { @@ -1928,8 +1529,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectMoreDomains() { Aggregate: attestation, } - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() s.client.On("Sign", mock.Anything, pubKey, mock.Anything).Return(expectedSignature, nil).Once() signature, root, err := rm.SignBeaconObject(ctx, aggregateAndProof, domain, pubKey, slot, spectypes.DomainAggregateAndProof) @@ -1938,14 +1537,11 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectMoreDomains() { s.EqualValues(expectedSignature[:], signature) s.NotEqual([32]byte{}, root) s.client.AssertExpectations(s.T()) - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignRandao", func() { epoch := spectypes.SSZUint64(42) - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() s.client.On("Sign", mock.Anything, pubKey, mock.Anything).Return(expectedSignature, nil).Once() signature, root, err := rm.SignBeaconObject(ctx, epoch, domain, pubKey, slot, spectypes.DomainRandao) @@ -1954,7 +1550,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectMoreDomains() { s.EqualValues(expectedSignature[:], signature) s.NotEqual([32]byte{}, root) s.client.AssertExpectations(s.T()) - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignApplicationBuilder", func() { @@ -1965,8 +1560,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectMoreDomains() { Pubkey: phase0.BLSPubKey{1, 2, 3, 4, 5}, } - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() s.client.On("Sign", mock.Anything, pubKey, mock.Anything).Return(expectedSignature, nil).Once() signature, root, err := rm.SignBeaconObject(ctx, validatorReg, domain, pubKey, slot, spectypes.DomainApplicationBuilder) @@ -1975,7 +1568,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectMoreDomains() { s.EqualValues(expectedSignature[:], signature) s.NotEqual([32]byte{}, root) s.client.AssertExpectations(s.T()) - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignContributionAndProof", func() { @@ -1991,8 +1583,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectMoreDomains() { SelectionProof: phase0.BLSSignature{1, 2, 3, 4, 5}, } - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() s.client.On("Sign", mock.Anything, pubKey, mock.Anything).Return(expectedSignature, nil).Once() signature, root, err := rm.SignBeaconObject(ctx, contributionAndProof, domain, pubKey, slot, spectypes.DomainContributionAndProof) @@ -2001,7 +1591,6 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectMoreDomains() { s.EqualValues(expectedSignature[:], signature) s.NotEqual([32]byte{}, root) s.client.AssertExpectations(s.T()) - s.consensusClient.AssertExpectations(s.T()) }) } @@ -2012,27 +1601,14 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectTypeCastErrors() { rm := &RemoteKeyManager{ logger: s.logger, - netCfg: testNetCfg, + beaconConfig: testNetCfg, signerClient: s.client, - consensusClient: s.consensusClient, getOperatorId: func() spectypes.OperatorID { return 1 }, operatorPubKey: &MockOperatorPublicKey{}, slashingProtector: mockSlashingProtector, signLocks: map[signKey]*sync.RWMutex{}, } - mockFork := &phase0.Fork{ - PreviousVersion: phase0.Version{1, 2, 3, 4}, - CurrentVersion: phase0.Version{5, 6, 7, 8}, - Epoch: 10, - } - - genesis := ð2api.Genesis{ - GenesisTime: time.Unix(12345, 0), - GenesisValidatorsRoot: phase0.Root{9, 8, 7}, - GenesisForkVersion: phase0.Version{1, 2, 3, 4}, - } - pubKey := phase0.BLSPubKey{1, 2, 3} domain := phase0.Domain{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32} slot := phase0.Slot(123) @@ -2040,121 +1616,81 @@ func (s *RemoteKeyManagerTestSuite) TestSignBeaconObjectTypeCastErrors() { s.Run("AttesterTypeCastError", func() { wrongType := &phase0.VoluntaryExit{} - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() - _, _, err := rm.SignBeaconObject(ctx, wrongType, domain, pubKey, slot, spectypes.DomainAttester) s.ErrorContains(err, "could not cast obj to AttestationData") - s.consensusClient.AssertExpectations(s.T()) }) s.Run("AggregateAndProofTypeCastError", func() { wrongType := &phase0.VoluntaryExit{} - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() - _, _, err := rm.SignBeaconObject(ctx, wrongType, domain, pubKey, slot, spectypes.DomainAggregateAndProof) s.ErrorContains(err, "obj type is unknown") - s.consensusClient.AssertExpectations(s.T()) }) s.Run("RandaoTypeCastError", func() { wrongType := &phase0.VoluntaryExit{} - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() - _, _, err := rm.SignBeaconObject(ctx, wrongType, domain, pubKey, slot, spectypes.DomainRandao) s.ErrorContains(err, "could not cast obj to SSZUint64") - s.consensusClient.AssertExpectations(s.T()) }) s.Run("ApplicationBuilderTypeCastError", func() { wrongType := &phase0.VoluntaryExit{} - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() - _, _, err := rm.SignBeaconObject(ctx, wrongType, domain, pubKey, slot, spectypes.DomainApplicationBuilder) s.ErrorContains(err, "could not cast obj to ValidatorRegistration") - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignContributionAndProofTypeCastError", func() { wrongType := &phase0.VoluntaryExit{} - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() - _, _, err := rm.SignBeaconObject(ctx, wrongType, domain, pubKey, slot, spectypes.DomainContributionAndProof) s.ErrorContains(err, "could not cast obj to ContributionAndProof") - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignSyncCommitteeSelectionProofTypeCastError", func() { wrongType := &phase0.VoluntaryExit{} - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() - _, _, err := rm.SignBeaconObject(ctx, wrongType, domain, pubKey, slot, spectypes.DomainSyncCommitteeSelectionProof) s.ErrorContains(err, "could not cast obj to SyncAggregatorSelectionData") - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignVoluntaryExitTypeCastError", func() { wrongType := spectypes.SSZUint64(1) - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() - _, _, err := rm.SignBeaconObject(ctx, wrongType, domain, pubKey, slot, spectypes.DomainVoluntaryExit) s.ErrorContains(err, "could not cast obj to VoluntaryExit") - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignSyncCommitteeTypeCastError", func() { wrongType := spectypes.SSZUint64(1) - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() - _, _, err := rm.SignBeaconObject(ctx, wrongType, domain, pubKey, slot, spectypes.DomainSyncCommittee) s.ErrorContains(err, "could not cast obj to SSZBytes") - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignSelectionProofTypeCastError", func() { wrongType := &phase0.VoluntaryExit{} - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil).Once() - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil).Once() - _, _, err := rm.SignBeaconObject(ctx, wrongType, domain, pubKey, slot, spectypes.DomainSelectionProof) s.ErrorContains(err, "could not cast obj to SSZUint64") - s.consensusClient.AssertExpectations(s.T()) }) s.Run("SignProposerTypeCastError", func() { wrongType := &phase0.VoluntaryExit{} - s.consensusClient.On("ForkAtEpoch", mock.Anything, mock.Anything).Return(mockFork, nil) - s.consensusClient.On("Genesis", mock.Anything).Return(genesis, nil) - _, _, err := rm.SignBeaconObject(ctx, wrongType, domain, pubKey, slot, spectypes.DomainProposer) s.ErrorContains(err, "obj type is unknown") - s.consensusClient.AssertExpectations(s.T()) }) } @@ -2163,8 +1699,6 @@ func (s *RemoteKeyManagerTestSuite) TestNewRemoteKeyManager() { s.txn.On("Commit").Return(nil).Maybe() s.txn.On("Rollback").Return(nil).Maybe() - networkCfg := networkconfig.NetworkConfig{} - const sampleRSAPublicKey = ` -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArVzXJ1Xm3YIY8QYs2MFL @@ -2191,9 +1725,7 @@ QwIDAQAB logger, testNetCfg, s.client, - s.consensusClient, s.db, - networkCfg, getOperatorId, ) @@ -2207,8 +1739,6 @@ func (s *RemoteKeyManagerTestSuite) TestNewRemoteKeyManager_OperatorIdentity_Wro s.txn.On("Commit").Return(nil).Maybe() s.txn.On("Rollback").Return(nil).Maybe() - networkCfg := networkconfig.NetworkConfig{} - invalidPubKey := "invalid-public-key-format" s.client.On("OperatorIdentity", mock.Anything).Return(invalidPubKey, nil) @@ -2223,9 +1753,7 @@ func (s *RemoteKeyManagerTestSuite) TestNewRemoteKeyManager_OperatorIdentity_Wro logger, testNetCfg, s.client, - s.consensusClient, s.db, - networkCfg, getOperatorId, ) @@ -2239,8 +1767,6 @@ func (s *RemoteKeyManagerTestSuite) TestNewRemoteKeyManager_OperatorIdentity_Err s.txn.On("Commit").Return(nil).Maybe() s.txn.On("Rollback").Return(nil).Maybe() - networkCfg := networkconfig.NetworkConfig{} - s.client.On("OperatorIdentity", mock.Anything).Return("", errors.New("err")) logger, _ := zap.NewDevelopment() @@ -2254,9 +1780,7 @@ func (s *RemoteKeyManagerTestSuite) TestNewRemoteKeyManager_OperatorIdentity_Err logger, testNetCfg, s.client, - s.consensusClient, s.db, - networkCfg, getOperatorId, ) diff --git a/ssvsigner/ekm/signer_storage.go b/ssvsigner/ekm/signer_storage.go index 46f789ebf3..164be8736b 100644 --- a/ssvsigner/ekm/signer_storage.go +++ b/ssvsigner/ekm/signer_storage.go @@ -21,7 +21,7 @@ import ( "go.uber.org/zap" "github.com/ssvlabs/ssv/logging" - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" + "github.com/ssvlabs/ssv/networkconfig" registry "github.com/ssvlabs/ssv/protocol/v2/blockchain/eth1" "github.com/ssvlabs/ssv/storage/basedb" ) @@ -44,6 +44,7 @@ const ( // TODO: review if we need all of them type Storage interface { registry.RegistryStore + core.Storage core.SlashingStore @@ -52,8 +53,6 @@ type Storage interface { SetEncryptionKey(hexKey string) error ListAccountsTxn(r basedb.Reader) ([]core.ValidatorAccount, error) SaveAccountTxn(rw basedb.ReadWriter, account core.ValidatorAccount) error - - BeaconNetwork() beacon.BeaconNetwork } // storage is an internal struct implementing the Storage interface. It uses @@ -64,18 +63,18 @@ type Storage interface { // (walletPrefix, highestAttPrefix, etc.). type storage struct { db basedb.Database - network beacon.BeaconNetwork + beaconConfig networkconfig.Beacon encryptionKey []byte logger *zap.Logger // struct logger is used because core.Storage does not support passing a logger lock sync.RWMutex } -func NewSignerStorage(db basedb.Database, network beacon.BeaconNetwork, logger *zap.Logger) Storage { +func NewSignerStorage(db basedb.Database, beaconConfig networkconfig.Beacon, logger *zap.Logger) Storage { return &storage{ - db: db, - network: network, - logger: logger.Named(logging.NameSignerStorage).Named(prefix + "storage"), - lock: sync.RWMutex{}, + db: db, + beaconConfig: beaconConfig, + logger: logger.Named(logging.NameSignerStorage).Named(prefix + "storage"), + lock: sync.RWMutex{}, } } @@ -102,7 +101,7 @@ func (s *storage) DropRegistryData() error { } func (s *storage) objPrefix(obj string) []byte { - return []byte(string(s.network.GetBeaconNetwork()) + obj) + return []byte(s.beaconConfig.GetNetworkName() + obj) } // Name returns storage name. @@ -112,7 +111,7 @@ func (s *storage) Name() string { // Network returns the network storage is related to. func (s *storage) Network() core.Network { - return core.Network(s.network.GetBeaconNetwork()) + return core.Network(s.beaconConfig.GetNetworkName()) } // SaveWallet stores the given wallet. @@ -429,7 +428,3 @@ func (s *storage) decrypt(nonceCipherText []byte) ([]byte, error) { // #nosec G407 false positive: https://github.com/securego/gosec/issues/1211 return gcm.Open(nil, nonce, ciphertext, nil) } - -func (s *storage) BeaconNetwork() beacon.BeaconNetwork { - return s.network -} diff --git a/ssvsigner/ekm/signer_storage_test.go b/ssvsigner/ekm/signer_storage_test.go index a95ff3e8ca..c1f9fc8bea 100644 --- a/ssvsigner/ekm/signer_storage_test.go +++ b/ssvsigner/ekm/signer_storage_test.go @@ -40,7 +40,7 @@ func newStorageForTest(t *testing.T) (Storage, func()) { return nil, func() {} } - s := NewSignerStorage(db, networkconfig.TestNetwork.Beacon.GetNetwork(), logger) + s := NewSignerStorage(db, networkconfig.TestNetwork, logger) return s, func() { db.Close() } @@ -361,7 +361,7 @@ func TestStorageUtilityFunctions(t *testing.T) { require.NoError(t, err) defer db.Close() - signerStorage := NewSignerStorage(db, networkconfig.TestNetwork.Beacon.GetNetwork(), logger) + signerStorage := NewSignerStorage(db, networkconfig.TestNetwork, logger) err = signerStorage.SetEncryptionKey("aabbccddee") require.NoError(t, err) @@ -379,7 +379,7 @@ func TestStorageUtilityFunctions(t *testing.T) { require.NoError(t, err) defer db.Close() - signerStorage := NewSignerStorage(db, networkconfig.TestNetwork.Beacon.GetNetwork(), logger) + signerStorage := NewSignerStorage(db, networkconfig.TestNetwork, logger) // create a test account wallet := hd.NewWallet(&core.WalletContext{Storage: signerStorage}) diff --git a/ssvsigner/ekm/slashing_protector.go b/ssvsigner/ekm/slashing_protector.go index bd63bde016..62b999b0f1 100644 --- a/ssvsigner/ekm/slashing_protector.go +++ b/ssvsigner/ekm/slashing_protector.go @@ -7,6 +7,8 @@ import ( "github.com/ssvlabs/eth2-key-manager/core" slashingprotection "github.com/ssvlabs/eth2-key-manager/slashing_protection" "go.uber.org/zap" + + "github.com/ssvlabs/ssv/networkconfig" ) // slashing_protector.go provides SlashingProtector, a wrapper around @@ -42,17 +44,20 @@ type slashingProtector interface { // or proposal is slashable. type SlashingProtector struct { logger *zap.Logger + beaconCfg networkconfig.Beacon signerStore Storage protection *slashingprotection.NormalProtection } func NewSlashingProtector( logger *zap.Logger, + beaconCfg networkconfig.Beacon, signerStore Storage, protection *slashingprotection.NormalProtection, ) *SlashingProtector { return &SlashingProtector{ logger: logger, + beaconCfg: beaconCfg, signerStore: signerStore, protection: protection, } @@ -110,7 +115,7 @@ func (sp *SlashingProtector) UpdateHighestProposal(pubKey phase0.BLSPubKey, slot // BumpSlashingProtection updates the slashing protection data for a given public key. func (sp *SlashingProtector) BumpSlashingProtection(pubKey phase0.BLSPubKey) error { - currentSlot := sp.signerStore.BeaconNetwork().EstimatedCurrentSlot() + currentSlot := sp.beaconCfg.EstimatedCurrentSlot() // Update highest attestation data for slashing protection. if err := sp.updateHighestAttestation(pubKey, currentSlot); err != nil { @@ -133,7 +138,7 @@ func (sp *SlashingProtector) updateHighestAttestation(pubKey phase0.BLSPubKey, s return fmt.Errorf("could not retrieve highest attestation: %w", err) } - currentEpoch := sp.signerStore.BeaconNetwork().EstimatedEpochAtSlot(slot) + currentEpoch := sp.beaconCfg.EstimatedEpochAtSlot(slot) minimalSP := sp.computeMinimalAttestationSP(currentEpoch) // Check if the retrieved highest attestation data is valid and not outdated. diff --git a/ssvsigner/ekm/slashing_protector_test.go b/ssvsigner/ekm/slashing_protector_test.go index 114f516060..04c0d0c764 100644 --- a/ssvsigner/ekm/slashing_protector_test.go +++ b/ssvsigner/ekm/slashing_protector_test.go @@ -11,13 +11,15 @@ import ( slashingprotection "github.com/ssvlabs/eth2-key-manager/slashing_protection" spectypes "github.com/ssvlabs/ssv-spec/types" "github.com/ssvlabs/ssv-spec/types/testingutils" + "github.com/ssvlabs/ssv/networkconfig" "github.com/stretchr/testify/require" "github.com/ssvlabs/ssv/logging" - "github.com/ssvlabs/ssv/ssvsigner/keys" "github.com/ssvlabs/ssv/storage/basedb" "github.com/ssvlabs/ssv/storage/kv" "github.com/ssvlabs/ssv/utils" + + "github.com/ssvlabs/ssv/ssvsigner/keys" ) func TestSlashing(t *testing.T) { @@ -36,8 +38,8 @@ func TestSlashing(t *testing.T) { require.NoError(t, km.AddShare(t.Context(), encryptedSK1, phase0.BLSPubKey(sk1.GetPublicKey().Serialize()))) - currentSlot := network.Beacon.EstimatedCurrentSlot() - currentEpoch := network.Beacon.EstimatedEpochAtSlot(currentSlot) + currentSlot := network.EstimatedCurrentSlot() + currentEpoch := network.EstimatedEpochAtSlot(currentSlot) highestTarget := currentEpoch + minSPAttestationEpochGap + 1 highestSource := highestTarget - 1 @@ -255,8 +257,8 @@ func TestConcurrentSlashingProtectionAttData(t *testing.T) { sk1 := &bls.SecretKey{} require.NoError(t, sk1.SetHexString(sk1Str)) - currentSlot := network.Beacon.EstimatedCurrentSlot() - currentEpoch := network.Beacon.EstimatedEpochAtSlot(currentSlot) + currentSlot := network.EstimatedCurrentSlot() + currentEpoch := network.EstimatedEpochAtSlot(currentSlot) highestTarget := currentEpoch + minSPAttestationEpochGap + 1 highestSource := highestTarget - 1 @@ -335,7 +337,7 @@ func TestConcurrentSlashingProtectionBeaconBlock(t *testing.T) { sk1 := &bls.SecretKey{} require.NoError(t, sk1.SetHexString(sk1Str)) - currentSlot := network.Beacon.EstimatedCurrentSlot() + currentSlot := network.EstimatedCurrentSlot() highestProposal := currentSlot + minSPProposalSlotGap + 1 blockContents := testingutils.TestingBlockContentsDeneb @@ -430,8 +432,8 @@ func TestConcurrentSlashingProtectionWithMultipleKeysAttData(t *testing.T) { require.NoError(t, km.AddShare(t.Context(), encryptedPrivKey, phase0.BLSPubKey(validator.sk.GetPublicKey().Serialize()))) } - currentSlot := network.Beacon.EstimatedCurrentSlot() - currentEpoch := network.Beacon.EstimatedEpochAtSlot(currentSlot) + currentSlot := network.EstimatedCurrentSlot() + currentEpoch := network.EstimatedEpochAtSlot(currentSlot) highestTarget := currentEpoch + minSPAttestationEpochGap + 1 highestSource := highestTarget - 1 @@ -534,7 +536,7 @@ func TestConcurrentSlashingProtectionWithMultipleKeysBeaconBlock(t *testing.T) { require.NoError(t, km.AddShare(t.Context(), encryptedPrivKey, phase0.BLSPubKey(validator.sk.GetPublicKey().Serialize()))) } - currentSlot := network.Beacon.EstimatedCurrentSlot() + currentSlot := network.EstimatedCurrentSlot() highestProposal := currentSlot + minSPProposalSlotGap + 1 blockContents := testingutils.TestingBlockContentsDeneb @@ -620,7 +622,7 @@ func TestComprehensiveSlashingBlockProposal(t *testing.T) { require.NoError(t, km.AddShare(t.Context(), encryptedPrivKey, sharePubKey)) // --- First Block Proposal --- - slotToSign := network.Beacon.EstimatedCurrentSlot() + 5 // Sign a block slightly in the future + slotToSign := network.EstimatedCurrentSlot() + 5 // Sign a block slightly in the future // Check directly with IsBeaconBlockSlashable err = km.IsBeaconBlockSlashable(sharePubKey, slotToSign) @@ -699,10 +701,10 @@ func TestSlashableBlockDoubleProposal(t *testing.T) { require.NoError(t, err) defer db.Close() - mockBeacon := utils.SetupMockBeaconNetwork(t, nil) - signerStore := NewSignerStorage(db, mockBeacon, logger) + mockNetworkConfig := utils.SetupMockNetworkConfig(t, networkconfig.TestNetwork.DomainType, nil) + signerStore := NewSignerStorage(db, mockNetworkConfig, logger) protection := slashingprotection.NewNormalProtection(signerStore) - protector := NewSlashingProtector(logger, signerStore, protection) + protector := NewSlashingProtector(logger, mockNetworkConfig, signerStore, protection) // Initialize test share key require.NoError(t, bls.Init(bls.BLS12_381)) @@ -715,7 +717,7 @@ func TestSlashableBlockDoubleProposal(t *testing.T) { require.NoError(t, err, "Failed to bump slashing protection") // --- First Block Proposal --- - slotToSign := mockBeacon.EstimatedCurrentSlot() + 5 // Sign a block slightly in the future + slotToSign := mockNetworkConfig.EstimatedCurrentSlot() + 5 // Sign a block slightly in the future // Check if first block at this slot is slashable - should not be err = protector.IsBeaconBlockSlashable(sharePubKey, slotToSign) @@ -755,10 +757,10 @@ func TestSlashableAttestationDoubleVote(t *testing.T) { require.NoError(t, err) defer db.Close() - mockBeacon := utils.SetupMockBeaconNetwork(t, nil) - signerStore := NewSignerStorage(db, mockBeacon, logger) + mockNetworkConfig := utils.SetupMockNetworkConfig(t, networkconfig.TestNetwork.DomainType, nil) + signerStore := NewSignerStorage(db, mockNetworkConfig, logger) protection := slashingprotection.NewNormalProtection(signerStore) - protector := NewSlashingProtector(logger, signerStore, protection) + protector := NewSlashingProtector(logger, mockNetworkConfig, signerStore, protection) // Initialize test share key require.NoError(t, bls.Init(bls.BLS12_381)) @@ -771,8 +773,8 @@ func TestSlashableAttestationDoubleVote(t *testing.T) { require.NoError(t, err, "Failed to bump slashing protection") // --- First Attestation --- - epochToSign := mockBeacon.EstimatedCurrentEpoch() + 2 - slotToSign := mockBeacon.FirstSlotAtEpoch(epochToSign) + epochToSign := mockNetworkConfig.EstimatedCurrentEpoch() + 2 + slotToSign := mockNetworkConfig.FirstSlotAtEpoch(epochToSign) attData1 := testingutils.TestingAttestationData(spec.DataVersionPhase0) attData1.Slot = slotToSign @@ -806,7 +808,7 @@ func TestSlashableAttestationDoubleVote(t *testing.T) { // --- Third Attestation (Higher Target Epoch) --- epochToSignHigher := epochToSign + 1 - slotToSignHigher := mockBeacon.FirstSlotAtEpoch(epochToSignHigher) + slotToSignHigher := mockNetworkConfig.FirstSlotAtEpoch(epochToSignHigher) attData3 := testingutils.TestingAttestationData(spec.DataVersionPhase0) attData3.Slot = slotToSignHigher @@ -831,10 +833,10 @@ func TestSlashableAttestationSurroundingVote(t *testing.T) { require.NoError(t, err) defer db.Close() - mockBeacon := utils.SetupMockBeaconNetwork(t, nil) - signerStore := NewSignerStorage(db, mockBeacon, logger) + mockNetworkConfig := utils.SetupMockNetworkConfig(t, networkconfig.TestNetwork.DomainType, nil) + signerStore := NewSignerStorage(db, mockNetworkConfig, logger) protection := slashingprotection.NewNormalProtection(signerStore) - protector := NewSlashingProtector(logger, signerStore, protection) + protector := NewSlashingProtector(logger, mockNetworkConfig, signerStore, protection) // Initialize test share key require.NoError(t, bls.Init(bls.BLS12_381)) @@ -847,9 +849,9 @@ func TestSlashableAttestationSurroundingVote(t *testing.T) { require.NoError(t, err, "Failed to bump slashing protection") // --- First Attestation (Inner) --- - innerSourceEpoch := mockBeacon.EstimatedCurrentEpoch() + 2 + innerSourceEpoch := mockNetworkConfig.EstimatedCurrentEpoch() + 2 innerTargetEpoch := innerSourceEpoch + 1 - innerSlot := mockBeacon.FirstSlotAtEpoch(innerTargetEpoch) // Slot within the target epoch + innerSlot := mockNetworkConfig.FirstSlotAtEpoch(innerTargetEpoch) // Slot within the target epoch attDataInner := testingutils.TestingAttestationData(spec.DataVersionPhase0) attDataInner.Slot = innerSlot @@ -868,7 +870,7 @@ func TestSlashableAttestationSurroundingVote(t *testing.T) { // --- Second Attestation (Surrounding) --- outerSourceEpoch := innerSourceEpoch - 1 // Lower source outerTargetEpoch := innerTargetEpoch + 1 // Higher target - outerSlot := mockBeacon.FirstSlotAtEpoch(outerTargetEpoch) + outerSlot := mockNetworkConfig.FirstSlotAtEpoch(outerTargetEpoch) attDataOuter := testingutils.TestingAttestationData(spec.DataVersionPhase0) attDataOuter.Slot = outerSlot @@ -884,7 +886,7 @@ func TestSlashableAttestationSurroundingVote(t *testing.T) { // --- Third Attestation (Non-Surrounding, Higher) --- higherSourceEpoch := innerSourceEpoch // Same source as inner higherTargetEpoch := outerTargetEpoch // Same target as outer (but source is higher than outer's source) - higherSlot := mockBeacon.FirstSlotAtEpoch(higherTargetEpoch) + higherSlot := mockNetworkConfig.FirstSlotAtEpoch(higherTargetEpoch) attDataHigher := testingutils.TestingAttestationData(spec.DataVersionPhase0) attDataHigher.Slot = higherSlot @@ -910,10 +912,10 @@ func TestSlashingDBIntegrity(t *testing.T) { db, err := kv.New(logger, basedb.Options{Path: dbPath}) require.NoError(t, err) - mockBeacon := utils.SetupMockBeaconNetwork(t, nil) - signerStore := NewSignerStorage(db, mockBeacon, logger) + mockNetworkConfig := utils.SetupMockNetworkConfig(t, networkconfig.TestNetwork.DomainType, nil) + signerStore := NewSignerStorage(db, mockNetworkConfig, logger) protection := slashingprotection.NewNormalProtection(signerStore) - protector := NewSlashingProtector(logger, signerStore, protection) + protector := NewSlashingProtector(logger, mockNetworkConfig, signerStore, protection) // Initialize test share key require.NoError(t, bls.Init(bls.BLS12_381)) @@ -926,7 +928,7 @@ func TestSlashingDBIntegrity(t *testing.T) { require.NoError(t, err, "DB Integrity: Failed to bump slashing protection (Phase 1)") // Sign a block - slotToSign := mockBeacon.EstimatedCurrentSlot() + 5 + slotToSign := mockNetworkConfig.EstimatedCurrentSlot() + 5 err = protector.IsBeaconBlockSlashable(sharePubKey, slotToSign) require.NoError(t, err, "DB Integrity: First block should not be slashable (Phase 1)") @@ -946,9 +948,9 @@ func TestSlashingDBIntegrity(t *testing.T) { require.NoError(t, err) defer db2.Close() - signerStore2 := NewSignerStorage(db2, mockBeacon, logger) + signerStore2 := NewSignerStorage(db2, mockNetworkConfig, logger) protection2 := slashingprotection.NewNormalProtection(signerStore2) - protector2 := NewSlashingProtector(logger, signerStore2, protection2) + protector2 := NewSlashingProtector(logger, mockNetworkConfig, signerStore2, protection2) // Attempt to sign the *same* block again - should fail due to persisted data t.Log("Attempting to sign the same block again - should fail") @@ -968,10 +970,10 @@ func TestSlashingConcurrency(t *testing.T) { require.NoError(t, err) defer db.Close() - mockBeacon := utils.SetupMockBeaconNetwork(t, nil) - signerStore := NewSignerStorage(db, mockBeacon, logger) + mockNetworkConfig := utils.SetupMockNetworkConfig(t, networkconfig.TestNetwork.DomainType, nil) + signerStore := NewSignerStorage(db, mockNetworkConfig, logger) protection := slashingprotection.NewNormalProtection(signerStore) - protector := NewSlashingProtector(logger, signerStore, protection) + protector := NewSlashingProtector(logger, mockNetworkConfig, signerStore, protection) // Initialize test share key require.NoError(t, bls.Init(bls.BLS12_381)) @@ -984,7 +986,7 @@ func TestSlashingConcurrency(t *testing.T) { require.NoError(t, err, "Concurrency: Failed to bump slashing protection") // Sign a valid block first to establish a baseline - slotToSign := mockBeacon.EstimatedCurrentSlot() + 5 + slotToSign := mockNetworkConfig.EstimatedCurrentSlot() + 5 err = protector.IsBeaconBlockSlashable(sharePubKey, slotToSign) require.NoError(t, err, "Concurrency: First block should not be slashable") @@ -1032,8 +1034,8 @@ func TestSlashingConcurrency(t *testing.T) { } case 3: // Check a new attestation - epoch := mockBeacon.EstimatedCurrentEpoch() + phase0.Epoch(routineID%10) + 1 - slot := mockBeacon.FirstSlotAtEpoch(epoch) + epoch := mockNetworkConfig.EstimatedCurrentEpoch() + phase0.Epoch(routineID%10) + 1 + slot := mockNetworkConfig.FirstSlotAtEpoch(epoch) attData := &phase0.AttestationData{ Slot: slot, Index: 0, diff --git a/ssvsigner/go.mod b/ssvsigner/go.mod index b6e9853bcd..62d57e2930 100644 --- a/ssvsigner/go.mod +++ b/ssvsigner/go.mod @@ -25,8 +25,8 @@ require ( github.com/holiman/uint256 v1.3.2 github.com/microsoft/go-crypto-openssl v0.2.9 github.com/prysmaticlabs/go-bitfield v0.0.0-20240618144021-706c95b2dd15 - github.com/ssvlabs/eth2-key-manager v1.5.2 - github.com/ssvlabs/ssv v1.2.1-0.20250512164336-eaf440d63581 + github.com/ssvlabs/eth2-key-manager v1.5.5 + github.com/ssvlabs/ssv v1.2.1-0.20250527163031-8fc6fde91908 github.com/ssvlabs/ssv-spec v1.1.3 github.com/stretchr/testify v1.9.0 github.com/valyala/fasthttp v1.58.0 diff --git a/ssvsigner/go.sum b/ssvsigner/go.sum index 4a60f08b08..df0965e587 100644 --- a/ssvsigner/go.sum +++ b/ssvsigner/go.sum @@ -320,10 +320,10 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/ssvlabs/eth2-key-manager v1.5.2 h1:gF+8FJkoV1VXpVCPspyVW/Jdky0kt9Pndk88W8ePqx8= -github.com/ssvlabs/eth2-key-manager v1.5.2/go.mod h1:yeUzAP+SBJXgeXPiGBrLeLuHIQCpeJZV7Jz3Fwzm/zk= -github.com/ssvlabs/ssv v1.2.1-0.20250512164336-eaf440d63581 h1:Tp0RSFcC70HFzCikFvLdnx8+xBTl2muZLERX9TGvmT8= -github.com/ssvlabs/ssv v1.2.1-0.20250512164336-eaf440d63581/go.mod h1:BbVBaDhZhJqqeXYM18RUxebPVcdMTtxLVEs21uktSpg= +github.com/ssvlabs/eth2-key-manager v1.5.5 h1:AzwNowGvcmVpRCVHq10FVnpkVIUpoDo5YbJWNMnYA8Q= +github.com/ssvlabs/eth2-key-manager v1.5.5/go.mod h1:yeUzAP+SBJXgeXPiGBrLeLuHIQCpeJZV7Jz3Fwzm/zk= +github.com/ssvlabs/ssv v1.2.1-0.20250527163031-8fc6fde91908 h1:VJZGVaaxf73qbD2LacQK8AtDLzkh/ACgQjK0y9lKVEQ= +github.com/ssvlabs/ssv v1.2.1-0.20250527163031-8fc6fde91908/go.mod h1:DT9oD3vPPTRXNgSlpJZRkPw5Oq7JVO9P5m8IzbiBsGQ= github.com/ssvlabs/ssv-spec v1.1.3 h1:46K31kI4/vA7Vp3DaOuN7t2IABAmzeiMniCqYfzzpo8= github.com/ssvlabs/ssv-spec v1.1.3/go.mod h1:pto7dDv99uVfCZidiLrrKgFR6VYy6WY3PGI1TiGCsIU= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= diff --git a/utils/boot_node/node.go b/utils/boot_node/node.go index 77dca55ea0..6debfdd583 100644 --- a/utils/boot_node/node.go +++ b/utils/boot_node/node.go @@ -50,11 +50,11 @@ type bootNode struct { externalIP string tcpPort uint16 dbPath string - network networkconfig.NetworkConfig + ssvConfig networkconfig.SSVConfig } // New is the constructor of ssvNode -func New(logger *zap.Logger, networkConfig networkconfig.NetworkConfig, opts Options) (Node, error) { +func New(logger *zap.Logger, ssvConfig networkconfig.SSVConfig, opts Options) (Node, error) { return &bootNode{ logger: logger.Named(logging.NameBootNode), privateKey: opts.PrivateKey, @@ -63,7 +63,7 @@ func New(logger *zap.Logger, networkConfig networkconfig.NetworkConfig, opts Opt externalIP: opts.ExternalIP, tcpPort: opts.TCPPort, dbPath: opts.DbPath, - network: networkConfig, + ssvConfig: ssvConfig, }, nil } @@ -110,9 +110,9 @@ func (n *bootNode) Start(ctx context.Context) error { listener := n.createListener(ipAddr, n.discv5port, privKey) node := listener.LocalNode().Node() n.logger.Info("Running", - zap.String("node", node.String()), - zap.String("network", n.network.Name), - fields.ProtocolID(n.network.DiscoveryProtocolID), + zap.Stringer("node", node), + zap.Stringer("config", n.ssvConfig), + fields.ProtocolID(n.ssvConfig.DiscoveryProtocolID), ) handler := &handler{ @@ -171,7 +171,7 @@ func (n *bootNode) createListener(ipAddr string, port uint16, privateKey *ecdsa. listener, err := discover.ListenV5(conn, localNode, discover.Config{ PrivateKey: privateKey, - V5ProtocolID: &n.network.DiscoveryProtocolID, + V5ProtocolID: &n.ssvConfig.DiscoveryProtocolID, }) if err != nil { log.Fatal(err) diff --git a/utils/testutils.go b/utils/testutils.go index 47d38ba30a..6a0a065c9f 100644 --- a/utils/testutils.go +++ b/utils/testutils.go @@ -9,7 +9,7 @@ import ( spectypes "github.com/ssvlabs/ssv-spec/types" "go.uber.org/mock/gomock" - mocknetwork "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon/mocks" + "github.com/ssvlabs/ssv/networkconfig" ) type SlotValue struct { @@ -29,7 +29,7 @@ func (sv *SlotValue) GetSlot() phase0.Slot { return sv.slot } -func SetupMockBeaconNetwork(t *testing.T, currentSlot *SlotValue) *mocknetwork.MockBeaconNetwork { +func SetupMockNetworkConfig(t *testing.T, domainType spectypes.DomainType, currentSlot *SlotValue) *networkconfig.MockNetwork { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -39,55 +39,68 @@ func SetupMockBeaconNetwork(t *testing.T, currentSlot *SlotValue) *mocknetwork.M } beaconNetwork := spectypes.HoleskyNetwork // it must be something known by ekm - mockBeaconNetwork := mocknetwork.NewMockBeaconNetwork(ctrl) + mockNetwork := networkconfig.NewMockNetwork(ctrl) - mockBeaconNetwork.EXPECT().GetBeaconNetwork().Return(beaconNetwork).AnyTimes() - mockBeaconNetwork.EXPECT().SlotsPerEpoch().Return(beaconNetwork.SlotsPerEpoch()).AnyTimes() - mockBeaconNetwork.EXPECT().EstimatedCurrentSlot().DoAndReturn( + mockNetwork.EXPECT().GetSlotsPerEpoch().Return(beaconNetwork.SlotsPerEpoch()).AnyTimes() + mockNetwork.EXPECT().EstimatedCurrentSlot().DoAndReturn( func() phase0.Slot { return currentSlot.GetSlot() }, ).AnyTimes() - mockBeaconNetwork.EXPECT().EstimatedCurrentEpoch().DoAndReturn( + mockNetwork.EXPECT().EstimatedCurrentEpoch().DoAndReturn( func() phase0.Epoch { return phase0.Epoch(uint64(currentSlot.GetSlot()) / beaconNetwork.SlotsPerEpoch()) }, ).AnyTimes() - mockBeaconNetwork.EXPECT().EstimatedEpochAtSlot(gomock.Any()).DoAndReturn( + mockNetwork.EXPECT().EstimatedEpochAtSlot(gomock.Any()).DoAndReturn( func(slot phase0.Slot) phase0.Epoch { return beaconNetwork.EstimatedEpochAtSlot(slot) }, ).AnyTimes() - mockBeaconNetwork.EXPECT().FirstSlotAtEpoch(gomock.Any()).DoAndReturn( + mockNetwork.EXPECT().FirstSlotAtEpoch(gomock.Any()).DoAndReturn( func(epoch phase0.Epoch) phase0.Slot { return beaconNetwork.FirstSlotAtEpoch(epoch) }, ).AnyTimes() - mockBeaconNetwork.EXPECT().IsFirstSlotOfEpoch(gomock.Any()).DoAndReturn( + mockNetwork.EXPECT().IsFirstSlotOfEpoch(gomock.Any()).DoAndReturn( func(slot phase0.Slot) bool { - return uint64(slot)%mockBeaconNetwork.SlotsPerEpoch() == 0 + return uint64(slot)%mockNetwork.GetSlotsPerEpoch() == 0 }, ).AnyTimes() - mockBeaconNetwork.EXPECT().GetSlotStartTime(gomock.Any()).DoAndReturn( + mockNetwork.EXPECT().GetSlotStartTime(gomock.Any()).DoAndReturn( func(slot phase0.Slot) time.Time { - timeSinceGenesisStart := int64(uint64(slot) * uint64(beaconNetwork.SlotDurationSec().Seconds())) // #nosec G115 - minGenesisTime := int64(mockBeaconNetwork.GetBeaconNetwork().MinGenesisTime()) // #nosec G115 - start := time.Unix(minGenesisTime+timeSinceGenesisStart, 0) + timeSinceGenesisStart := time.Duration(slot) & beaconNetwork.SlotDurationSec() // #nosec G115 + minGenesisTime := mockNetwork.GetGenesisTime() // #nosec G115 + start := minGenesisTime.Add(timeSinceGenesisStart) return start }, ).AnyTimes() - mockBeaconNetwork.EXPECT().SlotDurationSec().DoAndReturn( + mockNetwork.EXPECT().GetSlotDuration().DoAndReturn( func() time.Duration { return 12 * time.Second }, ).AnyTimes() - mockBeaconNetwork.EXPECT().SlotsPerEpoch().DoAndReturn( - func() uint64 { + mockNetwork.EXPECT().GetSlotsPerEpoch().DoAndReturn( + func() phase0.Slot { return 32 }, ).AnyTimes() - return mockBeaconNetwork + mockNetwork.EXPECT().GetGenesisTime().DoAndReturn( + func() time.Time { + return time.Unix(int64(beaconNetwork.MinGenesisTime()), 0) // #nosec G115 -- genesis time is never above int64 + }, + ).AnyTimes() + + mockNetwork.EXPECT().EpochDuration().Return(time.Duration(mockNetwork.GetSlotsPerEpoch()) * mockNetwork.GetSlotDuration()).AnyTimes() // #nosec G115 + + mockNetwork.EXPECT().IntervalDuration().Return(mockNetwork.GetSlotDuration() / 3).AnyTimes() // #nosec G115 + + mockNetwork.EXPECT().GetDomainType().Return(domainType).AnyTimes() + + mockNetwork.EXPECT().GetNetworkName().Return(string(beaconNetwork)).AnyTimes() + + return mockNetwork }