mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 05:47:59 -05:00
Compare commits
12 Commits
use-next-s
...
v4.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75338dd83d | ||
|
|
99eebe9bac | ||
|
|
6b1efff4e8 | ||
|
|
763e9e3361 | ||
|
|
37182168e3 | ||
|
|
0325741318 | ||
|
|
150e8aa14d | ||
|
|
f4307a902c | ||
|
|
d257ef1742 | ||
|
|
aad7aa79d4 | ||
|
|
2eb2f87913 | ||
|
|
9214364c5e |
@@ -164,7 +164,7 @@ load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_depe
|
||||
go_rules_dependencies()
|
||||
|
||||
go_register_toolchains(
|
||||
go_version = "1.19.7",
|
||||
go_version = "1.19.8",
|
||||
nogo = "@//:nogo",
|
||||
)
|
||||
|
||||
|
||||
@@ -157,6 +157,7 @@ func (c *Client) do(ctx context.Context, method string, path string, body io.Rea
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req.Header.Add("User-Agent", version.BuildData())
|
||||
for _, o := range opts {
|
||||
o(req)
|
||||
}
|
||||
|
||||
@@ -403,6 +403,19 @@ func (s *Service) saveOrphanedOperations(ctx context.Context, orphanedRoot [32]b
|
||||
}
|
||||
saveOrphanedAttCount.Inc()
|
||||
}
|
||||
for _, as := range orphanedBlk.Block().Body().AttesterSlashings() {
|
||||
if err := s.cfg.SlashingPool.InsertAttesterSlashing(ctx, s.headStateReadOnly(ctx), as); err != nil {
|
||||
log.WithError(err).Error("Could not insert reorg attester slashing")
|
||||
}
|
||||
}
|
||||
for _, vs := range orphanedBlk.Block().Body().ProposerSlashings() {
|
||||
if err := s.cfg.SlashingPool.InsertProposerSlashing(ctx, s.headStateReadOnly(ctx), vs); err != nil {
|
||||
log.WithError(err).Error("Could not insert reorg proposer slashing")
|
||||
}
|
||||
}
|
||||
for _, v := range orphanedBlk.Block().Body().VoluntaryExits() {
|
||||
s.cfg.ExitPool.InsertVoluntaryExit(v)
|
||||
}
|
||||
if orphanedBlk.Version() >= version.Capella {
|
||||
changes, err := orphanedBlk.Block().Body().BLSToExecutionChanges()
|
||||
if err != nil {
|
||||
|
||||
@@ -326,6 +326,88 @@ func TestSaveOrphanedAtts(t *testing.T) {
|
||||
require.DeepEqual(t, wantAtts, atts)
|
||||
}
|
||||
|
||||
func TestSaveOrphanedOps(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
config := params.BeaconConfig()
|
||||
config.ShardCommitteePeriod = 0
|
||||
params.OverrideBeaconConfig(config)
|
||||
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
service := setupBeaconChain(t, beaconDB)
|
||||
service.genesisTime = time.Now().Add(time.Duration(-10*int64(1)*int64(params.BeaconConfig().SecondsPerSlot)) * time.Second)
|
||||
|
||||
// Chain setup
|
||||
// 0 -- 1 -- 2 -- 3
|
||||
// \-4
|
||||
st, keys := util.DeterministicGenesisState(t, 64)
|
||||
service.head = &head{state: st}
|
||||
blkG, err := util.GenerateFullBlock(st, keys, util.DefaultBlockGenConfig(), 0)
|
||||
assert.NoError(t, err)
|
||||
|
||||
util.SaveBlock(t, ctx, service.cfg.BeaconDB, blkG)
|
||||
rG, err := blkG.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
blk1, err := util.GenerateFullBlock(st, keys, util.DefaultBlockGenConfig(), 1)
|
||||
assert.NoError(t, err)
|
||||
blk1.Block.ParentRoot = rG[:]
|
||||
r1, err := blk1.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
blk2, err := util.GenerateFullBlock(st, keys, util.DefaultBlockGenConfig(), 2)
|
||||
assert.NoError(t, err)
|
||||
blk2.Block.ParentRoot = r1[:]
|
||||
r2, err := blk2.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
blkConfig := util.DefaultBlockGenConfig()
|
||||
blkConfig.NumBLSChanges = 5
|
||||
blkConfig.NumProposerSlashings = 1
|
||||
blkConfig.NumAttesterSlashings = 1
|
||||
blkConfig.NumVoluntaryExits = 1
|
||||
blk3, err := util.GenerateFullBlock(st, keys, blkConfig, 3)
|
||||
assert.NoError(t, err)
|
||||
blk3.Block.ParentRoot = r2[:]
|
||||
r3, err := blk3.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
blk4 := util.NewBeaconBlock()
|
||||
blk4.Block.Slot = 4
|
||||
blk4.Block.ParentRoot = rG[:]
|
||||
r4, err := blk4.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
ojc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||
ofc := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
|
||||
|
||||
for _, blk := range []*ethpb.SignedBeaconBlock{blkG, blk1, blk2, blk3, blk4} {
|
||||
r, err := blk.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
state, blkRoot, err := prepareForkchoiceState(ctx, blk.Block.Slot, r, bytesutil.ToBytes32(blk.Block.ParentRoot), [32]byte{}, ojc, ofc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, service.cfg.ForkChoiceStore.InsertNode(ctx, state, blkRoot))
|
||||
util.SaveBlock(t, ctx, beaconDB, blk)
|
||||
}
|
||||
|
||||
require.NoError(t, service.saveOrphanedOperations(ctx, r3, r4))
|
||||
require.Equal(t, 3, service.cfg.AttPool.AggregatedAttestationCount())
|
||||
wantAtts := []*ethpb.Attestation{
|
||||
blk3.Block.Body.Attestations[0],
|
||||
blk2.Block.Body.Attestations[0],
|
||||
blk1.Block.Body.Attestations[0],
|
||||
}
|
||||
atts := service.cfg.AttPool.AggregatedAttestations()
|
||||
sort.Slice(atts, func(i, j int) bool {
|
||||
return atts[i].Data.Slot > atts[j].Data.Slot
|
||||
})
|
||||
require.DeepEqual(t, wantAtts, atts)
|
||||
require.Equal(t, 1, len(service.cfg.SlashingPool.PendingProposerSlashings(ctx, st, false)))
|
||||
require.Equal(t, 1, len(service.cfg.SlashingPool.PendingAttesterSlashings(ctx, st, false)))
|
||||
exits, err := service.cfg.ExitPool.PendingExits()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(exits))
|
||||
}
|
||||
|
||||
func TestSaveOrphanedAtts_CanFilter(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
@@ -179,10 +179,14 @@ func (s *Service) prunePostBlockOperationPools(ctx context.Context, blk interfac
|
||||
return errors.Wrap(err, "could not process BLSToExecutionChanges")
|
||||
}
|
||||
|
||||
// Mark attester slashings as seen so we don't include same ones in future blocks.
|
||||
// Mark slashings as seen so we don't include same ones in future blocks.
|
||||
for _, as := range blk.Block().Body().AttesterSlashings() {
|
||||
s.cfg.SlashingPool.MarkIncludedAttesterSlashing(as)
|
||||
}
|
||||
for _, ps := range blk.Block().Body().ProposerSlashings() {
|
||||
s.cfg.SlashingPool.MarkIncludedProposerSlashing(ps)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
mockExecution "github.com/prysmaticlabs/prysm/v4/beacon-chain/execution/testing"
|
||||
doublylinkedtree "github.com/prysmaticlabs/prysm/v4/beacon-chain/forkchoice/doubly-linked-tree"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/attestations"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/slashings"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/voluntaryexits"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p"
|
||||
state_native "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stategen"
|
||||
@@ -131,6 +133,8 @@ func setupBeaconChain(t *testing.T, beaconDB db.Database) *Service {
|
||||
WithDepositCache(depositCache),
|
||||
WithChainStartFetcher(web3Service),
|
||||
WithAttestationPool(attestations.NewPool()),
|
||||
WithSlashingPool(slashings.NewPool()),
|
||||
WithExitPool(voluntaryexits.NewPool()),
|
||||
WithP2PBroadcaster(&mockBroadcaster{}),
|
||||
WithStateNotifier(&mockBeaconNode{}),
|
||||
WithForkChoiceStore(fc),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
types "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
)
|
||||
|
||||
@@ -36,14 +37,14 @@ var (
|
||||
// NextSlotState returns the saved state for the given blockroot.
|
||||
// It returns the last updated state if it matches. Otherwise it returns the previously
|
||||
// updated state if it matches its root. If no root matches it returns nil
|
||||
func NextSlotState(root []byte) state.BeaconState {
|
||||
func NextSlotState(root []byte, wantedSlot types.Slot) state.BeaconState {
|
||||
nsc.Lock()
|
||||
defer nsc.Unlock()
|
||||
if bytes.Equal(root, nsc.lastRoot) {
|
||||
if bytes.Equal(root, nsc.lastRoot) && nsc.lastState.Slot() <= wantedSlot {
|
||||
nextSlotCacheHit.Inc()
|
||||
return nsc.lastState.Copy()
|
||||
}
|
||||
if bytes.Equal(root, nsc.prevRoot) {
|
||||
if bytes.Equal(root, nsc.prevRoot) && nsc.prevState.Slot() <= wantedSlot {
|
||||
nextSlotCacheHit.Inc()
|
||||
return nsc.prevState.Copy()
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/util"
|
||||
)
|
||||
@@ -13,12 +14,12 @@ import (
|
||||
func TestTrailingSlotState_RoundTrip(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
r := []byte{'a'}
|
||||
s := transition.NextSlotState(r)
|
||||
s := transition.NextSlotState(r, 0)
|
||||
require.Equal(t, nil, s)
|
||||
|
||||
s, _ = util.DeterministicGenesisState(t, 1)
|
||||
require.NoError(t, transition.UpdateNextSlotCache(ctx, r, s))
|
||||
s = transition.NextSlotState(r)
|
||||
s = transition.NextSlotState(r, 1)
|
||||
require.Equal(t, primitives.Slot(1), s.Slot())
|
||||
|
||||
lastRoot, lastState := transition.LastCachedState()
|
||||
@@ -26,10 +27,23 @@ func TestTrailingSlotState_RoundTrip(t *testing.T) {
|
||||
require.Equal(t, s.Slot(), lastState.Slot())
|
||||
|
||||
require.NoError(t, transition.UpdateNextSlotCache(ctx, r, s))
|
||||
s = transition.NextSlotState(r)
|
||||
s = transition.NextSlotState(r, 2)
|
||||
require.Equal(t, primitives.Slot(2), s.Slot())
|
||||
|
||||
lastRoot, lastState = transition.LastCachedState()
|
||||
require.DeepEqual(t, r, lastRoot)
|
||||
require.Equal(t, s.Slot(), lastState.Slot())
|
||||
}
|
||||
|
||||
func TestTrailingSlotState_StateAdvancedBeyondRequest(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
r := []byte{'a'}
|
||||
s := transition.NextSlotState(r, 0)
|
||||
require.Equal(t, nil, s)
|
||||
|
||||
s, _ = util.DeterministicGenesisState(t, 1)
|
||||
assert.NoError(t, s.SetSlot(2))
|
||||
require.NoError(t, transition.UpdateNextSlotCache(ctx, r, s))
|
||||
s = transition.NextSlotState(r, 1)
|
||||
require.Equal(t, nil, s)
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ func ProcessSlotsUsingNextSlotCache(
|
||||
ctx, span := trace.StartSpan(ctx, "core.state.ProcessSlotsUsingNextSlotCache")
|
||||
defer span.End()
|
||||
|
||||
nextSlotState := NextSlotState(parentRoot)
|
||||
nextSlotState := NextSlotState(parentRoot, slot)
|
||||
if nextSlotState != nil {
|
||||
parentState = nextSlotState
|
||||
}
|
||||
|
||||
@@ -346,12 +346,19 @@ func (bs *Server) StreamBlocks(req *ethpb.StreamBlocksRequest, stream ethpb.Beac
|
||||
// StreamChainHead to clients every single time the head block and state of the chain change.
|
||||
// DEPRECATED: This endpoint is superseded by the /eth/v1/events Beacon API endpoint
|
||||
func (bs *Server) StreamChainHead(_ *emptypb.Empty, stream ethpb.BeaconChain_StreamChainHeadServer) error {
|
||||
stateChannel := make(chan *feed.Event, 1)
|
||||
stateChannel := make(chan *feed.Event, 4)
|
||||
stateSub := bs.StateNotifier.StateFeed().Subscribe(stateChannel)
|
||||
defer stateSub.Unsubscribe()
|
||||
for {
|
||||
select {
|
||||
case stateEvent := <-stateChannel:
|
||||
// In the event our node is in sync mode
|
||||
// we do not send the chainhead to the caller
|
||||
// due to the possibility of deadlocks when retrieving
|
||||
// all the chain related data.
|
||||
if bs.SyncChecker.Syncing() {
|
||||
continue
|
||||
}
|
||||
if stateEvent.Type == statefeed.BlockProcessed {
|
||||
res, err := bs.chainHeadRetrieval(stream.Context())
|
||||
if err != nil {
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
statefeed "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/state"
|
||||
dbTest "github.com/prysmaticlabs/prysm/v4/beacon-chain/db/testing"
|
||||
state_native "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native"
|
||||
mockSync "github.com/prysmaticlabs/prysm/v4/beacon-chain/sync/initial-sync/testing"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/features"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
@@ -287,6 +288,7 @@ func TestServer_StreamChainHead_OnHeadUpdated(t *testing.T) {
|
||||
CurrentJustifiedCheckPoint: s.CurrentJustifiedCheckpoint(),
|
||||
PreviousJustifiedCheckPoint: s.PreviousJustifiedCheckpoint()},
|
||||
OptimisticModeFetcher: &chainMock.ChainService{},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
}
|
||||
exitRoutine := make(chan bool)
|
||||
ctrl := gomock.NewController(t)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -68,6 +69,7 @@ func (vs *Server) setExecutionData(ctx context.Context, blk interfaces.SignedBea
|
||||
v, err = builderPayload.Value()
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("Proposer: failed to get builder payload value") // Default to local if can't get builder value.
|
||||
v = big.NewInt(0) // Default to local if can't get builder value.
|
||||
}
|
||||
builderValue := v.Uint64()
|
||||
|
||||
@@ -86,6 +88,7 @@ func (vs *Server) setExecutionData(ctx context.Context, blk interfaces.SignedBea
|
||||
blk.SetBlinded(true)
|
||||
if err := blk.SetExecution(builderPayload); err != nil {
|
||||
log.WithError(err).Warn("Proposer: failed to set builder payload")
|
||||
blk.SetBlinded(false)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
@@ -102,12 +105,12 @@ func (vs *Server) setExecutionData(ctx context.Context, blk interfaces.SignedBea
|
||||
blk.SetBlinded(true)
|
||||
if err := blk.SetExecution(builderPayload); err != nil {
|
||||
log.WithError(err).Warn("Proposer: failed to set builder payload")
|
||||
blk.SetBlinded(false)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
executionData, err := vs.getExecutionPayload(ctx, slot, idx, blk.Block().ParentRoot(), headState)
|
||||
@@ -148,6 +151,9 @@ func (vs *Server) getPayloadHeaderFromBuilder(ctx context.Context, slot primitiv
|
||||
if signedBid.IsNil() {
|
||||
return nil, errors.New("builder returned nil bid")
|
||||
}
|
||||
if signedBid.Version() != b.Version() {
|
||||
return nil, fmt.Errorf("builder bid response version: %d is different from head block version: %d", signedBid.Version(), b.Version())
|
||||
}
|
||||
bid, err := signedBid.Message()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get bid")
|
||||
|
||||
@@ -172,7 +172,7 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
vs.BlockBuilder = &builderTest.MockBuilderService{
|
||||
BidCapella: sBid,
|
||||
}
|
||||
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockBellatrix())
|
||||
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
|
||||
require.NoError(t, err)
|
||||
chain := &blockchainTest.ChainService{ForkChoiceStore: doublylinkedtree.New(), Genesis: time.Now(), Block: wb}
|
||||
vs.ForkFetcher = chain
|
||||
|
||||
@@ -66,6 +66,10 @@ func (vs *Server) unblindBuilderBlockCapella(ctx context.Context, b interfaces.R
|
||||
randaoReveal := b.Block().Body().RandaoReveal()
|
||||
graffiti := b.Block().Body().Graffiti()
|
||||
sig := b.Signature()
|
||||
blsToExecChange, err := b.Block().Body().BLSToExecutionChanges()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get bls to execution changes")
|
||||
}
|
||||
sb := ðpb.SignedBlindedBeaconBlockCapella{
|
||||
Block: ðpb.BlindedBeaconBlockCapella{
|
||||
Slot: b.Block().Slot(),
|
||||
@@ -83,6 +87,7 @@ func (vs *Server) unblindBuilderBlockCapella(ctx context.Context, b interfaces.R
|
||||
VoluntaryExits: b.Block().Body().VoluntaryExits(),
|
||||
SyncAggregate: agg,
|
||||
ExecutionPayloadHeader: header,
|
||||
BlsToExecutionChanges: blsToExecChange,
|
||||
},
|
||||
},
|
||||
Signature: sig[:],
|
||||
@@ -122,16 +127,17 @@ func (vs *Server) unblindBuilderBlockCapella(ctx context.Context, b interfaces.R
|
||||
ParentRoot: sb.Block.ParentRoot,
|
||||
StateRoot: sb.Block.StateRoot,
|
||||
Body: ðpb.BeaconBlockBodyCapella{
|
||||
RandaoReveal: sb.Block.Body.RandaoReveal,
|
||||
Eth1Data: sb.Block.Body.Eth1Data,
|
||||
Graffiti: sb.Block.Body.Graffiti,
|
||||
ProposerSlashings: sb.Block.Body.ProposerSlashings,
|
||||
AttesterSlashings: sb.Block.Body.AttesterSlashings,
|
||||
Attestations: sb.Block.Body.Attestations,
|
||||
Deposits: sb.Block.Body.Deposits,
|
||||
VoluntaryExits: sb.Block.Body.VoluntaryExits,
|
||||
SyncAggregate: agg,
|
||||
ExecutionPayload: capellaPayload,
|
||||
RandaoReveal: sb.Block.Body.RandaoReveal,
|
||||
Eth1Data: sb.Block.Body.Eth1Data,
|
||||
Graffiti: sb.Block.Body.Graffiti,
|
||||
ProposerSlashings: sb.Block.Body.ProposerSlashings,
|
||||
AttesterSlashings: sb.Block.Body.AttesterSlashings,
|
||||
Attestations: sb.Block.Body.Attestations,
|
||||
Deposits: sb.Block.Body.Deposits,
|
||||
VoluntaryExits: sb.Block.Body.VoluntaryExits,
|
||||
SyncAggregate: agg,
|
||||
ExecutionPayload: capellaPayload,
|
||||
BlsToExecutionChanges: blsToExecChange,
|
||||
},
|
||||
},
|
||||
Signature: sb.Signature,
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/ssz"
|
||||
v1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1"
|
||||
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/util"
|
||||
)
|
||||
@@ -100,6 +101,12 @@ func TestServer_unblindBuilderCapellaBlock(t *testing.T) {
|
||||
GasLimit: 123,
|
||||
WithdrawalsRoot: wdRoot[:],
|
||||
}
|
||||
b.Block.Body.BlsToExecutionChanges = []*eth.SignedBLSToExecutionChange{
|
||||
{Message: ð.BLSToExecutionChange{ValidatorIndex: 1, FromBlsPubkey: []byte{'a'}}},
|
||||
{Message: ð.BLSToExecutionChange{ValidatorIndex: 2, FromBlsPubkey: []byte{'b'}}},
|
||||
{Message: ð.BLSToExecutionChange{ValidatorIndex: 3, FromBlsPubkey: []byte{'c'}}},
|
||||
{Message: ð.BLSToExecutionChange{ValidatorIndex: 4, FromBlsPubkey: []byte{'d'}}},
|
||||
}
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
@@ -113,6 +120,12 @@ func TestServer_unblindBuilderCapellaBlock(t *testing.T) {
|
||||
b.Block.Slot = 1
|
||||
b.Block.ProposerIndex = 2
|
||||
b.Block.Body.ExecutionPayload = p
|
||||
b.Block.Body.BlsToExecutionChanges = []*eth.SignedBLSToExecutionChange{
|
||||
{Message: ð.BLSToExecutionChange{ValidatorIndex: 1, FromBlsPubkey: []byte{'a'}}},
|
||||
{Message: ð.BLSToExecutionChange{ValidatorIndex: 2, FromBlsPubkey: []byte{'b'}}},
|
||||
{Message: ð.BLSToExecutionChange{ValidatorIndex: 3, FromBlsPubkey: []byte{'c'}}},
|
||||
{Message: ð.BLSToExecutionChange{ValidatorIndex: 4, FromBlsPubkey: []byte{'d'}}},
|
||||
}
|
||||
wb, err := blocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return wb
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_binary")
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"log.go",
|
||||
"main.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/server",
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//api/gateway:go_default_library",
|
||||
"//beacon-chain/gateway:go_default_library",
|
||||
"//beacon-chain/rpc/apimiddleware:go_default_library",
|
||||
"//cmd/beacon-chain/flags:go_default_library",
|
||||
"//runtime/maxprocs:go_default_library",
|
||||
"@com_github_gorilla_mux//:go_default_library",
|
||||
"@com_github_joonix_log//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_binary(
|
||||
name = "server",
|
||||
embed = [":go_default_library"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
@@ -1,5 +0,0 @@
|
||||
package main
|
||||
|
||||
import "github.com/sirupsen/logrus"
|
||||
|
||||
var log = logrus.New()
|
||||
@@ -1,96 +0,0 @@
|
||||
// Package main allows for creation of an HTTP-JSON to gRPC
|
||||
// gateway as a binary go process.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
joonix "github.com/joonix/log"
|
||||
"github.com/prysmaticlabs/prysm/v4/api/gateway"
|
||||
beaconGateway "github.com/prysmaticlabs/prysm/v4/beacon-chain/gateway"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/v4/cmd/beacon-chain/flags"
|
||||
_ "github.com/prysmaticlabs/prysm/v4/runtime/maxprocs"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
beaconRPC = flag.String("beacon-rpc", "localhost:4000", "Beacon chain gRPC endpoint")
|
||||
port = flag.Int("port", 8000, "Port to serve on")
|
||||
host = flag.String("host", "127.0.0.1", "Host to serve on")
|
||||
debug = flag.Bool("debug", false, "Enable debug logging")
|
||||
allowedOrigins = flag.String("corsdomain", "localhost:4242", "A comma separated list of CORS domains to allow")
|
||||
enableDebugRPCEndpoints = flag.Bool("enable-debug-rpc-endpoints", false, "Enable debug rpc endpoints such as /eth/v1alpha1/beacon/state")
|
||||
grpcMaxMsgSize = flag.Int("grpc-max-msg-size", math.MaxInt32, "Integer to define max recieve message call size")
|
||||
httpModules = flag.String(
|
||||
"http-modules",
|
||||
strings.Join([]string{flags.PrysmAPIModule, flags.EthAPIModule}, ","),
|
||||
"Comma-separated list of API module names. Possible values: `"+flags.PrysmAPIModule+`,`+flags.EthAPIModule+"`.",
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
logrus.SetFormatter(joonix.NewFormatter())
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if *debug {
|
||||
log.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
gatewayConfig := beaconGateway.DefaultConfig(*enableDebugRPCEndpoints, *httpModules)
|
||||
muxs := make([]*gateway.PbMux, 0)
|
||||
if gatewayConfig.V1AlphaPbMux != nil {
|
||||
muxs = append(muxs, gatewayConfig.V1AlphaPbMux)
|
||||
}
|
||||
if gatewayConfig.EthPbMux != nil {
|
||||
muxs = append(muxs, gatewayConfig.EthPbMux)
|
||||
}
|
||||
opts := []gateway.Option{
|
||||
gateway.WithRouter(r),
|
||||
gateway.WithPbHandlers(muxs),
|
||||
gateway.WithMuxHandler(gatewayConfig.Handler),
|
||||
gateway.WithRemoteAddr(*beaconRPC),
|
||||
gateway.WithGatewayAddr(fmt.Sprintf("%s:%d", *host, *port)),
|
||||
gateway.WithAllowedOrigins(strings.Split(*allowedOrigins, ",")),
|
||||
gateway.WithMaxCallRecvMsgSize(uint64(*grpcMaxMsgSize)),
|
||||
}
|
||||
|
||||
if flags.EnableHTTPEthAPI(*httpModules) {
|
||||
opts = append(opts, gateway.WithApiMiddleware(&apimiddleware.BeaconEndpointFactory{}))
|
||||
}
|
||||
|
||||
gw, err := gateway.New(context.Background(), opts...)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
r.HandleFunc("/swagger/", gateway.SwaggerServer())
|
||||
r.HandleFunc("/healthz", healthzServer(gw))
|
||||
|
||||
gw.Start()
|
||||
|
||||
select {}
|
||||
}
|
||||
|
||||
// healthzServer returns a simple health handler which returns ok.
|
||||
func healthzServer(gw *gateway.Gateway) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
if err := gw.Status(); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadGateway)
|
||||
return
|
||||
}
|
||||
if _, err := fmt.Fprintln(w, "ok"); err != nil {
|
||||
log.WithError(err).Error("failed to respond to healthz")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,10 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["genesis.go"],
|
||||
srcs = [
|
||||
"genesis.go",
|
||||
"genesis_mainnet.go",
|
||||
],
|
||||
embedsrcs = ["mainnet.ssz.snappy"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/genesis",
|
||||
visibility = ["//beacon-chain/db:__subpackages__"],
|
||||
|
||||
@@ -6,24 +6,18 @@ import (
|
||||
"github.com/golang/snappy"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
state_native "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed mainnet.ssz.snappy
|
||||
mainnetRawSSZCompressed []byte // 1.8Mb
|
||||
)
|
||||
var embeddedStates = map[string]*[]byte{}
|
||||
|
||||
// State returns a copy of the genesis state from a hardcoded value.
|
||||
func State(name string) (state.BeaconState, error) {
|
||||
switch name {
|
||||
case params.MainnetName:
|
||||
return load(mainnetRawSSZCompressed)
|
||||
default:
|
||||
// No state found.
|
||||
return nil, nil
|
||||
sb, exists := embeddedStates[name]
|
||||
if exists {
|
||||
return load(*sb)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// load a compressed ssz state file into a beacon state struct.
|
||||
|
||||
19
beacon-chain/state/genesis/genesis_mainnet.go
Normal file
19
beacon-chain/state/genesis/genesis_mainnet.go
Normal file
@@ -0,0 +1,19 @@
|
||||
//go:build !noMainnetGenesis
|
||||
// +build !noMainnetGenesis
|
||||
|
||||
package genesis
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed mainnet.ssz.snappy
|
||||
mainnetRawSSZCompressed []byte // 1.8Mb
|
||||
)
|
||||
|
||||
func init() {
|
||||
embeddedStates[params.MainnetName] = &mainnetRawSSZCompressed
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
runtimeDebug "runtime/debug"
|
||||
|
||||
gethlog "github.com/ethereum/go-ethereum/log"
|
||||
@@ -198,7 +197,6 @@ func main() {
|
||||
if ctx.IsSet(flags.SetGCPercent.Name) {
|
||||
runtimeDebug.SetGCPercent(ctx.Int(flags.SetGCPercent.Name))
|
||||
}
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
if err := debug.Setup(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -110,5 +110,6 @@ docker_push(
|
||||
go_binary(
|
||||
name = "prysmctl",
|
||||
embed = [":go_default_library"],
|
||||
gotags = ["noMainnetGenesis"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
runtimeDebug "runtime/debug"
|
||||
|
||||
joonix "github.com/joonix/log"
|
||||
@@ -183,7 +182,6 @@ func main() {
|
||||
log.WithError(err).Error("Cannot update data directory")
|
||||
}
|
||||
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
if err := debug.Setup(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ go_test(
|
||||
srcs = [
|
||||
"activation_test.go",
|
||||
"attestation_data_test.go",
|
||||
"beacon_api_beacon_chain_client_test.go",
|
||||
"beacon_api_helpers_test.go",
|
||||
"beacon_api_node_client_test.go",
|
||||
"beacon_api_validator_client_test.go",
|
||||
|
||||
@@ -3,16 +3,24 @@ package beacon_api
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/client/iface"
|
||||
)
|
||||
|
||||
type beaconApiBeaconChainClient struct {
|
||||
fallbackClient iface.BeaconChainClient
|
||||
jsonRestHandler jsonRestHandler
|
||||
fallbackClient iface.BeaconChainClient
|
||||
jsonRestHandler jsonRestHandler
|
||||
stateValidatorsProvider stateValidatorsProvider
|
||||
}
|
||||
|
||||
func (c beaconApiBeaconChainClient) GetChainHead(ctx context.Context, in *empty.Empty) (*ethpb.ChainHead, error) {
|
||||
@@ -34,12 +42,166 @@ func (c beaconApiBeaconChainClient) ListValidatorBalances(ctx context.Context, i
|
||||
}
|
||||
|
||||
func (c beaconApiBeaconChainClient) ListValidators(ctx context.Context, in *ethpb.ListValidatorsRequest) (*ethpb.Validators, error) {
|
||||
if c.fallbackClient != nil {
|
||||
return c.fallbackClient.ListValidators(ctx, in)
|
||||
pageSize := in.PageSize
|
||||
|
||||
// We follow the gRPC behavior here, which returns a maximum of 250 results when pageSize == 0
|
||||
if pageSize == 0 {
|
||||
pageSize = 250
|
||||
}
|
||||
|
||||
// TODO: Implement me
|
||||
panic("beaconApiBeaconChainClient.ListValidators is not implemented. To use a fallback client, pass a fallback client as the last argument of NewBeaconApiBeaconChainClientWithFallback.")
|
||||
var pageToken uint64
|
||||
var err error
|
||||
|
||||
if in.PageToken != "" {
|
||||
if pageToken, err = strconv.ParseUint(in.PageToken, 10, 64); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse page token `%s`", in.PageToken)
|
||||
}
|
||||
}
|
||||
|
||||
var statuses []string
|
||||
if in.Active {
|
||||
statuses = []string{"active"}
|
||||
}
|
||||
|
||||
pubkeys := make([]string, len(in.PublicKeys))
|
||||
for idx, pubkey := range in.PublicKeys {
|
||||
pubkeys[idx] = hexutil.Encode(pubkey)
|
||||
}
|
||||
|
||||
var stateValidators *apimiddleware.StateValidatorsResponseJson
|
||||
var epoch primitives.Epoch
|
||||
|
||||
switch queryFilter := in.QueryFilter.(type) {
|
||||
case *ethpb.ListValidatorsRequest_Epoch:
|
||||
slot, err := slots.EpochStart(queryFilter.Epoch)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get first slot for epoch `%d`", queryFilter.Epoch)
|
||||
}
|
||||
if stateValidators, err = c.stateValidatorsProvider.GetStateValidatorsForSlot(ctx, slot, pubkeys, in.Indices, statuses); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get state validators for slot `%d`", slot)
|
||||
}
|
||||
epoch = slots.ToEpoch(slot)
|
||||
case *ethpb.ListValidatorsRequest_Genesis:
|
||||
if stateValidators, err = c.stateValidatorsProvider.GetStateValidatorsForSlot(ctx, 0, pubkeys, in.Indices, statuses); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get genesis state validators")
|
||||
}
|
||||
epoch = 0
|
||||
case nil:
|
||||
if stateValidators, err = c.stateValidatorsProvider.GetStateValidatorsForHead(ctx, pubkeys, in.Indices, statuses); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get head state validators")
|
||||
}
|
||||
|
||||
blockHeader := apimiddleware.BlockHeaderResponseJson{}
|
||||
if _, err := c.jsonRestHandler.GetRestJsonResponse(ctx, "/eth/v1/beacon/headers/head", &blockHeader); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get head block header")
|
||||
}
|
||||
|
||||
if blockHeader.Data == nil || blockHeader.Data.Header == nil {
|
||||
return nil, errors.New("block header data is nil")
|
||||
}
|
||||
|
||||
if blockHeader.Data.Header.Message == nil {
|
||||
return nil, errors.New("block header message is nil")
|
||||
}
|
||||
|
||||
slot, err := strconv.ParseUint(blockHeader.Data.Header.Message.Slot, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse header slot `%s`", blockHeader.Data.Header.Message.Slot)
|
||||
}
|
||||
|
||||
epoch = slots.ToEpoch(primitives.Slot(slot))
|
||||
default:
|
||||
return nil, errors.Errorf("unsupported query filter type `%v`", reflect.TypeOf(queryFilter))
|
||||
}
|
||||
|
||||
if stateValidators.Data == nil {
|
||||
return nil, errors.New("state validators data is nil")
|
||||
}
|
||||
|
||||
start := pageToken * uint64(pageSize)
|
||||
if start > uint64(len(stateValidators.Data)) {
|
||||
start = uint64(len(stateValidators.Data))
|
||||
}
|
||||
|
||||
end := start + uint64(pageSize)
|
||||
if end > uint64(len(stateValidators.Data)) {
|
||||
end = uint64(len(stateValidators.Data))
|
||||
}
|
||||
|
||||
validators := make([]*ethpb.Validators_ValidatorContainer, end-start)
|
||||
for idx := start; idx < end; idx++ {
|
||||
stateValidator := stateValidators.Data[idx]
|
||||
|
||||
if stateValidator.Validator == nil {
|
||||
return nil, errors.Errorf("state validator at index `%d` is nil", idx)
|
||||
}
|
||||
|
||||
pubkey, err := hexutil.Decode(stateValidator.Validator.PublicKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode validator pubkey `%s`", stateValidator.Validator.PublicKey)
|
||||
}
|
||||
|
||||
withdrawalCredentials, err := hexutil.Decode(stateValidator.Validator.WithdrawalCredentials)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode validator withdrawal credentials `%s`", stateValidator.Validator.WithdrawalCredentials)
|
||||
}
|
||||
|
||||
effectiveBalance, err := strconv.ParseUint(stateValidator.Validator.EffectiveBalance, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse validator effective balance `%s`", stateValidator.Validator.EffectiveBalance)
|
||||
}
|
||||
|
||||
validatorIndex, err := strconv.ParseUint(stateValidator.Index, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse validator index `%s`", stateValidator.Index)
|
||||
}
|
||||
|
||||
activationEligibilityEpoch, err := strconv.ParseUint(stateValidator.Validator.ActivationEligibilityEpoch, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse validator activation eligibility epoch `%s`", stateValidator.Validator.ActivationEligibilityEpoch)
|
||||
}
|
||||
|
||||
activationEpoch, err := strconv.ParseUint(stateValidator.Validator.ActivationEpoch, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse validator activation epoch `%s`", stateValidator.Validator.ActivationEpoch)
|
||||
}
|
||||
|
||||
exitEpoch, err := strconv.ParseUint(stateValidator.Validator.ExitEpoch, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse validator exit epoch `%s`", stateValidator.Validator.ExitEpoch)
|
||||
}
|
||||
|
||||
withdrawableEpoch, err := strconv.ParseUint(stateValidator.Validator.WithdrawableEpoch, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse validator withdrawable epoch `%s`", stateValidator.Validator.WithdrawableEpoch)
|
||||
}
|
||||
|
||||
validators[idx-start] = ðpb.Validators_ValidatorContainer{
|
||||
Index: primitives.ValidatorIndex(validatorIndex),
|
||||
Validator: ðpb.Validator{
|
||||
PublicKey: pubkey,
|
||||
WithdrawalCredentials: withdrawalCredentials,
|
||||
EffectiveBalance: effectiveBalance,
|
||||
Slashed: stateValidator.Validator.Slashed,
|
||||
ActivationEligibilityEpoch: primitives.Epoch(activationEligibilityEpoch),
|
||||
ActivationEpoch: primitives.Epoch(activationEpoch),
|
||||
ExitEpoch: primitives.Epoch(exitEpoch),
|
||||
WithdrawableEpoch: primitives.Epoch(withdrawableEpoch),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var nextPageToken string
|
||||
if end < uint64(len(stateValidators.Data)) {
|
||||
nextPageToken = strconv.FormatUint(pageToken+1, 10)
|
||||
}
|
||||
|
||||
return ðpb.Validators{
|
||||
TotalSize: int32(len(stateValidators.Data)),
|
||||
Epoch: epoch,
|
||||
ValidatorList: validators,
|
||||
NextPageToken: nextPageToken,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c beaconApiBeaconChainClient) GetValidatorQueue(ctx context.Context, in *empty.Empty) (*ethpb.ValidatorQueue, error) {
|
||||
@@ -76,7 +238,8 @@ func NewBeaconApiBeaconChainClientWithFallback(host string, timeout time.Duratio
|
||||
}
|
||||
|
||||
return &beaconApiBeaconChainClient{
|
||||
jsonRestHandler: jsonRestHandler,
|
||||
fallbackClient: fallbackClient,
|
||||
jsonRestHandler: jsonRestHandler,
|
||||
fallbackClient: fallbackClient,
|
||||
stateValidatorsProvider: beaconApiStateValidatorsProvider{jsonRestHandler: jsonRestHandler},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,579 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/client/beacon-api/mock"
|
||||
)
|
||||
|
||||
func TestListValidators(t *testing.T) {
|
||||
const blockHeaderEndpoint = "/eth/v1/beacon/headers/head"
|
||||
|
||||
t.Run("invalid token", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
ctx := context.Background()
|
||||
|
||||
beaconChainClient := beaconApiBeaconChainClient{}
|
||||
_, err := beaconChainClient.ListValidators(ctx, ðpb.ListValidatorsRequest{
|
||||
PageToken: "foo",
|
||||
})
|
||||
assert.ErrorContains(t, "failed to parse page token `foo`", err)
|
||||
})
|
||||
|
||||
t.Run("query filter epoch overflow", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
ctx := context.Background()
|
||||
|
||||
beaconChainClient := beaconApiBeaconChainClient{}
|
||||
_, err := beaconChainClient.ListValidators(ctx, ðpb.ListValidatorsRequest{
|
||||
QueryFilter: ðpb.ListValidatorsRequest_Epoch{
|
||||
Epoch: math.MaxUint64,
|
||||
},
|
||||
})
|
||||
assert.ErrorContains(t, fmt.Sprintf("failed to get first slot for epoch `%d`", uint64(math.MaxUint64)), err)
|
||||
})
|
||||
|
||||
t.Run("fails to get validators for epoch filter", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
ctx := context.Background()
|
||||
|
||||
stateValidatorsProvider := mock.NewMockstateValidatorsProvider(ctrl)
|
||||
stateValidatorsProvider.EXPECT().GetStateValidatorsForSlot(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(
|
||||
nil,
|
||||
errors.New("foo error"),
|
||||
)
|
||||
|
||||
beaconChainClient := beaconApiBeaconChainClient{stateValidatorsProvider: stateValidatorsProvider}
|
||||
_, err := beaconChainClient.ListValidators(ctx, ðpb.ListValidatorsRequest{
|
||||
QueryFilter: ðpb.ListValidatorsRequest_Epoch{
|
||||
Epoch: 0,
|
||||
},
|
||||
})
|
||||
assert.ErrorContains(t, "failed to get state validators for slot `0`: foo error", err)
|
||||
})
|
||||
|
||||
t.Run("fails to get validators for genesis filter", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
ctx := context.Background()
|
||||
|
||||
stateValidatorsProvider := mock.NewMockstateValidatorsProvider(ctrl)
|
||||
stateValidatorsProvider.EXPECT().GetStateValidatorsForSlot(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(
|
||||
nil,
|
||||
errors.New("bar error"),
|
||||
)
|
||||
|
||||
beaconChainClient := beaconApiBeaconChainClient{stateValidatorsProvider: stateValidatorsProvider}
|
||||
_, err := beaconChainClient.ListValidators(ctx, ðpb.ListValidatorsRequest{
|
||||
QueryFilter: ðpb.ListValidatorsRequest_Genesis{},
|
||||
})
|
||||
assert.ErrorContains(t, "failed to get genesis state validators: bar error", err)
|
||||
})
|
||||
|
||||
t.Run("fails to get validators for nil filter", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
ctx := context.Background()
|
||||
|
||||
stateValidatorsProvider := mock.NewMockstateValidatorsProvider(ctrl)
|
||||
stateValidatorsProvider.EXPECT().GetStateValidatorsForHead(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(
|
||||
nil,
|
||||
errors.New("foo error"),
|
||||
)
|
||||
|
||||
beaconChainClient := beaconApiBeaconChainClient{stateValidatorsProvider: stateValidatorsProvider}
|
||||
_, err := beaconChainClient.ListValidators(ctx, ðpb.ListValidatorsRequest{
|
||||
QueryFilter: nil,
|
||||
})
|
||||
assert.ErrorContains(t, "failed to get head state validators: foo error", err)
|
||||
})
|
||||
|
||||
t.Run("fails to get latest block header for nil filter", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
ctx := context.Background()
|
||||
|
||||
stateValidatorsProvider := mock.NewMockstateValidatorsProvider(ctrl)
|
||||
stateValidatorsProvider.EXPECT().GetStateValidatorsForHead(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
jsonRestHandler := mock.NewMockjsonRestHandler(ctrl)
|
||||
jsonRestHandler.EXPECT().GetRestJsonResponse(ctx, blockHeaderEndpoint, gomock.Any()).Return(
|
||||
nil,
|
||||
errors.New("bar error"),
|
||||
)
|
||||
|
||||
beaconChainClient := beaconApiBeaconChainClient{
|
||||
stateValidatorsProvider: stateValidatorsProvider,
|
||||
jsonRestHandler: jsonRestHandler,
|
||||
}
|
||||
_, err := beaconChainClient.ListValidators(ctx, ðpb.ListValidatorsRequest{
|
||||
QueryFilter: nil,
|
||||
})
|
||||
assert.ErrorContains(t, "failed to get head block header: bar error", err)
|
||||
})
|
||||
|
||||
t.Run("fails to read block header response", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
expectedError string
|
||||
blockHeaderResponse apimiddleware.BlockHeaderResponseJson
|
||||
}{
|
||||
{
|
||||
name: "nil data",
|
||||
blockHeaderResponse: apimiddleware.BlockHeaderResponseJson{
|
||||
Data: nil,
|
||||
},
|
||||
expectedError: "block header data is nil",
|
||||
},
|
||||
{
|
||||
name: "nil data header",
|
||||
blockHeaderResponse: apimiddleware.BlockHeaderResponseJson{
|
||||
Data: &apimiddleware.BlockHeaderContainerJson{
|
||||
Header: nil,
|
||||
},
|
||||
},
|
||||
expectedError: "block header data is nil",
|
||||
},
|
||||
{
|
||||
name: "nil message",
|
||||
blockHeaderResponse: apimiddleware.BlockHeaderResponseJson{
|
||||
Data: &apimiddleware.BlockHeaderContainerJson{
|
||||
Header: &apimiddleware.BeaconBlockHeaderContainerJson{
|
||||
Message: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: "block header message is nil",
|
||||
},
|
||||
{
|
||||
name: "invalid header slot",
|
||||
blockHeaderResponse: apimiddleware.BlockHeaderResponseJson{
|
||||
Data: &apimiddleware.BlockHeaderContainerJson{
|
||||
Header: &apimiddleware.BeaconBlockHeaderContainerJson{
|
||||
Message: &apimiddleware.BeaconBlockHeaderJson{
|
||||
Slot: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: "failed to parse header slot `foo`",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
ctx := context.Background()
|
||||
|
||||
stateValidatorsProvider := mock.NewMockstateValidatorsProvider(ctrl)
|
||||
stateValidatorsProvider.EXPECT().GetStateValidatorsForHead(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
jsonRestHandler := mock.NewMockjsonRestHandler(ctrl)
|
||||
jsonRestHandler.EXPECT().GetRestJsonResponse(ctx, blockHeaderEndpoint, gomock.Any()).Return(
|
||||
nil,
|
||||
nil,
|
||||
).SetArg(
|
||||
2,
|
||||
testCase.blockHeaderResponse,
|
||||
)
|
||||
|
||||
beaconChainClient := beaconApiBeaconChainClient{
|
||||
stateValidatorsProvider: stateValidatorsProvider,
|
||||
jsonRestHandler: jsonRestHandler,
|
||||
}
|
||||
_, err := beaconChainClient.ListValidators(ctx, ðpb.ListValidatorsRequest{
|
||||
QueryFilter: nil,
|
||||
})
|
||||
assert.ErrorContains(t, testCase.expectedError, err)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("fails to get validators for genesis filter", func(t *testing.T) {
|
||||
generateValidStateValidatorsResponse := func() *apimiddleware.StateValidatorsResponseJson {
|
||||
return &apimiddleware.StateValidatorsResponseJson{
|
||||
Data: []*apimiddleware.ValidatorContainerJson{
|
||||
{
|
||||
Index: "1",
|
||||
Validator: &apimiddleware.ValidatorJson{
|
||||
PublicKey: hexutil.Encode([]byte{3}),
|
||||
WithdrawalCredentials: hexutil.Encode([]byte{4}),
|
||||
EffectiveBalance: "5",
|
||||
Slashed: true,
|
||||
ActivationEligibilityEpoch: "6",
|
||||
ActivationEpoch: "7",
|
||||
ExitEpoch: "8",
|
||||
WithdrawableEpoch: "9",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
generateStateValidatorsResponse func() *apimiddleware.StateValidatorsResponseJson
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "nil validator",
|
||||
generateStateValidatorsResponse: func() *apimiddleware.StateValidatorsResponseJson {
|
||||
validatorsResponse := generateValidStateValidatorsResponse()
|
||||
validatorsResponse.Data[0].Validator = nil
|
||||
return validatorsResponse
|
||||
},
|
||||
expectedError: "state validator at index `0` is nil",
|
||||
},
|
||||
{
|
||||
name: "invalid pubkey",
|
||||
generateStateValidatorsResponse: func() *apimiddleware.StateValidatorsResponseJson {
|
||||
validatorsResponse := generateValidStateValidatorsResponse()
|
||||
validatorsResponse.Data[0].Validator.PublicKey = "foo"
|
||||
return validatorsResponse
|
||||
},
|
||||
expectedError: "failed to decode validator pubkey `foo`",
|
||||
},
|
||||
{
|
||||
name: "invalid withdrawal credentials",
|
||||
generateStateValidatorsResponse: func() *apimiddleware.StateValidatorsResponseJson {
|
||||
validatorsResponse := generateValidStateValidatorsResponse()
|
||||
validatorsResponse.Data[0].Validator.WithdrawalCredentials = "bar"
|
||||
return validatorsResponse
|
||||
},
|
||||
expectedError: "failed to decode validator withdrawal credentials `bar`",
|
||||
},
|
||||
{
|
||||
name: "invalid effective balance",
|
||||
generateStateValidatorsResponse: func() *apimiddleware.StateValidatorsResponseJson {
|
||||
validatorsResponse := generateValidStateValidatorsResponse()
|
||||
validatorsResponse.Data[0].Validator.EffectiveBalance = "foo"
|
||||
return validatorsResponse
|
||||
},
|
||||
expectedError: "failed to parse validator effective balance `foo`",
|
||||
},
|
||||
{
|
||||
name: "invalid validator index",
|
||||
generateStateValidatorsResponse: func() *apimiddleware.StateValidatorsResponseJson {
|
||||
validatorsResponse := generateValidStateValidatorsResponse()
|
||||
validatorsResponse.Data[0].Index = "bar"
|
||||
return validatorsResponse
|
||||
},
|
||||
expectedError: "failed to parse validator index `bar`",
|
||||
},
|
||||
{
|
||||
name: "invalid activation eligibility epoch",
|
||||
generateStateValidatorsResponse: func() *apimiddleware.StateValidatorsResponseJson {
|
||||
validatorsResponse := generateValidStateValidatorsResponse()
|
||||
validatorsResponse.Data[0].Validator.ActivationEligibilityEpoch = "foo"
|
||||
return validatorsResponse
|
||||
},
|
||||
expectedError: "failed to parse validator activation eligibility epoch `foo`",
|
||||
},
|
||||
{
|
||||
name: "invalid activation epoch",
|
||||
generateStateValidatorsResponse: func() *apimiddleware.StateValidatorsResponseJson {
|
||||
validatorsResponse := generateValidStateValidatorsResponse()
|
||||
validatorsResponse.Data[0].Validator.ActivationEpoch = "bar"
|
||||
return validatorsResponse
|
||||
},
|
||||
expectedError: "failed to parse validator activation epoch `bar`",
|
||||
},
|
||||
{
|
||||
name: "invalid exit epoch",
|
||||
generateStateValidatorsResponse: func() *apimiddleware.StateValidatorsResponseJson {
|
||||
validatorsResponse := generateValidStateValidatorsResponse()
|
||||
validatorsResponse.Data[0].Validator.ExitEpoch = "foo"
|
||||
return validatorsResponse
|
||||
},
|
||||
expectedError: "failed to parse validator exit epoch `foo`",
|
||||
},
|
||||
{
|
||||
name: "invalid withdrawable epoch",
|
||||
generateStateValidatorsResponse: func() *apimiddleware.StateValidatorsResponseJson {
|
||||
validatorsResponse := generateValidStateValidatorsResponse()
|
||||
validatorsResponse.Data[0].Validator.WithdrawableEpoch = "bar"
|
||||
return validatorsResponse
|
||||
},
|
||||
expectedError: "failed to parse validator withdrawable epoch `bar`",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
ctx := context.Background()
|
||||
|
||||
stateValidatorsProvider := mock.NewMockstateValidatorsProvider(ctrl)
|
||||
stateValidatorsProvider.EXPECT().GetStateValidatorsForSlot(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(
|
||||
testCase.generateStateValidatorsResponse(),
|
||||
nil,
|
||||
)
|
||||
|
||||
beaconChainClient := beaconApiBeaconChainClient{stateValidatorsProvider: stateValidatorsProvider}
|
||||
_, err := beaconChainClient.ListValidators(ctx, ðpb.ListValidatorsRequest{
|
||||
QueryFilter: ðpb.ListValidatorsRequest_Genesis{},
|
||||
})
|
||||
assert.ErrorContains(t, testCase.expectedError, err)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("correctly returns the expected validators", func(t *testing.T) {
|
||||
generateValidStateValidatorsResponse := func() *apimiddleware.StateValidatorsResponseJson {
|
||||
return &apimiddleware.StateValidatorsResponseJson{
|
||||
Data: []*apimiddleware.ValidatorContainerJson{
|
||||
{
|
||||
Index: "1",
|
||||
Validator: &apimiddleware.ValidatorJson{
|
||||
PublicKey: hexutil.Encode([]byte{2}),
|
||||
WithdrawalCredentials: hexutil.Encode([]byte{3}),
|
||||
EffectiveBalance: "4",
|
||||
Slashed: true,
|
||||
ActivationEligibilityEpoch: "5",
|
||||
ActivationEpoch: "6",
|
||||
ExitEpoch: "7",
|
||||
WithdrawableEpoch: "8",
|
||||
},
|
||||
},
|
||||
{
|
||||
Index: "9",
|
||||
Validator: &apimiddleware.ValidatorJson{
|
||||
PublicKey: hexutil.Encode([]byte{10}),
|
||||
WithdrawalCredentials: hexutil.Encode([]byte{11}),
|
||||
EffectiveBalance: "12",
|
||||
Slashed: false,
|
||||
ActivationEligibilityEpoch: "13",
|
||||
ActivationEpoch: "14",
|
||||
ExitEpoch: "15",
|
||||
WithdrawableEpoch: "16",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
generateJsonStateValidatorsResponse func() *apimiddleware.StateValidatorsResponseJson
|
||||
generateProtoValidatorsResponse func() *ethpb.Validators
|
||||
pubkeys [][]byte
|
||||
pubkeyStrings []string
|
||||
indices []primitives.ValidatorIndex
|
||||
statuses []string
|
||||
pageSize int32
|
||||
pageToken string
|
||||
}{
|
||||
{
|
||||
name: "page size 0",
|
||||
generateJsonStateValidatorsResponse: func() *apimiddleware.StateValidatorsResponseJson {
|
||||
validValidatorsResponse := generateValidStateValidatorsResponse()
|
||||
|
||||
// Generate more than 250 validators, but expect only 250 to be returned
|
||||
validators := make([]*apimiddleware.ValidatorContainerJson, 267)
|
||||
for idx := 0; idx < len(validators); idx++ {
|
||||
validators[idx] = validValidatorsResponse.Data[0]
|
||||
}
|
||||
|
||||
validatorsResponse := &apimiddleware.StateValidatorsResponseJson{
|
||||
Data: validators,
|
||||
}
|
||||
|
||||
return validatorsResponse
|
||||
},
|
||||
generateProtoValidatorsResponse: func() *ethpb.Validators {
|
||||
validators := make([]*ethpb.Validators_ValidatorContainer, 250)
|
||||
for idx := 0; idx < len(validators); idx++ {
|
||||
validators[idx] = ðpb.Validators_ValidatorContainer{
|
||||
Index: 1,
|
||||
Validator: ðpb.Validator{
|
||||
PublicKey: []byte{2},
|
||||
WithdrawalCredentials: []byte{3},
|
||||
EffectiveBalance: 4,
|
||||
Slashed: true,
|
||||
ActivationEligibilityEpoch: 5,
|
||||
ActivationEpoch: 6,
|
||||
ExitEpoch: 7,
|
||||
WithdrawableEpoch: 8,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return ðpb.Validators{
|
||||
ValidatorList: validators,
|
||||
TotalSize: 267,
|
||||
Epoch: 0,
|
||||
NextPageToken: "1",
|
||||
}
|
||||
},
|
||||
pubkeys: [][]byte{},
|
||||
pubkeyStrings: make([]string, 0),
|
||||
indices: []primitives.ValidatorIndex{},
|
||||
statuses: nil,
|
||||
pageSize: 0,
|
||||
pageToken: "",
|
||||
},
|
||||
{
|
||||
name: "pageSize==1 and pageToken==0",
|
||||
generateJsonStateValidatorsResponse: generateValidStateValidatorsResponse,
|
||||
generateProtoValidatorsResponse: func() *ethpb.Validators {
|
||||
return ðpb.Validators{
|
||||
ValidatorList: []*ethpb.Validators_ValidatorContainer{
|
||||
{
|
||||
Index: 1,
|
||||
Validator: ðpb.Validator{
|
||||
PublicKey: []byte{2},
|
||||
WithdrawalCredentials: []byte{3},
|
||||
EffectiveBalance: 4,
|
||||
Slashed: true,
|
||||
ActivationEligibilityEpoch: 5,
|
||||
ActivationEpoch: 6,
|
||||
ExitEpoch: 7,
|
||||
WithdrawableEpoch: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
TotalSize: 2,
|
||||
Epoch: 0,
|
||||
NextPageToken: "1",
|
||||
}
|
||||
},
|
||||
pageSize: 1,
|
||||
pageToken: "0",
|
||||
},
|
||||
{
|
||||
name: "pageSize==2 and pageToken==0",
|
||||
generateJsonStateValidatorsResponse: generateValidStateValidatorsResponse,
|
||||
generateProtoValidatorsResponse: func() *ethpb.Validators {
|
||||
return ðpb.Validators{
|
||||
ValidatorList: []*ethpb.Validators_ValidatorContainer{
|
||||
{
|
||||
Index: 1,
|
||||
Validator: ðpb.Validator{
|
||||
PublicKey: []byte{2},
|
||||
WithdrawalCredentials: []byte{3},
|
||||
EffectiveBalance: 4,
|
||||
Slashed: true,
|
||||
ActivationEligibilityEpoch: 5,
|
||||
ActivationEpoch: 6,
|
||||
ExitEpoch: 7,
|
||||
WithdrawableEpoch: 8,
|
||||
},
|
||||
},
|
||||
{
|
||||
Index: 9,
|
||||
Validator: ðpb.Validator{
|
||||
PublicKey: []byte{10},
|
||||
WithdrawalCredentials: []byte{11},
|
||||
EffectiveBalance: 12,
|
||||
Slashed: false,
|
||||
ActivationEligibilityEpoch: 13,
|
||||
ActivationEpoch: 14,
|
||||
ExitEpoch: 15,
|
||||
WithdrawableEpoch: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
TotalSize: 2,
|
||||
Epoch: 0,
|
||||
NextPageToken: "",
|
||||
}
|
||||
},
|
||||
pageSize: 2,
|
||||
pageToken: "0",
|
||||
},
|
||||
{
|
||||
name: "pageSize==1 and pageToken==1",
|
||||
generateJsonStateValidatorsResponse: generateValidStateValidatorsResponse,
|
||||
generateProtoValidatorsResponse: func() *ethpb.Validators {
|
||||
return ðpb.Validators{
|
||||
ValidatorList: []*ethpb.Validators_ValidatorContainer{
|
||||
{
|
||||
Index: 9,
|
||||
Validator: ðpb.Validator{
|
||||
PublicKey: []byte{10},
|
||||
WithdrawalCredentials: []byte{11},
|
||||
EffectiveBalance: 12,
|
||||
Slashed: false,
|
||||
ActivationEligibilityEpoch: 13,
|
||||
ActivationEpoch: 14,
|
||||
ExitEpoch: 15,
|
||||
WithdrawableEpoch: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
TotalSize: 2,
|
||||
Epoch: 0,
|
||||
NextPageToken: "",
|
||||
}
|
||||
},
|
||||
pageSize: 1,
|
||||
pageToken: "1",
|
||||
},
|
||||
{
|
||||
name: "pageSize==1 and pageToken==2",
|
||||
generateJsonStateValidatorsResponse: generateValidStateValidatorsResponse,
|
||||
generateProtoValidatorsResponse: func() *ethpb.Validators {
|
||||
return ðpb.Validators{
|
||||
ValidatorList: []*ethpb.Validators_ValidatorContainer{},
|
||||
TotalSize: 2,
|
||||
Epoch: 0,
|
||||
NextPageToken: "",
|
||||
}
|
||||
},
|
||||
pageSize: 1,
|
||||
pageToken: "2",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
ctx := context.Background()
|
||||
|
||||
stateValidatorsProvider := mock.NewMockstateValidatorsProvider(ctrl)
|
||||
stateValidatorsProvider.EXPECT().GetStateValidatorsForSlot(ctx, primitives.Slot(0), make([]string, 0), []primitives.ValidatorIndex{}, nil).Return(
|
||||
testCase.generateJsonStateValidatorsResponse(),
|
||||
nil,
|
||||
)
|
||||
|
||||
beaconChainClient := beaconApiBeaconChainClient{stateValidatorsProvider: stateValidatorsProvider}
|
||||
validators, err := beaconChainClient.ListValidators(ctx, ðpb.ListValidatorsRequest{
|
||||
QueryFilter: ðpb.ListValidatorsRequest_Genesis{},
|
||||
PublicKeys: [][]byte{},
|
||||
Indices: []primitives.ValidatorIndex{},
|
||||
Active: false,
|
||||
PageSize: testCase.pageSize,
|
||||
PageToken: testCase.pageToken,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, validators)
|
||||
|
||||
expectedValidators := testCase.generateProtoValidatorsResponse()
|
||||
assert.DeepEqual(t, expectedValidators, validators)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
apimiddleware "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/apimiddleware"
|
||||
primitives "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
)
|
||||
|
||||
// MockstateValidatorsProvider is a mock of stateValidatorsProvider interface.
|
||||
@@ -49,3 +50,33 @@ func (mr *MockstateValidatorsProviderMockRecorder) GetStateValidators(arg0, arg1
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStateValidators", reflect.TypeOf((*MockstateValidatorsProvider)(nil).GetStateValidators), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// GetStateValidatorsForHead mocks base method.
|
||||
func (m *MockstateValidatorsProvider) GetStateValidatorsForHead(arg0 context.Context, arg1 []string, arg2 []primitives.ValidatorIndex, arg3 []string) (*apimiddleware.StateValidatorsResponseJson, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetStateValidatorsForHead", arg0, arg1, arg2, arg3)
|
||||
ret0, _ := ret[0].(*apimiddleware.StateValidatorsResponseJson)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetStateValidatorsForHead indicates an expected call of GetStateValidatorsForHead.
|
||||
func (mr *MockstateValidatorsProviderMockRecorder) GetStateValidatorsForHead(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStateValidatorsForHead", reflect.TypeOf((*MockstateValidatorsProvider)(nil).GetStateValidatorsForHead), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// GetStateValidatorsForSlot mocks base method.
|
||||
func (m *MockstateValidatorsProvider) GetStateValidatorsForSlot(arg0 context.Context, arg1 primitives.Slot, arg2 []string, arg3 []primitives.ValidatorIndex, arg4 []string) (*apimiddleware.StateValidatorsResponseJson, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetStateValidatorsForSlot", arg0, arg1, arg2, arg3, arg4)
|
||||
ret0, _ := ret[0].(*apimiddleware.StateValidatorsResponseJson)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetStateValidatorsForSlot indicates an expected call of GetStateValidatorsForSlot.
|
||||
func (mr *MockstateValidatorsProviderMockRecorder) GetStateValidatorsForSlot(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStateValidatorsForSlot", reflect.TypeOf((*MockstateValidatorsProvider)(nil).GetStateValidatorsForSlot), arg0, arg1, arg2, arg3, arg4)
|
||||
}
|
||||
|
||||
@@ -2,15 +2,19 @@ package beacon_api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
neturl "net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
rpcmiddleware "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
)
|
||||
|
||||
type stateValidatorsProvider interface {
|
||||
GetStateValidators(context.Context, []string, []int64, []string) (*rpcmiddleware.StateValidatorsResponseJson, error)
|
||||
GetStateValidatorsForSlot(context.Context, primitives.Slot, []string, []primitives.ValidatorIndex, []string) (*rpcmiddleware.StateValidatorsResponseJson, error)
|
||||
GetStateValidatorsForHead(context.Context, []string, []primitives.ValidatorIndex, []string) (*rpcmiddleware.StateValidatorsResponseJson, error)
|
||||
}
|
||||
|
||||
type beaconApiStateValidatorsProvider struct {
|
||||
@@ -24,9 +28,59 @@ func (c beaconApiStateValidatorsProvider) GetStateValidators(
|
||||
statuses []string,
|
||||
) (*rpcmiddleware.StateValidatorsResponseJson, error) {
|
||||
params := neturl.Values{}
|
||||
|
||||
stringPubKeysSet := make(map[string]struct{}, len(stringPubkeys))
|
||||
indexesSet := make(map[int64]struct{}, len(indexes))
|
||||
for _, index := range indexes {
|
||||
if _, ok := indexesSet[index]; !ok {
|
||||
indexesSet[index] = struct{}{}
|
||||
params.Add("id", strconv.FormatInt(index, 10))
|
||||
}
|
||||
}
|
||||
|
||||
return c.getStateValidatorsHelper(ctx, "/eth/v1/beacon/states/head/validators", params, stringPubkeys, statuses)
|
||||
}
|
||||
|
||||
func (c beaconApiStateValidatorsProvider) GetStateValidatorsForSlot(
|
||||
ctx context.Context,
|
||||
slot primitives.Slot,
|
||||
stringPubkeys []string,
|
||||
indices []primitives.ValidatorIndex,
|
||||
statuses []string,
|
||||
) (*rpcmiddleware.StateValidatorsResponseJson, error) {
|
||||
params := convertValidatorIndicesToParams(indices)
|
||||
url := fmt.Sprintf("/eth/v1/beacon/states/%d/validators", slot)
|
||||
return c.getStateValidatorsHelper(ctx, url, params, stringPubkeys, statuses)
|
||||
}
|
||||
|
||||
func (c beaconApiStateValidatorsProvider) GetStateValidatorsForHead(
|
||||
ctx context.Context,
|
||||
stringPubkeys []string,
|
||||
indices []primitives.ValidatorIndex,
|
||||
statuses []string,
|
||||
) (*rpcmiddleware.StateValidatorsResponseJson, error) {
|
||||
params := convertValidatorIndicesToParams(indices)
|
||||
return c.getStateValidatorsHelper(ctx, "/eth/v1/beacon/states/head/validators", params, stringPubkeys, statuses)
|
||||
}
|
||||
|
||||
func convertValidatorIndicesToParams(indices []primitives.ValidatorIndex) neturl.Values {
|
||||
params := neturl.Values{}
|
||||
indicesSet := make(map[primitives.ValidatorIndex]struct{}, len(indices))
|
||||
for _, index := range indices {
|
||||
if _, ok := indicesSet[index]; !ok {
|
||||
indicesSet[index] = struct{}{}
|
||||
params.Add("id", strconv.FormatUint(uint64(index), 10))
|
||||
}
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
func (c beaconApiStateValidatorsProvider) getStateValidatorsHelper(
|
||||
ctx context.Context,
|
||||
endpoint string,
|
||||
params neturl.Values,
|
||||
stringPubkeys []string,
|
||||
statuses []string,
|
||||
) (*rpcmiddleware.StateValidatorsResponseJson, error) {
|
||||
stringPubKeysSet := make(map[string]struct{}, len(stringPubkeys))
|
||||
|
||||
for _, stringPubkey := range stringPubkeys {
|
||||
if _, ok := stringPubKeysSet[stringPubkey]; !ok {
|
||||
@@ -35,22 +89,11 @@ func (c beaconApiStateValidatorsProvider) GetStateValidators(
|
||||
}
|
||||
}
|
||||
|
||||
for _, index := range indexes {
|
||||
if _, ok := indexesSet[index]; !ok {
|
||||
indexesSet[index] = struct{}{}
|
||||
params.Add("id", strconv.FormatInt(index, 10))
|
||||
}
|
||||
}
|
||||
|
||||
for _, status := range statuses {
|
||||
params.Add("status", status)
|
||||
}
|
||||
|
||||
url := buildURL(
|
||||
"/eth/v1/beacon/states/head/validators",
|
||||
params,
|
||||
)
|
||||
|
||||
url := buildURL(endpoint, params)
|
||||
stateValidatorsJson := &rpcmiddleware.StateValidatorsResponseJson{}
|
||||
|
||||
if _, err := c.jsonRestHandler.GetRestJsonResponse(ctx, url, stateValidatorsJson); err != nil {
|
||||
|
||||
@@ -19,11 +19,11 @@ func TestGetStateValidators_Nominal(t *testing.T) {
|
||||
|
||||
url := strings.Join([]string{
|
||||
"/eth/v1/beacon/states/head/validators?",
|
||||
"id=12345&",
|
||||
"id=0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13&", // active_ongoing
|
||||
"id=0x80000e851c0f53c3246ff726d7ff7766661ca5e12a07c45c114d208d54f0f8233d4380b2e9aff759d69795d1df905526&", // active_exiting
|
||||
"id=0x424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242&", // does not exist
|
||||
"id=0x800015473bdc3a7f45ef8eb8abc598bc20021e55ad6e6ad1d745aaef9730dd2c28ec08bf42df18451de94dd4a6d24ec5&", // exited_slashed
|
||||
"id=12345&", // active_ongoing
|
||||
"status=active_ongoing&status=active_exiting&status=exited_slashed&status=exited_unslashed",
|
||||
}, "")
|
||||
|
||||
|
||||
@@ -218,6 +218,8 @@ func TestKeyReload_ActiveKey(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestKeyReload_NoActiveKey(t *testing.T) {
|
||||
t.Skip("Flakey test. Skipping until we can figure out how to test this properly")
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
km := &mockKeymanager{}
|
||||
v := &testutil.FakeValidator{Km: km}
|
||||
|
||||
@@ -251,6 +251,8 @@ func TestWaitForActivation_RefetchKeys(t *testing.T) {
|
||||
|
||||
// Regression test for a scenario where you start with an inactive key and then import an active key.
|
||||
func TestWaitForActivation_AccountsChanged(t *testing.T) {
|
||||
t.Skip("Flakey test. Skipping until we can figure out how to test this properly")
|
||||
|
||||
hook := logTest.NewGlobal()
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
Reference in New Issue
Block a user