-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
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