Skip to content

Commit

Permalink
EIP6110: add validator index cache
Browse files Browse the repository at this point in the history
  • Loading branch information
terencechain committed Apr 30, 2024
1 parent 2c5a2e8 commit 9ccd63c
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 3 deletions.
1 change: 1 addition & 0 deletions beacon-chain/state/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ type WriteOnlyBeaconState interface {
SetLatestExecutionPayloadHeader(payload interfaces.ExecutionData) error
SetNextWithdrawalIndex(i uint64) error
SetNextWithdrawalValidatorIndex(i primitives.ValidatorIndex) error
SaveValidatorIndices()
}

// ReadOnlyValidator defines a struct which only has read access to validator methods.
Expand Down
2 changes: 2 additions & 0 deletions beacon-chain/state/state-native/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ go_library(
"ssz.go",
"state_trie.go",
"types.go",
"validator_index_cache.go",
] + select({
"//config:mainnet": ["beacon_state_mainnet.go"],
"//config:minimal": ["beacon_state_minimal.go"],
Expand Down Expand Up @@ -72,6 +73,7 @@ go_library(
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
"@com_github_prysmaticlabs_fastssz//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
],
Expand Down
1 change: 1 addition & 0 deletions beacon-chain/state/state-native/beacon_state_mainnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ type BeaconState struct {
stateFieldLeaves map[types.FieldIndex]*fieldtrie.FieldTrie
rebuildTrie map[types.FieldIndex]bool
valMapHandler *stateutil.ValidatorMapHandler
validatorIndexCache *validatorIndexCache
merkleLayers [][][]byte
sharedFieldReferences map[types.FieldIndex]*stateutil.Reference
}
Expand Down
4 changes: 4 additions & 0 deletions beacon-chain/state/state-native/getters_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ func (b *BeaconState) ValidatorIndexByPubkey(key [fieldparams.BLSPubkeyLength]by
b.lock.RLock()
defer b.lock.RUnlock()

if features.Get().EIP6110ValidatorIndexCache {
return b.getValidatorIndex(key)
}

var numOfVals int
if features.Get().EnableExperimentalState {
numOfVals = b.validatorsMultiValue.Len(b)
Expand Down
13 changes: 13 additions & 0 deletions beacon-chain/state/state-native/setters_misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/v5/config/features"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/container/slice"
Expand Down Expand Up @@ -107,6 +108,18 @@ func (b *BeaconState) SetHistoricalRoots(val [][]byte) error {
return nil
}

// SaveValidatorIndices save validator indices of beacon chain to cache
func (b *BeaconState) SaveValidatorIndices() {
if !features.Get().EIP6110ValidatorIndexCache {
return
}

b.lock.Lock()
defer b.lock.Unlock()

b.saveValidatorIndices()
}

// AppendHistoricalRoots for the beacon state. Appends the new value
// to the end of list.
func (b *BeaconState) AppendHistoricalRoots(root [32]byte) error {
Expand Down
3 changes: 2 additions & 1 deletion beacon-chain/state/state-native/state_trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,8 @@ func (b *BeaconState) Copy() state.BeaconState {
stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount),

// Share the reference to validator index map.
valMapHandler: b.valMapHandler,
valMapHandler: b.valMapHandler,
validatorIndexCache: b.validatorIndexCache,
}

if features.Get().EnableExperimentalState {
Expand Down
97 changes: 97 additions & 0 deletions beacon-chain/state/state-native/validator_index_cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package state_native

import (
"sync"

"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/config/features"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
consensus_types "github.com/prysmaticlabs/prysm/v5/consensus-types"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
log "github.com/sirupsen/logrus"
)

// validatorIndexCache maintains a mapping from validator public keys to their indices within the beacon state.
// It includes a lastFinalizedIndex to track updates up to the last finalized validator index,
// and uses a mutex for concurrent read/write access to the cache.
type validatorIndexCache struct {
indexMap map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex // Maps BLS public keys to validator indices.
lastFinalizedIndex int // Index of the last finalized validator to avoid reading validators before this point.
mutex sync.RWMutex
}

// newValidatorIndexCache initializes a new validator index cache with an empty index map.
func newValidatorIndexCache() *validatorIndexCache {

Check failure on line 25 in beacon-chain/state/state-native/validator_index_cache.go

View workflow job for this annotation

GitHub Actions / Lint

func `newValidatorIndexCache` is unused (unused)
return &validatorIndexCache{
indexMap: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
}
}

// getValidatorIndex retrieves the validator index for a given public key from the cache.
// If the public key is not found in the cache, it searches through the state starting from the last finalized index.
func (b *BeaconState) getValidatorIndex(pubKey [fieldparams.BLSPubkeyLength]byte) (primitives.ValidatorIndex, bool) {
b.validatorIndexCache.mutex.RLock()
index, found := b.validatorIndexCache.indexMap[pubKey]
b.validatorIndexCache.mutex.RUnlock()
if found {
return index, true
}

finalizedIndex := b.validatorIndexCache.lastFinalizedIndex
pubKeys, err := b.publicKeysSinceIndexReadOnly(finalizedIndex)
if err != nil {
log.WithError(err).Errorf("Failed to get public keys since last finalized index %d", finalizedIndex)
return 0, false
}
for i, key := range pubKeys {
if key == pubKey {
index := primitives.ValidatorIndex(finalizedIndex + i)
return index, true
}
}
return 0, false
}

// saveValidatorIndices updates the validator index cache with new indices.
// It processes validator indices starting after the last finalized index and updates the tracker.
func (b *BeaconState) saveValidatorIndices() {
b.validatorIndexCache.mutex.Lock()
defer b.validatorIndexCache.mutex.Unlock()

finalizedIndex := b.validatorIndexCache.lastFinalizedIndex
pubKeys, err := b.publicKeysSinceIndexReadOnly(finalizedIndex)
if err != nil {
log.WithError(err).Errorf("Failed to retrieve public keys starting from the last finalized index %d", finalizedIndex)
return
}
for i, key := range pubKeys {
b.validatorIndexCache.indexMap[key] = primitives.ValidatorIndex(finalizedIndex + i)
}
b.validatorIndexCache.lastFinalizedIndex += len(pubKeys)
}

// publicKeysSinceIndexReadOnly constructs a list of validator public keys starting from a specified index.
// The indices in the returned list correspond to their respective validator indices in the state.
// It returns an error if the specified index is out of bounds. This function is read-only and does not use locks.
func (b *BeaconState) publicKeysSinceIndexReadOnly(index int) ([][fieldparams.BLSPubkeyLength]byte, error) {
totalValidators := b.validatorsLen()
if index >= totalValidators {
return nil, errors.Wrapf(consensus_types.ErrOutOfBounds, "index %d is out of bounds %d", index, totalValidators)
}
result := make([][fieldparams.BLSPubkeyLength]byte, totalValidators-index)
for i := index; i < totalValidators; i++ {
var publicKey [fieldparams.BLSPubkeyLength]byte
if features.Get().EnableExperimentalState {
val, err := b.validatorsMultiValue.At(b, uint64(i))
if err != nil {
return nil, err
}
publicKey = bytesutil.ToBytes48(val.PublicKey)
} else {
publicKey = bytesutil.ToBytes48(b.validators[i].PublicKey)
}
result[i-index] = publicKey
}
return result, nil
}
10 changes: 8 additions & 2 deletions config/features/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@ type Flags struct {
// BlobSaveFsync requires blob saving to block on fsync to ensure blobs are durably persisted before passing DA.
BlobSaveFsync bool

SaveInvalidBlock bool // SaveInvalidBlock saves invalid block to temp.
SaveInvalidBlob bool // SaveInvalidBlob saves invalid blob to temp.
SaveInvalidBlock bool // SaveInvalidBlock saves invalid block to temp.
SaveInvalidBlob bool // SaveInvalidBlob saves invalid blob to temp.
EIP6110ValidatorIndexCache bool // EIP6110ValidatorIndexCache specifies whether to use the new validator index cache.

// KeystoreImportDebounceInterval specifies the time duration the validator waits to reload new keys if they have
// changed on disk. This feature is for advanced use cases only.
Expand Down Expand Up @@ -254,6 +255,11 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
cfg.EnableQUIC = true
}

if ctx.IsSet(eip6110ValidatorCache.Name) {
logEnabled(eip6110ValidatorCache)
cfg.EIP6110ValidatorIndexCache = true
}

cfg.AggregateIntervals = [3]time.Duration{aggregateFirstInterval.Value, aggregateSecondInterval.Value, aggregateThirdInterval.Value}
Init(cfg)
return nil
Expand Down
6 changes: 6 additions & 0 deletions config/features/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ var (
Name: "enable-quic",
Usage: "Enables connection using the QUIC protocol for peers which support it.",
}
// eip6110ValidatorCache is a flag for enabling the EIP-6110 validator cache.
eip6110ValidatorCache = &cli.BoolFlag{
Name: "eip6110-validator-cache",
Usage: "Enables the EIP-6110 validator cache.",
}
)

// devModeFlags holds list of flags that are set when development mode is on.
Expand Down Expand Up @@ -223,6 +228,7 @@ var BeaconChainFlags = append(deprecatedBeaconFlags, append(deprecatedFlags, []c
EnableLightClient,
BlobSaveFsync,
EnableQUIC,
eip6110ValidatorCache,
}...)...)

// E2EBeaconChainFlags contains a list of the beacon chain feature flags to be tested in E2E.
Expand Down

0 comments on commit 9ccd63c

Please sign in to comment.