Compare commits

...

28 Commits

Author SHA1 Message Date
Preston Van Loon
f4681fde19 windows: Fix build after PR 12293 (#12296) 2023-04-17 20:43:40 +00:00
Preston Van Loon
0c7292b85b prysmctl: Add support for writing signed validator exits to disk (#12262)
* prysmctl: Add support for writing signed validator exits to disk

* Add dir suffix

* Add test to ensure no broadcast call was made

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-04-17 18:01:13 +00:00
james-prysm
10b438e2c8 Keymanager fixes for bad file writes (#12284)
* WIP changes for keymanager

* WIP fix

* WIP needs unit tests

* fixing order

* adding unit test

* fixing linter

* updating unit tests and creating more reusable functions

* making accountStore copy method part of struct

* Update validator/keymanager/local/delete_test.go

* Update validator/keymanager/local/delete.go

* Update validator/keymanager/local/delete.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* Update validator/keymanager/local/import.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* Update validator/keymanager/local/delete.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* Update validator/keymanager/local/delete.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* Update validator/keymanager/local/import.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* Update validator/keymanager/local/import.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* Update validator/keymanager/local/delete.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* addressing suggestion of not reinitializing from reading the file but instead update the information based on memory on hand

* Update validator/accounts/wallet_create.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* adding changes based on suggestions

* making logs more consistent

* fixing linting

---------

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
2023-04-17 12:08:27 -05:00
Nishant Das
e2386cfb11 Fix Attester Slashing Validation In Gossip (#12295)
* fix slashing checks

* fix to make it more performant

* gaz

* fix up

* potuz's comment

* potuz's comment

* fix cache

* change index in test for better case

* gaz

---------

Co-authored-by: Potuz <potuz@prysmaticlabs.com>
2023-04-17 15:35:38 +00:00
Radosław Kapka
898cb0b512 Deflake TestWaitForActivation_AccountsChanged (#12282)
Co-authored-by: terencechain <terence@prysmaticlabs.com>
2023-04-17 15:04:42 +00:00
Sammy Rosso
6944d22ce3 Add support for engine_exchangeCapabilities (#12224)
* Add call to engine_exchangeCapabilities

* Fix unused import

* Cleanup + tests

* Warning only when needed

* Add Radek' feedback
2023-04-17 12:55:22 +00:00
Nishant Das
1eb00866ea Add License For Vendored Dependency (#12294) 2023-04-17 08:16:47 +00:00
Nishant Das
4862d57b13 Fix Broken Dependency (#12293)
* vendor in library

* comment

* lint

* lint
2023-04-17 12:22:34 +08:00
Potuz
191b0c4652 Correctly use Gwei to compare builder bid value (#12290)
* Correctly use Gwei to compare builder bid value

* Minimal 1gwei for TestProduceBlindedBlock

---------

Co-authored-by: terence tsao <terence@prysmaticlabs.com>
2023-04-16 16:47:51 +08:00
kasey
c09977d8a8 support modifying genesis.json for capella (#12283)
Co-authored-by: kasey <kasey@users.noreply.github.com>
2023-04-14 20:32:40 +00:00
Nishant Das
52d72c989e Check that Builder Is Configured (#12279)
* add configured check

* tests

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-04-14 10:21:46 +00:00
Justin Traglia
9f7711e74a Enable misspell linter & fix findings (#12272)
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2023-04-14 10:03:11 +00:00
Nishant Das
f376427add fix panic (#12277) 2023-04-13 22:42:57 -07:00
kasey
ff1b03ab13 prysmctl using the same genesis func as e2e (#12268)
* prysmctl using the same genesis func as e2e

* add whitespace to genesis.json for readability

* fix typo in fork name

* don't require validator count if deposits given

* add gosec exception

* the other nosec :(

* appease deepsource

* fix comments on renamed public value/func

---------

Co-authored-by: kasey <kasey@users.noreply.github.com>
2023-04-13 17:19:06 +00:00
Potuz
5fdd4e9148 Add prepare-all-payloads flag (#12260)
* Add prepare-all-payloads flag

* add unit tests

* do not use the nsc

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-04-13 14:47:13 +00:00
Patrice Vignola
80e26143eb Add REST API endpoint for beacon chain client's GetChainHead (#12245)
* Add REST API endpoint for beacon chain client's GetChainHead

* Remove unused parameters

---------

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2023-04-13 11:42:59 +00:00
Nishant Das
75338dd83d Fix User Agent In Builder Client (#12264) 2023-04-13 03:12:51 +00:00
terencechain
99eebe9bac Fix capella unblind block with bls field (#12263) 2023-04-12 18:47:34 -07:00
keithchew
6b1efff4e8 allow setting GOMAXPROCS from environment variable (#12256)
* allow setting GOMAXPROCS from environment variable

* remove unused import
2023-04-10 23:28:48 -04:00
Preston Van Loon
763e9e3361 Update go to version 1.19.8 (#12238)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-04-10 19:03:24 +00:00
Nishant Das
37182168e3 Fix Deadlock in StreamChainHead (#12250)
* fix it possibly

* buffer it more

* fix test
2023-04-07 15:41:31 -05:00
terencechain
0325741318 Add orphaned operations to the appropriate pools in saveOrphanedOps() and mark included slashings in prunePostBlockOperationPools(). (#12249)
Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
2023-04-06 16:26:01 -05:00
Preston Van Loon
150e8aa14d Remove unused beacon-chain/server binary (#12241)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-04-06 20:56:55 +00:00
kasey
f4307a902c build tag to exclude mainnet genesis from prysmctl (#12244)
Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
2023-04-06 19:45:35 +00:00
james-prysm
d257ef1742 Builder: fix nil panic edgecase (#12236)
* adding fix for buildervalue nil

* fixing linting

* changing based on review comment

* editing based on suggestions

* fixing unit test

* fixing linting

* fall back to local

* fix linting

* updating based on slack feedback
2023-04-06 14:19:51 -05:00
Nishant Das
aad7aa79d4 Fix Next State Mismatch (#12247)
* fix mismatch

* add regression test
2023-04-06 17:52:15 +08:00
terencechain
2eb2f87913 Default to local payload should set block to not blind (#12243) 2023-04-05 23:29:41 +00:00
Patrice Vignola
9214364c5e Add REST API endpoint for beacon chain client's ListValidators (#12228)
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2023-04-05 13:00:49 +02:00
117 changed files with 2976 additions and 610 deletions

View File

@@ -17,6 +17,7 @@ linters:
- errcheck
- gosimple
- gocognit
- misspell
linters-settings:
gocognit:

View File

@@ -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",
)

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 := &ethpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}
ofc := &ethpb.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)

View File

@@ -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()

View File

@@ -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) {

View File

@@ -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
}

View File

@@ -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),

View File

@@ -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)

View File

@@ -37,3 +37,18 @@ func Test_RegisterValidator(t *testing.T) {
require.NoError(t, s.RegisterValidator(ctx, []*eth.SignedValidatorRegistrationV1{{Message: &eth.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)
}

View File

@@ -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

View File

@@ -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()
}

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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:

View File

@@ -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)
}
})
}

View File

@@ -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",

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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())
}

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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 := &ethpbalpha.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)

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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.

View File

@@ -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")

View File

@@ -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(&ethpb.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 := &ethpb.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 := &ethpb.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()

View File

@@ -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

View File

@@ -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)

View File

@@ -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 := &ethpb.SignedBlindedBeaconBlockCapella{
Block: &ethpb.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: &ethpb.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,

View File

@@ -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: &eth.BLSToExecutionChange{ValidatorIndex: 1, FromBlsPubkey: []byte{'a'}}},
{Message: &eth.BLSToExecutionChange{ValidatorIndex: 2, FromBlsPubkey: []byte{'b'}}},
{Message: &eth.BLSToExecutionChange{ValidatorIndex: 3, FromBlsPubkey: []byte{'c'}}},
{Message: &eth.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: &eth.BLSToExecutionChange{ValidatorIndex: 1, FromBlsPubkey: []byte{'a'}}},
{Message: &eth.BLSToExecutionChange{ValidatorIndex: 2, FromBlsPubkey: []byte{'b'}}},
{Message: &eth.BLSToExecutionChange{ValidatorIndex: 3, FromBlsPubkey: []byte{'c'}}},
{Message: &eth.BLSToExecutionChange{ValidatorIndex: 4, FromBlsPubkey: []byte{'d'}}},
}
wb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
return wb

View File

@@ -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},
}
}

View File

@@ -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"],
)

View File

@@ -1,5 +0,0 @@
package main
import "github.com/sirupsen/logrus"
var log = logrus.New()

View File

@@ -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")
}
}
}

View File

@@ -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__"],

View File

@@ -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.

View 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
}

View File

@@ -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",

View File

@@ -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",

View File

@@ -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()

View File

@@ -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))
}
}

View File

@@ -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:

View File

@@ -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
}

View File

@@ -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. " +

View File

@@ -110,5 +110,6 @@ docker_push(
go_binary(
name = "prysmctl",
embed = [":go_default_library"],
gotags = ["noMainnetGenesis"],
visibility = ["//visibility:public"],
)

View File

@@ -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",

View File

@@ -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 = &ethpb.Eth1Data{
e1d := &ethpb.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 = &ethpb.Deposit_Data{
PublicKey: pubKeyBytes,
WithdrawalCredentials: withdrawalbytes,
return root, &ethpb.Deposit_Data{
PublicKey: pk,
WithdrawalCredentials: creds,
Amount: input.Amount,
Signature: signatureBytes,
}
dataRoot = dataRootBytes
return
Signature: sig,
}, nil
}
func writeToOutputFile(

View File

@@ -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)
}
}

