Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion cmd/exit_broadcast.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import (
"fmt"
"os"
"path/filepath"
"slices"
"strings"
"time"

eth2v1 "github.com/attestantio/go-eth2-client/api/v1"
eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"
k1 "github.com/decred/dcrd/dcrec/secp256k1/v4"
libp2plog "github.com/ipfs/go-log/v2"
Expand Down Expand Up @@ -252,9 +254,42 @@ func fetchFullExit(ctx context.Context, exitFilePath string, config exitConfig,
}

func broadcastExitsToBeacon(ctx context.Context, eth2Cl eth2wrap.Client, exits map[core.PubKey]eth2p0.SignedVoluntaryExit) error {
blsKeys := []eth2p0.BLSPubKey{}

for key := range exits {
blsKey, err := key.ToETH2()
if err != nil {
return errors.Wrap(err, "convert core pubkey to eth2 pubkey", z.Str("core_pubkey", key.String()))
}

blsKeys = append(blsKeys, blsKey)
}

rawValData, err := queryBeaconForValidator(ctx, eth2Cl, blsKeys, nil, []eth2v1.ValidatorState{eth2v1.ValidatorStateActiveOngoing})
if err != nil {
return errors.Wrap(err, "fetch all validators indices from beacon")
}

activePubKeys := []eth2p0.BLSPubKey{}
for _, val := range rawValData.Data {
activePubKeys = append(activePubKeys, val.Validator.PublicKey)
}

activeExits := make(map[core.PubKey]eth2p0.SignedVoluntaryExit)

for validator, fullExit := range exits {
valCtx := log.WithCtx(ctx, z.Str("validator", validator.String()))

eth2Key, err := validator.ToETH2()
if err != nil {
return errors.Wrap(err, "convert core pubkey to eth2 pubkey", z.Str("core_pubkey", validator.String()))
}

if !slices.Contains(activePubKeys, eth2Key) {
log.Info(valCtx, "Validator is not active, skipping broadcast")
continue
}

rawPkBytes, err := validator.Bytes()
if err != nil {
return errors.Wrap(err, "serialize validator key bytes", z.Str("validator", validator.String()))
Expand Down Expand Up @@ -284,9 +319,11 @@ func broadcastExitsToBeacon(ctx context.Context, eth2Cl eth2wrap.Client, exits m
if err := tbls.Verify(pubkey, exitRoot[:], signature); err != nil {
return errors.Wrap(err, "exit message signature not verified")
}

activeExits[validator] = fullExit
}

for validator, fullExit := range exits {
for validator, fullExit := range activeExits {
valCtx := log.WithCtx(ctx, z.Str("validator", validator.String()))
if err := eth2Cl.SubmitVoluntaryExit(valCtx, &fullExit); err != nil {
return errors.Wrap(err, "submit voluntary exit")
Expand Down
23 changes: 13 additions & 10 deletions cmd/exit_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,26 +225,28 @@ func signAllValidatorsExits(ctx context.Context, config exitConfig, eth2Cl eth2w
valsEth2 = append(valsEth2, eth2PK)
}

rawValData, err := queryBeaconForValidator(ctx, eth2Cl, valsEth2, nil)
rawValData, err := queryBeaconForValidator(ctx, eth2Cl, valsEth2, nil, []eth2v1.ValidatorState{eth2v1.ValidatorStatePendingQueued, eth2v1.ValidatorStateActiveOngoing})
if err != nil {
return nil, errors.Wrap(err, "fetch all validators indices from beacon")
}

activeShares := make(keystore.ValidatorShares)

for _, val := range rawValData.Data {
share, ok := shares[core.PubKeyFrom48Bytes(val.Validator.PublicKey)]
if !ok {
return nil, errors.New("validator public key not found in cluster lock", z.Str("validator_public_key", val.Validator.PublicKey.String()))
}

share.Index = int(val.Index)
shares[core.PubKeyFrom48Bytes(val.Validator.PublicKey)] = share
activeShares[core.PubKeyFrom48Bytes(val.Validator.PublicKey)] = share
}

log.Info(ctx, "Signing partial exit message for all active validators")
log.Info(ctx, "Signing partial exit message for all active validators", z.Int("active_validators", len(activeShares)), z.Int("inactive_validators", len(shares)-len(activeShares)))

var exitBlobs []obolapi.ExitBlob

for pk, share := range shares {
for pk, share := range activeShares {
exitMsg, err := signExit(ctx, eth2Cl, eth2p0.ValidatorIndex(share.Index), share.Share, eth2p0.Epoch(config.ExitEpoch))
if err != nil {
return nil, errors.Wrap(err, "sign partial exit message", z.Str("validator_public_key", pk.String()), z.Int("validator_index", share.Index), z.Int("exit_epoch", int(config.ExitEpoch)))
Expand Down Expand Up @@ -277,7 +279,7 @@ func fetchValidatorBLSPubKey(ctx context.Context, config exitConfig, eth2Cl eth2
return valEth2, nil
}

rawValData, err := queryBeaconForValidator(ctx, eth2Cl, nil, []eth2p0.ValidatorIndex{eth2p0.ValidatorIndex(config.ValidatorIndex)})
rawValData, err := queryBeaconForValidator(ctx, eth2Cl, nil, []eth2p0.ValidatorIndex{eth2p0.ValidatorIndex(config.ValidatorIndex)}, nil)
if err != nil {
return eth2p0.BLSPubKey{}, errors.Wrap(err, "fetch validator pubkey from beacon", z.Str("beacon_address", eth2Cl.Address()), z.U64("validator_index", config.ValidatorIndex))
}
Expand All @@ -301,7 +303,7 @@ func fetchValidatorIndex(ctx context.Context, config exitConfig, eth2Cl eth2wrap
return 0, errors.Wrap(err, "convert core pubkey to eth2 pubkey", z.Str("core_pubkey", config.ValidatorPubkey))
}

rawValData, err := queryBeaconForValidator(ctx, eth2Cl, []eth2p0.BLSPubKey{valEth2}, nil)
rawValData, err := queryBeaconForValidator(ctx, eth2Cl, []eth2p0.BLSPubKey{valEth2}, nil, nil)
if err != nil {
return 0, errors.Wrap(err, "fetch validator index from beacon", z.Str("beacon_address", eth2Cl.Address()), z.Str("validator_pubkey", valEth2.String()))
}
Expand All @@ -315,11 +317,12 @@ func fetchValidatorIndex(ctx context.Context, config exitConfig, eth2Cl eth2wrap
return 0, errors.New("validator public key not found in beacon node response", z.Str("beacon_address", eth2Cl.Address()), z.Str("validator_pubkey", valEth2.String()), z.Any("raw_response", rawValData))
}

func queryBeaconForValidator(ctx context.Context, eth2Cl eth2wrap.Client, pubKeys []eth2p0.BLSPubKey, indices []eth2p0.ValidatorIndex) (*eth2api.Response[map[eth2p0.ValidatorIndex]*eth2v1.Validator], error) {
func queryBeaconForValidator(ctx context.Context, eth2Cl eth2wrap.Client, pubKeys []eth2p0.BLSPubKey, indices []eth2p0.ValidatorIndex, states []eth2v1.ValidatorState) (*eth2api.Response[map[eth2p0.ValidatorIndex]*eth2v1.Validator], error) {
valAPICallOpts := &eth2api.ValidatorsOpts{
State: "head",
PubKeys: pubKeys,
Indices: indices,
State: "head",
PubKeys: pubKeys,
Indices: indices,
ValidatorStates: states,
}

rawValData, err := eth2Cl.Validators(ctx, valAPICallOpts)
Expand Down
Loading