mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
Compare commits
29 Commits
use-next-s
...
v4.0.3-pat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9984b6cf38 | ||
|
|
f4681fde19 | ||
|
|
0c7292b85b | ||
|
|
10b438e2c8 | ||
|
|
e2386cfb11 | ||
|
|
898cb0b512 | ||
|
|
6944d22ce3 | ||
|
|
1eb00866ea | ||
|
|
4862d57b13 | ||
|
|
191b0c4652 | ||
|
|
c09977d8a8 | ||
|
|
52d72c989e | ||
|
|
9f7711e74a | ||
|
|
f376427add | ||
|
|
ff1b03ab13 | ||
|
|
5fdd4e9148 | ||
|
|
80e26143eb | ||
|
|
75338dd83d | ||
|
|
99eebe9bac | ||
|
|
6b1efff4e8 | ||
|
|
763e9e3361 | ||
|
|
37182168e3 | ||
|
|
0325741318 | ||
|
|
150e8aa14d | ||
|
|
f4307a902c | ||
|
|
d257ef1742 | ||
|
|
aad7aa79d4 | ||
|
|
2eb2f87913 | ||
|
|
9214364c5e |
@@ -17,6 +17,7 @@ linters:
|
||||
- errcheck
|
||||
- gosimple
|
||||
- gocognit
|
||||
- misspell
|
||||
|
||||
linters-settings:
|
||||
gocognit:
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db/kv"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/execution"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
consensusblocks "github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
|
||||
@@ -258,7 +259,7 @@ func (s *Service) getPayloadAttribute(ctx context.Context, st state.BeaconState,
|
||||
emptyAttri := payloadattribute.EmptyWithVersion(st.Version())
|
||||
// Root is `[32]byte{}` since we are retrieving proposer ID of a given slot. During insertion at assignment the root was not known.
|
||||
proposerID, _, ok := s.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(slot, [32]byte{} /* root */)
|
||||
if !ok { // There's no need to build attribute if there is no proposer for slot.
|
||||
if !ok && !features.Get().PrepareAllPayloads { // There's no need to build attribute if there is no proposer for slot.
|
||||
return false, emptyAttri, 0
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
bstate "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/beacon-chain/state/stategen"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/features"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
consensusblocks "github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
|
||||
@@ -824,6 +825,31 @@ func Test_GetPayloadAttribute(t *testing.T) {
|
||||
require.Equal(t, suggestedAddr, common.BytesToAddress(attr.SuggestedFeeRecipient()))
|
||||
}
|
||||
|
||||
func Test_GetPayloadAttribute_PrepareAllPayloads(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
resetCfg := features.InitWithReset(&features.Flags{
|
||||
PrepareAllPayloads: true,
|
||||
})
|
||||
defer resetCfg()
|
||||
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
opts := []Option{
|
||||
WithDatabase(beaconDB),
|
||||
WithStateGen(stategen.New(beaconDB, doublylinkedtree.New())),
|
||||
WithProposerIdsCache(cache.NewProposerPayloadIDsCache()),
|
||||
}
|
||||
|
||||
hook := logTest.NewGlobal()
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
|
||||
hasPayload, attr, vId := service.getPayloadAttribute(ctx, st, 0)
|
||||
require.Equal(t, true, hasPayload)
|
||||
require.Equal(t, primitives.ValidatorIndex(0), vId)
|
||||
require.Equal(t, params.BeaconConfig().EthBurnAddressHex, common.BytesToAddress(attr.SuggestedFeeRecipient()).String())
|
||||
require.LogsContain(t, hook, "Fee recipient is currently using the burn address")
|
||||
}
|
||||
|
||||
func Test_GetPayloadAttributeV2(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
|
||||
func (s *Service) isNewProposer(slot primitives.Slot) bool {
|
||||
_, _, ok := s.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(slot, [32]byte{} /* root */)
|
||||
return ok
|
||||
return ok || features.Get().PrepareAllPayloads
|
||||
}
|
||||
|
||||
func (s *Service) isNewHead(r [32]byte) bool {
|
||||
|
||||
@@ -54,7 +54,7 @@ type head struct {
|
||||
|
||||
// This saves head info to the local service cache, it also saves the
|
||||
// new head root to the DB.
|
||||
// Caller of the method MUST aqcuire a lock on forkchoice.
|
||||
// Caller of the method MUST acquire a lock on forkchoice.
|
||||
func (s *Service) saveHead(ctx context.Context, newHeadRoot [32]byte, headBlock interfaces.ReadOnlySignedBeaconBlock, headState state.BeaconState) error {
|
||||
ctx, span := trace.StartSpan(ctx, "blockChain.saveHead")
|
||||
defer span.End()
|
||||
@@ -89,13 +89,13 @@ func (s *Service) saveHead(ctx context.Context, newHeadRoot [32]byte, headBlock
|
||||
newHeadSlot := headBlock.Block().Slot()
|
||||
newStateRoot := headBlock.Block().StateRoot()
|
||||
|
||||
// A chain re-org occurred, so we fire an event notifying the rest of the services.
|
||||
r, err := s.HeadRoot(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get old head root")
|
||||
}
|
||||
oldHeadRoot := bytesutil.ToBytes32(r)
|
||||
if headBlock.Block().ParentRoot() != oldHeadRoot {
|
||||
// A chain re-org occurred, so we fire an event notifying the rest of the services.
|
||||
commonRoot, forkSlot, err := s.cfg.ForkChoiceStore.CommonAncestor(ctx, oldHeadRoot, newHeadRoot)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not find common ancestor root")
|
||||
@@ -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)
|
||||
|
||||
@@ -696,7 +696,7 @@ func (s *Service) lateBlockTasks(ctx context.Context) {
|
||||
// Head root should be empty when retrieving proposer index for the next slot.
|
||||
_, id, has := s.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(s.CurrentSlot()+1, [32]byte{} /* head root */)
|
||||
// There exists proposer for next slot, but we haven't called fcu w/ payload attribute yet.
|
||||
if !has || id != [8]byte{} {
|
||||
if (!has && !features.Get().PrepareAllPayloads) || id != [8]byte{} {
|
||||
return
|
||||
}
|
||||
s.headLock.RLock()
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/attestations"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stategen"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/features"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
consensusblocks "github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
|
||||
@@ -2169,6 +2170,28 @@ func TestFillMissingBlockPayloadId_DiffSlotExitEarly(t *testing.T) {
|
||||
require.LogsDoNotContain(t, logHook, "could not perform late block tasks")
|
||||
}
|
||||
|
||||
func TestFillMissingBlockPayloadId_PrepareAllPayloads(t *testing.T) {
|
||||
logHook := logTest.NewGlobal()
|
||||
resetCfg := features.InitWithReset(&features.Flags{
|
||||
PrepareAllPayloads: true,
|
||||
})
|
||||
defer resetCfg()
|
||||
|
||||
fc := doublylinkedtree.New()
|
||||
ctx := context.Background()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
opts := []Option{
|
||||
WithForkChoiceStore(fc),
|
||||
WithStateGen(stategen.New(beaconDB, fc)),
|
||||
}
|
||||
|
||||
service, err := NewService(ctx, opts...)
|
||||
require.NoError(t, err)
|
||||
service.lateBlockTasks(ctx)
|
||||
require.LogsDoNotContain(t, logHook, "could not perform late block tasks")
|
||||
}
|
||||
|
||||
// Helper function to simulate the block being on time or delayed for proposer
|
||||
// boost. It alters the genesisTime tracked by the store.
|
||||
func driftGenesisTime(s *Service, slot int64, delay int64) {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -77,7 +77,8 @@ func (s *Service) Start() {
|
||||
}
|
||||
|
||||
// Stop halts the service.
|
||||
func (*Service) Stop() error {
|
||||
func (s *Service) Stop() error {
|
||||
s.cancel()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -89,6 +90,9 @@ func (s *Service) SubmitBlindedBlock(ctx context.Context, b interfaces.ReadOnlyS
|
||||
defer func() {
|
||||
submitBlindedBlockLatency.Observe(float64(time.Since(start).Milliseconds()))
|
||||
}()
|
||||
if s.c == nil {
|
||||
return nil, ErrNoBuilder
|
||||
}
|
||||
|
||||
return s.c.SubmitBlindedBlock(ctx, b)
|
||||
}
|
||||
@@ -101,6 +105,9 @@ func (s *Service) GetHeader(ctx context.Context, slot primitives.Slot, parentHas
|
||||
defer func() {
|
||||
getHeaderLatency.Observe(float64(time.Since(start).Milliseconds()))
|
||||
}()
|
||||
if s.c == nil {
|
||||
return nil, ErrNoBuilder
|
||||
}
|
||||
|
||||
return s.c.GetHeader(ctx, slot, parentHash, pubKey)
|
||||
}
|
||||
@@ -124,6 +131,9 @@ func (s *Service) RegisterValidator(ctx context.Context, reg []*ethpb.SignedVali
|
||||
defer func() {
|
||||
registerValidatorLatency.Observe(float64(time.Since(start).Milliseconds()))
|
||||
}()
|
||||
if s.c == nil {
|
||||
return ErrNoBuilder
|
||||
}
|
||||
|
||||
idxs := make([]primitives.ValidatorIndex, 0)
|
||||
msgs := make([]*ethpb.ValidatorRegistrationV1, 0)
|
||||
|
||||
@@ -37,3 +37,18 @@ func Test_RegisterValidator(t *testing.T) {
|
||||
require.NoError(t, s.RegisterValidator(ctx, []*eth.SignedValidatorRegistrationV1{{Message: ð.ValidatorRegistrationV1{Pubkey: pubkey[:], FeeRecipient: feeRecipient[:]}}}))
|
||||
assert.Equal(t, true, builder.RegisteredVals[pubkey])
|
||||
}
|
||||
|
||||
func Test_BuilderMethodsWithouClient(t *testing.T) {
|
||||
s, err := NewService(context.Background())
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, s.Configured())
|
||||
|
||||
_, err = s.GetHeader(context.Background(), 0, [32]byte{}, [48]byte{})
|
||||
assert.ErrorContains(t, ErrNoBuilder.Error(), err)
|
||||
|
||||
_, err = s.SubmitBlindedBlock(context.Background(), nil)
|
||||
assert.ErrorContains(t, ErrNoBuilder.Error(), err)
|
||||
|
||||
err = s.RegisterValidator(context.Background(), nil)
|
||||
assert.ErrorContains(t, ErrNoBuilder.Error(), err)
|
||||
}
|
||||
|
||||
@@ -19,8 +19,7 @@ const (
|
||||
Initialized
|
||||
// Synced is sent when the beacon node has completed syncing and is ready to participate in the network.
|
||||
Synced
|
||||
// Reorg is an event sent when the new head state's slot after a block
|
||||
// transition is lower than its previous head state slot value.
|
||||
// Reorg is an event sent when the new head is not a descendant of the previous head.
|
||||
Reorg
|
||||
// FinalizedCheckpoint event.
|
||||
FinalizedCheckpoint
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -30,6 +30,20 @@ import (
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
var (
|
||||
supportedEngineEndpoints = []string{
|
||||
NewPayloadMethod,
|
||||
NewPayloadMethodV2,
|
||||
ForkchoiceUpdatedMethod,
|
||||
ForkchoiceUpdatedMethodV2,
|
||||
GetPayloadMethod,
|
||||
GetPayloadMethodV2,
|
||||
ExchangeTransitionConfigurationMethod,
|
||||
GetPayloadBodiesByHashV1,
|
||||
GetPayloadBodiesByRangeV1,
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
// NewPayloadMethod v1 request string for JSON-RPC.
|
||||
NewPayloadMethod = "engine_newPayloadV1"
|
||||
@@ -53,6 +67,8 @@ const (
|
||||
GetPayloadBodiesByHashV1 = "engine_getPayloadBodiesByHashV1"
|
||||
// GetPayloadBodiesByRangeV1 v1 request string for JSON-RPC.
|
||||
GetPayloadBodiesByRangeV1 = "engine_getPayloadBodiesByRangeV1"
|
||||
// ExchangeCapabilities request string for JSON-RPC.
|
||||
ExchangeCapabilities = "engine_exchangeCapabilities"
|
||||
// Defines the seconds before timing out engine endpoints with non-block execution semantics.
|
||||
defaultEngineTimeout = time.Second
|
||||
)
|
||||
@@ -278,6 +294,35 @@ func (s *Service) ExchangeTransitionConfiguration(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) ExchangeCapabilities(ctx context.Context) ([]string, error) {
|
||||
if !features.Get().EnableOptionalEngineMethods {
|
||||
return nil, errors.New("optional engine methods not enabled")
|
||||
}
|
||||
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.ExchangeCapabilities")
|
||||
defer span.End()
|
||||
|
||||
result := &pb.ExchangeCapabilities{}
|
||||
err := s.rpcClient.CallContext(ctx, &result, ExchangeCapabilities, supportedEngineEndpoints)
|
||||
|
||||
var unsupported []string
|
||||
for _, s1 := range supportedEngineEndpoints {
|
||||
supported := false
|
||||
for _, s2 := range result.SupportedMethods {
|
||||
if s1 == s2 {
|
||||
supported = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !supported {
|
||||
unsupported = append(unsupported, s1)
|
||||
}
|
||||
}
|
||||
if len(unsupported) != 0 {
|
||||
log.Warnf("Please update client, detected the following unsupported engine methods: %s", unsupported)
|
||||
}
|
||||
return result.SupportedMethods, handleRPCError(err)
|
||||
}
|
||||
|
||||
// GetTerminalBlockHash returns the valid terminal block hash based on total difficulty.
|
||||
//
|
||||
// Spec code:
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/util"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
@@ -2320,3 +2321,77 @@ func TestCapella_PayloadBodiesByRange(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ExchangeCapabilities(t *testing.T) {
|
||||
resetFn := features.InitWithReset(&features.Flags{
|
||||
EnableOptionalEngineMethods: true,
|
||||
})
|
||||
defer resetFn()
|
||||
t.Run("empty response works", func(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
defer func() {
|
||||
require.NoError(t, r.Body.Close())
|
||||
}()
|
||||
exchangeCapabilities := &pb.ExchangeCapabilities{}
|
||||
resp := map[string]interface{}{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": exchangeCapabilities,
|
||||
}
|
||||
err := json.NewEncoder(w).Encode(resp)
|
||||
require.NoError(t, err)
|
||||
}))
|
||||
ctx := context.Background()
|
||||
logHook := logTest.NewGlobal()
|
||||
|
||||
rpcClient, err := rpc.DialHTTP(srv.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
service := &Service{}
|
||||
service.rpcClient = rpcClient
|
||||
|
||||
results, err := service.ExchangeCapabilities(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, len(results))
|
||||
|
||||
for _, item := range results {
|
||||
require.NotNil(t, item)
|
||||
}
|
||||
assert.LogsContain(t, logHook, "Please update client, detected the following unsupported engine methods:")
|
||||
})
|
||||
t.Run("list of items", func(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
defer func() {
|
||||
require.NoError(t, r.Body.Close())
|
||||
}()
|
||||
exchangeCapabilities := &pb.ExchangeCapabilities{
|
||||
SupportedMethods: []string{"A", "B", "C"},
|
||||
}
|
||||
|
||||
resp := map[string]interface{}{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": exchangeCapabilities,
|
||||
}
|
||||
err := json.NewEncoder(w).Encode(resp)
|
||||
require.NoError(t, err)
|
||||
}))
|
||||
ctx := context.Background()
|
||||
|
||||
rpcClient, err := rpc.DialHTTP(srv.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
service := &Service{}
|
||||
service.rpcClient = rpcClient
|
||||
|
||||
results, err := service.ExchangeCapabilities(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 3, len(results))
|
||||
|
||||
for _, item := range results {
|
||||
require.NotNil(t, item)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ go_library(
|
||||
name = "go_default_library",
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"genesis.go",
|
||||
"mock_engine_client.go",
|
||||
"mock_execution_chain.go",
|
||||
"mock_faulty_powchain.go",
|
||||
@@ -30,9 +29,7 @@ go_library(
|
||||
"@com_github_ethereum_go_ethereum//accounts/abi/bind/backends:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//core:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//params:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
|
||||
"@com_github_holiman_uint256//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
|
||||
@@ -233,7 +233,7 @@ func New(cliCtx *cli.Context, opts ...Option) (*BeaconNode, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debugln("Registering Intial Sync Service")
|
||||
log.Debugln("Registering Initial Sync Service")
|
||||
if err := beacon.registerInitialSyncService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -332,7 +332,7 @@ func (s *Service) isPeerAtLimit(inbound bool) bool {
|
||||
return activePeers >= maxPeers || numOfConns >= maxPeers
|
||||
}
|
||||
|
||||
// PeersFromStringAddrs convers peer raw ENRs into multiaddrs for p2p.
|
||||
// PeersFromStringAddrs converts peer raw ENRs into multiaddrs for p2p.
|
||||
func PeersFromStringAddrs(addrs []string) ([]ma.Multiaddr, error) {
|
||||
var allAddrs []ma.Multiaddr
|
||||
enodeString, multiAddrString := parseGenericAddrs(addrs)
|
||||
|
||||
@@ -125,6 +125,7 @@ func NewService(ctx context.Context, cfg *Config) (*Service, error) {
|
||||
s.ipLimiter = leakybucket.NewCollector(ipLimit, ipBurst, 30*time.Second, true /* deleteEmptyBuckets */)
|
||||
|
||||
opts := s.buildOptions(ipAddr, s.privKey)
|
||||
opts = append(opts, libp2p.ResourceManager(&network.NullResourceManager{}))
|
||||
h, err := libp2p.New(opts...)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to create p2p host")
|
||||
|
||||
@@ -272,7 +272,7 @@ func (p *TestP2P) AddConnectionHandler(f, _ func(ctx context.Context, id peer.ID
|
||||
|
||||
p.peers.SetConnectionState(conn.RemotePeer(), peers.PeerConnecting)
|
||||
if err := f(ctx, conn.RemotePeer()); err != nil {
|
||||
logrus.WithError(err).Error("Could not send succesful hello rpc request")
|
||||
logrus.WithError(err).Error("Could not send successful hello rpc request")
|
||||
if err := p.Disconnect(conn.RemotePeer()); err != nil {
|
||||
logrus.WithError(err).Errorf("Unable to close peer %s", conn.RemotePeer())
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ func privKey(cfg *Config) (*ecdsa.PrivateKey, error) {
|
||||
if err != nil && defaultKeysExist {
|
||||
return nil, err
|
||||
}
|
||||
// Default keys have the next highest precendence, if they exist.
|
||||
// Default keys have the next highest precedence, if they exist.
|
||||
if defaultKeysExist {
|
||||
return privKeyFromFile(defaultKeyPath)
|
||||
}
|
||||
|
||||
@@ -261,9 +261,13 @@ func (s *Server) handleStateEvents(
|
||||
// This event stream is intended to be used by builders and relays.
|
||||
// parent_ fields are based on state at N_{current_slot}, while the rest of fields are based on state of N_{current_slot + 1}
|
||||
func (s *Server) streamPayloadAttributes(stream ethpbservice.Events_StreamEventsServer) error {
|
||||
headRoot, err := s.HeadFetcher.HeadRoot(s.Ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get head root")
|
||||
}
|
||||
st, err := s.HeadFetcher.HeadState(s.Ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "could not get head state")
|
||||
}
|
||||
// advance the headstate
|
||||
headState, err := transition.ProcessSlotsIfPossible(s.Ctx, st, s.ChainInfoFetcher.CurrentSlot()+1)
|
||||
@@ -276,11 +280,6 @@ func (s *Server) streamPayloadAttributes(stream ethpbservice.Events_StreamEvents
|
||||
return err
|
||||
}
|
||||
|
||||
headRoot, err := s.HeadFetcher.HeadRoot(s.Ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
headPayload, err := headBlock.Block().Body().Execution()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -2751,6 +2751,7 @@ func TestProduceBlindedBlock(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
wr, err := ssz.WithdrawalSliceRoot(wds, fieldparams.MaxWithdrawalsPerPayload)
|
||||
require.NoError(t, err)
|
||||
v := big.NewInt(1e9) // minimal 1 gwei
|
||||
bid := ðpbalpha.BuilderBidCapella{
|
||||
Header: &enginev1.ExecutionPayloadHeaderCapella{
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
@@ -2767,7 +2768,7 @@ func TestProduceBlindedBlock(t *testing.T) {
|
||||
WithdrawalsRoot: wr[:],
|
||||
},
|
||||
Pubkey: sk.PublicKey().Marshal(),
|
||||
Value: bytesutil.PadTo([]byte{1, 2, 3}, 32),
|
||||
Value: bytesutil.PadTo(bytesutil.ReverseByteOrder(v.Bytes()), 32),
|
||||
}
|
||||
d := params.BeaconConfig().DomainApplicationBuilder
|
||||
domain, err := signing.ComputeDomain(d, nil, nil)
|
||||
@@ -2949,6 +2950,7 @@ func TestProduceBlindedBlockSSZ(t *testing.T) {
|
||||
SlashingsPool: slashings.NewPool(),
|
||||
ExitPool: voluntaryexits.NewPool(),
|
||||
StateGen: stategen.New(db, doublylinkedtree.New()),
|
||||
BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true},
|
||||
}
|
||||
|
||||
proposerSlashings := make([]*ethpbalpha.ProposerSlashing, 1)
|
||||
@@ -3114,6 +3116,7 @@ func TestProduceBlindedBlockSSZ(t *testing.T) {
|
||||
ExitPool: voluntaryexits.NewPool(),
|
||||
StateGen: stategen.New(db, doublylinkedtree.New()),
|
||||
SyncCommitteePool: synccommittee.NewStore(),
|
||||
BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true},
|
||||
}
|
||||
|
||||
proposerSlashings := make([]*ethpbalpha.ProposerSlashing, 1)
|
||||
@@ -3321,6 +3324,7 @@ func TestProduceBlindedBlockSSZ(t *testing.T) {
|
||||
StateGen: stategen.New(db, doublylinkedtree.New()),
|
||||
SyncCommitteePool: synccommittee.NewStore(),
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true},
|
||||
}
|
||||
|
||||
proposerSlashings := make([]*ethpbalpha.ProposerSlashing, 1)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -503,7 +503,7 @@ func (bs *Server) GetValidatorParticipation(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Get as close as we can to the end of the current epoch without going past the curent slot.
|
||||
// Get as close as we can to the end of the current epoch without going past the current slot.
|
||||
// The above check ensures a future *epoch* isn't requested, but the end slot of the requested epoch could still
|
||||
// be past the current slot. In that case, use the current slot as the best approximation of the requested epoch.
|
||||
// Replayer will make sure the slot ultimately used is canonical.
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -32,6 +33,8 @@ var builderGetPayloadMissCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Help: "The number of get payload misses for validator requests to builder",
|
||||
})
|
||||
|
||||
var gweiPerEth = big.NewInt(int64(params.BeaconConfig().GweiPerEth))
|
||||
|
||||
// blockBuilderTimeout is the maximum amount of time allowed for a block builder to respond to a
|
||||
// block request. This value is known as `BUILDER_PROPOSAL_DELAY_TOLERANCE` in builder spec.
|
||||
const blockBuilderTimeout = 1 * time.Second
|
||||
@@ -64,11 +67,14 @@ func (vs *Server) setExecutionData(ctx context.Context, blk interfaces.SignedBea
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get local payload value")
|
||||
}
|
||||
v.Div(v, gweiPerEth)
|
||||
localValue := v.Uint64()
|
||||
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.
|
||||
}
|
||||
v.Div(v, gweiPerEth)
|
||||
builderValue := v.Uint64()
|
||||
|
||||
withdrawalsMatched, err := matchingWithdrawalsRoot(localPayload, builderPayload)
|
||||
@@ -86,6 +92,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
|
||||
}
|
||||
@@ -93,7 +100,7 @@ func (vs *Server) setExecutionData(ctx context.Context, blk interfaces.SignedBea
|
||||
if !higherValueBuilder {
|
||||
log.WithFields(logrus.Fields{
|
||||
"localGweiValue": localValue,
|
||||
"localBoostPercentage": 100 + boost,
|
||||
"localBoostPercentage": boost,
|
||||
"builderGweiValue": builderValue,
|
||||
}).Warn("Proposer: using local execution payload because higher value")
|
||||
}
|
||||
@@ -102,12 +109,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 +155,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")
|
||||
|
||||
@@ -69,6 +69,7 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
FinalizationFetcher: &blockchainTest.ChainService{},
|
||||
BeaconDB: beaconDB,
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true},
|
||||
}
|
||||
|
||||
t.Run("No builder configured. Use local block", func(t *testing.T) {
|
||||
@@ -116,7 +117,8 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
Signature: sk.Sign(sr[:]).Marshal(),
|
||||
}
|
||||
vs.BlockBuilder = &builderTest.MockBuilderService{
|
||||
BidCapella: sBid,
|
||||
BidCapella: sBid,
|
||||
HasConfigured: true,
|
||||
}
|
||||
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockBellatrix())
|
||||
require.NoError(t, err)
|
||||
@@ -170,9 +172,10 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
Signature: sk.Sign(sr[:]).Marshal(),
|
||||
}
|
||||
vs.BlockBuilder = &builderTest.MockBuilderService{
|
||||
BidCapella: sBid,
|
||||
BidCapella: sBid,
|
||||
HasConfigured: true,
|
||||
}
|
||||
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
|
||||
@@ -193,7 +196,7 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(3), e.BlockNumber()) // Local block
|
||||
|
||||
require.LogsContain(t, hook, "builderGweiValue=1000000000 localBoostPercentage=100 localGweiValue=2000000000")
|
||||
require.LogsContain(t, hook, "builderGweiValue=1 localBoostPercentage=0 localGweiValue=2")
|
||||
})
|
||||
t.Run("Builder configured. Local block and boost has higher value", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
@@ -208,13 +211,14 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(3), e.BlockNumber()) // Local block
|
||||
|
||||
require.LogsContain(t, hook, "builderGweiValue=1000000000 localBoostPercentage=101 localGweiValue=1000000000")
|
||||
require.LogsContain(t, hook, "builderGweiValue=1 localBoostPercentage=1 localGweiValue=1")
|
||||
})
|
||||
t.Run("Builder configured. Builder returns fault. Use local block", func(t *testing.T) {
|
||||
blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
|
||||
require.NoError(t, err)
|
||||
vs.BlockBuilder = &builderTest.MockBuilderService{
|
||||
ErrGetHeader: errors.New("fault"),
|
||||
ErrGetHeader: errors.New("fault"),
|
||||
HasConfigured: true,
|
||||
}
|
||||
vs.ExecutionEngineCaller = &powtesting.EngineClient{PayloadIDBytes: id, ExecutionPayloadCapella: &v1.ExecutionPayloadCapella{BlockNumber: 4}, BlockValue: big.NewInt(0)}
|
||||
require.NoError(t, vs.setExecutionData(context.Background(), blk, capellaTransitionState))
|
||||
@@ -224,6 +228,119 @@ func TestServer_setExecutionData(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// Regression #12289
|
||||
func TestNoWeiOverflow(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
|
||||
ctx := context.Background()
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.BellatrixForkEpoch = 0
|
||||
cfg.CapellaForkEpoch = 0
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
|
||||
beaconDB := dbTest.SetupDB(t)
|
||||
capellaTransitionState, _ := util.DeterministicGenesisStateCapella(t, 1)
|
||||
wrappedHeaderCapella, err := blocks.WrappedExecutionPayloadHeaderCapella(&v1.ExecutionPayloadHeaderCapella{BlockNumber: 1}, big.NewInt(0))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, capellaTransitionState.SetLatestExecutionPayloadHeader(wrappedHeaderCapella))
|
||||
b2pbCapella := util.NewBeaconBlockCapella()
|
||||
b2rCapella, err := b2pbCapella.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
util.SaveBlock(t, context.Background(), beaconDB, b2pbCapella)
|
||||
require.NoError(t, capellaTransitionState.SetFinalizedCheckpoint(ðpb.Checkpoint{
|
||||
Root: b2rCapella[:],
|
||||
}))
|
||||
require.NoError(t, beaconDB.SaveFeeRecipientsByValidatorIDs(context.Background(), []primitives.ValidatorIndex{0}, []common.Address{{}}))
|
||||
|
||||
withdrawals := []*v1.Withdrawal{{
|
||||
Index: 1,
|
||||
ValidatorIndex: 2,
|
||||
Address: make([]byte, fieldparams.FeeRecipientLength),
|
||||
Amount: 3,
|
||||
}}
|
||||
id := &v1.PayloadIDBytes{0x1}
|
||||
vs := &Server{
|
||||
ExecutionEngineCaller: &powtesting.EngineClient{PayloadIDBytes: id, ExecutionPayloadCapella: &v1.ExecutionPayloadCapella{BlockNumber: 1, Withdrawals: withdrawals}, BlockValue: big.NewInt(18395081606530051)},
|
||||
HeadFetcher: &blockchainTest.ChainService{State: capellaTransitionState},
|
||||
FinalizationFetcher: &blockchainTest.ChainService{},
|
||||
BeaconDB: beaconDB,
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true},
|
||||
}
|
||||
|
||||
blk, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockCapella())
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, vs.BeaconDB.SaveRegistrationsByValidatorIDs(ctx, []primitives.ValidatorIndex{blk.Block().ProposerIndex()},
|
||||
[]*ethpb.ValidatorRegistrationV1{{FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}}))
|
||||
ti, err := slots.ToTime(uint64(time.Now().Unix()), 0)
|
||||
require.NoError(t, err)
|
||||
sk, err := bls.RandKey()
|
||||
require.NoError(t, err)
|
||||
wr, err := ssz.WithdrawalSliceRoot(withdrawals, fieldparams.MaxWithdrawalsPerPayload)
|
||||
require.NoError(t, err)
|
||||
builderValue := bytesutil.ReverseByteOrder(big.NewInt(195513127666416350).Bytes())
|
||||
bid := ðpb.BuilderBidCapella{
|
||||
Header: &v1.ExecutionPayloadHeaderCapella{
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
TransactionsRoot: bytesutil.PadTo([]byte{1}, fieldparams.RootLength),
|
||||
ParentHash: params.BeaconConfig().ZeroHash[:],
|
||||
Timestamp: uint64(ti.Unix()),
|
||||
BlockNumber: 2,
|
||||
WithdrawalsRoot: wr[:],
|
||||
},
|
||||
Pubkey: sk.PublicKey().Marshal(),
|
||||
Value: bytesutil.PadTo(builderValue, 32),
|
||||
}
|
||||
d := params.BeaconConfig().DomainApplicationBuilder
|
||||
domain, err := signing.ComputeDomain(d, nil, nil)
|
||||
require.NoError(t, err)
|
||||
sr, err := signing.ComputeSigningRoot(bid, domain)
|
||||
require.NoError(t, err)
|
||||
sBid := ðpb.SignedBuilderBidCapella{
|
||||
Message: bid,
|
||||
Signature: sk.Sign(sr[:]).Marshal(),
|
||||
}
|
||||
vs.BlockBuilder = &builderTest.MockBuilderService{
|
||||
BidCapella: sBid,
|
||||
HasConfigured: true,
|
||||
}
|
||||
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
|
||||
require.NoError(t, err)
|
||||
chain := &blockchainTest.ChainService{ForkChoiceStore: doublylinkedtree.New(), Genesis: time.Now(), Block: wb}
|
||||
vs.ForkFetcher = chain
|
||||
vs.ForkchoiceFetcher = chain
|
||||
vs.ForkchoiceFetcher.SetForkChoiceGenesisTime(uint64(time.Now().Unix()))
|
||||
vs.TimeFetcher = chain
|
||||
vs.HeadFetcher = chain
|
||||
require.NoError(t, vs.setExecutionData(context.Background(), blk, capellaTransitionState))
|
||||
e, err := blk.Block().Body().Execution()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(2), e.BlockNumber()) // Builder block
|
||||
|
||||
t.Run("Boosted local block, no overflow", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.LocalBlockValueBoost = 20 // Boost 1%.
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
|
||||
require.NoError(t, err)
|
||||
vs.ExecutionEngineCaller = &powtesting.EngineClient{PayloadIDBytes: id, ExecutionPayloadCapella: &v1.ExecutionPayloadCapella{BlockNumber: 3, Withdrawals: withdrawals}, BlockValue: big.NewInt(162927606e9)} // 0.16 ETH
|
||||
require.NoError(t, vs.setExecutionData(context.Background(), blk, capellaTransitionState))
|
||||
e, err := blk.Block().Body().Execution()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(3), e.BlockNumber()) // Local block
|
||||
|
||||
require.LogsContain(t, hook, "builderGweiValue=195513127 localBoostPercentage=20 localGweiValue=162927606")
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_getPayloadHeader(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
bc := params.BeaconConfig()
|
||||
|
||||
@@ -14,6 +14,9 @@ import (
|
||||
// - Validator has registered to use builder (ie called registerBuilder API end point)
|
||||
// - Circuit breaker has not been activated (ie the liveness of the chain is healthy)
|
||||
func (vs *Server) canUseBuilder(ctx context.Context, slot primitives.Slot, idx primitives.ValidatorIndex) (bool, error) {
|
||||
if !vs.BlockBuilder.Configured() {
|
||||
return false, nil
|
||||
}
|
||||
activated, err := vs.circuitBreakBuilder(slot)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
blockchainTest "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
|
||||
testing2 "github.com/prysmaticlabs/prysm/v4/beacon-chain/builder/testing"
|
||||
dbTest "github.com/prysmaticlabs/prysm/v4/beacon-chain/db/testing"
|
||||
doublylinkedtree "github.com/prysmaticlabs/prysm/v4/beacon-chain/forkchoice/doubly-linked-tree"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
||||
@@ -96,12 +97,23 @@ func TestServer_validatorRegistered(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServer_canUseBuilder(t *testing.T) {
|
||||
proposerServer := &Server{}
|
||||
proposerServer := &Server{
|
||||
BlockBuilder: &testing2.MockBuilderService{
|
||||
HasConfigured: false,
|
||||
},
|
||||
}
|
||||
reg, err := proposerServer.canUseBuilder(context.Background(), 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, reg)
|
||||
proposerServer.BlockBuilder = &testing2.MockBuilderService{
|
||||
HasConfigured: true,
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
proposerServer.ForkchoiceFetcher = &blockchainTest.ChainService{ForkChoiceStore: doublylinkedtree.New()}
|
||||
proposerServer.ForkchoiceFetcher.SetForkChoiceGenesisTime(uint64(time.Now().Unix()))
|
||||
reg, err := proposerServer.canUseBuilder(ctx, params.BeaconConfig().MaxBuilderConsecutiveMissedSlots+1, 0)
|
||||
reg, err = proposerServer.canUseBuilder(ctx, params.BeaconConfig().MaxBuilderConsecutiveMissedSlots+1, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, reg)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -481,6 +481,7 @@ func getProposerServer(db db.HeadAccessDatabase, headState state.BeaconState, he
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
BeaconDB: db,
|
||||
BLSChangesPool: blstoexec.NewPool(),
|
||||
BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -20,17 +20,7 @@ go_library(
|
||||
"validator_root.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stateutil",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//proto/migration:__subpackages__",
|
||||
"//proto/prysm/v1alpha1:__subpackages__",
|
||||
"//proto/testing:__subpackages__",
|
||||
"//slasher:__subpackages__",
|
||||
"//testing:__subpackages__",
|
||||
"//tools/blocktree:__pkg__",
|
||||
"//tools/pcli:__pkg__",
|
||||
"//validator/client:__pkg__",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/core/transition/stateutils:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
|
||||
@@ -211,6 +211,7 @@ go_test(
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//consensus-types/wrapper:go_default_library",
|
||||
"//container/leaky-bucket:go_default_library",
|
||||
"//container/slice:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//crypto/rand:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
|
||||
@@ -5,10 +5,14 @@ import (
|
||||
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v4/container/slice"
|
||||
"github.com/prysmaticlabs/prysm/v4/monitoring/tracing"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
@@ -39,10 +43,11 @@ func (s *Service) validateAttesterSlashing(ctx context.Context, pid peer.ID, msg
|
||||
return pubsub.ValidationReject, errWrongMessage
|
||||
}
|
||||
|
||||
if slashing == nil || slashing.Attestation_1 == nil || slashing.Attestation_2 == nil {
|
||||
slashedVals := blocks.SlashableAttesterIndices(slashing)
|
||||
if slashedVals == nil {
|
||||
return pubsub.ValidationReject, errNilMessage
|
||||
}
|
||||
if s.hasSeenAttesterSlashingIndices(slashing.Attestation_1.AttestingIndices, slashing.Attestation_2.AttestingIndices) {
|
||||
if s.hasSeenAttesterSlashingIndices(slashedVals) {
|
||||
return pubsub.ValidationIgnore, nil
|
||||
}
|
||||
|
||||
@@ -53,7 +58,20 @@ func (s *Service) validateAttesterSlashing(ctx context.Context, pid peer.ID, msg
|
||||
if err := blocks.VerifyAttesterSlashing(ctx, headState, slashing); err != nil {
|
||||
return pubsub.ValidationReject, err
|
||||
}
|
||||
|
||||
isSlashable := false
|
||||
for _, v := range slashedVals {
|
||||
val, err := headState.ValidatorAtIndexReadOnly(primitives.ValidatorIndex(v))
|
||||
if err != nil {
|
||||
return pubsub.ValidationIgnore, err
|
||||
}
|
||||
if helpers.IsSlashableValidator(val.ActivationEpoch(), val.WithdrawableEpoch(), val.Slashed(), slots.ToEpoch(headState.Slot())) {
|
||||
isSlashable = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isSlashable {
|
||||
return pubsub.ValidationReject, errors.Errorf("none of the validators are slashable: %v", slashedVals)
|
||||
}
|
||||
s.cfg.chain.ReceiveAttesterSlashing(ctx, slashing)
|
||||
|
||||
msg.ValidatorData = slashing // Used in downstream subscriber
|
||||
@@ -61,9 +79,7 @@ func (s *Service) validateAttesterSlashing(ctx context.Context, pid peer.ID, msg
|
||||
}
|
||||
|
||||
// Returns true if the node has already received a valid attester slashing with the attesting indices.
|
||||
func (s *Service) hasSeenAttesterSlashingIndices(indices1, indices2 []uint64) bool {
|
||||
slashableIndices := slice.IntersectionUint64(indices1, indices2)
|
||||
|
||||
func (s *Service) hasSeenAttesterSlashingIndices(slashableIndices []uint64) bool {
|
||||
s.seenAttesterSlashingLock.RLock()
|
||||
defer s.seenAttesterSlashingLock.RUnlock()
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
mockSync "github.com/prysmaticlabs/prysm/v4/beacon-chain/sync/initial-sync/testing"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v4/container/slice"
|
||||
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/assert"
|
||||
@@ -111,6 +112,61 @@ func TestValidateAttesterSlashing_ValidSlashing(t *testing.T) {
|
||||
assert.NotNil(t, msg.ValidatorData, "Decoded message was not set on the message validator data")
|
||||
}
|
||||
|
||||
func TestValidateAttesterSlashing_InvalidSlashing_WithdrawableEpoch(t *testing.T) {
|
||||
p := p2ptest.NewTestP2P(t)
|
||||
ctx := context.Background()
|
||||
|
||||
slashing, s := setupValidAttesterSlashing(t)
|
||||
// Set only one of the validators as withdrawn
|
||||
vals := s.Validators()
|
||||
vals[1].WithdrawableEpoch = primitives.Epoch(1)
|
||||
|
||||
require.NoError(t, s.SetValidators(vals))
|
||||
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
p2p: p,
|
||||
chain: &mock.ChainService{State: s, Genesis: time.Now()},
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
},
|
||||
seenAttesterSlashingCache: make(map[uint64]bool),
|
||||
subHandler: newSubTopicHandler(),
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err := p.Encoding().EncodeGossip(buf, slashing)
|
||||
require.NoError(t, err)
|
||||
|
||||
topic := p2p.GossipTypeMapping[reflect.TypeOf(slashing)]
|
||||
d, err := r.currentForkDigest()
|
||||
assert.NoError(t, err)
|
||||
topic = r.addDigestToTopic(topic, d)
|
||||
msg := &pubsub.Message{
|
||||
Message: &pubsubpb.Message{
|
||||
Data: buf.Bytes(),
|
||||
Topic: &topic,
|
||||
},
|
||||
}
|
||||
res, err := r.validateAttesterSlashing(ctx, "foobar", msg)
|
||||
assert.NoError(t, err)
|
||||
valid := res == pubsub.ValidationAccept
|
||||
|
||||
assert.Equal(t, true, valid, "Rejected Validation")
|
||||
|
||||
// Set all validators as withdrawn.
|
||||
vals = s.Validators()
|
||||
for _, vv := range vals {
|
||||
vv.WithdrawableEpoch = primitives.Epoch(1)
|
||||
}
|
||||
|
||||
require.NoError(t, s.SetValidators(vals))
|
||||
res, err = r.validateAttesterSlashing(ctx, "foobar", msg)
|
||||
assert.ErrorContains(t, "none of the validators are slashable", err)
|
||||
invalid := res == pubsub.ValidationReject
|
||||
|
||||
assert.Equal(t, true, invalid, "Passed Validation")
|
||||
}
|
||||
|
||||
func TestValidateAttesterSlashing_CanFilter(t *testing.T) {
|
||||
p := p2ptest.NewTestP2P(t)
|
||||
ctx := context.Background()
|
||||
@@ -290,6 +346,7 @@ func TestSeenAttesterSlashingIndices(t *testing.T) {
|
||||
seenAttesterSlashingCache: map[uint64]bool{},
|
||||
}
|
||||
r.setAttesterSlashingIndicesSeen(tc.saveIndices1, tc.saveIndices2)
|
||||
assert.Equal(t, tc.seen, r.hasSeenAttesterSlashingIndices(tc.checkIndices1, tc.checkIndices2))
|
||||
slashedVals := slice.IntersectionUint64(tc.checkIndices1, tc.checkIndices2)
|
||||
assert.Equal(t, tc.seen, r.hasSeenAttesterSlashingIndices(slashedVals))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ func (s *Service) validateBeaconBlock(ctx context.Context, blk interfaces.ReadOn
|
||||
// [REJECT] The block's execution payload timestamp is correct with respect to the slot --
|
||||
// i.e. execution_payload.timestamp == compute_timestamp_at_slot(state, block.slot).
|
||||
//
|
||||
// If exection_payload verification of block's parent by an execution node is not complete:
|
||||
// If execution_payload verification of block's parent by an execution node is not complete:
|
||||
// [REJECT] The block's parent (defined by block.parent_root) passes all validation (excluding execution
|
||||
// node verification of the block.body.execution_payload).
|
||||
// otherwise:
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ var (
|
||||
// P2PDenyList defines a list of CIDR subnets to disallow connections from them.
|
||||
P2PDenyList = &cli.StringSliceFlag{
|
||||
Name: "p2p-denylist",
|
||||
Usage: "The CIDR subnets for denying certainy peer connections. " +
|
||||
Usage: "The CIDR subnets for denying certainty peer connections. " +
|
||||
"Using \"private\" would deny all private subnets. Example: " +
|
||||
"192.168.0.0/16 would deny connections from peers on your local network only. The " +
|
||||
"default is to accept all connections.",
|
||||
@@ -222,7 +222,7 @@ var (
|
||||
// GrpcMaxCallRecvMsgSizeFlag defines the max call message size for GRPC
|
||||
GrpcMaxCallRecvMsgSizeFlag = &cli.IntFlag{
|
||||
Name: "grpc-max-msg-size",
|
||||
Usage: "Integer to define max recieve message call size. If serving a public gRPC server, " +
|
||||
Usage: "Integer to define max receive message call size. If serving a public gRPC server, " +
|
||||
"set this to a more reasonable size to avoid resource exhaustion from large messages. " +
|
||||
"Validators with as many as 10000 keys can be run with a max message size of less than " +
|
||||
"50Mb. The default here is set to a very high value for local users. " +
|
||||
|
||||
@@ -110,5 +110,6 @@ docker_push(
|
||||
go_binary(
|
||||
name = "prysmctl",
|
||||
embed = [":go_default_library"],
|
||||
gotags = ["noMainnetGenesis"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
@@ -9,11 +9,7 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/v4/cmd/prysmctl/testnet",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/core/altair:go_default_library",
|
||||
"//beacon-chain/core/capella:go_default_library",
|
||||
"//beacon-chain/core/execution:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/state-native:go_default_library",
|
||||
"//cmd/flags:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//container/trie:go_default_library",
|
||||
@@ -21,6 +17,7 @@ go_library(
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//runtime/interop:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//core:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//ethclient:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
|
||||
"@com_github_ghodss_yaml//:go_default_library",
|
||||
|
||||
@@ -5,20 +5,17 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/altair"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/capella"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/execution"
|
||||
"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/cmd/flags"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/container/trie"
|
||||
@@ -32,17 +29,19 @@ import (
|
||||
|
||||
var (
|
||||
generateGenesisStateFlags = struct {
|
||||
DepositJsonFile string
|
||||
ChainConfigFile string
|
||||
ConfigName string
|
||||
NumValidators uint64
|
||||
GenesisTime uint64
|
||||
OutputSSZ string
|
||||
OutputJSON string
|
||||
OutputYaml string
|
||||
ForkName string
|
||||
OverrideEth1Data bool
|
||||
ExecutionEndpoint string
|
||||
DepositJsonFile string
|
||||
ChainConfigFile string
|
||||
ConfigName string
|
||||
NumValidators uint64
|
||||
GenesisTime uint64
|
||||
OutputSSZ string
|
||||
OutputJSON string
|
||||
OutputYaml string
|
||||
ForkName string
|
||||
OverrideEth1Data bool
|
||||
ExecutionEndpoint string
|
||||
GethGenesisJsonIn string
|
||||
GethGenesisJsonOut string
|
||||
}{}
|
||||
log = logrus.WithField("prefix", "genesis")
|
||||
outputSSZFlag = &cli.StringFlag{
|
||||
@@ -106,6 +105,16 @@ var (
|
||||
Usage: "Overrides Eth1Data with values from execution client. If unset, defaults to false",
|
||||
Value: false,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "geth-genesis-json-in",
|
||||
Destination: &generateGenesisStateFlags.GethGenesisJsonIn,
|
||||
Usage: "Path to a \"genesis.json\" file, containing a json representation of Geth's core.Genesis",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "geth-genesis-json-out",
|
||||
Destination: &generateGenesisStateFlags.GethGenesisJsonOut,
|
||||
Usage: "Path to write generated \"genesis.json\" file, containing a json representation of Geth's core.Genesis",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "execution-endpoint",
|
||||
Destination: &generateGenesisStateFlags.ExecutionEndpoint,
|
||||
@@ -146,9 +155,6 @@ type depositDataJSON struct {
|
||||
}
|
||||
|
||||
func cliActionGenerateGenesisState(cliCtx *cli.Context) error {
|
||||
if generateGenesisStateFlags.GenesisTime == 0 {
|
||||
log.Info("No genesis time specified, defaulting to now()")
|
||||
}
|
||||
outputJson := generateGenesisStateFlags.OutputJSON
|
||||
outputYaml := generateGenesisStateFlags.OutputYaml
|
||||
outputSSZ := generateGenesisStateFlags.OutputSSZ
|
||||
@@ -164,14 +170,10 @@ func cliActionGenerateGenesisState(cliCtx *cli.Context) error {
|
||||
if err := setGlobalParams(); err != nil {
|
||||
return fmt.Errorf("could not set config params: %v", err)
|
||||
}
|
||||
genesisState, err := generateGenesis(cliCtx.Context)
|
||||
st, err := generateGenesis(cliCtx.Context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not generate genesis state: %v", err)
|
||||
}
|
||||
st, err := upgradeStateToForkName(cliCtx.Context, genesisState, generateGenesisStateFlags.ForkName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if outputJson != "" {
|
||||
if err := writeToOutputFile(outputJson, st, json.Marshal); err != nil {
|
||||
@@ -202,42 +204,6 @@ func cliActionGenerateGenesisState(cliCtx *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func upgradeStateToForkName(ctx context.Context, pbst *ethpb.BeaconState, name string) (state.BeaconState, error) {
|
||||
st, err := state_native.InitializeFromProtoUnsafePhase0(pbst)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if name == "" || name == "phase0" {
|
||||
return st, nil
|
||||
}
|
||||
|
||||
st, err = altair.UpgradeToAltair(ctx, st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if name == "altair" {
|
||||
return st, nil
|
||||
}
|
||||
|
||||
st, err = execution.UpgradeToBellatrix(st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if name == "bellatrix" {
|
||||
return st, nil
|
||||
}
|
||||
|
||||
st, err = capella.UpgradeToCapella(st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if name == "capella" {
|
||||
return st, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unrecognized fork name '%s'", name)
|
||||
}
|
||||
|
||||
func setGlobalParams() error {
|
||||
chainConfigFile := generateGenesisStateFlags.ChainConfigFile
|
||||
if chainConfigFile != "" {
|
||||
@@ -251,43 +217,79 @@ func setGlobalParams() error {
|
||||
return params.SetActive(cfg.Copy())
|
||||
}
|
||||
|
||||
func generateGenesis(ctx context.Context) (genesisState *ethpb.BeaconState, err error) {
|
||||
genesisTime := generateGenesisStateFlags.GenesisTime
|
||||
numValidators := generateGenesisStateFlags.NumValidators
|
||||
depositJsonFile := generateGenesisStateFlags.DepositJsonFile
|
||||
overrideEth1Data := generateGenesisStateFlags.OverrideEth1Data
|
||||
if depositJsonFile != "" {
|
||||
expanded, err := file.ExpandPath(depositJsonFile)
|
||||
func generateGenesis(ctx context.Context) (state.BeaconState, error) {
|
||||
f := &generateGenesisStateFlags
|
||||
if f.GenesisTime == 0 {
|
||||
f.GenesisTime = uint64(time.Now().Unix())
|
||||
log.Info("No genesis time specified, defaulting to now()")
|
||||
}
|
||||
|
||||
v, err := version.FromString(f.ForkName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts := make([]interop.PremineGenesisOpt, 0)
|
||||
nv := f.NumValidators
|
||||
if f.DepositJsonFile != "" {
|
||||
expanded, err := file.ExpandPath(f.DepositJsonFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inputJSON, err := os.Open(expanded) // #nosec G304
|
||||
log.Printf("reading deposits from JSON at %s", expanded)
|
||||
b, err := os.ReadFile(expanded) // #nosec G304
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err = inputJSON.Close(); err != nil {
|
||||
log.WithError(err).Printf("Could not close file %s", depositJsonFile)
|
||||
}
|
||||
}()
|
||||
log.Printf("Generating genesis state from input JSON deposit data %s", depositJsonFile)
|
||||
genesisState, err = genesisStateFromJSONValidators(ctx, inputJSON, genesisTime)
|
||||
roots, dds, err := depositEntriesFromJSON(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts = append(opts, interop.WithDepositData(dds, roots))
|
||||
} else if nv == 0 {
|
||||
return nil, fmt.Errorf(
|
||||
"expected --num-validators > 0 or --deposit-json-file to have been provided",
|
||||
)
|
||||
}
|
||||
|
||||
gen := &core.Genesis{}
|
||||
if f.GethGenesisJsonIn != "" {
|
||||
gbytes, err := os.ReadFile(f.GethGenesisJsonIn) // #nosec G304
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to read %s", f.GethGenesisJsonIn)
|
||||
}
|
||||
if err := json.Unmarshal(gbytes, gen); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// set timestamps for genesis and shanghai fork
|
||||
gen.Timestamp = f.GenesisTime
|
||||
gen.Config.ShanghaiTime = interop.GethShanghaiTime(f.GenesisTime, params.BeaconConfig())
|
||||
if v > version.Altair {
|
||||
// set ttd to zero so EL goes post-merge immediately
|
||||
gen.Config.TerminalTotalDifficulty = big.NewInt(0)
|
||||
}
|
||||
} else {
|
||||
if numValidators == 0 {
|
||||
return nil, fmt.Errorf(
|
||||
"expected --num-validators > 0 to have been provided",
|
||||
)
|
||||
}
|
||||
// If no JSON input is specified, we create the state deterministically from interop keys.
|
||||
genesisState, _, err = interop.GenerateGenesisState(ctx, genesisTime, numValidators)
|
||||
gen = interop.GethTestnetGenesis(f.GenesisTime, params.BeaconConfig())
|
||||
}
|
||||
|
||||
if f.GethGenesisJsonOut != "" {
|
||||
gbytes, err := json.MarshalIndent(gen, "", "\t")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := os.WriteFile(f.GethGenesisJsonOut, gbytes, os.ModePerm); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to write %s", f.GethGenesisJsonOut)
|
||||
}
|
||||
}
|
||||
if overrideEth1Data {
|
||||
|
||||
gb := gen.ToBlock()
|
||||
|
||||
// TODO: expose the PregenesisCreds option with a cli flag - for now defaulting to no withdrawal credentials at genesis
|
||||
genesisState, err := interop.NewPreminedGenesis(ctx, f.GenesisTime, nv, 0, v, gb, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if f.OverrideEth1Data {
|
||||
log.Print("Overriding Eth1Data with data from execution client")
|
||||
conn, err := rpc.Dial(generateGenesisStateFlags.ExecutionEndpoint)
|
||||
if err != nil {
|
||||
@@ -309,67 +311,63 @@ func generateGenesis(ctx context.Context) (genesisState *ethpb.BeaconState, err
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get hash tree root")
|
||||
}
|
||||
genesisState.Eth1Data = ðpb.Eth1Data{
|
||||
e1d := ðpb.Eth1Data{
|
||||
DepositRoot: depositRoot[:],
|
||||
DepositCount: 0,
|
||||
BlockHash: header.Hash().Bytes(),
|
||||
}
|
||||
genesisState.Eth1DepositIndex = 0
|
||||
if err := genesisState.SetEth1Data(e1d); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := genesisState.SetEth1DepositIndex(0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return genesisState, err
|
||||
}
|
||||
|
||||
func genesisStateFromJSONValidators(ctx context.Context, r io.Reader, genesisTime uint64) (*ethpb.BeaconState, error) {
|
||||
enc, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func depositEntriesFromJSON(enc []byte) ([][]byte, []*ethpb.Deposit_Data, error) {
|
||||
var depositJSON []*depositDataJSON
|
||||
if err := json.Unmarshal(enc, &depositJSON); err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
depositDataList := make([]*ethpb.Deposit_Data, len(depositJSON))
|
||||
depositDataRoots := make([][]byte, len(depositJSON))
|
||||
dds := make([]*ethpb.Deposit_Data, len(depositJSON))
|
||||
roots := make([][]byte, len(depositJSON))
|
||||
for i, val := range depositJSON {
|
||||
data, dataRootBytes, err := depositJSONToDepositData(val)
|
||||
root, data, err := depositJSONToDepositData(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
depositDataList[i] = data
|
||||
depositDataRoots[i] = dataRootBytes
|
||||
dds[i] = data
|
||||
roots[i] = root
|
||||
}
|
||||
beaconState, _, err := interop.GenerateGenesisStateFromDepositData(ctx, genesisTime, depositDataList, depositDataRoots)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return beaconState, nil
|
||||
return roots, dds, nil
|
||||
}
|
||||
|
||||
func depositJSONToDepositData(input *depositDataJSON) (depositData *ethpb.Deposit_Data, dataRoot []byte, err error) {
|
||||
pubKeyBytes, err := hex.DecodeString(strings.TrimPrefix(input.PubKey, "0x"))
|
||||
func depositJSONToDepositData(input *depositDataJSON) ([]byte, *ethpb.Deposit_Data, error) {
|
||||
root, err := hex.DecodeString(strings.TrimPrefix(input.DepositDataRoot, "0x"))
|
||||
if err != nil {
|
||||
return
|
||||
return nil, nil, err
|
||||
}
|
||||
withdrawalbytes, err := hex.DecodeString(strings.TrimPrefix(input.WithdrawalCredentials, "0x"))
|
||||
pk, err := hex.DecodeString(strings.TrimPrefix(input.PubKey, "0x"))
|
||||
if err != nil {
|
||||
return
|
||||
return nil, nil, err
|
||||
}
|
||||
signatureBytes, err := hex.DecodeString(strings.TrimPrefix(input.Signature, "0x"))
|
||||
creds, err := hex.DecodeString(strings.TrimPrefix(input.WithdrawalCredentials, "0x"))
|
||||
if err != nil {
|
||||
return
|
||||
return nil, nil, err
|
||||
}
|
||||
dataRootBytes, err := hex.DecodeString(strings.TrimPrefix(input.DepositDataRoot, "0x"))
|
||||
sig, err := hex.DecodeString(strings.TrimPrefix(input.Signature, "0x"))
|
||||
if err != nil {
|
||||
return
|
||||
return nil, nil, err
|
||||
}
|
||||
depositData = ðpb.Deposit_Data{
|
||||
PublicKey: pubKeyBytes,
|
||||
WithdrawalCredentials: withdrawalbytes,
|
||||
return root, ðpb.Deposit_Data{
|
||||
PublicKey: pk,
|
||||
WithdrawalCredentials: creds,
|
||||
Amount: input.Amount,
|
||||
Signature: signatureBytes,
|
||||
}
|
||||
dataRoot = dataRootBytes
|
||||
return
|
||||
Signature: sig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func writeToOutputFile(
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package testnet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
@@ -18,13 +16,10 @@ func Test_genesisStateFromJSONValidators(t *testing.T) {
|
||||
jsonData := createGenesisDepositData(t, numKeys)
|
||||
jsonInput, err := json.Marshal(jsonData)
|
||||
require.NoError(t, err)
|
||||
ctx := context.Background()
|
||||
genesisState, err := genesisStateFromJSONValidators(
|
||||
ctx, bytes.NewReader(jsonInput), 0, /* genesis time defaults to time.Now() */
|
||||
)
|
||||
_, dds, err := depositEntriesFromJSON(jsonInput)
|
||||
require.NoError(t, err)
|
||||
for i, val := range genesisState.Validators {
|
||||
assert.DeepEqual(t, fmt.Sprintf("%#x", val.PublicKey), jsonData[i].PubKey)
|
||||
for i := range dds {
|
||||
assert.DeepEqual(t, fmt.Sprintf("%#x", dds[i].PublicKey), jsonData[i].PubKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ var (
|
||||
var Commands = []*cli.Command{
|
||||
{
|
||||
Name: "validator",
|
||||
Aliases: []string{"v", "sign"}, // remove sign command should be depreciated but having as backwards compatability.
|
||||
Aliases: []string{"v", "sign"}, // remove sign command should be depreciated but having as backwards compatibility.
|
||||
Usage: "commands that affect the state of validators such as exiting or withdrawing",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
@@ -110,6 +110,7 @@ var Commands = []*cli.Command{
|
||||
flags.GrpcRetryDelayFlag,
|
||||
flags.ExitAllFlag,
|
||||
flags.ForceExitFlag,
|
||||
flags.VoluntaryExitJSONOutputPath,
|
||||
features.Mainnet,
|
||||
features.PraterTestnet,
|
||||
features.SepoliaTestnet,
|
||||
|
||||
@@ -49,6 +49,7 @@ go_test(
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//build/bazel:go_default_library",
|
||||
"//cmd/validator/flags:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
|
||||
@@ -166,6 +166,7 @@ var Commands = &cli.Command{
|
||||
flags.GrpcRetryDelayFlag,
|
||||
flags.ExitAllFlag,
|
||||
flags.ForceExitFlag,
|
||||
flags.VoluntaryExitJSONOutputPath,
|
||||
features.Mainnet,
|
||||
features.PraterTestnet,
|
||||
features.SepoliaTestnet,
|
||||
|
||||
@@ -83,6 +83,7 @@ func AccountsExit(c *cli.Context, r io.Reader) error {
|
||||
accounts.WithBeaconRPCProvider(beaconRPCProvider),
|
||||
accounts.WithBeaconRESTApiProvider(c.String(flags.BeaconRESTApiProviderFlag.Name)),
|
||||
accounts.WithGRPCHeaders(grpcHeaders),
|
||||
accounts.WithExitJSONOutputPath(c.String(flags.VoluntaryExitJSONOutputPath.Name)),
|
||||
}
|
||||
// Get full set of public keys from the keymanager.
|
||||
validatingPublicKeys, err := km.FetchValidatingPublicKeys(c.Context)
|
||||
|
||||
@@ -3,12 +3,15 @@ package accounts
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/prysmaticlabs/prysm/v4/build/bazel"
|
||||
"github.com/prysmaticlabs/prysm/v4/io/file"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
||||
@@ -305,3 +308,93 @@ func TestExitAccountsCli_OK_ForceExit(t *testing.T) {
|
||||
require.Equal(t, 1, len(formattedExitedKeys))
|
||||
assert.Equal(t, "0x"+keystore.Pubkey[:12], formattedExitedKeys[0])
|
||||
}
|
||||
|
||||
func TestExitAccountsCli_WriteJSON_NoBroadcast(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
mockValidatorClient := validatormock.NewMockValidatorClient(ctrl)
|
||||
mockNodeClient := validatormock.NewMockNodeClient(ctrl)
|
||||
|
||||
mockValidatorClient.EXPECT().
|
||||
ValidatorIndex(gomock.Any(), gomock.Any()).
|
||||
Return(ðpb.ValidatorIndexResponse{Index: 1}, nil)
|
||||
|
||||
// Any time in the past will suffice
|
||||
genesisTime := ×tamppb.Timestamp{
|
||||
Seconds: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
|
||||
}
|
||||
|
||||
mockNodeClient.EXPECT().
|
||||
GetGenesis(gomock.Any(), gomock.Any()).
|
||||
Return(ðpb.Genesis{GenesisTime: genesisTime}, nil)
|
||||
|
||||
mockValidatorClient.EXPECT().
|
||||
DomainData(gomock.Any(), gomock.Any()).
|
||||
Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil)
|
||||
|
||||
walletDir, _, passwordFilePath := setupWalletAndPasswordsDir(t)
|
||||
// Write a directory where we will import keys from.
|
||||
keysDir := filepath.Join(t.TempDir(), "keysDir")
|
||||
require.NoError(t, os.MkdirAll(keysDir, os.ModePerm))
|
||||
|
||||
// Create keystore file in the keys directory we can then import from in our wallet.
|
||||
keystore, _ := createKeystore(t, keysDir)
|
||||
time.Sleep(time.Second)
|
||||
|
||||
// We initialize a wallet with a local keymanager.
|
||||
cliCtx := setupWalletCtx(t, &testWalletConfig{
|
||||
// Wallet configuration flags.
|
||||
walletDir: walletDir,
|
||||
keymanagerKind: keymanager.Local,
|
||||
walletPasswordFile: passwordFilePath,
|
||||
accountPasswordFile: passwordFilePath,
|
||||
// Flag required for ImportAccounts to work.
|
||||
keysDir: keysDir,
|
||||
// Flag required for ExitAccounts to work.
|
||||
voluntaryExitPublicKeys: keystore.Pubkey,
|
||||
})
|
||||
opts := []accounts.Option{
|
||||
accounts.WithWalletDir(walletDir),
|
||||
accounts.WithKeymanagerType(keymanager.Local),
|
||||
accounts.WithWalletPassword(password),
|
||||
}
|
||||
acc, err := accounts.NewCLIManager(opts...)
|
||||
require.NoError(t, err)
|
||||
_, err = acc.WalletCreate(cliCtx.Context)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, accountsImport(cliCtx))
|
||||
|
||||
_, km, err := walletWithKeymanager(cliCtx)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, km)
|
||||
|
||||
validatingPublicKeys, err := km.FetchValidatingPublicKeys(cliCtx.Context)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, validatingPublicKeys)
|
||||
|
||||
rawPubKeys, formattedPubKeys, err := accounts.FilterExitAccountsFromUserInput(
|
||||
cliCtx, &bytes.Buffer{}, validatingPublicKeys, true,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, rawPubKeys)
|
||||
require.NotNil(t, formattedPubKeys)
|
||||
|
||||
out := path.Join(bazel.TestTmpDir(), "exits")
|
||||
|
||||
cfg := accounts.PerformExitCfg{
|
||||
ValidatorClient: mockValidatorClient,
|
||||
NodeClient: mockNodeClient,
|
||||
Keymanager: km,
|
||||
RawPubKeys: rawPubKeys,
|
||||
FormattedPubKeys: formattedPubKeys,
|
||||
OutputDirectory: out,
|
||||
}
|
||||
rawExitedKeys, formattedExitedKeys, err := accounts.PerformVoluntaryExit(cliCtx.Context, cfg)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(rawExitedKeys))
|
||||
assert.DeepEqual(t, rawPubKeys[0], rawExitedKeys[0])
|
||||
require.Equal(t, 1, len(formattedExitedKeys))
|
||||
assert.Equal(t, "0x"+keystore.Pubkey[:12], formattedExitedKeys[0])
|
||||
|
||||
require.Equal(t, true, file.FileExists(path.Join(out, "validator-exit-1.json")), "Expected file to exist")
|
||||
}
|
||||
|
||||
@@ -230,6 +230,12 @@ var (
|
||||
Name: "force-exit",
|
||||
Usage: "Exit without displaying the confirmation prompt",
|
||||
}
|
||||
VoluntaryExitJSONOutputPath = &cli.StringFlag{
|
||||
Name: "exit-json-output-dir",
|
||||
Usage: "The output directory to write voluntary exits as individual unencrypted JSON " +
|
||||
"files. If this flag is provided, voluntary exits will be written to the provided " +
|
||||
"directory and will not be broadcasted.",
|
||||
}
|
||||
// BackupPasswordFile for encrypting accounts a user wishes to back up.
|
||||
BackupPasswordFile = &cli.StringFlag{
|
||||
Name: "backup-password-file",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -65,6 +65,8 @@ type Flags struct {
|
||||
EnableVerboseSigVerification bool // EnableVerboseSigVerification specifies whether to verify individual signature if batch verification fails
|
||||
EnableOptionalEngineMethods bool // EnableOptionalEngineMethods specifies whether to activate capella specific engine methods
|
||||
|
||||
PrepareAllPayloads bool // PrepareAllPayloads informs the engine to prepare a block on every slot.
|
||||
|
||||
// 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.
|
||||
KeystoreImportDebounceInterval time.Duration
|
||||
@@ -206,6 +208,10 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
|
||||
logEnabled(enableOptionalEngineMethods)
|
||||
cfg.EnableOptionalEngineMethods = true
|
||||
}
|
||||
if ctx.IsSet(prepareAllPayloads.Name) {
|
||||
logEnabled(prepareAllPayloads)
|
||||
cfg.PrepareAllPayloads = true
|
||||
}
|
||||
Init(cfg)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -114,6 +114,10 @@ var (
|
||||
Name: "enable-optional-engine-methods",
|
||||
Usage: "Enables the optional engine methods",
|
||||
}
|
||||
prepareAllPayloads = &cli.BoolFlag{
|
||||
Name: "prepare-all-payloads",
|
||||
Usage: "Informs the engine to prepare all local payloads. Useful for relayers and builders",
|
||||
}
|
||||
)
|
||||
|
||||
// devModeFlags holds list of flags that are set when development mode is on.
|
||||
@@ -159,6 +163,7 @@ var BeaconChainFlags = append(deprecatedBeaconFlags, append(deprecatedFlags, []c
|
||||
enableFullSSZDataLogging,
|
||||
enableVerboseSigVerification,
|
||||
enableOptionalEngineMethods,
|
||||
prepareAllPayloads,
|
||||
}...)...)
|
||||
|
||||
// E2EBeaconChainFlags contains a list of the beacon chain feature flags to be tested in E2E.
|
||||
|
||||
6
deps.bzl
6
deps.bzl
@@ -4061,12 +4061,6 @@ def prysm_deps():
|
||||
sum = "h1:264/meVYWt1wFw6Mtn+xwkZkXjID42gNra4rycoiDXI=",
|
||||
version = "v2.8.2",
|
||||
)
|
||||
go_repository(
|
||||
name = "com_github_wercker_journalhook",
|
||||
importpath = "github.com/wercker/journalhook",
|
||||
sum = "h1:shC1HB1UogxN5Ech3Yqaaxj1X/P656PPCB4RbojIJqc=",
|
||||
version = "v0.0.0-20180428041537-5d0a5ae867b3",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_willf_bitset",
|
||||
|
||||
4
go.mod
4
go.mod
@@ -77,7 +77,6 @@ require (
|
||||
github.com/wealdtech/go-bytesutil v1.1.1
|
||||
github.com/wealdtech/go-eth2-util v1.6.3
|
||||
github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.3
|
||||
github.com/wercker/journalhook v0.0.0-20180428041537-5d0a5ae867b3
|
||||
go.etcd.io/bbolt v1.3.5
|
||||
go.opencensus.io v0.24.0
|
||||
go.uber.org/automaxprocs v1.3.0
|
||||
@@ -212,6 +211,7 @@ require (
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||
github.com/tklauser/numcpus v0.6.0 // indirect
|
||||
@@ -242,7 +242,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
|
||||
github.com/fatih/color v1.9.0 // indirect
|
||||
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
|
||||
3
go.sum
3
go.sum
@@ -1186,6 +1186,7 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
@@ -1267,8 +1268,6 @@ github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.3 h1:SxrDVSr+oXuT1
|
||||
github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.3/go.mod h1:qiIimacW5NhVRy8o+YxWo9YrecXqDAKKbL0+sOa0SJ4=
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.8.2 h1:264/meVYWt1wFw6Mtn+xwkZkXjID42gNra4rycoiDXI=
|
||||
github.com/wealdtech/go-eth2-wallet-types/v2 v2.8.2/go.mod h1:k6kmiKWSWBTd4OxFifTEkPaBLhZspnO2KFD5XJY9nqg=
|
||||
github.com/wercker/journalhook v0.0.0-20180428041537-5d0a5ae867b3 h1:shC1HB1UogxN5Ech3Yqaaxj1X/P656PPCB4RbojIJqc=
|
||||
github.com/wercker/journalhook v0.0.0-20180428041537-5d0a5ae867b3/go.mod h1:XCsSkdKK4gwBMNrOCZWww0pX6AOt+2gYc5Z6jBRrNVg=
|
||||
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
|
||||
github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
|
||||
|
||||
@@ -1,20 +1,29 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"journald.go",
|
||||
"journald_linux.go",
|
||||
"journalhook_linux.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v4/monitoring/journald",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = select({
|
||||
"@io_bazel_rules_go//go/platform:android": [
|
||||
"@com_github_wercker_journalhook//:go_default_library",
|
||||
"@com_github_coreos_go_systemd//journal:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"@com_github_wercker_journalhook//:go_default_library",
|
||||
"@com_github_coreos_go_systemd//journal:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["journalhook_linux_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
)
|
||||
|
||||
21
monitoring/journald/LICENSE
Normal file
21
monitoring/journald/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Wercker
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -3,11 +3,20 @@
|
||||
package journald
|
||||
|
||||
import (
|
||||
"github.com/wercker/journalhook"
|
||||
"io"
|
||||
|
||||
"github.com/coreos/go-systemd/journal"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Enable enables the journald logrus hook
|
||||
// Enable adds the Journal hook if journal is enabled
|
||||
// Sets log output to ioutil.Discard so stdout isn't captured.
|
||||
func Enable() error {
|
||||
journalhook.Enable()
|
||||
if !journal.Enabled() {
|
||||
logrus.Warning("Journal not available but user requests we log to it. Ignoring")
|
||||
} else {
|
||||
logrus.AddHook(&JournalHook{})
|
||||
logrus.SetOutput(io.Discard)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
77
monitoring/journald/journalhook_linux.go
Normal file
77
monitoring/journald/journalhook_linux.go
Normal file
@@ -0,0 +1,77 @@
|
||||
//go:build linux
|
||||
|
||||
// Package journald was copied directly from https://github.com/wercker/journalhook,
|
||||
// where this library was previously hosted.
|
||||
package journald
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/go-systemd/journal"
|
||||
logrus "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type JournalHook struct{}
|
||||
|
||||
var (
|
||||
severityMap = map[logrus.Level]journal.Priority{
|
||||
logrus.DebugLevel: journal.PriDebug,
|
||||
logrus.InfoLevel: journal.PriInfo,
|
||||
logrus.WarnLevel: journal.PriWarning,
|
||||
logrus.ErrorLevel: journal.PriErr,
|
||||
logrus.FatalLevel: journal.PriCrit,
|
||||
logrus.PanicLevel: journal.PriEmerg,
|
||||
}
|
||||
)
|
||||
|
||||
func stringifyOp(r rune) rune {
|
||||
// Journal wants uppercase strings. See `validVarName`
|
||||
// https://github.com/coreos/go-systemd/blob/ff118ad0f8d9cf99903d3391ca3a295671022cee/journal/journal.go#L137-L147
|
||||
switch {
|
||||
case r >= 'A' && r <= 'Z':
|
||||
return r
|
||||
case r >= '0' && r <= '9':
|
||||
return r
|
||||
case r == '_':
|
||||
return r
|
||||
case r >= 'a' && r <= 'z':
|
||||
return r - 32
|
||||
default:
|
||||
return rune('_')
|
||||
}
|
||||
}
|
||||
|
||||
func stringifyKey(key string) string {
|
||||
key = strings.Map(stringifyOp, key)
|
||||
key = strings.TrimPrefix(key, "_")
|
||||
return key
|
||||
}
|
||||
|
||||
// Journal wants strings but logrus takes anything.
|
||||
func stringifyEntries(data map[string]interface{}) map[string]string {
|
||||
entries := make(map[string]string)
|
||||
for k, v := range data {
|
||||
|
||||
key := stringifyKey(k)
|
||||
entries[key] = fmt.Sprint(v)
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
// Fire fires an entry into the journal.
|
||||
func (hook *JournalHook) Fire(entry *logrus.Entry) error {
|
||||
return journal.Send(entry.Message, severityMap[entry.Level], stringifyEntries(entry.Data))
|
||||
}
|
||||
|
||||
// Levels returns a slice of `Levels` the hook is fired for.
|
||||
func (hook *JournalHook) Levels() []logrus.Level {
|
||||
return []logrus.Level{
|
||||
logrus.PanicLevel,
|
||||
logrus.FatalLevel,
|
||||
logrus.ErrorLevel,
|
||||
logrus.WarnLevel,
|
||||
logrus.InfoLevel,
|
||||
logrus.DebugLevel,
|
||||
}
|
||||
}
|
||||
27
monitoring/journald/journalhook_linux_test.go
Normal file
27
monitoring/journald/journalhook_linux_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package journald
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestStringifyEntries(t *testing.T) {
|
||||
input := map[string]interface{}{
|
||||
"foo": "bar",
|
||||
"baz": 123,
|
||||
"foo-foo": "x",
|
||||
"-bar": "1",
|
||||
}
|
||||
|
||||
output := stringifyEntries(input)
|
||||
if output["FOO"] != "bar" {
|
||||
t.Fatalf("%v", output)
|
||||
t.Fatalf("expected value 'bar'. Got %q", output["FOO"])
|
||||
}
|
||||
if output["BAZ"] != "123" {
|
||||
t.Fatalf("expected value '123'. Got %q", output["BAZ"])
|
||||
}
|
||||
if output["FOO_FOO"] != "x" {
|
||||
t.Fatalf("expected value 'x'. Got %q", output["FOO_FOO"])
|
||||
}
|
||||
if output["BAR"] != "1" {
|
||||
t.Fatalf("expected value 'x'. Got %q", output["BAR"])
|
||||
}
|
||||
}
|
||||
90
proto/engine/v1/execution_engine.pb.go
generated
90
proto/engine/v1/execution_engine.pb.go
generated
@@ -1323,6 +1323,53 @@ func (x *Blob) GetData() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ExchangeCapabilities struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
SupportedMethods []string `protobuf:"bytes,1,rep,name=supported_methods,json=supportedMethods,proto3" json:"supported_methods,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ExchangeCapabilities) Reset() {
|
||||
*x = ExchangeCapabilities{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_engine_v1_execution_engine_proto_msgTypes[14]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ExchangeCapabilities) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ExchangeCapabilities) ProtoMessage() {}
|
||||
|
||||
func (x *ExchangeCapabilities) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_engine_v1_execution_engine_proto_msgTypes[14]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ExchangeCapabilities.ProtoReflect.Descriptor instead.
|
||||
func (*ExchangeCapabilities) Descriptor() ([]byte, []int) {
|
||||
return file_proto_engine_v1_execution_engine_proto_rawDescGZIP(), []int{14}
|
||||
}
|
||||
|
||||
func (x *ExchangeCapabilities) GetSupportedMethods() []string {
|
||||
if x != nil {
|
||||
return x.SupportedMethods
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_proto_engine_v1_execution_engine_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_proto_engine_v1_execution_engine_proto_rawDesc = []byte{
|
||||
@@ -1596,17 +1643,21 @@ var file_proto_engine_v1_execution_engine_proto_rawDesc = []byte{
|
||||
0x67, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x26, 0x0a, 0x04, 0x42, 0x6c,
|
||||
0x6f, 0x62, 0x12, 0x1e, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x42, 0x0a, 0x8a, 0xb5, 0x18, 0x06, 0x31, 0x33, 0x31, 0x30, 0x37, 0x32, 0x52, 0x04, 0x64, 0x61,
|
||||
0x74, 0x61, 0x42, 0x96, 0x01, 0x0a, 0x16, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72,
|
||||
0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x14, 0x45,
|
||||
0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x50, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
|
||||
0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f,
|
||||
0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x34, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65,
|
||||
0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x76,
|
||||
0x31, 0xaa, 0x02, 0x12, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x6e, 0x67,
|
||||
0x69, 0x6e, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x12, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75,
|
||||
0x6d, 0x5c, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5c, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x33,
|
||||
0x74, 0x61, 0x22, 0x43, 0x0a, 0x14, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x61,
|
||||
0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x75,
|
||||
0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x18,
|
||||
0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
|
||||
0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x42, 0x96, 0x01, 0x0a, 0x16, 0x6f, 0x72, 0x67, 0x2e,
|
||||
0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e,
|
||||
0x76, 0x31, 0x42, 0x14, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x67,
|
||||
0x69, 0x6e, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63,
|
||||
0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x34, 0x2f, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x65, 0x6e,
|
||||
0x67, 0x69, 0x6e, 0x65, 0x76, 0x31, 0xaa, 0x02, 0x12, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75,
|
||||
0x6d, 0x2e, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x12, 0x45, 0x74,
|
||||
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x5c, 0x76, 0x31,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -1622,7 +1673,7 @@ func file_proto_engine_v1_execution_engine_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_proto_engine_v1_execution_engine_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_proto_engine_v1_execution_engine_proto_msgTypes = make([]protoimpl.MessageInfo, 14)
|
||||
var file_proto_engine_v1_execution_engine_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
|
||||
var file_proto_engine_v1_execution_engine_proto_goTypes = []interface{}{
|
||||
(PayloadStatus_Status)(0), // 0: ethereum.engine.v1.PayloadStatus.Status
|
||||
(*ExecutionPayload)(nil), // 1: ethereum.engine.v1.ExecutionPayload
|
||||
@@ -1639,6 +1690,7 @@ var file_proto_engine_v1_execution_engine_proto_goTypes = []interface{}{
|
||||
(*Withdrawal)(nil), // 12: ethereum.engine.v1.Withdrawal
|
||||
(*BlobsBundle)(nil), // 13: ethereum.engine.v1.BlobsBundle
|
||||
(*Blob)(nil), // 14: ethereum.engine.v1.Blob
|
||||
(*ExchangeCapabilities)(nil), // 15: ethereum.engine.v1.ExchangeCapabilities
|
||||
}
|
||||
var file_proto_engine_v1_execution_engine_proto_depIdxs = []int32{
|
||||
12, // 0: ethereum.engine.v1.ExecutionPayloadBodyV1.withdrawals:type_name -> ethereum.engine.v1.Withdrawal
|
||||
@@ -1828,6 +1880,18 @@ func file_proto_engine_v1_execution_engine_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_engine_v1_execution_engine_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ExchangeCapabilities); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
@@ -1835,7 +1899,7 @@ func file_proto_engine_v1_execution_engine_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_proto_engine_v1_execution_engine_proto_rawDesc,
|
||||
NumEnums: 1,
|
||||
NumMessages: 14,
|
||||
NumMessages: 15,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -177,3 +177,7 @@ message Blob {
|
||||
// Each blob consists of `BLS_FIELD_ELEMENT`(32) multiplies `FIELD_ELEMENTS_PER_BLOB`(4096)
|
||||
bytes data = 1 [(ethereum.eth.ext.ssz_size) = "131072"]; // 32 * 4096 = 131072
|
||||
}
|
||||
|
||||
message ExchangeCapabilities {
|
||||
repeated string supported_methods = 1;
|
||||
}
|
||||
|
||||
@@ -6,23 +6,39 @@ go_library(
|
||||
"generate_genesis_state.go",
|
||||
"generate_genesis_state_bellatrix.go",
|
||||
"generate_keys.go",
|
||||
"genesis.go",
|
||||
"premine-state.go",
|
||||
"premined_genesis_state.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/v4/runtime/interop",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//async:go_default_library",
|
||||
"//beacon-chain/core/altair:go_default_library",
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/signing:go_default_library",
|
||||
"//beacon-chain/core/transition:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/state-native:go_default_library",
|
||||
"//beacon-chain/state/stateutil:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
"//container/trie:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//crypto/hash:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//proto/engine/v1:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"//time:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//core:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//params:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package testing
|
||||
package interop
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -67,6 +67,7 @@ var DefaultDepositContractStorage = map[string]string{
|
||||
var bigz = big.NewInt(0)
|
||||
var minerBalance = big.NewInt(0)
|
||||
|
||||
// DefaultCliqueSigner is the testnet miner (clique signer) address encoded in the special way EIP-225 requires.
|
||||
// EIP-225 assigns a special meaning to the `extra-data` field in the block header for clique chains.
|
||||
// In a clique chain, this field contains one secp256k1 "miner" signature. This allows other nodes to
|
||||
// verify that the block was signed by an authorized signer, in place of the typical PoW verification.
|
||||
@@ -76,14 +77,9 @@ var minerBalance = big.NewInt(0)
|
||||
// The following value is for the key used by the e2e test "miner" node.
|
||||
const DefaultCliqueSigner = "0x0000000000000000000000000000000000000000000000000000000000000000878705ba3f8bc32fcf7f4caa1a35e72af65cf7660000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
|
||||
// DefaultTestnetGenesis creates a genesis.json for eth1 clients with a set of defaults suitable for ephemeral testnets,
|
||||
// like in an e2e test. The parameters are minimal but the full value is returned unmarshaled so that it can be
|
||||
// customized as desired.
|
||||
func GethTestnetGenesis(genesisTime uint64, cfg *clparams.BeaconChainConfig) core.Genesis {
|
||||
ttd, ok := big.NewInt(0).SetString(clparams.BeaconConfig().TerminalTotalDifficulty, 10)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unable to parse TerminalTotalDifficulty as an integer = %s", clparams.BeaconConfig().TerminalTotalDifficulty))
|
||||
}
|
||||
// GethShanghaiTime calculates the absolute time of the shanghai (aka capella) fork block
|
||||
// by adding the relative time of the capella the fork epoch to the given genesis timestamp.
|
||||
func GethShanghaiTime(genesisTime uint64, cfg *clparams.BeaconChainConfig) *uint64 {
|
||||
var shanghaiTime *uint64
|
||||
if cfg.CapellaForkEpoch != math.MaxUint64 {
|
||||
startSlot, err := slots.EpochStart(cfg.CapellaForkEpoch)
|
||||
@@ -93,6 +89,19 @@ func GethTestnetGenesis(genesisTime uint64, cfg *clparams.BeaconChainConfig) cor
|
||||
shanghaiTime = &newTime
|
||||
}
|
||||
}
|
||||
return shanghaiTime
|
||||
}
|
||||
|
||||
// GethTestnetGenesis creates a genesis.json for eth1 clients with a set of defaults suitable for ephemeral testnets,
|
||||
// like in an e2e test. The parameters are minimal but the full value is returned unmarshaled so that it can be
|
||||
// customized as desired.
|
||||
func GethTestnetGenesis(genesisTime uint64, cfg *clparams.BeaconChainConfig) *core.Genesis {
|
||||
ttd, ok := big.NewInt(0).SetString(clparams.BeaconConfig().TerminalTotalDifficulty, 10)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unable to parse TerminalTotalDifficulty as an integer = %s", clparams.BeaconConfig().TerminalTotalDifficulty))
|
||||
}
|
||||
|
||||
shanghaiTime := GethShanghaiTime(genesisTime, cfg)
|
||||
cc := ¶ms.ChainConfig{
|
||||
ChainID: big.NewInt(defaultTestChainId),
|
||||
HomesteadBlock: bigz,
|
||||
@@ -124,7 +133,7 @@ func GethTestnetGenesis(genesisTime uint64, cfg *clparams.BeaconChainConfig) cor
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("unable to decode DefaultCliqueSigner, with error %v", err.Error()))
|
||||
}
|
||||
return core.Genesis{
|
||||
return &core.Genesis{
|
||||
Config: cc,
|
||||
Nonce: 0, // overridden for authorized signer votes in clique, so we should leave it empty?
|
||||
Timestamp: genesisTime,
|
||||
@@ -1,4 +1,4 @@
|
||||
package util
|
||||
package interop
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -19,32 +19,52 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/runtime/interop"
|
||||
"github.com/prysmaticlabs/prysm/v4/runtime/version"
|
||||
)
|
||||
|
||||
var errUnsupportedVersion = errors.New("schema version not supported by premineGenesisConfig")
|
||||
var errUnsupportedVersion = errors.New("schema version not supported by PremineGenesisConfig")
|
||||
|
||||
type premineGenesisConfig struct {
|
||||
type PremineGenesisConfig struct {
|
||||
GenesisTime uint64
|
||||
NVals uint64
|
||||
PregenesisCreds uint64
|
||||
Version int // as in "github.com/prysmaticlabs/prysm/v4/runtime/version"
|
||||
GB *types.Block // geth genesis block
|
||||
depositEntries *depositEntries
|
||||
}
|
||||
|
||||
type depositEntries struct {
|
||||
dds []*ethpb.Deposit_Data
|
||||
roots [][]byte
|
||||
}
|
||||
|
||||
type PremineGenesisOpt func(*PremineGenesisConfig)
|
||||
|
||||
func WithDepositData(dds []*ethpb.Deposit_Data, roots [][]byte) PremineGenesisOpt {
|
||||
return func(cfg *PremineGenesisConfig) {
|
||||
cfg.depositEntries = &depositEntries{
|
||||
dds: dds,
|
||||
roots: roots,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewPreminedGenesis creates a genesis BeaconState at the given fork version, suitable for using as an e2e genesis.
|
||||
func NewPreminedGenesis(ctx context.Context, t, nvals, pCreds uint64, version int, gb *types.Block) (state.BeaconState, error) {
|
||||
return (&premineGenesisConfig{
|
||||
func NewPreminedGenesis(ctx context.Context, t, nvals, pCreds uint64, version int, gb *types.Block, opts ...PremineGenesisOpt) (state.BeaconState, error) {
|
||||
cfg := &PremineGenesisConfig{
|
||||
GenesisTime: t,
|
||||
NVals: nvals,
|
||||
PregenesisCreds: pCreds,
|
||||
Version: version,
|
||||
GB: gb,
|
||||
}).prepare(ctx)
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(cfg)
|
||||
}
|
||||
return cfg.prepare(ctx)
|
||||
}
|
||||
|
||||
func (s *premineGenesisConfig) prepare(ctx context.Context) (state.BeaconState, error) {
|
||||
func (s *PremineGenesisConfig) prepare(ctx context.Context) (state.BeaconState, error) {
|
||||
switch s.Version {
|
||||
case version.Phase0, version.Altair, version.Bellatrix:
|
||||
default:
|
||||
@@ -65,7 +85,7 @@ func (s *premineGenesisConfig) prepare(ctx context.Context) (state.BeaconState,
|
||||
return st, nil
|
||||
}
|
||||
|
||||
func (s *premineGenesisConfig) empty() (state.BeaconState, error) {
|
||||
func (s *PremineGenesisConfig) empty() (state.BeaconState, error) {
|
||||
var e state.BeaconState
|
||||
var err error
|
||||
switch s.Version {
|
||||
@@ -129,7 +149,7 @@ func (s *premineGenesisConfig) empty() (state.BeaconState, error) {
|
||||
return e.Copy(), nil
|
||||
}
|
||||
|
||||
func (s *premineGenesisConfig) processDeposits(ctx context.Context, g state.BeaconState) error {
|
||||
func (s *PremineGenesisConfig) processDeposits(ctx context.Context, g state.BeaconState) error {
|
||||
deposits, err := s.deposits()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -147,35 +167,42 @@ func (s *premineGenesisConfig) processDeposits(ctx context.Context, g state.Beac
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *premineGenesisConfig) deposits() ([]*ethpb.Deposit, error) {
|
||||
prv, pub, err := s.keys()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (s *PremineGenesisConfig) deposits() ([]*ethpb.Deposit, error) {
|
||||
if s.depositEntries == nil {
|
||||
prv, pub, err := s.keys()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dds, roots, err := DepositDataFromKeysWithExecCreds(prv, pub, s.PregenesisCreds)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not generate deposit data from keys")
|
||||
}
|
||||
s.depositEntries = &depositEntries{
|
||||
dds: dds,
|
||||
roots: roots,
|
||||
}
|
||||
}
|
||||
items, roots, err := interop.DepositDataFromKeysWithExecCreds(prv, pub, s.PregenesisCreds)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not generate deposit data from keys")
|
||||
}
|
||||
t, err := trie.GenerateTrieFromItems(roots, params.BeaconConfig().DepositContractTreeDepth)
|
||||
|
||||
t, err := trie.GenerateTrieFromItems(s.depositEntries.roots, params.BeaconConfig().DepositContractTreeDepth)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not generate Merkle trie for deposit proofs")
|
||||
}
|
||||
deposits, err := interop.GenerateDepositsFromData(items, t)
|
||||
deposits, err := GenerateDepositsFromData(s.depositEntries.dds, t)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not generate deposits from the deposit data provided")
|
||||
}
|
||||
return deposits, nil
|
||||
}
|
||||
|
||||
func (s *premineGenesisConfig) keys() ([]bls.SecretKey, []bls.PublicKey, error) {
|
||||
prv, pub, err := interop.DeterministicallyGenerateKeys(0, s.NVals)
|
||||
func (s *PremineGenesisConfig) keys() ([]bls.SecretKey, []bls.PublicKey, error) {
|
||||
prv, pub, err := DeterministicallyGenerateKeys(0, s.NVals)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "could not deterministically generate keys for %d validators", s.NVals)
|
||||
}
|
||||
return prv, pub, nil
|
||||
}
|
||||
|
||||
func (s *premineGenesisConfig) setEth1Data(g state.BeaconState) error {
|
||||
func (s *PremineGenesisConfig) setEth1Data(g state.BeaconState) error {
|
||||
if err := g.SetEth1DepositIndex(0); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -194,7 +221,7 @@ func emptyDepositRoot() ([32]byte, error) {
|
||||
return t.HashTreeRoot()
|
||||
}
|
||||
|
||||
func (s *premineGenesisConfig) populate(g state.BeaconState) error {
|
||||
func (s *PremineGenesisConfig) populate(g state.BeaconState) error {
|
||||
if err := g.SetGenesisTime(s.GenesisTime); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -235,7 +262,7 @@ func (s *premineGenesisConfig) populate(g state.BeaconState) error {
|
||||
return s.setEth1Data(g)
|
||||
}
|
||||
|
||||
func (s *premineGenesisConfig) setGenesisValidatorsRoot(g state.BeaconState) error {
|
||||
func (s *PremineGenesisConfig) setGenesisValidatorsRoot(g state.BeaconState) error {
|
||||
vroot, err := stateutil.ValidatorRegistryRoot(g.Validators())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -243,7 +270,7 @@ func (s *premineGenesisConfig) setGenesisValidatorsRoot(g state.BeaconState) err
|
||||
return g.SetGenesisValidatorsRoot(vroot[:])
|
||||
}
|
||||
|
||||
func (s *premineGenesisConfig) setFork(g state.BeaconState) error {
|
||||
func (s *PremineGenesisConfig) setFork(g state.BeaconState) error {
|
||||
var pv, cv []byte
|
||||
switch s.Version {
|
||||
case version.Phase0:
|
||||
@@ -263,7 +290,7 @@ func (s *premineGenesisConfig) setFork(g state.BeaconState) error {
|
||||
return g.SetFork(fork)
|
||||
}
|
||||
|
||||
func (s *premineGenesisConfig) setInactivityScores(g state.BeaconState) error {
|
||||
func (s *PremineGenesisConfig) setInactivityScores(g state.BeaconState) error {
|
||||
if s.Version < version.Altair {
|
||||
return nil
|
||||
}
|
||||
@@ -281,7 +308,7 @@ func (s *premineGenesisConfig) setInactivityScores(g state.BeaconState) error {
|
||||
return g.SetInactivityScores(scores)
|
||||
}
|
||||
|
||||
func (s *premineGenesisConfig) setSyncCommittees(g state.BeaconState) error {
|
||||
func (s *PremineGenesisConfig) setSyncCommittees(g state.BeaconState) error {
|
||||
if s.Version < version.Altair {
|
||||
return nil
|
||||
}
|
||||
@@ -299,7 +326,7 @@ type rooter interface {
|
||||
HashTreeRoot() ([32]byte, error)
|
||||
}
|
||||
|
||||
func (s *premineGenesisConfig) setLatestBlockHeader(g state.BeaconState) error {
|
||||
func (s *PremineGenesisConfig) setLatestBlockHeader(g state.BeaconState) error {
|
||||
var body rooter
|
||||
switch s.Version {
|
||||
case version.Phase0:
|
||||
@@ -364,7 +391,7 @@ func (s *premineGenesisConfig) setLatestBlockHeader(g state.BeaconState) error {
|
||||
return g.SetLatestBlockHeader(lbh)
|
||||
}
|
||||
|
||||
func (s *premineGenesisConfig) setExecutionPayload(g state.BeaconState) error {
|
||||
func (s *PremineGenesisConfig) setExecutionPayload(g state.BeaconState) error {
|
||||
if s.Version < version.Bellatrix {
|
||||
return nil
|
||||
}
|
||||
@@ -16,6 +16,7 @@ go_library(
|
||||
"gitTag": "{STABLE_GIT_TAG}",
|
||||
},
|
||||
deps = [
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
||||
],
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package version
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
const (
|
||||
Phase0 = iota
|
||||
Altair
|
||||
@@ -7,22 +9,50 @@ const (
|
||||
Capella
|
||||
)
|
||||
|
||||
var versionToString = map[int]string{
|
||||
Phase0: "phase0",
|
||||
Altair: "altair",
|
||||
Bellatrix: "bellatrix",
|
||||
Capella: "capella",
|
||||
}
|
||||
|
||||
// stringToVersion and allVersions are populated in init()
|
||||
var stringToVersion = map[string]int{}
|
||||
var allVersions = []int{}
|
||||
|
||||
// ErrUnrecognizedVersionName means a string does not match the list of canonical version names.
|
||||
var ErrUnrecognizedVersionName = errors.New("version name doesn't map to a known value in the enum")
|
||||
|
||||
// FromString translates a canonical version name to the version number.
|
||||
func FromString(name string) (int, error) {
|
||||
v, ok := stringToVersion[name]
|
||||
if !ok {
|
||||
return 0, errors.Wrap(ErrUnrecognizedVersionName, name)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// String returns the canonical string form of a version.
|
||||
// Unrecognized versions won't generate an error and are represented by the string "unknown version".
|
||||
func String(version int) string {
|
||||
switch version {
|
||||
case Phase0:
|
||||
return "phase0"
|
||||
case Altair:
|
||||
return "altair"
|
||||
case Bellatrix:
|
||||
return "bellatrix"
|
||||
case Capella:
|
||||
return "capella"
|
||||
default:
|
||||
name, ok := versionToString[version]
|
||||
if !ok {
|
||||
return "unknown version"
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// All returns a list of all known fork versions.
|
||||
func All() []int {
|
||||
return []int{Phase0, Altair, Bellatrix, Capella}
|
||||
return allVersions
|
||||
}
|
||||
|
||||
func init() {
|
||||
allVersions = make([]int, len(versionToString))
|
||||
i := 0
|
||||
for v, s := range versionToString {
|
||||
allVersions[i] = v
|
||||
stringToVersion[s] = v
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ go_library(
|
||||
"//testing/endtoend/helpers:go_default_library",
|
||||
"//testing/endtoend/params:go_default_library",
|
||||
"//testing/endtoend/types:go_default_library",
|
||||
"//testing/util:go_default_library",
|
||||
"//validator/keymanager:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
|
||||
@@ -21,10 +21,10 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v4/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/io/file"
|
||||
"github.com/prysmaticlabs/prysm/v4/runtime/interop"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/endtoend/helpers"
|
||||
e2e "github.com/prysmaticlabs/prysm/v4/testing/endtoend/params"
|
||||
e2etypes "github.com/prysmaticlabs/prysm/v4/testing/endtoend/types"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/util"
|
||||
)
|
||||
|
||||
var _ e2etypes.ComponentRunner = (*BeaconNode)(nil)
|
||||
@@ -353,5 +353,5 @@ func generateGenesis(ctx context.Context) (state.BeaconState, error) {
|
||||
pcreds := e2e.TestParams.NumberOfExecutionCreds
|
||||
nvals := params.BeaconConfig().MinGenesisActiveValidatorCount
|
||||
version := e2etypes.GenesisFork()
|
||||
return util.NewPreminedGenesis(ctx, t, nvals, pcreds, version, gb)
|
||||
return interop.NewPreminedGenesis(ctx, t, nvals, pcreds, version, gb)
|
||||
}
|
||||
|
||||
@@ -15,13 +15,13 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/v4/testing/endtoend/components/eth1",
|
||||
visibility = ["//testing/endtoend:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/execution/testing:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//contracts/deposit:go_default_library",
|
||||
"//crypto/rand:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//io/file:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//runtime/interop:go_default_library",
|
||||
"//testing/endtoend/helpers:go_default_library",
|
||||
"//testing/endtoend/params:go_default_library",
|
||||
"//testing/endtoend/types:go_default_library",
|
||||
|
||||
@@ -16,10 +16,10 @@ import (
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/execution/testing"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
contracts "github.com/prysmaticlabs/prysm/v4/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/v4/io/file"
|
||||
"github.com/prysmaticlabs/prysm/v4/runtime/interop"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/endtoend/helpers"
|
||||
e2e "github.com/prysmaticlabs/prysm/v4/testing/endtoend/params"
|
||||
e2etypes "github.com/prysmaticlabs/prysm/v4/testing/endtoend/types"
|
||||
@@ -88,7 +88,7 @@ func (m *Miner) initAttempt(ctx context.Context, attempt int) (*os.File, error)
|
||||
}
|
||||
|
||||
gethJsonPath := path.Join(path.Dir(binaryPath), "genesis.json")
|
||||
gen := testing.GethTestnetGenesis(e2e.TestParams.Eth1GenesisTime, params.BeaconConfig())
|
||||
gen := interop.GethTestnetGenesis(e2e.TestParams.Eth1GenesisTime, params.BeaconConfig())
|
||||
log.Infof("eth1 miner genesis timestamp=%d", e2e.TestParams.Eth1GenesisTime)
|
||||
b, err := json.Marshal(gen)
|
||||
if err != nil {
|
||||
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
|
||||
"github.com/bazelbuild/rules_go/go/tools/bazel"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/execution/testing"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/io/file"
|
||||
"github.com/prysmaticlabs/prysm/v4/runtime/interop"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/endtoend/helpers"
|
||||
e2e "github.com/prysmaticlabs/prysm/v4/testing/endtoend/params"
|
||||
e2etypes "github.com/prysmaticlabs/prysm/v4/testing/endtoend/types"
|
||||
@@ -61,7 +61,7 @@ func (node *Node) Start(ctx context.Context) error {
|
||||
}
|
||||
gethJsonPath := path.Join(eth1Path, "genesis.json")
|
||||
|
||||
gen := testing.GethTestnetGenesis(e2e.TestParams.Eth1GenesisTime, params.BeaconConfig())
|
||||
gen := interop.GethTestnetGenesis(e2e.TestParams.Eth1GenesisTime, params.BeaconConfig())
|
||||
b, err := json.Marshal(gen)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -15,7 +15,6 @@ go_library(
|
||||
"deposits.go",
|
||||
"helpers.go",
|
||||
"merge.go",
|
||||
"premine-state.go",
|
||||
"state.go",
|
||||
"sync_aggregate.go",
|
||||
"sync_committee.go",
|
||||
@@ -55,7 +54,6 @@ go_library(
|
||||
"//testing/require:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
|
||||
@@ -40,6 +40,7 @@ go_library(
|
||||
"//validator/accounts/userprompt:go_default_library",
|
||||
"//validator/accounts/wallet:go_default_library",
|
||||
"//validator/client:go_default_library",
|
||||
"//validator/client/beacon-api:go_default_library",
|
||||
"//validator/client/iface:go_default_library",
|
||||
"//validator/client/node-client-factory:go_default_library",
|
||||
"//validator/client/validator-client-factory:go_default_library",
|
||||
@@ -72,12 +73,15 @@ go_test(
|
||||
data = glob(["testdata/**"]),
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/rpc/apimiddleware:go_default_library",
|
||||
"//build/bazel:go_default_library",
|
||||
"//cmd/validator/flags:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//io/file:go_default_library",
|
||||
"//proto/eth/service:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
|
||||
@@ -3,7 +3,9 @@ package accounts
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
@@ -12,7 +14,10 @@ import (
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v4/io/file"
|
||||
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/client"
|
||||
beacon_api "github.com/prysmaticlabs/prysm/v4/validator/client/beacon-api"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/client/iface"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/keymanager"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
@@ -25,6 +30,7 @@ type PerformExitCfg struct {
|
||||
Keymanager keymanager.IKeymanager
|
||||
RawPubKeys [][]byte
|
||||
FormattedPubKeys []string
|
||||
OutputDirectory string
|
||||
}
|
||||
|
||||
// ExitPassphrase exported for use in test.
|
||||
@@ -62,6 +68,7 @@ func (acm *AccountsCLIManager) Exit(ctx context.Context) error {
|
||||
acm.keymanager,
|
||||
acm.rawPubKeys,
|
||||
acm.formattedPubKeys,
|
||||
acm.exitJSONOutputPath,
|
||||
}
|
||||
rawExitedKeys, trimmedExitedKeys, err := PerformVoluntaryExit(ctx, cfg)
|
||||
if err != nil {
|
||||
@@ -78,7 +85,23 @@ func PerformVoluntaryExit(
|
||||
) (rawExitedKeys [][]byte, formattedExitedKeys []string, err error) {
|
||||
var rawNotExitedKeys [][]byte
|
||||
for i, key := range cfg.RawPubKeys {
|
||||
if err := client.ProposeExit(ctx, cfg.ValidatorClient, cfg.NodeClient, cfg.Keymanager.Sign, key); err != nil {
|
||||
// When output directory is present, only create the signed exit, but do not propose it.
|
||||
// Otherwise, propose the exit immediately.
|
||||
if len(cfg.OutputDirectory) > 0 {
|
||||
sve, err := client.CreateSignedVoluntaryExit(ctx, cfg.ValidatorClient, cfg.NodeClient, cfg.Keymanager.Sign, key)
|
||||
if err != nil {
|
||||
rawNotExitedKeys = append(rawNotExitedKeys, key)
|
||||
msg := err.Error()
|
||||
if strings.Contains(msg, blocks.ValidatorAlreadyExitedMsg) ||
|
||||
strings.Contains(msg, blocks.ValidatorCannotExitYetMsg) {
|
||||
log.Warningf("Could not create voluntary exit for account %s: %s", cfg.FormattedPubKeys[i], msg)
|
||||
} else {
|
||||
log.WithError(err).Errorf("voluntary exit failed for account %s", cfg.FormattedPubKeys[i])
|
||||
}
|
||||
} else if err := writeSignedVoluntaryExitJSON(ctx, sve, cfg.OutputDirectory); err != nil {
|
||||
log.WithError(err).Error("failed to write voluntary exit")
|
||||
}
|
||||
} else if err := client.ProposeExit(ctx, cfg.ValidatorClient, cfg.NodeClient, cfg.Keymanager.Sign, key); err != nil {
|
||||
rawNotExitedKeys = append(rawNotExitedKeys, key)
|
||||
|
||||
msg := err.Error()
|
||||
@@ -148,3 +171,24 @@ func displayExitInfo(rawExitedKeys [][]byte, trimmedExitedKeys []string) {
|
||||
log.Info("No successful voluntary exits")
|
||||
}
|
||||
}
|
||||
|
||||
func writeSignedVoluntaryExitJSON(ctx context.Context, sve *eth.SignedVoluntaryExit, outputDirectory string) error {
|
||||
if err := file.MkdirAll(outputDirectory); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsve := beacon_api.JsonifySignedVoluntaryExits([]*eth.SignedVoluntaryExit{sve})[0]
|
||||
b, err := json.Marshal(jsve)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to marshal JSON signed voluntary exit")
|
||||
}
|
||||
|
||||
filepath := path.Join(outputDirectory, fmt.Sprintf("validator-exit-%s.json", jsve.Exit.ValidatorIndex))
|
||||
if err := file.WriteFile(filepath, b); err != nil {
|
||||
return errors.Wrap(err, "failed to write validator exist json")
|
||||
}
|
||||
|
||||
log.Infof("Wrote signed validator exit JSON to %s", filepath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/v4/build/bazel"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v4/io/file"
|
||||
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
||||
"github.com/sirupsen/logrus/hooks/test"
|
||||
@@ -34,3 +42,26 @@ func TestPrepareAllKeys(t *testing.T) {
|
||||
assert.Equal(t, "0x6b6579310000", formatted[0])
|
||||
assert.Equal(t, "0x6b6579320000", formatted[1])
|
||||
}
|
||||
|
||||
func TestWriteSignedVoluntaryExitJSON(t *testing.T) {
|
||||
sve := ð.SignedVoluntaryExit{
|
||||
Exit: ð.VoluntaryExit{
|
||||
Epoch: 5,
|
||||
ValidatorIndex: 300,
|
||||
},
|
||||
Signature: []byte{0x01, 0x02},
|
||||
}
|
||||
|
||||
output := path.Join(bazel.TestTmpDir(), "TestWriteSignedVoluntaryExitJSON")
|
||||
require.NoError(t, writeSignedVoluntaryExitJSON(context.Background(), sve, output))
|
||||
|
||||
b, err := file.ReadFileAsBytes(path.Join(output, "validator-exit-300.json"))
|
||||
require.NoError(t, err)
|
||||
|
||||
svej := &apimiddleware.SignedVoluntaryExitJson{}
|
||||
require.NoError(t, json.Unmarshal(b, svej))
|
||||
|
||||
require.Equal(t, fmt.Sprintf("%d", sve.Exit.Epoch), svej.Exit.Epoch)
|
||||
require.Equal(t, fmt.Sprintf("%d", sve.Exit.ValidatorIndex), svej.Exit.ValidatorIndex)
|
||||
require.Equal(t, "0x0102", svej.Signature)
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ type AccountsCLIManager struct {
|
||||
filteredPubKeys []bls.PublicKey
|
||||
rawPubKeys [][]byte
|
||||
formattedPubKeys []string
|
||||
exitJSONOutputPath string
|
||||
walletDir string
|
||||
walletPassword string
|
||||
mnemonic string
|
||||
|
||||
@@ -205,6 +205,13 @@ func WithFormattedPubKeys(formattedPubKeys []string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func WithExitJSONOutputPath(outputPath string) Option {
|
||||
return func(acc *AccountsCLIManager) error {
|
||||
acc.exitJSONOutputPath = outputPath
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithWalletDir specifies the password for backups.
|
||||
func WithWalletDir(walletDir string) Option {
|
||||
return func(acc *AccountsCLIManager) error {
|
||||
|
||||
@@ -25,6 +25,7 @@ type Wallet struct {
|
||||
WalletPassword string
|
||||
UnlockAccounts bool
|
||||
lock sync.RWMutex
|
||||
HasWriteFileError bool
|
||||
}
|
||||
|
||||
// AccountNames --
|
||||
@@ -57,6 +58,11 @@ func (w *Wallet) Password() string {
|
||||
func (w *Wallet) WriteFileAtPath(_ context.Context, pathName, fileName string, data []byte) error {
|
||||
w.lock.Lock()
|
||||
defer w.lock.Unlock()
|
||||
if w.HasWriteFileError {
|
||||
// reset the flag to not contaminate other tests
|
||||
w.HasWriteFileError = false
|
||||
return errors.New("could not write keystore file for accounts")
|
||||
}
|
||||
if w.Files[pathName] == nil {
|
||||
w.Files[pathName] = make(map[string][]byte)
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ func OpenWalletOrElseCli(cliCtx *cli.Context, otherwise func(cliCtx *cli.Context
|
||||
|
||||
// NewWalletForWeb3Signer returns a new wallet for web3 signer which is temporary and not stored locally.
|
||||
func NewWalletForWeb3Signer() *Wallet {
|
||||
// wallet is just a temporary wallet for web3 signer used to call intialize keymanager.
|
||||
// wallet is just a temporary wallet for web3 signer used to call initialize keymanager.
|
||||
return &Wallet{
|
||||
walletDir: "",
|
||||
accountsPath: "",
|
||||
|
||||
@@ -162,6 +162,7 @@ go_test(
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
"@com_github_stretchr_testify//mock:go_default_library",
|
||||
"@com_github_tyler_smith_go_bip39//:go_default_library",
|
||||
"@com_github_wealdtech_go_eth2_util//:go_default_library",
|
||||
"@in_gopkg_d4l3k_messagediff_v1//:go_default_library",
|
||||
|
||||
@@ -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,25 +3,144 @@ 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) {
|
||||
if c.fallbackClient != nil {
|
||||
return c.fallbackClient.GetChainHead(ctx, in)
|
||||
func (c beaconApiBeaconChainClient) getHeadBlockHeaders(ctx context.Context) (*apimiddleware.BlockHeaderResponseJson, error) {
|
||||
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")
|
||||
}
|
||||
|
||||
// TODO: Implement me
|
||||
panic("beaconApiBeaconChainClient.GetChainHead is not implemented. To use a fallback client, pass a fallback client as the last argument of NewBeaconApiBeaconChainClientWithFallback.")
|
||||
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")
|
||||
}
|
||||
|
||||
return &blockHeader, nil
|
||||
}
|
||||
|
||||
func (c beaconApiBeaconChainClient) GetChainHead(ctx context.Context, _ *empty.Empty) (*ethpb.ChainHead, error) {
|
||||
const endpoint = "/eth/v1/beacon/states/head/finality_checkpoints"
|
||||
|
||||
finalityCheckpoints := apimiddleware.StateFinalityCheckpointResponseJson{}
|
||||
if _, err := c.jsonRestHandler.GetRestJsonResponse(ctx, endpoint, &finalityCheckpoints); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to query %s", endpoint)
|
||||
}
|
||||
|
||||
if finalityCheckpoints.Data == nil {
|
||||
return nil, errors.New("finality checkpoints data is nil")
|
||||
}
|
||||
|
||||
if finalityCheckpoints.Data.Finalized == nil {
|
||||
return nil, errors.New("finalized checkpoint is nil")
|
||||
}
|
||||
|
||||
finalizedEpoch, err := strconv.ParseUint(finalityCheckpoints.Data.Finalized.Epoch, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse finalized epoch `%s`", finalityCheckpoints.Data.Finalized.Epoch)
|
||||
}
|
||||
|
||||
finalizedSlot, err := slots.EpochStart(primitives.Epoch(finalizedEpoch))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get first slot for epoch `%d`", finalizedEpoch)
|
||||
}
|
||||
|
||||
finalizedRoot, err := hexutil.Decode(finalityCheckpoints.Data.Finalized.Root)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode finalized checkpoint root `%s`", finalityCheckpoints.Data.Finalized.Root)
|
||||
}
|
||||
|
||||
if finalityCheckpoints.Data.CurrentJustified == nil {
|
||||
return nil, errors.New("current justified checkpoint is nil")
|
||||
}
|
||||
|
||||
justifiedEpoch, err := strconv.ParseUint(finalityCheckpoints.Data.CurrentJustified.Epoch, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse current justified checkpoint epoch `%s`", finalityCheckpoints.Data.CurrentJustified.Epoch)
|
||||
}
|
||||
|
||||
justifiedSlot, err := slots.EpochStart(primitives.Epoch(justifiedEpoch))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get first slot for epoch `%d`", justifiedEpoch)
|
||||
}
|
||||
|
||||
justifiedRoot, err := hexutil.Decode(finalityCheckpoints.Data.CurrentJustified.Root)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode current justified checkpoint root `%s`", finalityCheckpoints.Data.CurrentJustified.Root)
|
||||
}
|
||||
|
||||
if finalityCheckpoints.Data.PreviousJustified == nil {
|
||||
return nil, errors.New("previous justified checkpoint is nil")
|
||||
}
|
||||
|
||||
previousJustifiedEpoch, err := strconv.ParseUint(finalityCheckpoints.Data.PreviousJustified.Epoch, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse previous justified checkpoint epoch `%s`", finalityCheckpoints.Data.PreviousJustified.Epoch)
|
||||
}
|
||||
|
||||
previousJustifiedSlot, err := slots.EpochStart(primitives.Epoch(previousJustifiedEpoch))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get first slot for epoch `%d`", previousJustifiedEpoch)
|
||||
}
|
||||
|
||||
previousJustifiedRoot, err := hexutil.Decode(finalityCheckpoints.Data.PreviousJustified.Root)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode previous justified checkpoint root `%s`", finalityCheckpoints.Data.PreviousJustified.Root)
|
||||
}
|
||||
|
||||
blockHeader, err := c.getHeadBlockHeaders(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get head block headers")
|
||||
}
|
||||
|
||||
headSlot, err := strconv.ParseUint(blockHeader.Data.Header.Message.Slot, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse head block slot `%s`", blockHeader.Data.Header.Message.Slot)
|
||||
}
|
||||
|
||||
headEpoch := slots.ToEpoch(primitives.Slot(headSlot))
|
||||
|
||||
headBlockRoot, err := hexutil.Decode(blockHeader.Data.Root)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode head block root `%s`", blockHeader.Data.Root)
|
||||
}
|
||||
|
||||
return ðpb.ChainHead{
|
||||
HeadSlot: primitives.Slot(headSlot),
|
||||
HeadEpoch: headEpoch,
|
||||
HeadBlockRoot: headBlockRoot,
|
||||
FinalizedSlot: finalizedSlot,
|
||||
FinalizedEpoch: primitives.Epoch(finalizedEpoch),
|
||||
FinalizedBlockRoot: finalizedRoot,
|
||||
JustifiedSlot: justifiedSlot,
|
||||
JustifiedEpoch: primitives.Epoch(justifiedEpoch),
|
||||
JustifiedBlockRoot: justifiedRoot,
|
||||
PreviousJustifiedSlot: previousJustifiedSlot,
|
||||
PreviousJustifiedEpoch: primitives.Epoch(previousJustifiedEpoch),
|
||||
PreviousJustifiedBlockRoot: previousJustifiedRoot,
|
||||
OptimisticStatus: blockHeader.ExecutionOptimistic,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c beaconApiBeaconChainClient) ListValidatorBalances(ctx context.Context, in *ethpb.ListValidatorBalancesRequest) (*ethpb.ValidatorBalances, error) {
|
||||
@@ -34,12 +153,158 @@ 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, err := c.getHeadBlockHeaders(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get head block headers")
|
||||
}
|
||||
|
||||
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 +341,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,927 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"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/time/slots"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/client/beacon-api/mock"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetChainHead(t *testing.T) {
|
||||
const finalityCheckpointsEndpoint = "/eth/v1/beacon/states/head/finality_checkpoints"
|
||||
const headBlockHeadersEndpoint = "/eth/v1/beacon/headers/head"
|
||||
|
||||
generateValidFinalityCheckpointsResponse := func() apimiddleware.StateFinalityCheckpointResponseJson {
|
||||
return apimiddleware.StateFinalityCheckpointResponseJson{
|
||||
Data: &apimiddleware.StateFinalityCheckpointResponse_StateFinalityCheckpointJson{
|
||||
PreviousJustified: &apimiddleware.CheckpointJson{
|
||||
Epoch: "1",
|
||||
Root: hexutil.Encode([]byte{2}),
|
||||
},
|
||||
CurrentJustified: &apimiddleware.CheckpointJson{
|
||||
Epoch: "3",
|
||||
Root: hexutil.Encode([]byte{4}),
|
||||
},
|
||||
Finalized: &apimiddleware.CheckpointJson{
|
||||
Epoch: "5",
|
||||
Root: hexutil.Encode([]byte{6}),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("fails to get finality checkpoints", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
generateFinalityCheckpointsResponse func() apimiddleware.StateFinalityCheckpointResponseJson
|
||||
finalityCheckpointsError error
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "query failed",
|
||||
finalityCheckpointsError: errors.New("foo error"),
|
||||
expectedError: fmt.Sprintf("failed to query %s: foo error", finalityCheckpointsEndpoint),
|
||||
generateFinalityCheckpointsResponse: func() apimiddleware.StateFinalityCheckpointResponseJson {
|
||||
return apimiddleware.StateFinalityCheckpointResponseJson{}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil finality checkpoints data",
|
||||
expectedError: "finality checkpoints data is nil",
|
||||
generateFinalityCheckpointsResponse: func() apimiddleware.StateFinalityCheckpointResponseJson {
|
||||
validResponse := generateValidFinalityCheckpointsResponse()
|
||||
validResponse.Data = nil
|
||||
return validResponse
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil finalized checkpoint",
|
||||
expectedError: "finalized checkpoint is nil",
|
||||
generateFinalityCheckpointsResponse: func() apimiddleware.StateFinalityCheckpointResponseJson {
|
||||
validResponse := generateValidFinalityCheckpointsResponse()
|
||||
validResponse.Data.Finalized = nil
|
||||
return validResponse
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid finalized epoch",
|
||||
expectedError: "failed to parse finalized epoch `foo`",
|
||||
generateFinalityCheckpointsResponse: func() apimiddleware.StateFinalityCheckpointResponseJson {
|
||||
validResponse := generateValidFinalityCheckpointsResponse()
|
||||
validResponse.Data.Finalized.Epoch = "foo"
|
||||
return validResponse
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "failed to get first slot of finalized epoch",
|
||||
expectedError: fmt.Sprintf("failed to get first slot for epoch `%d`", uint64(math.MaxUint64)),
|
||||
generateFinalityCheckpointsResponse: func() apimiddleware.StateFinalityCheckpointResponseJson {
|
||||
validResponse := generateValidFinalityCheckpointsResponse()
|
||||
validResponse.Data.Finalized.Epoch = strconv.FormatUint(uint64(math.MaxUint64), 10)
|
||||
return validResponse
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid finalized root",
|
||||
expectedError: "failed to decode finalized checkpoint root `bar`",
|
||||
generateFinalityCheckpointsResponse: func() apimiddleware.StateFinalityCheckpointResponseJson {
|
||||
validResponse := generateValidFinalityCheckpointsResponse()
|
||||
validResponse.Data.Finalized.Root = "bar"
|
||||
return validResponse
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil current justified checkpoint",
|
||||
expectedError: "current justified checkpoint is nil",
|
||||
generateFinalityCheckpointsResponse: func() apimiddleware.StateFinalityCheckpointResponseJson {
|
||||
validResponse := generateValidFinalityCheckpointsResponse()
|
||||
validResponse.Data.CurrentJustified = nil
|
||||
return validResponse
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil current justified epoch",
|
||||
expectedError: "failed to parse current justified checkpoint epoch `foo`",
|
||||
generateFinalityCheckpointsResponse: func() apimiddleware.StateFinalityCheckpointResponseJson {
|
||||
validResponse := generateValidFinalityCheckpointsResponse()
|
||||
validResponse.Data.CurrentJustified.Epoch = "foo"
|
||||
return validResponse
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "failed to get first slot of current justified epoch",
|
||||
expectedError: fmt.Sprintf("failed to get first slot for epoch `%d`", uint64(math.MaxUint64)),
|
||||
generateFinalityCheckpointsResponse: func() apimiddleware.StateFinalityCheckpointResponseJson {
|
||||
validResponse := generateValidFinalityCheckpointsResponse()
|
||||
validResponse.Data.CurrentJustified.Epoch = strconv.FormatUint(uint64(math.MaxUint64), 10)
|
||||
return validResponse
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid current justified root",
|
||||
expectedError: "failed to decode current justified checkpoint root `bar`",
|
||||
generateFinalityCheckpointsResponse: func() apimiddleware.StateFinalityCheckpointResponseJson {
|
||||
validResponse := generateValidFinalityCheckpointsResponse()
|
||||
validResponse.Data.CurrentJustified.Root = "bar"
|
||||
return validResponse
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil previous justified checkpoint",
|
||||
expectedError: "previous justified checkpoint is nil",
|
||||
generateFinalityCheckpointsResponse: func() apimiddleware.StateFinalityCheckpointResponseJson {
|
||||
validResponse := generateValidFinalityCheckpointsResponse()
|
||||
validResponse.Data.PreviousJustified = nil
|
||||
return validResponse
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil previous justified epoch",
|
||||
expectedError: "failed to parse previous justified checkpoint epoch `foo`",
|
||||
generateFinalityCheckpointsResponse: func() apimiddleware.StateFinalityCheckpointResponseJson {
|
||||
validResponse := generateValidFinalityCheckpointsResponse()
|
||||
validResponse.Data.PreviousJustified.Epoch = "foo"
|
||||
return validResponse
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "failed to get first slot of previous justified epoch",
|
||||
expectedError: fmt.Sprintf("failed to get first slot for epoch `%d`", uint64(math.MaxUint64)),
|
||||
generateFinalityCheckpointsResponse: func() apimiddleware.StateFinalityCheckpointResponseJson {
|
||||
validResponse := generateValidFinalityCheckpointsResponse()
|
||||
validResponse.Data.PreviousJustified.Epoch = strconv.FormatUint(uint64(math.MaxUint64), 10)
|
||||
return validResponse
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid previous justified root",
|
||||
expectedError: "failed to decode previous justified checkpoint root `bar`",
|
||||
generateFinalityCheckpointsResponse: func() apimiddleware.StateFinalityCheckpointResponseJson {
|
||||
validResponse := generateValidFinalityCheckpointsResponse()
|
||||
validResponse.Data.PreviousJustified.Root = "bar"
|
||||
return validResponse
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
ctx := context.Background()
|
||||
|
||||
finalityCheckpointsResponse := apimiddleware.StateFinalityCheckpointResponseJson{}
|
||||
jsonRestHandler := mock.NewMockjsonRestHandler(ctrl)
|
||||
jsonRestHandler.EXPECT().GetRestJsonResponse(ctx, finalityCheckpointsEndpoint, &finalityCheckpointsResponse).Return(
|
||||
nil,
|
||||
testCase.finalityCheckpointsError,
|
||||
).SetArg(
|
||||
2,
|
||||
testCase.generateFinalityCheckpointsResponse(),
|
||||
)
|
||||
|
||||
beaconChainClient := beaconApiBeaconChainClient{jsonRestHandler: jsonRestHandler}
|
||||
_, err := beaconChainClient.GetChainHead(ctx, &emptypb.Empty{})
|
||||
assert.ErrorContains(t, testCase.expectedError, err)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
generateValidBlockHeadersResponse := func() apimiddleware.BlockHeaderResponseJson {
|
||||
return apimiddleware.BlockHeaderResponseJson{
|
||||
Data: &apimiddleware.BlockHeaderContainerJson{
|
||||
Root: hexutil.Encode([]byte{7}),
|
||||
Header: &apimiddleware.BeaconBlockHeaderContainerJson{
|
||||
Message: &apimiddleware.BeaconBlockHeaderJson{
|
||||
Slot: "8",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("fails to get head block headers", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
generateHeadBlockHeadersResponse func() apimiddleware.BlockHeaderResponseJson
|
||||
headBlockHeadersError error
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "query failed",
|
||||
headBlockHeadersError: errors.New("foo error"),
|
||||
expectedError: "failed to get head block header",
|
||||
generateHeadBlockHeadersResponse: func() apimiddleware.BlockHeaderResponseJson {
|
||||
return apimiddleware.BlockHeaderResponseJson{}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil block header data",
|
||||
expectedError: "block header data is nil",
|
||||
generateHeadBlockHeadersResponse: func() apimiddleware.BlockHeaderResponseJson {
|
||||
validResponse := generateValidBlockHeadersResponse()
|
||||
validResponse.Data = nil
|
||||
return validResponse
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil block header data header",
|
||||
expectedError: "block header data is nil",
|
||||
generateHeadBlockHeadersResponse: func() apimiddleware.BlockHeaderResponseJson {
|
||||
validResponse := generateValidBlockHeadersResponse()
|
||||
validResponse.Data.Header = nil
|
||||
return validResponse
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil block header message",
|
||||
expectedError: "block header message is nil",
|
||||
generateHeadBlockHeadersResponse: func() apimiddleware.BlockHeaderResponseJson {
|
||||
validResponse := generateValidBlockHeadersResponse()
|
||||
validResponse.Data.Header.Message = nil
|
||||
return validResponse
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid message slot",
|
||||
expectedError: "failed to parse head block slot `foo`",
|
||||
generateHeadBlockHeadersResponse: func() apimiddleware.BlockHeaderResponseJson {
|
||||
validResponse := generateValidBlockHeadersResponse()
|
||||
validResponse.Data.Header.Message.Slot = "foo"
|
||||
return validResponse
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "invalid root",
|
||||
expectedError: "failed to decode head block root `bar`",
|
||||
generateHeadBlockHeadersResponse: func() apimiddleware.BlockHeaderResponseJson {
|
||||
validResponse := generateValidBlockHeadersResponse()
|
||||
validResponse.Data.Root = "bar"
|
||||
return validResponse
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
ctx := context.Background()
|
||||
|
||||
jsonRestHandler := mock.NewMockjsonRestHandler(ctrl)
|
||||
|
||||
finalityCheckpointsResponse := apimiddleware.StateFinalityCheckpointResponseJson{}
|
||||
jsonRestHandler.EXPECT().GetRestJsonResponse(ctx, finalityCheckpointsEndpoint, &finalityCheckpointsResponse).Return(
|
||||
nil,
|
||||
nil,
|
||||
).SetArg(
|
||||
2,
|
||||
generateValidFinalityCheckpointsResponse(),
|
||||
)
|
||||
|
||||
headBlockHeadersResponse := apimiddleware.BlockHeaderResponseJson{}
|
||||
jsonRestHandler.EXPECT().GetRestJsonResponse(ctx, headBlockHeadersEndpoint, &headBlockHeadersResponse).Return(
|
||||
nil,
|
||||
testCase.headBlockHeadersError,
|
||||
).SetArg(
|
||||
2,
|
||||
testCase.generateHeadBlockHeadersResponse(),
|
||||
)
|
||||
|
||||
beaconChainClient := beaconApiBeaconChainClient{jsonRestHandler: jsonRestHandler}
|
||||
_, err := beaconChainClient.GetChainHead(ctx, &emptypb.Empty{})
|
||||
assert.ErrorContains(t, testCase.expectedError, err)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("returns a valid chain head", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
ctx := context.Background()
|
||||
|
||||
jsonRestHandler := mock.NewMockjsonRestHandler(ctrl)
|
||||
|
||||
finalityCheckpointsResponse := apimiddleware.StateFinalityCheckpointResponseJson{}
|
||||
jsonRestHandler.EXPECT().GetRestJsonResponse(ctx, finalityCheckpointsEndpoint, &finalityCheckpointsResponse).Return(
|
||||
nil,
|
||||
nil,
|
||||
).SetArg(
|
||||
2,
|
||||
generateValidFinalityCheckpointsResponse(),
|
||||
)
|
||||
|
||||
headBlockHeadersResponse := apimiddleware.BlockHeaderResponseJson{}
|
||||
jsonRestHandler.EXPECT().GetRestJsonResponse(ctx, headBlockHeadersEndpoint, &headBlockHeadersResponse).Return(
|
||||
nil,
|
||||
nil,
|
||||
).SetArg(
|
||||
2,
|
||||
generateValidBlockHeadersResponse(),
|
||||
)
|
||||
|
||||
expectedPreviousJustifiedSlot, err := slots.EpochStart(1)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedCurrentJustifiedSlot, err := slots.EpochStart(3)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedFinalizedSlot, err := slots.EpochStart(5)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedChainHead := ðpb.ChainHead{
|
||||
PreviousJustifiedEpoch: 1,
|
||||
PreviousJustifiedBlockRoot: []byte{2},
|
||||
PreviousJustifiedSlot: expectedPreviousJustifiedSlot,
|
||||
JustifiedEpoch: 3,
|
||||
JustifiedBlockRoot: []byte{4},
|
||||
JustifiedSlot: expectedCurrentJustifiedSlot,
|
||||
FinalizedEpoch: 5,
|
||||
FinalizedBlockRoot: []byte{6},
|
||||
FinalizedSlot: expectedFinalizedSlot,
|
||||
HeadBlockRoot: []byte{7},
|
||||
HeadSlot: 8,
|
||||
HeadEpoch: slots.ToEpoch(8),
|
||||
}
|
||||
|
||||
beaconChainClient := beaconApiBeaconChainClient{jsonRestHandler: jsonRestHandler}
|
||||
chainHead, err := beaconChainClient.GetChainHead(ctx, &emptypb.Empty{})
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, expectedChainHead, chainHead)
|
||||
})
|
||||
}
|
||||
@@ -97,7 +97,8 @@ func jsonifyProposerSlashings(proposerSlashings []*ethpb.ProposerSlashing) []*ap
|
||||
return jsonProposerSlashings
|
||||
}
|
||||
|
||||
func jsonifySignedVoluntaryExits(voluntaryExits []*ethpb.SignedVoluntaryExit) []*apimiddleware.SignedVoluntaryExitJson {
|
||||
// JsonifySignedVoluntaryExits converts an array of voluntary exit structs to a JSON hex string compatible format.
|
||||
func JsonifySignedVoluntaryExits(voluntaryExits []*ethpb.SignedVoluntaryExit) []*apimiddleware.SignedVoluntaryExitJson {
|
||||
jsonSignedVoluntaryExits := make([]*apimiddleware.SignedVoluntaryExitJson, len(voluntaryExits))
|
||||
for index, signedVoluntaryExit := range voluntaryExits {
|
||||
jsonSignedVoluntaryExit := &apimiddleware.SignedVoluntaryExitJson{
|
||||
|
||||
@@ -508,7 +508,7 @@ func TestBeaconBlockJsonHelpers_JsonifySignedVoluntaryExits(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
result := jsonifySignedVoluntaryExits(input)
|
||||
result := JsonifySignedVoluntaryExits(input)
|
||||
assert.DeepEqual(t, expectedResult, result)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ func marshallBeaconBlockPhase0(block *ethpb.SignedBeaconBlock) ([]byte, error) {
|
||||
Graffiti: hexutil.Encode(block.Block.Body.Graffiti),
|
||||
ProposerSlashings: jsonifyProposerSlashings(block.Block.Body.ProposerSlashings),
|
||||
RandaoReveal: hexutil.Encode(block.Block.Body.RandaoReveal),
|
||||
VoluntaryExits: jsonifySignedVoluntaryExits(block.Block.Body.VoluntaryExits),
|
||||
VoluntaryExits: JsonifySignedVoluntaryExits(block.Block.Body.VoluntaryExits),
|
||||
},
|
||||
ParentRoot: hexutil.Encode(block.Block.ParentRoot),
|
||||
ProposerIndex: uint64ToString(block.Block.ProposerIndex),
|
||||
@@ -155,7 +155,7 @@ func marshallBeaconBlockAltair(block *ethpb.SignedBeaconBlockAltair) ([]byte, er
|
||||
Graffiti: hexutil.Encode(block.Block.Body.Graffiti),
|
||||
ProposerSlashings: jsonifyProposerSlashings(block.Block.Body.ProposerSlashings),
|
||||
RandaoReveal: hexutil.Encode(block.Block.Body.RandaoReveal),
|
||||
VoluntaryExits: jsonifySignedVoluntaryExits(block.Block.Body.VoluntaryExits),
|
||||
VoluntaryExits: JsonifySignedVoluntaryExits(block.Block.Body.VoluntaryExits),
|
||||
SyncAggregate: &apimiddleware.SyncAggregateJson{
|
||||
SyncCommitteeBits: hexutil.Encode(block.Block.Body.SyncAggregate.SyncCommitteeBits),
|
||||
SyncCommitteeSignature: hexutil.Encode(block.Block.Body.SyncAggregate.SyncCommitteeSignature),
|
||||
@@ -183,7 +183,7 @@ func marshallBeaconBlockBellatrix(block *ethpb.SignedBeaconBlockBellatrix) ([]by
|
||||
Graffiti: hexutil.Encode(block.Block.Body.Graffiti),
|
||||
ProposerSlashings: jsonifyProposerSlashings(block.Block.Body.ProposerSlashings),
|
||||
RandaoReveal: hexutil.Encode(block.Block.Body.RandaoReveal),
|
||||
VoluntaryExits: jsonifySignedVoluntaryExits(block.Block.Body.VoluntaryExits),
|
||||
VoluntaryExits: JsonifySignedVoluntaryExits(block.Block.Body.VoluntaryExits),
|
||||
SyncAggregate: &apimiddleware.SyncAggregateJson{
|
||||
SyncCommitteeBits: hexutil.Encode(block.Block.Body.SyncAggregate.SyncCommitteeBits),
|
||||
SyncCommitteeSignature: hexutil.Encode(block.Block.Body.SyncAggregate.SyncCommitteeSignature),
|
||||
@@ -227,7 +227,7 @@ func marshallBeaconBlockBlindedBellatrix(block *ethpb.SignedBlindedBeaconBlockBe
|
||||
Graffiti: hexutil.Encode(block.Block.Body.Graffiti),
|
||||
ProposerSlashings: jsonifyProposerSlashings(block.Block.Body.ProposerSlashings),
|
||||
RandaoReveal: hexutil.Encode(block.Block.Body.RandaoReveal),
|
||||
VoluntaryExits: jsonifySignedVoluntaryExits(block.Block.Body.VoluntaryExits),
|
||||
VoluntaryExits: JsonifySignedVoluntaryExits(block.Block.Body.VoluntaryExits),
|
||||
SyncAggregate: &apimiddleware.SyncAggregateJson{
|
||||
SyncCommitteeBits: hexutil.Encode(block.Block.Body.SyncAggregate.SyncCommitteeBits),
|
||||
SyncCommitteeSignature: hexutil.Encode(block.Block.Body.SyncAggregate.SyncCommitteeSignature),
|
||||
@@ -271,7 +271,7 @@ func marshallBeaconBlockCapella(block *ethpb.SignedBeaconBlockCapella) ([]byte,
|
||||
Graffiti: hexutil.Encode(block.Block.Body.Graffiti),
|
||||
ProposerSlashings: jsonifyProposerSlashings(block.Block.Body.ProposerSlashings),
|
||||
RandaoReveal: hexutil.Encode(block.Block.Body.RandaoReveal),
|
||||
VoluntaryExits: jsonifySignedVoluntaryExits(block.Block.Body.VoluntaryExits),
|
||||
VoluntaryExits: JsonifySignedVoluntaryExits(block.Block.Body.VoluntaryExits),
|
||||
SyncAggregate: &apimiddleware.SyncAggregateJson{
|
||||
SyncCommitteeBits: hexutil.Encode(block.Block.Body.SyncAggregate.SyncCommitteeBits),
|
||||
SyncCommitteeSignature: hexutil.Encode(block.Block.Body.SyncAggregate.SyncCommitteeSignature),
|
||||
@@ -317,7 +317,7 @@ func marshallBeaconBlockBlindedCapella(block *ethpb.SignedBlindedBeaconBlockCape
|
||||
Graffiti: hexutil.Encode(block.Block.Body.Graffiti),
|
||||
ProposerSlashings: jsonifyProposerSlashings(block.Block.Body.ProposerSlashings),
|
||||
RandaoReveal: hexutil.Encode(block.Block.Body.RandaoReveal),
|
||||
VoluntaryExits: jsonifySignedVoluntaryExits(block.Block.Body.VoluntaryExits),
|
||||
VoluntaryExits: JsonifySignedVoluntaryExits(block.Block.Body.VoluntaryExits),
|
||||
SyncAggregate: &apimiddleware.SyncAggregateJson{
|
||||
SyncCommitteeBits: hexutil.Encode(block.Block.Body.SyncAggregate.SyncCommitteeBits),
|
||||
SyncCommitteeSignature: hexutil.Encode(block.Block.Body.SyncAggregate.SyncCommitteeSignature),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user