Skip to content

Commit

Permalink
Add 'ExitEpoch' field to 'Share' data structure, update SSZ encoder a…
Browse files Browse the repository at this point in the history
…nd tests
  • Loading branch information
oleg-ssvlabs committed Jan 27, 2025
1 parent ff4fa98 commit e4389ae
Show file tree
Hide file tree
Showing 15 changed files with 216 additions and 156 deletions.
2 changes: 2 additions & 0 deletions api/handlers/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ type validatorJSON struct {
Index phase0.ValidatorIndex `json:"index"`
Status string `json:"status"`
ActivationEpoch phase0.Epoch `json:"activation_epoch"`
ExitEpoch phase0.Epoch `json:"exit_epoch"`
Owner api.Hex `json:"owner"`
Committee []spectypes.OperatorID `json:"committee"`
Quorum uint64 `json:"quorum"`
Expand All @@ -187,6 +188,7 @@ func validatorFromShare(share *types.SSVShare) *validatorJSON {
v.Index = share.ValidatorIndex
v.Status = share.Status.String()
v.ActivationEpoch = share.ActivationEpoch
v.ExitEpoch = share.ExitEpoch

Check warning on line 191 in api/handlers/validators.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/validators.go#L191

Added line #L191 was not covered by tests
}
return v
}
2 changes: 2 additions & 0 deletions eth/eventhandler/event_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"encoding/json"
"fmt"
"math"
"math/big"
"net/http/httptest"
"strings"
Expand Down Expand Up @@ -746,6 +747,7 @@ func TestHandleBlockEventsStream(t *testing.T) {
require.NotNil(t, valShare)
valShare.ValidatorIndex = 1
valShare.ActivationEpoch = 0
valShare.ExitEpoch = math.MaxUint64
valShare.Status = eth2apiv1.ValidatorStateActiveOngoing
err := eh.nodeStorage.Shares().Save(nil, valShare)
require.NoError(t, err)
Expand Down
9 changes: 6 additions & 3 deletions message/validation/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1811,12 +1811,14 @@ func generateShares(t *testing.T, ks *spectestingutils.TestKeySet, ns storage.St
require.NoError(t, ns.Shares().Save(nil, inactiveShare))

slot := netCfg.Beacon.EstimatedCurrentSlot()
epoch := netCfg.Beacon.EstimatedEpochAtSlot(slot)
activationEpoch := netCfg.Beacon.EstimatedEpochAtSlot(slot)
exitEpoch := phase0.Epoch(math.MaxUint64)

nonUpdatedMetadataShare := &ssvtypes.SSVShare{
Share: *spectestingutils.TestingShare(ks, spectestingutils.TestingValidatorIndex),
Status: eth2apiv1.ValidatorStatePendingQueued,
ActivationEpoch: epoch,
ActivationEpoch: activationEpoch,
ExitEpoch: exitEpoch,
Liquidated: false,
}

Expand All @@ -1829,7 +1831,8 @@ func generateShares(t *testing.T, ks *spectestingutils.TestKeySet, ns storage.St
nonUpdatedMetadataNextEpochShare := &ssvtypes.SSVShare{
Share: *spectestingutils.TestingShare(ks, spectestingutils.TestingValidatorIndex),
Status: eth2apiv1.ValidatorStatePendingQueued,
ActivationEpoch: epoch + 1,
ActivationEpoch: activationEpoch + 1,
ExitEpoch: exitEpoch,
Liquidated: false,
}

Expand Down
1 change: 1 addition & 0 deletions operator/validator/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,7 @@ func (c *controller) startValidatorsForMetadata(_ context.Context, validators me
v.Share.ValidatorIndex = share.ValidatorIndex
v.Share.Status = share.Status
v.Share.ActivationEpoch = share.ActivationEpoch
v.Share.ExitEpoch = share.ExitEpoch

Check warning on line 559 in operator/validator/controller.go

View check run for this annotation

Codecov / codecov/patch

operator/validator/controller.go#L559

Added line #L559 was not covered by tests
started, err := c.startValidator(v)
if err != nil {
c.logger.Warn("could not start validator", zap.Error(err))
Expand Down
62 changes: 31 additions & 31 deletions operator/validator/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import (
"crypto/rand"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"math"
"sync"
"testing"
"time"
Expand All @@ -15,7 +17,6 @@ import (
"github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/ethereum/go-ethereum/common"
"github.com/herumi/bls-eth-go-binary/bls"
"github.com/pkg/errors"
specqbft "github.com/ssvlabs/ssv-spec/qbft"
spectypes "github.com/ssvlabs/ssv-spec/types"
"github.com/ssvlabs/ssv/ekm"
Expand Down Expand Up @@ -95,7 +96,7 @@ func TestNewController(t *testing.T) {
}

func TestSetupValidatorsExporter(t *testing.T) {
passedEpoch := phase0.Epoch(1)
passedEpoch, exitEpoch := phase0.Epoch(1), phase0.Epoch(math.MaxUint64)
operators := buildOperators(t)

operatorDataStore := operatordatastore.New(buildOperatorData(0, "67Ce5c69260bd819B4e0AD13f4b873074D479811"))
Expand All @@ -115,34 +116,36 @@ func TestSetupValidatorsExporter(t *testing.T) {
Committee: operators,
ValidatorPubKey: spectypes.ValidatorPK(secretKey.GetPublicKey().Serialize()),
},
Status: 3, // ValidatorStatePendingInitialized
ActivationEpoch: passedEpoch,
Liquidated: false,
Status: eth2apiv1.ValidatorStateActiveOngoing,
ActivationEpoch: passedEpoch,
ExitEpoch: exitEpoch,
Liquidated: false,
BeaconMetadataLastUpdated: time.Now(),
},
{
Share: spectypes.Share{
Committee: operators,
ValidatorPubKey: spectypes.ValidatorPK(secretKey2.GetPublicKey().Serialize()),
},
Status: 3, // Some other status
ActivationEpoch: passedEpoch,
Liquidated: false,
Status: eth2apiv1.ValidatorStateActiveOngoing,
ActivationEpoch: passedEpoch,
ExitEpoch: exitEpoch,
Liquidated: false,
BeaconMetadataLastUpdated: time.Now(),
},
}
_ = sharesWithMetadata

sharesWithoutMetadata := []*types.SSVShare{
{
Share: spectypes.Share{
//OperatorID: 1,
Committee: operators,
ValidatorPubKey: spectypes.ValidatorPK(secretKey.GetPublicKey().Serialize()),
},
Liquidated: false,
},
{
Share: spectypes.Share{
//OperatorID: 2,
Committee: operators,
ValidatorPubKey: spectypes.ValidatorPK(secretKey2.GetPublicKey().Serialize()),
},
Expand Down Expand Up @@ -305,7 +308,7 @@ func TestSetupValidators(t *testing.T) {
logger := logging.TestLogger(t)

// Init global variables
passedEpoch := phase0.Epoch(1)
activationEpoch, exitEpoch := phase0.Epoch(1), phase0.Epoch(math.MaxUint64)
operatorIds := []uint64{1, 2, 3, 4}
var validatorPublicKey phase0.BLSPubKey

Expand All @@ -331,18 +334,18 @@ func TestSetupValidators(t *testing.T) {

shareWithMetaData := &types.SSVShare{
Share: spectypes.Share{
ValidatorIndex: 1,
// OperatorID: 2,
ValidatorIndex: 1,
Committee: operators[:1],
ValidatorPubKey: spectypes.ValidatorPK(validatorKey),
},
Status: 3, // ValidatorStateActiveOngoing
ActivationEpoch: passedEpoch,
Status: eth2apiv1.ValidatorStateActiveOngoing,
ActivationEpoch: activationEpoch,
ExitEpoch: exitEpoch,
BeaconMetadataLastUpdated: time.Now(),
}

shareWithoutMetaData := &types.SSVShare{
Share: spectypes.Share{
// OperatorID: 2,
Committee: operators[:1],
ValidatorPubKey: spectypes.ValidatorPK(validatorKey),
},
Expand All @@ -364,10 +367,11 @@ func TestSetupValidators(t *testing.T) {

bcResponse := map[phase0.ValidatorIndex]*eth2apiv1.Validator{
0: {
Status: 3,
Status: eth2apiv1.ValidatorStateActiveOngoing,
Index: 2,
Validator: &phase0.Validator{
ActivationEpoch: passedEpoch,
ActivationEpoch: activationEpoch,
ExitEpoch: exitEpoch,
PublicKey: validatorPublicKey,
},
},
Expand Down Expand Up @@ -584,7 +588,7 @@ func TestGetValidatorStats(t *testing.T) {
ctrl := gomock.NewController(t)
sharesStorage := mocks.NewMockSharesStorage(ctrl)
bc := beacon.NewMockBeaconNode(ctrl)
passedEpoch := phase0.Epoch(1)
activationEpoch, exitEpoch := phase0.Epoch(1), phase0.Epoch(math.MaxUint64)

netCfg := networkconfig.TestNetwork
bc.EXPECT().GetBeaconNetwork().Return(netCfg.Beacon.GetBeaconNetwork()).AnyTimes()
Expand All @@ -603,15 +607,15 @@ func TestGetValidatorStats(t *testing.T) {
Share: spectypes.Share{
Committee: operators,
},
Status: 3, // ValidatorStatePendingInitialized
ActivationEpoch: passedEpoch,
Status: eth2apiv1.ValidatorStateActiveOngoing,
ActivationEpoch: activationEpoch,
ExitEpoch: exitEpoch,
},
{
Share: spectypes.Share{
Committee: operators[1:],
},
Status: 1, // Some other status
ActivationEpoch: passedEpoch,
Status: eth2apiv1.ValidatorStatePendingInitialized, // Some other status
},
}

Expand Down Expand Up @@ -650,8 +654,7 @@ func TestGetValidatorStats(t *testing.T) {
Share: spectypes.Share{
Committee: operators,
},
Status: 3, // ValidatorStatePendingInitialized
ActivationEpoch: passedEpoch,
Status: eth2apiv1.ValidatorStateActiveOngoing,
},
}

Expand Down Expand Up @@ -682,8 +685,7 @@ func TestGetValidatorStats(t *testing.T) {
Share: spectypes.Share{
Committee: nil,
},
Status: 3, // ValidatorStatePendingInitialized
ActivationEpoch: passedEpoch,
Status: eth2apiv1.ValidatorStateActiveOngoing,
},
}

Expand Down Expand Up @@ -721,15 +723,13 @@ func TestGetValidatorStats(t *testing.T) {
Share: spectypes.Share{
Committee: operators,
},
Status: 3, // ValidatorStatePendingInitialized
ActivationEpoch: passedEpoch,
Status: eth2apiv1.ValidatorStateActiveOngoing,
},
{
Share: spectypes.Share{
Committee: operators,
},
Status: 1,
ActivationEpoch: passedEpoch,
Status: eth2apiv1.ValidatorStatePendingInitialized,
},
}

Expand Down
14 changes: 4 additions & 10 deletions operator/validator/metadata/syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,23 +135,16 @@ func (s *Syncer) Sync(ctx context.Context, pubKeys []spectypes.ValidatorPK) (Val
return nil, fmt.Errorf("fetch metadata: %w", err)
}

s.logger.Debug("🆕 fetched validators metadata",
fields.Took(time.Since(fetchStart)),
zap.Int("metadatas", len(metadata)),
zap.Int("validators", len(pubKeys)),
)
logger := s.logger.With(zap.Int("metadatas", len(metadata)), zap.Int("validators", len(pubKeys)))
logger.Debug("🆕 fetched validators metadata", fields.Took(time.Since(fetchStart)))

updateStart := time.Now()
// TODO: Refactor share storage to support passing context.
if err := s.shareStorage.UpdateValidatorsMetadata(metadata); err != nil {
return metadata, fmt.Errorf("update metadata: %w", err)
}

s.logger.Debug("🆕 saved validators metadata",
fields.Took(time.Since(updateStart)),
zap.Int("metadatas", len(metadata)),
zap.Int("validators", len(pubKeys)),
)
logger.Debug("🆕 saved validators metadata", fields.Took(time.Since(updateStart)))

return metadata, nil
}
Expand Down Expand Up @@ -179,6 +172,7 @@ func (s *Syncer) Fetch(_ context.Context, pubKeys []spectypes.ValidatorPK) (Vali
Status: v.Status,
Index: v.Index,
ActivationEpoch: v.Validator.ActivationEpoch,
ExitEpoch: v.Validator.ExitEpoch,
}
results[spectypes.ValidatorPK(v.Validator.PublicKey)] = meta
}
Expand Down
5 changes: 4 additions & 1 deletion operator/validator/metadata/syncer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"crypto/rand"
"fmt"
"math"
"testing"
"time"

Expand Down Expand Up @@ -41,7 +42,7 @@ func TestUpdateValidatorMetadata(t *testing.T) {
committee[i] = &spectypes.ShareMember{Signer: id, SharePubKey: operatorKey}
}

validatorMetadata := &beacon.ValidatorMetadata{Index: 1, ActivationEpoch: passedEpoch, Status: eth2apiv1.ValidatorStateActiveOngoing}
validatorMetadata := &beacon.ValidatorMetadata{Index: 1, ActivationEpoch: passedEpoch, ExitEpoch: phase0.Epoch(math.MaxUint64), Status: eth2apiv1.ValidatorStateActiveOngoing}

pubKey := spectypes.ValidatorPK{0x1}

Expand Down Expand Up @@ -443,6 +444,7 @@ func TestSyncer_Stream(t *testing.T) {
},
Status: eth2apiv1.ValidatorStateActiveOngoing,
ActivationEpoch: 0,
ExitEpoch: 0,
Liquidated: false,
}

Expand Down Expand Up @@ -476,6 +478,7 @@ func TestSyncer_Stream(t *testing.T) {
Index: 1,
Status: eth2apiv1.ValidatorStateActiveOngoing,
ActivationEpoch: 0,
ExitEpoch: 0,
},
}

Expand Down
4 changes: 3 additions & 1 deletion protocol/v2/blockchain/beacon/validator_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type ValidatorMetadata struct {
Status eth2apiv1.ValidatorState `json:"status"`
Index phase0.ValidatorIndex `json:"index"`
ActivationEpoch phase0.Epoch `json:"activation_epoch"`
ExitEpoch phase0.Epoch `json:"exit_epoch"`
}

// Equals returns true if the given metadata is equal to current
Expand All @@ -19,7 +20,8 @@ func (m *ValidatorMetadata) Equals(other *ValidatorMetadata) bool {
m.Status == other.Status &&
m.Index == other.Index &&
m.Balance == other.Balance &&
m.ActivationEpoch == other.ActivationEpoch
m.ActivationEpoch == other.ActivationEpoch &&
m.ExitEpoch == other.ExitEpoch
}

// Pending returns true if the validator is pending
Expand Down
4 changes: 3 additions & 1 deletion protocol/v2/types/ssvshare.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ type SSVShare struct {
Status eth2apiv1.ValidatorState
// ActivationEpoch is validator (this share belongs to) epoch it activates at.
ActivationEpoch phase0.Epoch
// ExitEpoch is the epoch at which the validator (that this share belongs to) exited.
ExitEpoch phase0.Epoch
// OwnerAddress is validator (this share belongs to) owner address.
OwnerAddress common.Address
// Liquidated is validator (this share belongs to) liquidation status (true or false).
Expand Down Expand Up @@ -85,7 +87,7 @@ func (s *SSVShare) IsActive() bool {
return s.Status == eth2apiv1.ValidatorStateActiveOngoing
}

// Exiting returns true if the validator is existing or exited
// Exiting returns true if the validator is exiting or exited
func (s *SSVShare) Exiting() bool {
return s.Status.IsExited() || s.Status.HasExited()
}
Expand Down
3 changes: 3 additions & 0 deletions registry/storage/mocks/validatorstore.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit e4389ae

Please sign in to comment.