-
Notifications
You must be signed in to change notification settings - Fork 995
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
slasher retrieve and cache validator public key (#5220)
* cache and retrieval of validator public keys * fix comments * fix comment * fix variables * gaz * ivan feedback fixes * goimports * fix test * comments on in line slice update Co-authored-by: terence tsao <terence@prysmaticlabs.com>
- Loading branch information
1 parent
f385a1d
commit 0df1226
Showing
8 changed files
with
240 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package beaconclient | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/pkg/errors" | ||
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" | ||
"go.opencensus.io/trace" | ||
) | ||
|
||
// FindOrGetPublicKeys gets public keys from cache or request validators public | ||
// keys from a beacon node via gRPC. | ||
func (bs *Service) FindOrGetPublicKeys( | ||
ctx context.Context, | ||
validatorIndices []uint64, | ||
) (map[uint64][]byte, error) { | ||
ctx, span := trace.StartSpan(ctx, "beaconclient.FindOrGetPublicKeys") | ||
defer span.End() | ||
|
||
validators := make(map[uint64][]byte, len(validatorIndices)) | ||
notFound := 0 | ||
for _, validatorIdx := range validatorIndices { | ||
pub, exists := bs.publicKeyCache.Get(validatorIdx) | ||
if exists { | ||
validators[validatorIdx] = pub | ||
continue | ||
} | ||
// inline removal of cached elements from slice | ||
validatorIndices[notFound] = validatorIdx | ||
notFound++ | ||
} | ||
// trim the slice to its new size | ||
validatorIndices = validatorIndices[:notFound] | ||
|
||
if len(validators) > 0 { | ||
log.Tracef( | ||
"Retrieved validators public keys from cache: %v", | ||
validators, | ||
) | ||
} | ||
|
||
if notFound == 0 { | ||
return validators, nil | ||
} | ||
vc, err := bs.beaconClient.ListValidators(ctx, ðpb.ListValidatorsRequest{ | ||
Indices: validatorIndices, | ||
}) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "could not request validators public key: %d", validatorIndices) | ||
} | ||
for _, v := range vc.ValidatorList { | ||
validators[v.Index] = v.Validator.PublicKey | ||
bs.publicKeyCache.Set(v.Index, v.Validator.PublicKey) | ||
} | ||
log.Tracef( | ||
"Retrieved validators id public key map: %v", | ||
validators, | ||
) | ||
return validators, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package beaconclient | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"testing" | ||
|
||
"github.com/golang/mock/gomock" | ||
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" | ||
"github.com/prysmaticlabs/prysm/shared/mock" | ||
"github.com/prysmaticlabs/prysm/shared/testutil" | ||
"github.com/prysmaticlabs/prysm/slasher/cache" | ||
"github.com/sirupsen/logrus" | ||
logTest "github.com/sirupsen/logrus/hooks/test" | ||
) | ||
|
||
func TestService_RequestValidator(t *testing.T) { | ||
hook := logTest.NewGlobal() | ||
logrus.SetLevel(logrus.TraceLevel) | ||
ctrl := gomock.NewController(t) | ||
defer ctrl.Finish() | ||
client := mock.NewMockBeaconChainClient(ctrl) | ||
validatorCache, err := cache.NewPublicKeyCache(0, nil) | ||
if err != nil { | ||
t.Fatalf("could not create new cache: %v", err) | ||
} | ||
bs := Service{ | ||
beaconClient: client, | ||
publicKeyCache: validatorCache, | ||
} | ||
wanted := ðpb.Validators{ | ||
ValidatorList: []*ethpb.Validators_ValidatorContainer{ | ||
{ | ||
Index: 0, Validator: ðpb.Validator{PublicKey: []byte{1, 2, 3}}, | ||
}, | ||
{ | ||
Index: 1, Validator: ðpb.Validator{PublicKey: []byte{2, 4, 5}}, | ||
}, | ||
}, | ||
} | ||
wanted2 := ðpb.Validators{ | ||
ValidatorList: []*ethpb.Validators_ValidatorContainer{ | ||
{ | ||
Index: 3, Validator: ðpb.Validator{PublicKey: []byte{3, 4, 5}}, | ||
}, | ||
}, | ||
} | ||
client.EXPECT().ListValidators( | ||
gomock.Any(), | ||
gomock.Any(), | ||
).Return(wanted, nil) | ||
|
||
client.EXPECT().ListValidators( | ||
gomock.Any(), | ||
gomock.Any(), | ||
).Return(wanted2, nil) | ||
|
||
// We request public key of validator id 0,1. | ||
res, err := bs.FindOrGetPublicKeys(context.Background(), []uint64{0, 1}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
for i, v := range wanted.ValidatorList { | ||
if !bytes.Equal(res[v.Index], wanted.ValidatorList[i].Validator.PublicKey) { | ||
t.Errorf("Wanted %v, received %v", wanted, res) | ||
} | ||
} | ||
|
||
testutil.AssertLogsContain(t, hook, "Retrieved validators id public key map:") | ||
testutil.AssertLogsDoNotContain(t, hook, "Retrieved validators public keys from cache:") | ||
// We expect public key of validator id 0 to be in cache. | ||
res, err = bs.FindOrGetPublicKeys(context.Background(), []uint64{0, 3}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
for i, v := range wanted2.ValidatorList { | ||
if !bytes.Equal(res[v.Index], wanted2.ValidatorList[i].Validator.PublicKey) { | ||
t.Errorf("Wanted %v, received %v", wanted2, res) | ||
} | ||
} | ||
testutil.AssertLogsContain(t, hook, "Retrieved validators public keys from cache: map[0:[1 2 3]]") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package cache | ||
|
||
import ( | ||
lru "github.com/hashicorp/golang-lru" | ||
"github.com/prometheus/client_golang/prometheus" | ||
"github.com/prometheus/client_golang/prometheus/promauto" | ||
) | ||
|
||
var ( | ||
// validatorsCacheSize defines the max number of validators public keys the cache can hold. | ||
validatorsCacheSize = 300000 | ||
// Metrics for the validator cache. | ||
validatorsCacheHit = promauto.NewCounter(prometheus.CounterOpts{ | ||
Name: "validators_cache_hit", | ||
Help: "The total number of cache hits on the validators cache.", | ||
}) | ||
validatorsCacheMiss = promauto.NewCounter(prometheus.CounterOpts{ | ||
Name: "validators_cache_miss", | ||
Help: "The total number of cache misses on the validators cache.", | ||
}) | ||
) | ||
|
||
// PublicKeyCache is used to store the public keys needed for signature verification. | ||
type PublicKeyCache struct { | ||
cache *lru.Cache | ||
} | ||
|
||
// NewPublicKeyCache initializes the cache. | ||
func NewPublicKeyCache(size int, onEvicted func(key interface{}, value interface{})) (*PublicKeyCache, error) { | ||
if size != 0 { | ||
validatorsCacheSize = size | ||
} | ||
cache, err := lru.NewWithEvict(validatorsCacheSize, onEvicted) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &PublicKeyCache{cache: cache}, nil | ||
} | ||
|
||
// Get returns an ok bool and the cached value for the requested validator id key, if any. | ||
func (c *PublicKeyCache) Get(validatorIdx uint64) ([]byte, bool) { | ||
item, exists := c.cache.Get(validatorIdx) | ||
if exists && item != nil { | ||
validatorsCacheHit.Inc() | ||
return item.([]byte), true | ||
} | ||
|
||
validatorsCacheMiss.Inc() | ||
return nil, false | ||
} | ||
|
||
// Set the response in the cache. | ||
func (c *PublicKeyCache) Set(validatorIdx uint64, publicKey []byte) { | ||
_ = c.cache.Add(validatorIdx, publicKey) | ||
} | ||
|
||
// Delete removes a validator id from the cache and returns if it existed or not. | ||
// Performs the onEviction function before removal. | ||
func (c *PublicKeyCache) Delete(validatorIdx uint64) bool { | ||
return c.cache.Remove(validatorIdx) | ||
} | ||
|
||
// Has returns true if the key exists in the cache. | ||
func (c *PublicKeyCache) Has(validatorIdx uint64) bool { | ||
return c.cache.Contains(validatorIdx) | ||
} | ||
|
||
// Clear removes all keys from the ValidatorCache. | ||
func (c *PublicKeyCache) Clear() { | ||
c.cache.Purge() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters