From c1c388b223c736ae634a56dab768e5d612dea25c Mon Sep 17 00:00:00 2001 From: iurii Date: Mon, 18 Nov 2024 13:28:09 +0200 Subject: [PATCH] aggregate commit for https://github.com/ssvlabs/ssv/pull/1798 to trigger CI run --- beacon/goclient/attest.go | 71 ++++- beacon/goclient/attest_test.go | 308 +++++++++++++++++++++ beacon/goclient/goclient.go | 48 +++- beacon/goclient/goclient_test.go | 88 +++--- go.mod | 37 +-- go.sum | 99 ++++--- protocol/v2/blockchain/beacon/duty_data.go | 77 ------ protocol/v2/ssv/runner/committee.go | 6 +- utils/hashmap/hashmap.go | 7 + utils/hashmap/hashmap_test.go | 44 ++- 10 files changed, 569 insertions(+), 216 deletions(-) create mode 100644 beacon/goclient/attest_test.go delete mode 100644 protocol/v2/blockchain/beacon/duty_data.go diff --git a/beacon/goclient/attest.go b/beacon/goclient/attest.go index 7b8dab1dc0..ead167774e 100644 --- a/beacon/goclient/attest.go +++ b/beacon/goclient/attest.go @@ -9,6 +9,7 @@ import ( eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/jellydator/ttlcache/v3" ) // AttesterDuties returns attester duties for a given epoch. @@ -27,22 +28,70 @@ func (gc *GoClient) AttesterDuties(ctx context.Context, epoch phase0.Epoch, vali return resp.Data, nil } -func (gc *GoClient) GetAttestationData(slot phase0.Slot, committeeIndex phase0.CommitteeIndex) (*phase0.AttestationData, spec.DataVersion, error) { - attDataReqStart := time.Now() - resp, err := gc.client.AttestationData(gc.ctx, &api.AttestationDataOpts{ - Slot: slot, - CommitteeIndex: committeeIndex, +// GetAttestationData returns attestation data for a given slot and committeeIndex. +// Multiple calls for the same slot are joined into a single request, after which +// the result is cached for a short duration, deep copied and returned +// with the provided committeeIndex set. +func (gc *GoClient) GetAttestationData(slot phase0.Slot, committeeIndex phase0.CommitteeIndex) ( + *phase0.AttestationData, + spec.DataVersion, + error, +) { + // Check cache. + cachedResult := gc.attestationDataCache.Get(slot) + if cachedResult != nil { + data, err := withCommitteeIndex(cachedResult.Value(), committeeIndex) + if err != nil { + return nil, DataVersionNil, fmt.Errorf("failed to set committee index: %w", err) + } + return data, spec.DataVersionPhase0, nil + } + + // Have to make beacon node request and cache the result. + result, err, _ := gc.attestationReqInflight.Do(slot, func() (*phase0.AttestationData, error) { + attDataReqStart := time.Now() + resp, err := gc.client.AttestationData(gc.ctx, &api.AttestationDataOpts{ + Slot: slot, + }) + metricsAttesterDataRequest.Observe(time.Since(attDataReqStart).Seconds()) + if err != nil { + return nil, fmt.Errorf("failed to get attestation data: %w", err) + } + if resp == nil { + return nil, fmt.Errorf("attestation data response is nil") + } + + // Caching resulting value here (as part of inflight request) guarantees only 1 request + // will ever be done for a given slot. + gc.attestationDataCache.Set(slot, resp.Data, ttlcache.DefaultTTL) + + return resp.Data, nil }) if err != nil { - return nil, DataVersionNil, fmt.Errorf("failed to get attestation data: %w", err) - } - if resp == nil { - return nil, DataVersionNil, fmt.Errorf("attestation data response is nil") + return nil, DataVersionNil, err } - metricsAttesterDataRequest.Observe(time.Since(attDataReqStart).Seconds()) + data, err := withCommitteeIndex(result, committeeIndex) + if err != nil { + return nil, DataVersionNil, fmt.Errorf("failed to set committee index: %w", err) + } + return data, spec.DataVersionPhase0, nil +} - return resp.Data, spec.DataVersionPhase0, nil +// withCommitteeIndex returns a deep copy of the attestation data with the provided committee index set. +func withCommitteeIndex(data *phase0.AttestationData, committeeIndex phase0.CommitteeIndex) (*phase0.AttestationData, error) { + // Marshal & unmarshal to make a deep copy. This is safer because it won't break silently if + // a new field is added to AttestationData. + ssz, err := data.MarshalSSZ() + if err != nil { + return nil, fmt.Errorf("failed to marshal attestation data: %w", err) + } + var cpy phase0.AttestationData + if err := cpy.UnmarshalSSZ(ssz); err != nil { + return nil, fmt.Errorf("failed to unmarshal attestation data: %w", err) + } + cpy.Index = committeeIndex + return &cpy, nil } // SubmitAttestations implements Beacon interface diff --git a/beacon/goclient/attest_test.go b/beacon/goclient/attest_test.go new file mode 100644 index 0000000000..e3f653e760 --- /dev/null +++ b/beacon/goclient/attest_test.go @@ -0,0 +1,308 @@ +package goclient + +import ( + "context" + "fmt" + "math/rand" + "net/http" + "net/http/httptest" + "strconv" + "testing" + "time" + + "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" + operatordatastore "github.com/ssvlabs/ssv/operator/datastore" + "github.com/ssvlabs/ssv/operator/slotticker" + "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" + registrystorage "github.com/ssvlabs/ssv/registry/storage" + "github.com/ssvlabs/ssv/utils/hashmap" + "github.com/stretchr/testify/require" + "go.uber.org/zap" +) + +func TestGoClient_GetAttestationData(t *testing.T) { + ctx := context.Background() + // roots is a bunch of random roots. + roots := []string{ + "0x1662a3d288b0338436d74083b4ce68908a0ece0661aa236acd95c8a4c3f6e8fc", + "0x631d529ec5a78dcdeed7c253549a937d12cecbf9090e4a6ba9bd62141d62fa46", + "0x623ec12028d777d55a9e007c8bcd3fb6262878f2dfd00d1d810374b8f63df7b3", + "0x623ec12028d777d55a9e007ccccc3fb6262878f2dfd00d1d810374b8f63df7b3", + "0x623ec12028d777d55a9e007ddddd3fb6262878f2dfd00d1d810374b8f63df7b3", + } + // epochs is a bunch of random epochs. + epochs := []int64{318584, 318585, 318586, 318587, 318588} + + newMockServer := func() (server *httptest.Server, serverGotRequests *hashmap.Map[phase0.Slot, int]) { + // serverGotRequests keeps track of server requests made (in thread-safe manner). + serverGotRequests = hashmap.New[phase0.Slot, int]() + + server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + t.Logf("mock server handling request: %s", r.URL.Path) + + expInitRequests := map[string][]byte{ + "/eth/v1/node/syncing": []byte(`{ + "data": { + "head_slot": "4239945", + "sync_distance": "1", + "is_syncing": false, + "is_optimistic": false, + "el_offline": false + } + }`), + "/eth/v1/node/version": []byte(`{ + "data": { + "version": "Lighthouse/v4.5.0-441fc16/x86_64-linux" + } + }`), + } + for reqPath, respData := range expInitRequests { + if reqPath == r.URL.Path { + w.Header().Set("Content-Type", "application/json") + if _, err := w.Write(respData); err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + return + } + } + + require.Equal(t, http.MethodGet, r.Method) + require.Equal(t, "/eth/v1/validator/attestation_data", r.URL.Path) + + slotStr := r.URL.Query().Get("slot") + slotUint, err := strconv.ParseUint(slotStr, 10, 64) + require.NoError(t, err) + require.NotZero(t, slotUint) + slot := phase0.Slot(slotUint) + + committeeIndexStr := r.URL.Query().Get("committee_index") + committeeIndex, err := strconv.ParseUint(committeeIndexStr, 10, 64) + require.NoError(t, err) + require.Zero(t, committeeIndex) + + // Record this server request. + for { + prevCnt, _ := serverGotRequests.GetOrSet(slot, 0) + success := serverGotRequests.CompareAndSwap(slot, prevCnt, prevCnt+1) + if success { + break + } + } + + blockRoot := roots[rand.Int()%len(roots)] + sourceRoot := roots[rand.Int()%len(roots)] + targetRoot := roots[rand.Int()%len(roots)] + sourceEpoch := epochs[rand.Int()%len(epochs)] + targetEpoch := epochs[rand.Int()%len(epochs)] + + resp := []byte(fmt.Sprintf(`{ + "data": { + "slot": "%d", + "index": "%d", + "beacon_block_root": "%s", + "source": { + "epoch": "%d", + "root": "%s" + }, + "target": { + "epoch": "%d", + "root": "%s" + } + } + }`, slot, committeeIndex, blockRoot, sourceEpoch, sourceRoot, targetEpoch, targetRoot)) + + w.Header().Set("Content-Type", "application/json") + if _, err := w.Write(resp); err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + })) + + return server, serverGotRequests + } + + t.Run("requests must be cached (per slot)", func(t *testing.T) { + slot1 := phase0.Slot(12345678) + slot2 := phase0.Slot(12345679) + committeeIndex1 := phase0.CommitteeIndex(1) + committeeIndex2 := phase0.CommitteeIndex(2) + + server, serverGotRequests := newMockServer() + + client, err := New( + zap.NewNop(), + beacon.Options{ + Context: ctx, + Network: beacon.NewNetwork(types.MainNetwork), + BeaconNodeAddr: server.URL, + CommonTimeout: 1 * time.Second, + LongTimeout: 1 * time.Second, + }, + operatordatastore.New(®istrystorage.OperatorData{ID: 1}), + func() slotticker.SlotTicker { + return slotticker.New(zap.NewNop(), slotticker.Config{ + SlotDuration: 12 * time.Second, + GenesisTime: time.Now(), + }) + }, + ) + require.NoError(t, err) + + // First request with slot1. + gotResult1a, gotVersion, err := client.GetAttestationData(slot1, committeeIndex1) + require.NoError(t, err) + require.Equal(t, spec.DataVersionPhase0, gotVersion) + require.Equal(t, slot1, gotResult1a.Slot) + require.Equal(t, committeeIndex1, gotResult1a.Index) + require.NotEmpty(t, gotResult1a.BeaconBlockRoot) + require.NotEmpty(t, gotResult1a.Source.Epoch) + require.NotEmpty(t, gotResult1a.Source.Root) + require.NotEmpty(t, gotResult1a.Target.Epoch) + require.NotEmpty(t, gotResult1a.Target.Root) + + // Second request with slot1, result should have been cached. + gotResult1b, gotVersion, err := client.GetAttestationData(slot1, committeeIndex1) + require.NoError(t, err) + require.Equal(t, spec.DataVersionPhase0, gotVersion) + require.Equal(t, slot1, gotResult1b.Slot) + require.Equal(t, committeeIndex1, gotResult1b.Index) + require.NotEmpty(t, gotResult1b.BeaconBlockRoot) + require.NotEmpty(t, gotResult1b.Source.Epoch) + require.NotEmpty(t, gotResult1b.Source.Root) + require.NotEmpty(t, gotResult1b.Target.Epoch) + require.NotEmpty(t, gotResult1b.Target.Root) + // Cached result returned must contain the same data. + require.Equal(t, gotResult1b.BeaconBlockRoot, gotResult1a.BeaconBlockRoot) + require.Equal(t, gotResult1b.Source.Epoch, gotResult1a.Source.Epoch) + require.Equal(t, gotResult1b.Source.Root, gotResult1a.Source.Root) + require.Equal(t, gotResult1b.Target.Epoch, gotResult1a.Target.Epoch) + require.Equal(t, gotResult1b.Target.Root, gotResult1a.Target.Root) + + // Third request with slot2. + gotResult2a, gotVersion, err := client.GetAttestationData(slot2, committeeIndex2) + require.NoError(t, err) + require.Equal(t, spec.DataVersionPhase0, gotVersion) + require.Equal(t, slot2, gotResult2a.Slot) + require.Equal(t, committeeIndex2, gotResult2a.Index) + require.NotEmpty(t, gotResult2a.BeaconBlockRoot) + require.NotEmpty(t, gotResult2a.Source.Epoch) + require.NotEmpty(t, gotResult2a.Source.Root) + require.NotEmpty(t, gotResult2a.Target.Epoch) + require.NotEmpty(t, gotResult2a.Target.Root) + + // Fourth request with slot2, result should have been cached. + gotResult2b, gotVersion, err := client.GetAttestationData(slot2, committeeIndex2) + require.NoError(t, err) + require.Equal(t, spec.DataVersionPhase0, gotVersion) + require.Equal(t, slot2, gotResult2b.Slot) + require.Equal(t, committeeIndex2, gotResult2b.Index) + require.NotEmpty(t, gotResult2b.BeaconBlockRoot) + require.NotEmpty(t, gotResult2b.Source.Epoch) + require.NotEmpty(t, gotResult2b.Source.Root) + require.NotEmpty(t, gotResult2b.Target.Epoch) + require.NotEmpty(t, gotResult2b.Target.Root) + // Cached result returned must contain the same data. + require.Equal(t, gotResult2b.BeaconBlockRoot, gotResult2a.BeaconBlockRoot) + require.Equal(t, gotResult2b.Source.Epoch, gotResult2a.Source.Epoch) + require.Equal(t, gotResult2b.Source.Root, gotResult2a.Source.Root) + require.Equal(t, gotResult2b.Target.Epoch, gotResult2a.Target.Epoch) + require.Equal(t, gotResult2b.Target.Root, gotResult2a.Target.Root) + + // Second request with slot1, result STILL should be cached. + gotResult1c, gotVersion, err := client.GetAttestationData(slot1, committeeIndex1) + require.NoError(t, err) + require.Equal(t, spec.DataVersionPhase0, gotVersion) + require.Equal(t, slot1, gotResult1c.Slot) + require.Equal(t, committeeIndex1, gotResult1c.Index) + require.NotEmpty(t, gotResult1c.BeaconBlockRoot) + require.NotEmpty(t, gotResult1c.Source.Epoch) + require.NotEmpty(t, gotResult1c.Source.Root) + require.NotEmpty(t, gotResult1c.Target.Epoch) + require.NotEmpty(t, gotResult1c.Target.Root) + // Cached result returned must contain the same data. + require.Equal(t, gotResult1c.BeaconBlockRoot, gotResult1a.BeaconBlockRoot) + require.Equal(t, gotResult1c.Source.Epoch, gotResult1a.Source.Epoch) + require.Equal(t, gotResult1c.Source.Root, gotResult1a.Source.Root) + require.Equal(t, gotResult1c.Target.Epoch, gotResult1a.Target.Epoch) + require.Equal(t, gotResult1c.Target.Root, gotResult1a.Target.Root) + + uniqueSlots := serverGotRequests.SlowLen() + require.Equal(t, 2, uniqueSlots) + reqCntSlot1, ok := serverGotRequests.Get(slot1) + require.True(t, ok) + require.Equal(t, 1, reqCntSlot1) + reqCntSlot2, ok := serverGotRequests.Get(slot2) + require.True(t, ok) + require.Equal(t, 1, reqCntSlot2) + }) + + t.Run("concurrency: race conditions and deadlocks", func(t *testing.T) { + server, serverGotRequests := newMockServer() + + client, err := New( + zap.NewNop(), + beacon.Options{ + Context: ctx, + Network: beacon.NewNetwork(types.MainNetwork), + BeaconNodeAddr: server.URL, + CommonTimeout: 1 * time.Second, + LongTimeout: 1 * time.Second, + }, + operatordatastore.New(®istrystorage.OperatorData{ID: 1}), + func() slotticker.SlotTicker { + return slotticker.New(zap.NewNop(), slotticker.Config{ + SlotDuration: 12 * time.Second, + GenesisTime: time.Now(), + }) + }, + ) + require.NoError(t, err) + + // slotsTotalCnt is how many slots we want to spread our GetAttestationData requests between. + const slotsTotalCnt = 10 + // slotStartPos start at some non-0 slot (GetAttestationData requests will be made in the slot + // range [slotStartPos, slotStartPos + slotsTotalCnt). + const slotStartPos = 100000000 + + gotResults := hashmap.New[phase0.Slot, *phase0.AttestationData]() + + p := pool.New() + for i := 0; i < 1000; i++ { + slot := phase0.Slot(slotStartPos + i%slotsTotalCnt) + committeeIndex := phase0.CommitteeIndex(i % 64) + p.Go(func() { + gotResult, gotVersion, err := client.GetAttestationData(slot, committeeIndex) + require.NoError(t, err) + require.Equal(t, spec.DataVersionPhase0, gotVersion) + require.Equal(t, slot, gotResult.Slot) + require.Equal(t, committeeIndex, gotResult.Index) + + prevResult, ok := gotResults.GetOrSet(slot, gotResult) + if ok { + // Compare the result we got against previously observed (should have same data). + require.Equal(t, prevResult.BeaconBlockRoot, gotResult.BeaconBlockRoot) + require.Equal(t, prevResult.Source.Epoch, gotResult.Source.Epoch) + require.Equal(t, prevResult.Source.Root, gotResult.Source.Root) + require.Equal(t, prevResult.Target.Epoch, gotResult.Target.Epoch) + require.Equal(t, prevResult.Target.Root, gotResult.Target.Root) + } + }) + } + + p.Wait() + + for i := 0; i < slotsTotalCnt; i++ { + slot := phase0.Slot(slotStartPos + i) + // There is about ~1 % chance a particular slot didn't receive any requests, just + // accounting for that here by setting reqCnt to 1 in this case. + reqCnt, _ := serverGotRequests.GetOrSet(slot, 1) + require.Equal(t, 1, reqCnt) + } + uniqueSlots := serverGotRequests.SlowLen() + require.Equal(t, slotsTotalCnt, uniqueSlots) + }) +} diff --git a/beacon/goclient/goclient.go b/beacon/goclient/goclient.go index b631a09e36..3b496ea74d 100644 --- a/beacon/goclient/goclient.go +++ b/beacon/goclient/goclient.go @@ -13,17 +13,18 @@ import ( eth2clienthttp "github.com/attestantio/go-eth2-client/http" "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/jellydator/ttlcache/v3" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/rs/zerolog" spectypes "github.com/ssvlabs/ssv-spec/types" - "go.uber.org/zap" - "github.com/ssvlabs/ssv/logging/fields" operatordatastore "github.com/ssvlabs/ssv/operator/datastore" "github.com/ssvlabs/ssv/operator/slotticker" beaconprotocol "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" "github.com/ssvlabs/ssv/utils/casts" + "go.uber.org/zap" + "tailscale.com/util/singleflight" ) const ( @@ -141,19 +142,31 @@ var _ NodeClientProvider = (*GoClient)(nil) // GoClient implementing Beacon struct type GoClient struct { - log *zap.Logger - ctx context.Context - network beaconprotocol.Network - client Client - nodeVersion string - nodeClient NodeClient - gasLimit uint64 - operatorDataStore operatordatastore.OperatorDataStore + log *zap.Logger + ctx context.Context + network beaconprotocol.Network + client Client + nodeVersion string + nodeClient NodeClient + gasLimit uint64 + + operatorDataStore operatordatastore.OperatorDataStore + registrationMu sync.Mutex registrationLastSlot phase0.Slot registrationCache map[phase0.BLSPubKey]*api.VersionedSignedValidatorRegistration - commonTimeout time.Duration - longTimeout time.Duration + + // attestationReqInflight helps prevent duplicate attestation data requests + // from running in parallel. + attestationReqInflight singleflight.Group[phase0.Slot, *phase0.AttestationData] + + // attestationDataCache helps reuse recently fetched attestation data. + // AttestationData is cached by slot only, because Beacon nodes should return the same + // data regardless of the requested committeeIndex. + attestationDataCache *ttlcache.Cache[phase0.Slot, *phase0.AttestationData] + + commonTimeout time.Duration + longTimeout time.Duration } // New init new client and go-client instance @@ -194,8 +207,13 @@ func New( gasLimit: opt.GasLimit, operatorDataStore: operatorDataStore, registrationCache: map[phase0.BLSPubKey]*api.VersionedSignedValidatorRegistration{}, - commonTimeout: commonTimeout, - longTimeout: longTimeout, + 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()), + ), + commonTimeout: commonTimeout, + longTimeout: longTimeout, } nodeVersionResp, err := client.client.NodeVersion(opt.Context, &api.NodeVersionOpts{}) @@ -216,6 +234,8 @@ func New( ) go client.registrationSubmitter(slotTickerProvider) + // Start automatic expired item deletion for attestationDataCache. + go client.attestationDataCache.Start() return client, nil } diff --git a/beacon/goclient/goclient_test.go b/beacon/goclient/goclient_test.go index 1e7bc0a421..989f57d56a 100644 --- a/beacon/goclient/goclient_test.go +++ b/beacon/goclient/goclient_test.go @@ -11,14 +11,13 @@ import ( "time" "github.com/attestantio/go-eth2-client/spec/phase0" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - "github.com/ssvlabs/ssv-spec/types" operatordatastore "github.com/ssvlabs/ssv/operator/datastore" "github.com/ssvlabs/ssv/operator/slotticker" "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" registrystorage "github.com/ssvlabs/ssv/registry/storage" + "github.com/stretchr/testify/require" + "go.uber.org/zap" ) func TestTimeouts(t *testing.T) { @@ -27,27 +26,35 @@ func TestTimeouts(t *testing.T) { const ( commonTimeout = 100 * time.Millisecond longTimeout = 500 * time.Millisecond + // mockServerEpoch is the epoch to use in requests to the mock server. + mockServerEpoch = 132502 ) // Too slow to dial. { - undialableServer := mockServer(t, delays{ - BaseDelay: commonTimeout * 2, + undialableServer := mockServer(t, func(r *http.Request) error { + time.Sleep(commonTimeout * 2) + return nil }) - _, err := mockClient(t, ctx, undialableServer.URL, commonTimeout, longTimeout) + _, err := mockClient(ctx, undialableServer.URL, commonTimeout, longTimeout) require.ErrorContains(t, err, "client is not active") } // Too slow to respond to the Validators request. { - unresponsiveServer := mockServer(t, delays{ - ValidatorsDelay: longTimeout * 2, - BeaconStateDelay: longTimeout / 2, + unresponsiveServer := mockServer(t, func(r *http.Request) error { + switch r.URL.Path { + case "/eth/v2/debug/beacon/states/head": + time.Sleep(longTimeout / 2) + case "/eth/v1/beacon/states/head/validators": + time.Sleep(longTimeout * 2) + } + return nil }) - client, err := mockClient(t, ctx, unresponsiveServer.URL, commonTimeout, longTimeout) + client, err := mockClient(ctx, unresponsiveServer.URL, commonTimeout, longTimeout) require.NoError(t, err) - validators, err := client.(*GoClient).GetValidatorData(nil) // Should call BeaconState internally. + validators, err := client.GetValidatorData(nil) // Should call BeaconState internally. require.NoError(t, err) var validatorKeys []phase0.BLSPubKey @@ -55,46 +62,54 @@ func TestTimeouts(t *testing.T) { validatorKeys = append(validatorKeys, v.Validator.PublicKey) } - _, err = client.(*GoClient).GetValidatorData(validatorKeys) // Shouldn't call BeaconState internally. + _, err = client.GetValidatorData(validatorKeys) // Shouldn't call BeaconState internally. require.ErrorContains(t, err, "context deadline exceeded") - duties, err := client.(*GoClient).ProposerDuties(ctx, mockServerEpoch, nil) + duties, err := client.ProposerDuties(ctx, mockServerEpoch, nil) require.NoError(t, err) require.NotEmpty(t, duties) } // Too slow to respond to proposer duties request. { - unresponsiveServer := mockServer(t, delays{ - ProposerDutiesDelay: longTimeout * 2, + unresponsiveServer := mockServer(t, func(r *http.Request) error { + switch r.URL.Path { + case "/eth/v1/validator/duties/proposer/" + fmt.Sprint(mockServerEpoch): + time.Sleep(longTimeout * 2) + } + return nil }) - client, err := mockClient(t, ctx, unresponsiveServer.URL, commonTimeout, longTimeout) + client, err := mockClient(ctx, unresponsiveServer.URL, commonTimeout, longTimeout) require.NoError(t, err) - _, err = client.(*GoClient).ProposerDuties(ctx, mockServerEpoch, nil) + _, err = client.ProposerDuties(ctx, mockServerEpoch, nil) require.ErrorContains(t, err, "context deadline exceeded") } // Fast enough. { - fastServer := mockServer(t, delays{ - BaseDelay: commonTimeout / 2, - BeaconStateDelay: longTimeout / 2, + fastServer := mockServer(t, func(r *http.Request) error { + time.Sleep(commonTimeout / 2) + switch r.URL.Path { + case "/eth/v2/debug/beacon/states/head": + time.Sleep(longTimeout / 2) + } + return nil }) - client, err := mockClient(t, ctx, fastServer.URL, commonTimeout, longTimeout) + client, err := mockClient(ctx, fastServer.URL, commonTimeout, longTimeout) require.NoError(t, err) - validators, err := client.(*GoClient).GetValidatorData(nil) + validators, err := client.GetValidatorData(nil) require.NoError(t, err) require.NotEmpty(t, validators) - duties, err := client.(*GoClient).ProposerDuties(ctx, mockServerEpoch, nil) + duties, err := client.ProposerDuties(ctx, mockServerEpoch, nil) require.NoError(t, err) require.NotEmpty(t, duties) } } -func mockClient(t *testing.T, ctx context.Context, serverURL string, commonTimeout, longTimeout time.Duration) (beacon.BeaconNode, error) { +func mockClient(ctx context.Context, serverURL string, commonTimeout, longTimeout time.Duration) (beacon.BeaconNode, error) { return New( zap.NewNop(), beacon.Options{ @@ -114,17 +129,7 @@ func mockClient(t *testing.T, ctx context.Context, serverURL string, commonTimeo ) } -type delays struct { - BaseDelay time.Duration - ProposerDutiesDelay time.Duration - BeaconStateDelay time.Duration - ValidatorsDelay time.Duration -} - -// epoch to use in requests to the mock server. -const mockServerEpoch = 132502 - -func mockServer(t *testing.T, delays delays) *httptest.Server { +func mockServer(t *testing.T, onRequestFn func(r *http.Request) error) *httptest.Server { var mockResponses map[string]json.RawMessage f, err := os.Open("testdata/mock-beacon-responses.json") require.NoError(t, err) @@ -140,18 +145,11 @@ func mockServer(t *testing.T, delays delays) *httptest.Server { return } - time.Sleep(delays.BaseDelay) - switch r.URL.Path { - case "/eth/v1/validator/duties/proposer/" + fmt.Sprint(mockServerEpoch): - time.Sleep(delays.ProposerDutiesDelay) - case "/eth/v2/debug/beacon/states/head": - time.Sleep(delays.BeaconStateDelay) - case "/eth/v1/beacon/states/head/validators": - time.Sleep(delays.ValidatorsDelay) - } + err := onRequestFn(r) + require.NoError(t, err) w.Header().Set("Content-Type", "application/json") - if _, err := w.Write([]byte(resp)); err != nil { + if _, err := w.Write(resp); err != nil { w.WriteHeader(http.StatusInternalServerError) return } diff --git a/go.mod b/go.mod index 4557de8afa..77ff1b5814 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/ssvlabs/ssv -go 1.22 +go 1.22.6 require ( github.com/aquasecurity/table v1.8.0 @@ -34,7 +34,7 @@ require ( github.com/prysmaticlabs/prysm/v4 v4.0.8 github.com/rs/zerolog v1.32.0 github.com/sourcegraph/conc v0.3.0 - github.com/spf13/cobra v1.7.0 + github.com/spf13/cobra v1.8.1 github.com/ssvlabs/eth2-key-manager v1.4.2 github.com/ssvlabs/ssv-spec v0.3.11-0.20240820113812-496d839e9614 github.com/ssvlabs/ssv-spec-pre-cc v0.0.0-20240725052506-c48532da6a63 @@ -52,10 +52,11 @@ require ( golang.org/x/text v0.16.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 + tailscale.com v1.72.0 ) require ( - github.com/BurntSushi/toml v1.3.2 // indirect + github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect github.com/DataDog/zstd v1.5.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/VictoriaMetrics/fastcache v1.12.2 // indirect @@ -63,7 +64,7 @@ require ( github.com/aristanetworks/goarista v0.0.0-20200805130819-fd197cf57d96 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.10.0 // indirect + github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/cockroachdb/errors v1.11.3 // indirect github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect @@ -74,30 +75,30 @@ require ( github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/elastic/gosigar v0.14.3 // indirect github.com/ethereum/c-kzg-4844 v1.0.0 // indirect github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 // indirect - github.com/fatih/color v1.16.0 // indirect + github.com/fatih/color v1.17.0 // indirect github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/goccy/go-yaml v1.11.3 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/goccy/go-yaml v1.12.0 // indirect + github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect @@ -189,7 +190,7 @@ require ( github.com/pion/turn/v2 v2.1.6 // indirect github.com/pion/webrtc/v3 v3.3.0 // indirect github.com/pk910/dynamic-ssz v0.0.3 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/polydawn/refmt v0.89.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect @@ -199,8 +200,8 @@ require ( github.com/quic-go/webtransport-go v0.8.0 // indirect github.com/r3labs/sse/v2 v2.10.0 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect - github.com/rivo/uniseg v0.4.3 // indirect - github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/rs/cors v1.7.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect @@ -219,9 +220,9 @@ require ( github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/otel v1.16.0 // indirect - go.opentelemetry.io/otel/metric v1.16.0 // indirect - go.opentelemetry.io/otel/trace v1.16.0 // indirect + go.opentelemetry.io/otel v1.22.0 // indirect + go.opentelemetry.io/otel/metric v1.22.0 // indirect + go.opentelemetry.io/otel/trace v1.22.0 // indirect go.uber.org/dig v1.17.1 // indirect go.uber.org/fx v1.22.1 // indirect golang.org/x/crypto v0.25.0 // indirect @@ -230,7 +231,7 @@ require ( golang.org/x/term v0.22.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.23.0 // indirect - golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect gonum.org/v1/gonum v0.13.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/Knetic/govaluate.v3 v3.0.0 // indirect diff --git a/go.sum b/go.sum index 6138044ecf..ea4bc6d76f 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= @@ -50,8 +50,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= -github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= +github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= @@ -99,8 +99,8 @@ github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8 github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I= github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= @@ -109,8 +109,9 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U= github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= @@ -128,8 +129,9 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUn github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= @@ -146,8 +148,8 @@ github.com/ethereum/go-ethereum v1.14.8 h1:NgOWvXS+lauK+zFukEvi85UmmsS/OkV0N23UZ github.com/ethereum/go-ethereum v1.14.8/go.mod h1:TJhyuDq0JDppAkFXgqjwpdlQApywnu/m10kFPxh8vvs= github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 h1:KrE8I4reeVvf7C1tm8elRjj4BdscTYzz/WAbYyf/JI4= github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0/go.mod h1:D9AJLVXSyZQXJQVk8oh1EwjISE+sJTn2duYIZC0dy3w= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/ferranbt/fastssz v0.0.0-20210120143747-11b9eff30ea9/go.mod h1:DyEu2iuLBnb/T51BlsiO3yLYdJC6UbGMrIkqK1KmQxM= github.com/ferranbt/fastssz v0.1.3 h1:ZI+z3JH05h4kgmFXdHuR1aWYsgrg7o+Fw7/NCzM16Mo= github.com/ferranbt/fastssz v0.1.3/go.mod h1:0Y9TEd/9XuFlh7mskMPfXiI2Dkw4Ddg9EyXt1W7MRvE= @@ -158,14 +160,14 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= @@ -205,12 +207,12 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg78 github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= -github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I= -github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= +github.com/goccy/go-yaml v1.12.0 h1:/1WHjnMsI1dlIBQutrvSMGZRQufVO3asrHfTwfACoPM= +github.com/goccy/go-yaml v1.12.0/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 h1:sQspH8M4niEijh3PFscJRLDnkL547IeP7kpPe3uUhEg= +github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466/go.mod h1:ZiQxhyQ+bbbfxUKVvjfO498oPYvtYhZzycal3G/NHmU= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -615,8 +617,9 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -663,12 +666,12 @@ github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtB github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= -github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= @@ -722,8 +725,8 @@ github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0b github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v0.0.0-20170901052352-ee1bd8ee15a1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.1.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/jwalterweatherman v0.0.0-20170901151539-12bd96e66386/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1-0.20170901120850-7aff26db30c1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -813,12 +816,12 @@ github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQ go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= -go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= -go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= -go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= -go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= -go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= +go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= +go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= +go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= +go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= +go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= @@ -973,7 +976,7 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1043,8 +1046,8 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 h1:LLhsEBxRTBLuKlQxFBYUOU8xyFgXv6cOTp2HASDlsDk= +golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.13.0 h1:a0T3bh+7fhRyqeNbiC3qVHYmkiQgit3wnNan/2c0HMM= gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU= google.golang.org/api v0.0.0-20170921000349-586095a6e407/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= @@ -1137,23 +1140,27 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -k8s.io/apimachinery v0.20.0 h1:jjzbTJRXk0unNS71L7h3lxGDH/2HPxMPaQY+MjECKL8= -k8s.io/apimachinery v0.20.0/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/client-go v0.20.0 h1:Xlax8PKbZsjX4gFvNtt4F5MoJ1V5prDvCuoq9B7iax0= -k8s.io/client-go v0.20.0/go.mod h1:4KWh/g+Ocd8KkCwKF8vUNnmqgv+EVnQDK4MBF4oB5tY= -k8s.io/klog/v2 v2.80.0 h1:lyJt0TWMPaGoODa8B8bUuxgHS3W/m/bNr2cca3brA/g= -k8s.io/klog/v2 v2.80.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/apimachinery v0.30.3 h1:q1laaWCmrszyQuSQCfNB8cFgCuDAoPszKY4ucAjDwHc= +k8s.io/apimachinery v0.30.3/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/client-go v0.30.3 h1:bHrJu3xQZNXIi8/MoxYtZBBWQQXwy16zqJwloXXfD3k= +k8s.io/client-go v0.30.3/go.mod h1:8d4pf8vYu665/kUbsxWAQ/JDBNWqfFeZnvFiVdmx89U= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 h1:slmdOY3vp8a7KQbHkL+FLbvbkgMqmXojpFUO/jENuqQ= olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= +tailscale.com v1.72.0 h1:emsPxupFM72zJLt2wvSzpa1vymqPYbL0WVVO+170/s0= +tailscale.com v1.72.0/go.mod h1:v7OHtg0KLAnhOVf81Z8WrjNefj238QbFhgkWJQoKxbs= diff --git a/protocol/v2/blockchain/beacon/duty_data.go b/protocol/v2/blockchain/beacon/duty_data.go deleted file mode 100644 index 448fa4e357..0000000000 --- a/protocol/v2/blockchain/beacon/duty_data.go +++ /dev/null @@ -1,77 +0,0 @@ -package beacon - -import ( - "github.com/attestantio/go-eth2-client/spec/phase0" -) - -// DutyData represent unified duty types data -type DutyData struct { - // Types that are valid to be assigned to Data: - // *InputValueAttestationData - // *InputValue_AggregationData - // *InputValue_BeaconBlock - Data IsInputValueData `protobuf_oneof:"data"` - // Types that are valid to be assigned to SignedData: - // *InputValueAttestation - // *InputValue_Aggregation - // *InputValue_Block - SignedData IsInputValueSignedData `protobuf_oneof:"signed_data"` -} - -// IsInputValueData interface representing input data -type IsInputValueData interface { - isInputValueData() -} - -// InputValueAttestationData implementing IsInputValueData -type InputValueAttestationData struct { - AttestationData *phase0.AttestationData -} - -// isInputValueData implementation -func (*InputValueAttestationData) isInputValueData() {} - -// GetData returns input data -func (m *DutyData) GetData() IsInputValueData { - if m != nil { - return m.Data - } - return nil -} - -// GetAttestationData return cast input data -func (m *DutyData) GetAttestationData() *phase0.AttestationData { - if x, ok := m.GetData().(*InputValueAttestationData); ok { - return x.AttestationData - } - return nil -} - -// IsInputValueSignedData interface representing input signed data -type IsInputValueSignedData interface { - isInputValueSignedData() -} - -// InputValueAttestation implementing IsInputValueSignedData -type InputValueAttestation struct { - Attestation *phase0.Attestation -} - -// isInputValueSignedData implementation -func (*InputValueAttestation) isInputValueSignedData() {} - -// GetSignedData returns input data -func (m *DutyData) GetSignedData() IsInputValueSignedData { - if m != nil { - return m.SignedData - } - return nil -} - -// GetAttestation return cast attestation input data -func (m *DutyData) GetAttestation() *phase0.Attestation { - if x, ok := m.GetSignedData().(*InputValueAttestation); ok { - return x.Attestation - } - return nil -} diff --git a/protocol/v2/ssv/runner/committee.go b/protocol/v2/ssv/runner/committee.go index 2ab53cfb6c..f02427b386 100644 --- a/protocol/v2/ssv/runner/committee.go +++ b/protocol/v2/ssv/runner/committee.go @@ -14,14 +14,13 @@ import ( "github.com/prysmaticlabs/go-bitfield" specqbft "github.com/ssvlabs/ssv-spec/qbft" spectypes "github.com/ssvlabs/ssv-spec/types" - "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" "github.com/ssvlabs/ssv/protocol/v2/ssv/runner/metrics" ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types" + "go.uber.org/zap" ) var ( @@ -669,11 +668,12 @@ func (cr *CommitteeRunner) expectedPostConsensusRootsAndBeaconObjects(logger *za func (cr *CommitteeRunner) executeDuty(logger *zap.Logger, duty spectypes.Duty) error { start := time.Now() slot := duty.DutySlot() + // We set committeeIndex to 0 for simplicity, there is no need to specify it exactly because + // all 64 Ethereum committees assigned to this slot will get the same data to attest for. attData, _, err := cr.GetBeaconNode().GetAttestationData(slot, 0) if err != nil { return errors.Wrap(err, "failed to get attestation data") } - //TODO committeeIndex is 0, is this correct? logger = logger.With( zap.Duration("attestation_data_time", time.Since(start)), fields.Slot(slot), diff --git a/utils/hashmap/hashmap.go b/utils/hashmap/hashmap.go index a65c788b6e..8dcf437a1f 100644 --- a/utils/hashmap/hashmap.go +++ b/utils/hashmap/hashmap.go @@ -29,11 +29,18 @@ func (m *Map[Key, Value]) GetOrSet(key Key, value Value) (Value, bool) { return actual.(Value), loaded } +func (m *Map[Key, Value]) CompareAndSwap(key Key, old, new Value) (swapped bool) { + return m.m.CompareAndSwap(key, old, new) +} + func (m *Map[Key, Value]) Set(key Key, value Value) { m.m.Store(key, value) } // SlowLen returns the number of elements in the map by iterating over all items. +// This method call doesn't block other operations (for example Set), hence the +// resulting length value returned might or might not reflect the outcome of +// concurrent operations happening with this Map. // // This implementation is quite expensive. If it becomes a bottleneck, // we should consider maintaining an internal atomic counter and diff --git a/utils/hashmap/hashmap_test.go b/utils/hashmap/hashmap_test.go index d729962481..eb25df419d 100644 --- a/utils/hashmap/hashmap_test.go +++ b/utils/hashmap/hashmap_test.go @@ -19,12 +19,14 @@ import ( func TestNew(t *testing.T) { t.Parallel() + m := New[uintptr, uintptr]() assert.Equal(t, 0, m.SlowLen()) } func TestSetString(t *testing.T) { t.Parallel() + m := New[int, string]() elephant := "elephant" monkey := "monkey" @@ -50,6 +52,7 @@ func TestSetString(t *testing.T) { func TestSetUint8(t *testing.T) { t.Parallel() + m := New[uint8, int]() m.Set(1, 128) // insert @@ -66,6 +69,7 @@ func TestSetUint8(t *testing.T) { func TestSetInt16(t *testing.T) { t.Parallel() + m := New[int16, int]() m.Set(1, 128) // insert @@ -82,6 +86,7 @@ func TestSetInt16(t *testing.T) { func TestSetFloat32(t *testing.T) { t.Parallel() + m := New[float32, int]() m.Set(1.1, 128) // insert @@ -98,6 +103,7 @@ func TestSetFloat32(t *testing.T) { func TestSetFloat64(t *testing.T) { t.Parallel() + m := New[float64, int]() m.Set(1.1, 128) // insert @@ -114,6 +120,7 @@ func TestSetFloat64(t *testing.T) { func TestSetInt64(t *testing.T) { t.Parallel() + m := New[int64, int]() m.Set(1, 128) // insert @@ -130,6 +137,7 @@ func TestSetInt64(t *testing.T) { func TestByteArray(t *testing.T) { t.Parallel() + m := New[[4]byte, int]() m.Set([4]byte{1, 2, 3, 4}, 128) // insert @@ -146,6 +154,7 @@ func TestByteArray(t *testing.T) { func TestGetNonExistingItem(t *testing.T) { t.Parallel() + m := New[int, string]() value, ok := m.Get(1) assert.False(t, ok) @@ -154,6 +163,7 @@ func TestGetNonExistingItem(t *testing.T) { func TestStringer(t *testing.T) { t.Parallel() + m := New[int, string]() // Test with zero items @@ -172,6 +182,7 @@ func TestStringer(t *testing.T) { func TestDelete(t *testing.T) { t.Parallel() + m := New[int, string]() elephant := "elephant" monkey := "monkey" @@ -217,6 +228,7 @@ func TestGetAndDelete(t *testing.T) { func TestRange(t *testing.T) { t.Parallel() + m := New[int, string]() items := map[int]string{} @@ -255,6 +267,8 @@ func TestRange(t *testing.T) { // nolint: funlen, gocognit func TestHashMap_parallel(t *testing.T) { + t.Parallel() + m := New[int, int]() max := 10 @@ -332,6 +346,7 @@ func TestHashMap_parallel(t *testing.T) { func TestHashMap_SetConcurrent(t *testing.T) { t.Parallel() + m := New[string, int]() var wg sync.WaitGroup @@ -384,9 +399,11 @@ func TestConcurrentInsertDelete(t *testing.T) { } } -func TestGetOrInsert(t *testing.T) { +func TestGetOrSet(t *testing.T) { t.Parallel() + m := New[int, string]() + value, ok := m.GetOrSet(1, "1") assert.False(t, ok) assert.Equal(t, "1", value) @@ -396,7 +413,28 @@ func TestGetOrInsert(t *testing.T) { assert.Equal(t, "1", value) } -func TestGetOrInsertHangIssue67(_ *testing.T) { +func TestCompareAndSwap(t *testing.T) { + t.Parallel() + + m := New[int, string]() + + ok := m.CompareAndSwap(1, "", "replacing zero value doesn't count as swap, and doesn't even succeed") + assert.False(t, ok) + + value, ok := m.Get(1) + assert.False(t, ok) + assert.Empty(t, value) + + m.Set(1, "0") + ok = m.CompareAndSwap(1, "1", "2") + assert.False(t, ok) + ok = m.CompareAndSwap(1, "0", "2") + assert.True(t, ok) +} + +func TestGetOrInsertHangIssue67(t *testing.T) { + t.Parallel() + m := New[string, int]() var wg sync.WaitGroup @@ -421,6 +459,8 @@ func TestGetOrInsertHangIssue67(_ *testing.T) { // See https://github.com/ssvlabs/ssv/issues/1682 func TestIssue1682(t *testing.T) { + t.Parallel() + type validatorStatus int const (