Skip to content

Commit

Permalink
message validation: check voluntary exit existence (#1468)
Browse files Browse the repository at this point in the history
message validation: check voluntary exit existence
  • Loading branch information
nkryuchkov authored Jul 16, 2024
1 parent 86fd07f commit c5dfdac
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 16 deletions.
2 changes: 1 addition & 1 deletion cli/operator/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"encoding/base64"
"fmt"
"github.com/ssvlabs/ssv/exporter/convert"
"log"
"math/big"
"net/http"
Expand All @@ -30,6 +29,7 @@ import (
"github.com/ssvlabs/ssv/eth/localevents"
exporterapi "github.com/ssvlabs/ssv/exporter/api"
"github.com/ssvlabs/ssv/exporter/api/decided"
"github.com/ssvlabs/ssv/exporter/convert"
ibftstorage "github.com/ssvlabs/ssv/ibft/storage"
ssv_identity "github.com/ssvlabs/ssv/identity"
"github.com/ssvlabs/ssv/logging"
Expand Down
9 changes: 5 additions & 4 deletions eth/eventhandler/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/hex"
"errors"
"fmt"

"github.com/ssvlabs/ssv/exporter/convert"

"github.com/attestantio/go-eth2-client/spec/phase0"
Expand Down Expand Up @@ -511,10 +512,6 @@ func (eh *EventHandler) handleValidatorExited(txn basedb.Txn, event *contract.Co
return nil, &MalformedEventError{Err: ErrShareBelongsToDifferentOwner}
}

if !share.BelongsToOperator(eh.operatorDataStore.GetOperatorID()) {
return nil, nil
}

if share.BeaconMetadata == nil {
return nil, nil
}
Expand All @@ -523,10 +520,14 @@ func (eh *EventHandler) handleValidatorExited(txn basedb.Txn, event *contract.Co
copy(pk[:], share.ValidatorPubKey[:])

ed := &duties.ExitDescriptor{
OwnValidator: false,
PubKey: pk,
ValidatorIndex: share.BeaconMetadata.Index,
BlockNumber: event.Raw.BlockNumber,
}
if share.BelongsToOperator(eh.operatorDataStore.GetOperatorID()) {
ed.OwnValidator = true
}

return ed, nil
}
Expand Down
13 changes: 9 additions & 4 deletions message/validation/common_checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func (mv *messageValidator) validateDutyCount(
) error {
dutyCount := signerStateBySlot.DutyCount(mv.netCfg.Beacon.EstimatedEpochAtSlot(msgSlot))

dutyLimit, exists := mv.dutyLimit(msgID, validatorIndexCount)
dutyLimit, exists := mv.dutyLimit(msgID, msgSlot, validatorIndexCount)
if !exists {
return nil
}
Expand All @@ -74,10 +74,15 @@ func (mv *messageValidator) validateDutyCount(
return nil
}

func (mv *messageValidator) dutyLimit(msgID spectypes.MessageID, validatorIndexCount int) (int, bool) {
func (mv *messageValidator) dutyLimit(msgID spectypes.MessageID, slot phase0.Slot, validatorIndexCount int) (int, bool) {
switch msgID.GetRoleType() {
case spectypes.RoleAggregator, spectypes.RoleValidatorRegistration, spectypes.RoleVoluntaryExit:
// TODO: better solution for RoleValidatorRegistration: https://github.com/ssvlabs/ssv/pull/1393#discussion_r1667687976
case spectypes.RoleVoluntaryExit:
pk := phase0.BLSPubKey{}
copy(pk[:], msgID.GetDutyExecutorID())

return mv.dutyStore.VoluntaryExit.GetDutyCount(slot, pk), true

case spectypes.RoleAggregator, spectypes.RoleValidatorRegistration:
return 2, true

case spectypes.RoleCommittee:
Expand Down
2 changes: 2 additions & 0 deletions operator/duties/dutystore/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ type Store struct {
Attester *Duties[eth2apiv1.AttesterDuty]
Proposer *Duties[eth2apiv1.ProposerDuty]
SyncCommittee *SyncCommitteeDuties
VoluntaryExit *VoluntaryExitDuties
}

func New() *Store {
return &Store{
Attester: NewDuties[eth2apiv1.AttesterDuty](),
Proposer: NewDuties[eth2apiv1.ProposerDuty](),
SyncCommittee: NewSyncCommitteeDuties(),
VoluntaryExit: NewVoluntaryExit(),
}
}
51 changes: 51 additions & 0 deletions operator/duties/dutystore/voluntary_exit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package dutystore

import (
"sync"

"github.com/attestantio/go-eth2-client/spec/phase0"
)

type VoluntaryExitDuties struct {
mu sync.RWMutex
m map[phase0.Slot]map[phase0.BLSPubKey]int
}

func NewVoluntaryExit() *VoluntaryExitDuties {
return &VoluntaryExitDuties{
m: make(map[phase0.Slot]map[phase0.BLSPubKey]int),
}
}

func (d *VoluntaryExitDuties) GetDutyCount(slot phase0.Slot, pk phase0.BLSPubKey) int {
d.mu.RLock()
defer d.mu.RUnlock()

v, ok := d.m[slot]
if !ok {
return 0
}

return v[pk]
}

func (d *VoluntaryExitDuties) AddDuty(slot phase0.Slot, pk phase0.BLSPubKey) {
d.mu.Lock()
defer d.mu.Unlock()

v, ok := d.m[slot]
if !ok {
d.m[slot] = map[phase0.BLSPubKey]int{
pk: 1,
}
} else {
v[pk]++
}
}

func (d *VoluntaryExitDuties) RemoveSlot(slot phase0.Slot) {
d.mu.Lock()
defer d.mu.Unlock()

delete(d.m, slot)
}
5 changes: 2 additions & 3 deletions operator/duties/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ import (
"github.com/prysmaticlabs/prysm/v4/async/event"
"github.com/sourcegraph/conc/pool"
genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types"
"go.uber.org/zap"

spectypes "github.com/ssvlabs/ssv-spec/types"
"go.uber.org/zap"

"github.com/ssvlabs/ssv/beacon/goclient"
"github.com/ssvlabs/ssv/logging"
Expand Down Expand Up @@ -149,7 +148,7 @@ func NewScheduler(opts *SchedulerOptions) *Scheduler {
NewAttesterHandler(dutyStore.Attester),
NewProposerHandler(dutyStore.Proposer),
NewSyncCommitteeHandler(dutyStore.SyncCommittee),
NewVoluntaryExitHandler(opts.ValidatorExitCh),
NewVoluntaryExitHandler(dutyStore.VoluntaryExit, opts.ValidatorExitCh),
NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee),
},

Expand Down
12 changes: 11 additions & 1 deletion operator/duties/voluntary_exit.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,29 @@ import (
"go.uber.org/zap"

"github.com/ssvlabs/ssv/logging/fields"
"github.com/ssvlabs/ssv/operator/duties/dutystore"
)

const voluntaryExitSlotsToPostpone = phase0.Slot(4)

type ExitDescriptor struct {
OwnValidator bool
PubKey phase0.BLSPubKey
ValidatorIndex phase0.ValidatorIndex
BlockNumber uint64
}

type VoluntaryExitHandler struct {
baseHandler
duties *dutystore.VoluntaryExitDuties
validatorExitCh <-chan ExitDescriptor
dutyQueue []*spectypes.BeaconDuty
blockSlots map[uint64]phase0.Slot
}

func NewVoluntaryExitHandler(validatorExitCh <-chan ExitDescriptor) *VoluntaryExitHandler {
func NewVoluntaryExitHandler(duties *dutystore.VoluntaryExitDuties, validatorExitCh <-chan ExitDescriptor) *VoluntaryExitHandler {
return &VoluntaryExitHandler{
duties: duties,
validatorExitCh: validatorExitCh,
dutyQueue: make([]*spectypes.BeaconDuty, 0),
blockSlots: map[uint64]phase0.Slot{},
Expand Down Expand Up @@ -63,6 +67,7 @@ func (h *VoluntaryExitHandler) HandleDuties(ctx context.Context) {
}

h.dutyQueue = pendingDuties
h.duties.RemoveSlot(currentSlot - phase0.Slot(h.network.SlotsPerEpoch()))

if dutyCount := len(dutiesForExecution); dutyCount != 0 {
h.dutiesExecutor.ExecuteDuties(h.logger, dutiesForExecution)
Expand Down Expand Up @@ -92,6 +97,11 @@ func (h *VoluntaryExitHandler) HandleDuties(ctx context.Context) {
ValidatorIndex: exitDescriptor.ValidatorIndex,
}

h.duties.AddDuty(dutySlot, exitDescriptor.PubKey)
if !exitDescriptor.OwnValidator {
continue
}

h.dutyQueue = append(h.dutyQueue, duty)

h.logger.Debug("🛠 scheduled duty for execution",
Expand Down
7 changes: 6 additions & 1 deletion operator/duties/voluntary_exit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ import (
spectypes "github.com/ssvlabs/ssv-spec/types"

"github.com/ssvlabs/ssv/beacon/goclient"
"github.com/ssvlabs/ssv/operator/duties/dutystore"
mocknetwork "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon/mocks"
)

func TestVoluntaryExitHandler_HandleDuties(t *testing.T) {
exitCh := make(chan ExitDescriptor)
handler := NewVoluntaryExitHandler(exitCh)
handler := NewVoluntaryExitHandler(dutystore.NewVoluntaryExit(), exitCh)

currentSlot := &SafeValue[phase0.Slot]{}
currentSlot.Set(0)
Expand All @@ -38,21 +39,25 @@ func TestVoluntaryExitHandler_HandleDuties(t *testing.T) {
const blockNumber = uint64(1)

normalExit := ExitDescriptor{
OwnValidator: true,
PubKey: phase0.BLSPubKey{1, 2, 3},
ValidatorIndex: phase0.ValidatorIndex(1),
BlockNumber: blockNumber,
}
sameBlockExit := ExitDescriptor{
OwnValidator: true,
PubKey: phase0.BLSPubKey{4, 5, 6},
ValidatorIndex: phase0.ValidatorIndex(2),
BlockNumber: normalExit.BlockNumber,
}
newBlockExit := ExitDescriptor{
OwnValidator: true,
PubKey: phase0.BLSPubKey{1, 2, 3},
ValidatorIndex: phase0.ValidatorIndex(1),
BlockNumber: normalExit.BlockNumber + 1,
}
pastBlockExit := ExitDescriptor{
OwnValidator: true,
PubKey: phase0.BLSPubKey{1, 2, 3},
ValidatorIndex: phase0.ValidatorIndex(1),
BlockNumber: normalExit.BlockNumber + 4,
Expand Down
3 changes: 1 addition & 2 deletions operator/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@ import (
"context"
"fmt"

"github.com/ssvlabs/ssv/network"

"go.uber.org/zap"

"github.com/ssvlabs/ssv/eth/executionclient"
"github.com/ssvlabs/ssv/exporter/api"
qbftstorage "github.com/ssvlabs/ssv/ibft/storage"
"github.com/ssvlabs/ssv/logging"
"github.com/ssvlabs/ssv/logging/fields"
"github.com/ssvlabs/ssv/network"
"github.com/ssvlabs/ssv/networkconfig"
"github.com/ssvlabs/ssv/operator/duties"
"github.com/ssvlabs/ssv/operator/duties/dutystore"
Expand Down

0 comments on commit c5dfdac

Please sign in to comment.