Compare commits

...

35 Commits

Author SHA1 Message Date
nisdas
9984b6cf38 add change in 2023-05-10 08:05:37 +08:00
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
Potuz
fb65421678 refactor next slot cache (#12233)
* refactor next slot cache

* fix test

* rename function

* fix spectests

* remove TODO comments

---------

Co-authored-by: Nishant Das <nishdas93@gmail.com>
2023-04-04 16:04:00 +00:00
terencechain
35e3eeddf9 Add flag to boost local block value (#12227)
* Add builder bid fraction to compare with local block value and use builder bid if (bid * fraction) > local block value

* Prioritize local block construction over relay/builder block construction using a boost value for local block construction

* Refactor builder and local block value calculation to use percentage comparison

* Add a test for local with boost

* Use uint64

* Fix log
2023-04-04 14:58:56 +00:00
Sammy Rosso
2618a114e5 Testnet: override Eth1Data for genesisState from input JSON (#12231)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2023-04-03 15:13:51 +00:00
Radosław Kapka
aac47640b4 Fix broadcast of slashings (#12230)
* Fix broadcast of slashings

* tests
2023-04-03 14:57:59 +00:00
Nishant Das
17cfc60bdd Enable Static Peer ID (#12220)
* static peer id

* kasey's review
2023-04-01 00:00:11 +00:00
Radosław Kapka
8d001d49d4 Unskip block tests (#12222) 2023-03-31 15:45:42 +00:00
128 changed files with 3189 additions and 702 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

@@ -671,9 +671,7 @@ func (s *Service) fillMissingPayloadIDRoutine(ctx context.Context, stateFeed *ev
for {
select {
case <-ticker.C():
if err := s.fillMissingBlockPayloadId(ctx); err != nil {
log.WithError(err).Error("Could not fill missing payload ID")
}
s.lateBlockTasks(ctx)
case <-ctx.Done():
log.Debug("Context closed, exiting routine")
@@ -683,11 +681,13 @@ func (s *Service) fillMissingPayloadIDRoutine(ctx context.Context, stateFeed *ev
}()
}
// fillMissingBlockPayloadId is called 4 seconds into the slot and calls FCU if we are proposing next slot
// and the cache has been missed
func (s *Service) fillMissingBlockPayloadId(ctx context.Context) error {
// lateBlockTasks is called 4 seconds into the slot and performs tasks
// related to late blocks. It emits a MissedSlot state feed event.
// It calls FCU and sets the right attributes if we are proposing next slot
// it also updates the next slot cache to deal with skipped slots.
func (s *Service) lateBlockTasks(ctx context.Context) {
if s.CurrentSlot() == s.HeadSlot() {
return nil
return
}
s.cfg.StateNotifier.StateFeed().Send(&feed.Event{
Type: statefeed.MissedSlot,
@@ -696,22 +696,32 @@ func (s *Service) fillMissingBlockPayloadId(ctx context.Context) error {
// 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{} {
return nil
if (!has && !features.Get().PrepareAllPayloads) || id != [8]byte{} {
return
}
s.headLock.RLock()
headBlock, err := s.headBlock()
if err != nil {
s.headLock.RUnlock()
return err
log.WithError(err).Debug("could not perform late block tasks: failed to retrieve head block")
return
}
headState := s.headState(ctx)
headRoot := s.headRoot()
headState := s.headState(ctx)
s.headLock.RUnlock()
_, err = s.notifyForkchoiceUpdate(ctx, &notifyForkchoiceUpdateArg{
headState: headState,
headRoot: headRoot,
headBlock: headBlock.Block(),
})
return err
if err != nil {
log.WithError(err).Debug("could not perform late block tasks: failed to update forkchoice with engine")
}
lastRoot, lastState := transition.LastCachedState()
if lastState == nil {
lastRoot, lastState = headRoot[:], headState
}
if err = transition.UpdateNextSlotCache(ctx, lastRoot, lastState); err != nil {
log.WithError(err).Debug("could not update next slot state cache")
}
}

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"
@@ -2153,6 +2154,7 @@ func TestOnBlock_HandleBlockAttestations(t *testing.T) {
}
func TestFillMissingBlockPayloadId_DiffSlotExitEarly(t *testing.T) {
logHook := logTest.NewGlobal()
fc := doublylinkedtree.New()
ctx := context.Background()
beaconDB := testDB.SetupDB(t)
@@ -2164,7 +2166,30 @@ func TestFillMissingBlockPayloadId_DiffSlotExitEarly(t *testing.T) {
service, err := NewService(ctx, opts...)
require.NoError(t, err)
require.NoError(t, service.fillMissingBlockPayloadId(ctx), 0)
service.lateBlockTasks(ctx)
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

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

@@ -5,15 +5,20 @@ import (
"context"
"sync"
"github.com/pkg/errors"
"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"
)
type nextSlotCache struct {
sync.RWMutex
root []byte
state state.BeaconState
sync.Mutex
prevRoot []byte
lastRoot []byte
prevState state.BeaconState
lastState state.BeaconState
}
var (
@@ -29,19 +34,22 @@ var (
})
)
// NextSlotState returns the saved state if the input root matches the root in `nextSlotCache`. Returns nil otherwise.
// This is useful to check before processing slots. With a cache hit, it will return last processed state with slot plus
// one advancement.
func NextSlotState(_ context.Context, root []byte) (state.BeaconState, error) {
nsc.RLock()
defer nsc.RUnlock()
if !bytes.Equal(root, nsc.root) || bytes.Equal(root, []byte{}) {
nextSlotCacheMiss.Inc()
return nil, nil
// 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, wantedSlot types.Slot) state.BeaconState {
nsc.Lock()
defer nsc.Unlock()
if bytes.Equal(root, nsc.lastRoot) && nsc.lastState.Slot() <= wantedSlot {
nextSlotCacheHit.Inc()
return nsc.lastState.Copy()
}
nextSlotCacheHit.Inc()
// Returning copied state.
return nsc.state.Copy(), nil
if bytes.Equal(root, nsc.prevRoot) && nsc.prevState.Slot() <= wantedSlot {
nextSlotCacheHit.Inc()
return nsc.prevState.Copy()
}
nextSlotCacheMiss.Inc()
return nil
}
// UpdateNextSlotCache updates the `nextSlotCache`. It saves the input state after advancing the state slot by 1
@@ -52,13 +60,25 @@ func UpdateNextSlotCache(ctx context.Context, root []byte, state state.BeaconSta
copied := state.Copy()
copied, err := ProcessSlots(ctx, copied, copied.Slot()+1)
if err != nil {
return err
return errors.Wrap(err, "could not process slots")
}
nsc.Lock()
defer nsc.Unlock()
nsc.root = root
nsc.state = copied
nsc.prevRoot = nsc.lastRoot
nsc.prevState = nsc.lastState
nsc.lastRoot = bytesutil.SafeCopyBytes(root)
nsc.lastState = copied
return nil
}
// LastCachedState returns the last cached state and root in the cache
func LastCachedState() ([]byte, state.BeaconState) {
nsc.Lock()
defer nsc.Unlock()
if nsc.lastState == nil {
return nil, nil
}
return bytesutil.SafeCopyBytes(nsc.lastRoot), nsc.lastState.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,18 +14,36 @@ import (
func TestTrailingSlotState_RoundTrip(t *testing.T) {
ctx := context.Background()
r := []byte{'a'}
s, err := transition.NextSlotState(ctx, r)
require.NoError(t, err)
s := transition.NextSlotState(r, 0)
require.Equal(t, nil, s)
s, _ = util.DeterministicGenesisState(t, 1)
require.NoError(t, transition.UpdateNextSlotCache(ctx, r, s))
s, err = transition.NextSlotState(ctx, r)
require.NoError(t, err)
s = transition.NextSlotState(r, 1)
require.Equal(t, primitives.Slot(1), s.Slot())
lastRoot, lastState := transition.LastCachedState()
require.DeepEqual(t, r, lastRoot)
require.Equal(t, s.Slot(), lastState.Slot())
require.NoError(t, transition.UpdateNextSlotCache(ctx, r, s))
s, err = transition.NextSlotState(ctx, r)
require.NoError(t, err)
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,25 +147,15 @@ func ProcessSlotsUsingNextSlotCache(
ctx, span := trace.StartSpan(ctx, "core.state.ProcessSlotsUsingNextSlotCache")
defer span.End()
// Check whether the parent state has been advanced by 1 slot in next slot cache.
nextSlotState, err := NextSlotState(ctx, parentRoot)
if err != nil {
return nil, err
}
cachedStateExists := nextSlotState != nil && !nextSlotState.IsNil()
// If the next slot state is not nil (i.e. cache hit).
// We replace next slot state with parent state.
if cachedStateExists {
nextSlotState := NextSlotState(parentRoot, slot)
if nextSlotState != nil {
parentState = nextSlotState
}
// In the event our cached state has advanced our
// state to the desired slot, we exit early.
if cachedStateExists && parentState.Slot() == slot {
if parentState.Slot() == slot {
return parentState, nil
}
// Since next slot cache only advances state by 1 slot,
// we check if there's more slots that need to process.
var err error
parentState, err = ProcessSlots(ctx, parentState, slot)
if err != nil {
return nil, errors.Wrap(err, "could not process slots")

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

@@ -67,6 +67,13 @@ func configureBuilderCircuitBreaker(cliCtx *cli.Context) error {
return err
}
}
if cliCtx.IsSet(flags.LocalBlockValueBoost.Name) {
c := params.BeaconConfig().Copy()
c.LocalBlockValueBoost = cliCtx.Uint64(flags.LocalBlockValueBoost.Name)
if err := params.SetActive(c); err != nil {
return err
}
}
return nil
}

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
}
@@ -538,6 +538,7 @@ func (b *BeaconNode) registerP2P(cliCtx *cli.Context) error {
HostAddress: cliCtx.String(cmd.P2PHost.Name),
HostDNS: cliCtx.String(cmd.P2PHostDNS.Name),
PrivateKey: cliCtx.String(cmd.P2PPrivKey.Name),
StaticPeerID: cliCtx.Bool(cmd.P2PStaticID.Name),
MetaDataDir: cliCtx.String(cmd.P2PMetadata.Name),
TCPPort: cliCtx.Uint(cmd.P2PTCPPort.Name),
UDPPort: cliCtx.Uint(cmd.P2PUDPPort.Name),

View File

@@ -10,6 +10,7 @@ import (
type Config struct {
NoDiscovery bool
EnableUPnP bool
StaticPeerID bool
StaticPeers []string
BootstrapNodeAddr []string
Discv5BootStrapAddr []string

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

@@ -5,6 +5,7 @@ import (
"encoding/hex"
"net"
"os"
"path"
"testing"
gethCrypto "github.com/ethereum/go-ethereum/crypto"
@@ -50,6 +51,32 @@ func TestPrivateKeyLoading(t *testing.T) {
assert.DeepEqual(t, rawBytes, newRaw, "Private keys do not match")
}
func TestPrivateKeyLoading_StaticPrivateKey(t *testing.T) {
params.SetupTestConfigCleanup(t)
tempDir := t.TempDir()
cfg := &Config{
StaticPeerID: true,
DataDir: tempDir,
}
pKey, err := privKey(cfg)
require.NoError(t, err, "Could not apply option")
newPkey, err := ecdsaprysm.ConvertToInterfacePrivkey(pKey)
require.NoError(t, err)
retrievedKey, err := privKeyFromFile(path.Join(tempDir, keyPath))
require.NoError(t, err)
retrievedPKey, err := ecdsaprysm.ConvertToInterfacePrivkey(retrievedKey)
require.NoError(t, err)
rawBytes, err := retrievedPKey.Raw()
require.NoError(t, err)
newRaw, err := newPkey.Raw()
require.NoError(t, err)
assert.DeepEqual(t, rawBytes, newRaw, "Private keys do not match")
}
func TestIPV6Support(t *testing.T) {
params.SetupTestConfigCleanup(t)
key, err := gethCrypto.GenerateKey()

View File

@@ -125,6 +125,7 @@ func NewService(ctx context.Context, cfg *Config) (*Service, error) {
s.ipLimiter = leakybucket.NewCollector(ipLimit, ipBurst, 30*time.Second, true /* deleteEmptyBuckets */)
opts := s.buildOptions(ipAddr, s.privKey)
opts = append(opts, libp2p.ResourceManager(&network.NullResourceManager{}))
h, err := libp2p.New(opts...)
if err != nil {
log.WithError(err).Error("Failed to create p2p host")

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

@@ -49,23 +49,43 @@ func privKey(cfg *Config) (*ecdsa.PrivateKey, error) {
defaultKeyPath := path.Join(cfg.DataDir, keyPath)
privateKeyPath := cfg.PrivateKey
// PrivateKey cli flag takes highest precedence.
if privateKeyPath != "" {
return privKeyFromFile(cfg.PrivateKey)
}
_, err := os.Stat(defaultKeyPath)
defaultKeysExist := !os.IsNotExist(err)
if err != nil && defaultKeysExist {
return nil, err
}
if privateKeyPath == "" && !defaultKeysExist {
priv, _, err := crypto.GenerateSecp256k1Key(rand.Reader)
// Default keys have the next highest precedence, if they exist.
if defaultKeysExist {
return privKeyFromFile(defaultKeyPath)
}
// There are no keys on the filesystem, so we need to generate one.
priv, _, err := crypto.GenerateSecp256k1Key(rand.Reader)
if err != nil {
return nil, err
}
// If the StaticPeerID flag is set, save the generated key as the default
// key, so that it will be used by default on the next node start.
if cfg.StaticPeerID {
rawbytes, err := priv.Raw()
if err != nil {
return nil, err
}
return ecdsaprysm.ConvertFromInterfacePrivKey(priv)
dst := make([]byte, hex.EncodedLen(len(rawbytes)))
hex.Encode(dst, rawbytes)
if err := file.WriteFile(defaultKeyPath, dst); err != nil {
return nil, err
}
log.Infof("Wrote network key to file")
// Read the key from the defaultKeyPath file just written
// for the strongest guarantee that the next start will be the same as this one.
return privKeyFromFile(defaultKeyPath)
}
if defaultKeysExist && privateKeyPath == "" {
privateKeyPath = defaultKeyPath
}
return privKeyFromFile(privateKeyPath)
return ecdsaprysm.ConvertFromInterfacePrivKey(priv)
}
// Retrieves a p2p networking private key from a file path.

View File

@@ -389,14 +389,6 @@ func TestServer_SubmitBlindedBlockSSZ_OK(t *testing.T) {
})
t.Run("Bellatrix", func(t *testing.T) {
// INFO: This code block can be removed once Bellatrix
// fork epoch is set to a value other than math.MaxUint64
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig()
cfg.BellatrixForkEpoch = cfg.AltairForkEpoch + 1000
cfg.ForkVersionSchedule[bytesutil.ToBytes4(cfg.BellatrixForkVersion)] = cfg.AltairForkEpoch + 1000
params.OverrideBeaconConfig(cfg)
beaconDB := dbTest.SetupDB(t)
ctx := context.Background()
@@ -445,16 +437,6 @@ func TestServer_SubmitBlindedBlockSSZ_OK(t *testing.T) {
})
t.Run("Capella", func(t *testing.T) {
t.Skip("This test needs Capella fork version configured properly")
// INFO: This code block can be removed once Capella
// fork epoch is set to a value other than math.MaxUint64
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig()
cfg.CapellaForkEpoch = cfg.BellatrixForkEpoch + 1000
cfg.ForkVersionSchedule[bytesutil.ToBytes4(cfg.CapellaForkVersion)] = cfg.BellatrixForkEpoch + 1000
params.OverrideBeaconConfig(cfg)
beaconDB := dbTest.SetupDB(t)
ctx := context.Background()

View File

@@ -623,15 +623,6 @@ func TestServer_SubmitBlockSSZ_OK(t *testing.T) {
})
t.Run("Capella", func(t *testing.T) {
t.Skip("This test needs Capella fork version configured properly")
// INFO: This code block can be removed once Capella
// fork epoch is set to a value other than math.MaxUint64
cfg := params.BeaconConfig()
cfg.CapellaForkEpoch = cfg.BellatrixForkEpoch + 1000
cfg.ForkVersionSchedule[bytesutil.ToBytes4(cfg.CapellaForkVersion)] = cfg.BellatrixForkEpoch + 1000
params.OverrideBeaconConfig(cfg)
beaconDB := dbTest.SetupDB(t)
ctx := context.Background()

View File

@@ -186,7 +186,7 @@ func (bs *Server) SubmitAttesterSlashing(ctx context.Context, req *ethpbv1.Attes
return nil, status.Errorf(codes.Internal, "Could not insert attester slashing into pool: %v", err)
}
if !features.Get().DisableBroadcastSlashings {
if err := bs.Broadcaster.Broadcast(ctx, req); err != nil {
if err := bs.Broadcaster.Broadcast(ctx, alphaSlashing); err != nil {
return nil, status.Errorf(codes.Internal, "Could not broadcast slashing object: %v", err)
}
}
@@ -242,7 +242,7 @@ func (bs *Server) SubmitProposerSlashing(ctx context.Context, req *ethpbv1.Propo
return nil, status.Errorf(codes.Internal, "Could not insert proposer slashing into pool: %v", err)
}
if !features.Get().DisableBroadcastSlashings {
if err := bs.Broadcaster.Broadcast(ctx, req); err != nil {
if err := bs.Broadcaster.Broadcast(ctx, alphaSlashing); err != nil {
return nil, status.Errorf(codes.Internal, "Could not broadcast slashing object: %v", err)
}
}

View File

@@ -452,6 +452,9 @@ func TestSubmitAttesterSlashing_Ok(t *testing.T) {
require.Equal(t, 1, len(pendingSlashings))
assert.DeepEqual(t, migration.V1AttSlashingToV1Alpha1(slashing), pendingSlashings[0])
assert.Equal(t, true, broadcaster.BroadcastCalled)
require.Equal(t, 1, len(broadcaster.BroadcastMessages))
_, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashing)
assert.Equal(t, true, ok)
}
func TestSubmitAttesterSlashing_AcrossFork(t *testing.T) {
@@ -526,6 +529,9 @@ func TestSubmitAttesterSlashing_AcrossFork(t *testing.T) {
require.Equal(t, 1, len(pendingSlashings))
assert.DeepEqual(t, migration.V1AttSlashingToV1Alpha1(slashing), pendingSlashings[0])
assert.Equal(t, true, broadcaster.BroadcastCalled)
require.Equal(t, 1, len(broadcaster.BroadcastMessages))
_, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashing)
assert.Equal(t, true, ok)
}
func TestSubmitAttesterSlashing_InvalidSlashing(t *testing.T) {
@@ -633,6 +639,9 @@ func TestSubmitProposerSlashing_Ok(t *testing.T) {
require.Equal(t, 1, len(pendingSlashings))
assert.DeepEqual(t, migration.V1ProposerSlashingToV1Alpha1(slashing), pendingSlashings[0])
assert.Equal(t, true, broadcaster.BroadcastCalled)
require.Equal(t, 1, len(broadcaster.BroadcastMessages))
_, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.ProposerSlashing)
assert.Equal(t, true, ok)
}
func TestSubmitProposerSlashing_AcrossFork(t *testing.T) {
@@ -695,6 +704,13 @@ func TestSubmitProposerSlashing_AcrossFork(t *testing.T) {
_, err = s.SubmitProposerSlashing(ctx, slashing)
require.NoError(t, err)
pendingSlashings := s.SlashingsPool.PendingProposerSlashings(ctx, bs, true)
require.Equal(t, 1, len(pendingSlashings))
assert.DeepEqual(t, migration.V1ProposerSlashingToV1Alpha1(slashing), pendingSlashings[0])
assert.Equal(t, true, broadcaster.BroadcastCalled)
require.Equal(t, 1, len(broadcaster.BroadcastMessages))
_, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.ProposerSlashing)
assert.Equal(t, true, ok)
}
func TestSubmitProposerSlashing_InvalidSlashing(t *testing.T) {

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
@@ -60,43 +63,58 @@ func (vs *Server) setExecutionData(ctx context.Context, blk interfaces.SignedBea
return errors.Wrap(err, "failed to get execution payload")
}
// Compare payload values between local and builder. Default to the local value if it is higher.
localValue, err := localPayload.Value()
v, err := localPayload.Value()
if err != nil {
return errors.Wrap(err, "failed to get local payload value")
}
builderValue, err := builderPayload.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)
if err != nil {
return errors.Wrap(err, "failed to match withdrawals root")
}
// Use builder payload if the following in true:
// builder_bid_value * 100 > local_block_value * (local-block-value-boost + 100)
boost := params.BeaconConfig().LocalBlockValueBoost
higherValueBuilder := builderValue*100 > localValue*(100+boost)
// If we can't get the builder value, just use local block.
if builderValue.Cmp(localValue) > 0 && withdrawalsMatched { // Builder value is higher and withdrawals match.
if higherValueBuilder && withdrawalsMatched { // Builder value is higher and withdrawals match.
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
}
}
log.WithFields(logrus.Fields{
"localValue": localValue,
"builderValue": builderValue,
}).Warn("Proposer: using local execution payload because higher value")
if !higherValueBuilder {
log.WithFields(logrus.Fields{
"localGweiValue": localValue,
"localBoostPercentage": boost,
"builderGweiValue": builderValue,
}).Warn("Proposer: using local execution payload because higher value")
}
return blk.SetExecution(localPayload)
default: // Bellatrix case.
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)
@@ -137,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

@@ -29,9 +29,12 @@ import (
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/util"
"github.com/prysmaticlabs/prysm/v4/time/slots"
logTest "github.com/sirupsen/logrus/hooks/test"
)
func TestServer_setExecutionData(t *testing.T) {
hook := logTest.NewGlobal()
ctx := context.Background()
cfg := params.BeaconConfig().Copy()
cfg.BellatrixForkEpoch = 0
@@ -66,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) {
@@ -113,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)
@@ -138,6 +143,7 @@ func TestServer_setExecutionData(t *testing.T) {
require.NoError(t, err)
wr, err := ssz.WithdrawalSliceRoot(withdrawals, fieldparams.MaxWithdrawalsPerPayload)
require.NoError(t, err)
builderValue := bytesutil.ReverseByteOrder(big.NewInt(1e9).Bytes())
bid := &ethpb.BuilderBidCapella{
Header: &v1.ExecutionPayloadHeaderCapella{
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
@@ -154,7 +160,7 @@ func TestServer_setExecutionData(t *testing.T) {
WithdrawalsRoot: wr[:],
},
Pubkey: sk.PublicKey().Marshal(),
Value: bytesutil.PadTo([]byte{1}, 32),
Value: bytesutil.PadTo(builderValue, 32),
}
d := params.BeaconConfig().DomainApplicationBuilder
domain, err := signing.ComputeDomain(d, nil, nil)
@@ -166,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
@@ -183,17 +190,35 @@ func TestServer_setExecutionData(t *testing.T) {
t.Run("Builder configured. Local block has higher value", func(t *testing.T) {
blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
require.NoError(t, err)
vs.ExecutionEngineCaller = &powtesting.EngineClient{PayloadIDBytes: id, ExecutionPayloadCapella: &v1.ExecutionPayloadCapella{BlockNumber: 3}, BlockValue: big.NewInt(3)}
vs.ExecutionEngineCaller = &powtesting.EngineClient{PayloadIDBytes: id, ExecutionPayloadCapella: &v1.ExecutionPayloadCapella{BlockNumber: 3}, BlockValue: big.NewInt(2 * 1e9)}
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=1 localBoostPercentage=0 localGweiValue=2")
})
t.Run("Builder configured. Local block and boost has higher value", func(t *testing.T) {
cfg := params.BeaconConfig().Copy()
cfg.LocalBlockValueBoost = 1 // 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}, BlockValue: big.NewInt(1e9)}
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=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))
@@ -203,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

@@ -27,6 +27,11 @@ var (
Usage: "Number of total skip slot to fallback from using relay/builder to local execution engine for block construction in last epoch rolling window",
Value: 8,
}
LocalBlockValueBoost = &cli.Float64Flag{
Name: "local-block-value-boost",
Usage: "A percentage boost for local block construction. This is used to prioritize local block construction over relay/builder block construction" +
"Boost is an additional percentage to multiple local block value. Use builder block if: builder_bid_value * 100 > local_block_value * (local-block-value-boost + 100)",
}
// ExecutionEngineEndpoint provides an HTTP access endpoint to connect to an execution client on the execution layer
ExecutionEngineEndpoint = &cli.StringFlag{
Name: "execution-endpoint",

View File

@@ -5,7 +5,6 @@ import (
"fmt"
"os"
"path/filepath"
"runtime"
runtimeDebug "runtime/debug"
gethlog "github.com/ethereum/go-ethereum/log"
@@ -89,6 +88,7 @@ var appFlags = []cli.Flag{
cmd.P2PHostDNS,
cmd.P2PMaxPeers,
cmd.P2PPrivKey,
cmd.P2PStaticID,
cmd.P2PMetadata,
cmd.P2PAllowList,
cmd.P2PDenyList,
@@ -197,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

@@ -149,6 +149,7 @@ var appHelpFlagGroups = []flagGroup{
cmd.P2PHostDNS,
cmd.P2PMaxPeers,
cmd.P2PPrivKey,
cmd.P2PStaticID,
cmd.P2PMetadata,
cmd.P2PAllowList,
cmd.P2PDenyList,

View File

@@ -144,6 +144,11 @@ var (
Usage: "The file containing the private key to use in communications with other peers.",
Value: "",
}
P2PStaticID = &cli.BoolFlag{
Name: "p2p-static-id",
Usage: "Enables the peer id of the node to be fixed by saving the generated network key to the default key path.",
Value: false,
}
// P2PMetadata defines a flag to specify the location of the peer metadata file.
P2PMetadata = &cli.StringFlag{
Name: "p2p-metadata",
@@ -167,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.",
@@ -217,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,39 +217,79 @@ func setGlobalParams() error {
return params.SetActive(cfg.Copy())
}
func generateGenesis(ctx context.Context) (*ethpb.BeaconState, error) {
genesisTime := generateGenesisStateFlags.GenesisTime
numValidators := generateGenesisStateFlags.NumValidators
depositJsonFile := generateGenesisStateFlags.DepositJsonFile
overrideEth1Data := generateGenesisStateFlags.OverrideEth1Data
if depositJsonFile != "" {
expanded, err := file.ExpandPath(depositJsonFile)
if err != nil {
return nil, err
}
inputJSON, err := os.Open(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)
return genesisStateFromJSONValidators(ctx, inputJSON, genesisTime)
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()")
}
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)
v, err := version.FromString(f.ForkName)
if err != nil {
return nil, err
}
if overrideEth1Data {
opts := make([]interop.PremineGenesisOpt, 0)
nv := f.NumValidators
if f.DepositJsonFile != "" {
expanded, err := file.ExpandPath(f.DepositJsonFile)
if err != nil {
return nil, err
}
log.Printf("reading deposits from JSON at %s", expanded)
b, err := os.ReadFile(expanded) // #nosec G304
if err != nil {
return nil, err
}
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 {
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)
}
}
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 {
@@ -305,67 +311,63 @@ func generateGenesis(ctx context.Context) (*ethpb.BeaconState, error) {
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

@@ -207,6 +207,7 @@ type BeaconChainConfig struct {
// Mev-boost circuit breaker
MaxBuilderConsecutiveMissedSlots primitives.Slot // MaxBuilderConsecutiveMissedSlots defines the number of consecutive skip slot to fallback from using relay/builder to local execution engine for block construction.
MaxBuilderEpochMissedSlots primitives.Slot // MaxBuilderEpochMissedSlots is defines the number of total skip slot (per epoch rolling windows) to fallback from using relay/builder to local execution engine for block construction.
LocalBlockValueBoost uint64 // LocalBlockValueBoost is the value boost for local block construction. This is used to prioritize local block construction over relay/builder block construction.
// Execution engine timeout value
ExecutionEngineTimeoutValue uint64 // ExecutionEngineTimeoutValue defines the seconds to wait before timing out engine endpoints with execution payload execution semantics (newPayload, forkchoiceUpdated).

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 {

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