View File

@@ -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,

View File

@@ -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",

View File

@@ -166,6 +166,7 @@ var Commands = &cli.Command{
flags.GrpcRetryDelayFlag,
flags.ExitAllFlag,
flags.ForceExitFlag,
flags.VoluntaryExitJSONOutputPath,
features.Mainnet,
features.PraterTestnet,
features.SepoliaTestnet,

View File

@@ -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)

View File

@@ -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(&ethpb.ValidatorIndexResponse{Index: 1}, nil)
// Any time in the past will suffice
genesisTime := &timestamppb.Timestamp{
Seconds: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
}
mockNodeClient.EXPECT().
GetGenesis(gomock.Any(), gomock.Any()).
Return(&ethpb.Genesis{GenesisTime: genesisTime}, nil)
mockValidatorClient.EXPECT().
DomainData(gomock.Any(), gomock.Any()).
Return(&ethpb.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")
}

View File

@@ -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",

View 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
}

View File

@@ -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
}

View File

@@ -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.

View File

@@ -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
View File

@@ -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
View File

@@ -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=

View File

@@ -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"],
)

View 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.

View File

@@ -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
}

View 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,
}
}

View 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"])
}
}

View File

@@ -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,
},

View File

@@ -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;
}

View File

@@ -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",
],
)

