diff --git a/beacon-chain/state/stategen/BUILD.bazel b/beacon-chain/state/stategen/BUILD.bazel index 3bcb428a4..0220108e7 100644 --- a/beacon-chain/state/stategen/BUILD.bazel +++ b/beacon-chain/state/stategen/BUILD.bazel @@ -36,6 +36,7 @@ go_library( "//consensus-types/blocks:go_default_library", "//consensus-types/interfaces:go_default_library", "//consensus-types/primitives:go_default_library", + "//crypto/bls:go_default_library", "//encoding/bytesutil:go_default_library", "//monitoring/tracing:go_default_library", "//proto/prysm/v1alpha1:go_default_library", diff --git a/beacon-chain/state/stategen/service.go b/beacon-chain/state/stategen/service.go index 614324478..ed64e6387 100644 --- a/beacon-chain/state/stategen/service.go +++ b/beacon-chain/state/stategen/service.go @@ -6,6 +6,7 @@ package stategen import ( "context" "sync" + "time" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v4/beacon-chain/db" @@ -14,12 +15,15 @@ import ( "github.com/prysmaticlabs/prysm/v4/beacon-chain/sync/backfill" "github.com/prysmaticlabs/prysm/v4/config/params" "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" + "github.com/prysmaticlabs/prysm/v4/crypto/bls" "github.com/prysmaticlabs/prysm/v4/encoding/bytesutil" "go.opencensus.io/trace" ) var defaultHotStateDBInterval primitives.Slot = 128 +var populatePubkeyCacheOnce sync.Once + // StateManager represents a management object that handles the internal // logic of maintaining both hot and cold states in DB. type StateManager interface { @@ -138,6 +142,24 @@ func (s *State) Resume(ctx context.Context, fState state.BeaconState) (state.Bea s.finalizedInfo = &finalizedInfo{slot: fState.Slot(), root: fRoot, state: fState.Copy()} + // Pre-populate the pubkey cache with the validator public keys from the finalized state. + // This process takes about 30 seconds on mainnet with 450,000 validators. + go populatePubkeyCacheOnce.Do(func() { + log.Debug("Populating pubkey cache") + start := time.Now() + if err := fState.ReadFromEveryValidator(func(_ int, val state.ReadOnlyValidator) error { + if ctx.Err() != nil { + return ctx.Err() + } + pub := val.PublicKey() + _, err := bls.PublicKeyFromBytes(pub[:]) + return err + }); err != nil { + log.WithError(err).Error("Failed to populate pubkey cache") + } + log.WithField("duration", time.Since(start)).Debug("Done populating pubkey cache") + }) + return fState, nil } diff --git a/crypto/bls/blst/BUILD.bazel b/crypto/bls/blst/BUILD.bazel index e9cdf5f2f..b75467a81 100644 --- a/crypto/bls/blst/BUILD.bazel +++ b/crypto/bls/blst/BUILD.bazel @@ -34,6 +34,7 @@ go_test( "public_key_test.go", "secret_key_test.go", "signature_test.go", + "test_helper_test.go", ], embed = [":go_default_library"], deps = [ diff --git a/crypto/bls/blst/public_key.go b/crypto/bls/blst/public_key.go index 618a0464d..9653e2722 100644 --- a/crypto/bls/blst/public_key.go +++ b/crypto/bls/blst/public_key.go @@ -12,7 +12,7 @@ import ( "github.com/prysmaticlabs/prysm/v4/crypto/bls/common" ) -var maxKeys = 1000000 +var maxKeys = 1_000_000 var pubkeyCache = lruwrpr.New(maxKeys) // PublicKey used in the BLS signature scheme. diff --git a/crypto/bls/blst/public_key_test.go b/crypto/bls/blst/public_key_test.go index 164dc42a6..cd83038c1 100644 --- a/crypto/bls/blst/public_key_test.go +++ b/crypto/bls/blst/public_key_test.go @@ -97,3 +97,27 @@ func TestPublicKeysEmpty(t *testing.T) { _, err := blst.AggregatePublicKeys(pubs) require.ErrorContains(t, "nil or empty public keys", err) } + +func BenchmarkPublicKeyFromBytes(b *testing.B) { + priv, err := blst.RandKey() + require.NoError(b, err) + pubkey := priv.PublicKey() + pubkeyBytes := pubkey.Marshal() + + b.Run("cache on", func(b *testing.B) { + blst.EnableCaches() + for i := 0; i < b.N; i++ { + _, err := blst.PublicKeyFromBytes(pubkeyBytes) + require.NoError(b, err) + } + }) + + b.Run("cache off", func(b *testing.B) { + blst.DisableCaches() + for i := 0; i < b.N; i++ { + _, err := blst.PublicKeyFromBytes(pubkeyBytes) + require.NoError(b, err) + } + }) + +} diff --git a/crypto/bls/blst/test_helper_test.go b/crypto/bls/blst/test_helper_test.go new file mode 100644 index 000000000..dd42f9988 --- /dev/null +++ b/crypto/bls/blst/test_helper_test.go @@ -0,0 +1,13 @@ +package blst + +// Note: These functions are for tests to access private globals, such as pubkeyCache. + +// DisableCaches sets the cache sizes to 0. +func DisableCaches() { + pubkeyCache.Resize(0) +} + +// EnableCaches sets the cache sizes to the default values. +func EnableCaches() { + pubkeyCache.Resize(maxKeys) +}