View File

@@ -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 := &params.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,

View File

@@ -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
}

View File

@@ -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",
],

View File

@@ -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++
}
}

View File

@@ -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",

View File

@@ -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)
}

View File

@@ -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",

View File

@@ -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 {

View File

@@ -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

View File

@@ -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",

View File

@@ -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",

View File

@@ -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
}

View File

@@ -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 := &eth.SignedVoluntaryExit{
Exit: &eth.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)
}

View File

@@ -56,6 +56,7 @@ type AccountsCLIManager struct {
filteredPubKeys []bls.PublicKey
rawPubKeys [][]byte
formattedPubKeys []string
exitJSONOutputPath string
walletDir string
walletPassword string
mnemonic string

View File

@@ -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 {

View File

@@ -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)
}

View File

@@ -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: "",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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 &ethpb.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] = &ethpb.Validators_ValidatorContainer{
Index: primitives.ValidatorIndex(validatorIndex),
Validator: &ethpb.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 &ethpb.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},
}
}

View File

@@ -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, &ethpb.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, &ethpb.ListValidatorsRequest{
QueryFilter: &ethpb.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, &ethpb.ListValidatorsRequest{
QueryFilter: &ethpb.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, &ethpb.ListValidatorsRequest{
QueryFilter: &ethpb.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, &ethpb.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, &ethpb.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, &ethpb.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, &ethpb.ListValidatorsRequest{
QueryFilter: &ethpb.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] = &ethpb.Validators_ValidatorContainer{
Index: 1,
Validator: &ethpb.Validator{
PublicKey: []byte{2},
WithdrawalCredentials: []byte{3},
EffectiveBalance: 4,
Slashed: true,
ActivationEligibilityEpoch: 5,
ActivationEpoch: 6,
ExitEpoch: 7,
WithdrawableEpoch: 8,
},
}
}
return &ethpb.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 &ethpb.Validators{
ValidatorList: []*ethpb.Validators_ValidatorContainer{
{
Index: 1,
Validator: &ethpb.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 &ethpb.Validators{
ValidatorList: []*ethpb.Validators_ValidatorContainer{
{
Index: 1,
Validator: &ethpb.Validator{
PublicKey: []byte{2},
WithdrawalCredentials: []byte{3},
EffectiveBalance: 4,
Slashed: true,
ActivationEligibilityEpoch: 5,
ActivationEpoch: 6,
ExitEpoch: 7,
WithdrawableEpoch: 8,
},
},
{
Index: 9,
Validator: &ethpb.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 &ethpb.Validators{
ValidatorList: []*ethpb.Validators_ValidatorContainer{
{
Index: 9,
Validator: &ethpb.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 &ethpb.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, &ethpb.ListValidatorsRequest{
QueryFilter: &ethpb.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 := &ethpb.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)
})
}

View File

@@ -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{

View File

@@ -508,7 +508,7 @@ func TestBeaconBlockJsonHelpers_JsonifySignedVoluntaryExits(t *testing.T) {
},
}
result := jsonifySignedVoluntaryExits(input)
result := JsonifySignedVoluntaryExits(input)
assert.DeepEqual(t, expectedResult, result)
}

View File

@@ -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)
}

View File

@@ -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),

View File

@@ -41,7 +41,7 @@ func TestProposeBeaconBlock_Altair(t *testing.T) {
Graffiti: hexutil.Encode(altairBlock.Altair.Block.Body.Graffiti),
ProposerSlashings: jsonifyProposerSlashings(altairBlock.Altair.Block.Body.ProposerSlashings),
RandaoReveal: hexutil.Encode(altairBlock.Altair.Block.Body.RandaoReveal),
VoluntaryExits: jsonifySignedVoluntaryExits(altairBlock.Altair.Block.Body.VoluntaryExits),
VoluntaryExits: JsonifySignedVoluntaryExits(altairBlock.Altair.Block.Body.VoluntaryExits),
SyncAggregate: &apimiddleware.SyncAggregateJson{
SyncCommitteeBits: hexutil.Encode(altairBlock.Altair.Block.Body.SyncAggregate.SyncCommitteeBits),
SyncCommitteeSignature: hexutil.Encode(altairBlock.Altair.Block.Body.SyncAggregate.SyncCommitteeSignature),

Some files were not shown because too many files have changed in this diff Show More