Compare commits

...

24 Commits

Author SHA1 Message Date
Potuz
b8cd77945d Move slasher handling down the pipeline (#14322) 2024-08-08 13:51:40 +00:00
Radosław Kapka
9a7f521f8a Fix state upgrade log (#14316) 2024-08-07 15:32:54 +00:00
Preston Van Loon
102f94f914 Clean up: Deduplicate fork spec tests pasta (#14289)
* Deduplicate code in spectest operations

Deduplicate code in blockchain core packages.

* Deduplicate code in blockchain core packages.
2024-08-07 15:16:37 +00:00
james-prysm
0c0a497651 engine_getPayloadBodiesByRangeV1 - fix, adding hexutil encoding on request parameters (#14314)
* adding hexutil encoding on request parameters

* fix for test

* fixing more tests

* deepsource fix
2024-08-07 15:05:24 +00:00
Sammy Rosso
e0785a8939 HTTP endpoint for GetValidatorActiveSetChanges (#14264)
* add GetValidatorActiveSetChanges

* fix linter

* fix errors

* James' review

* use stater

* fix merge conflict errors

* remove validator from func names

* rename util funcs
2024-08-06 11:56:43 +00:00
Potuz
af098e737e Check locally for min-bid and min-bid-difference (#14205)
* Check locally for min-bid and min-bid-difference

* fix tests

* Terence's fix

Co-authored-by: terence <terence@prysmaticlabs.com>

* Terence's review

* fix tests

---------

Co-authored-by: terence <terence@prysmaticlabs.com>
2024-08-05 14:20:26 +00:00
Potuz
1e4ede5585 Reduce cognitive complexity of ReceiveBlock (#14296)
* Reduce cognitive complexity of ReceiveBlock

* Remove stray line

Co-authored-by: terence <terence@prysmaticlabs.com>

---------

Co-authored-by: terence <terence@prysmaticlabs.com>
2024-08-05 12:44:49 +00:00
Sammy Rosso
fb2620364a HTTP endpoint for GetChainHead (#14262)
* add getChainHead endpoint

* James' review

* Radek' review

---------

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2024-08-05 08:49:37 +00:00
Ryan
68b38b6666 remove broken link (#14291)
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2024-08-02 21:36:09 +00:00
Jun Song
ff3e0856a1 fix(tests): Correct misleading variable names and expressions in test files (#14292)
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2024-08-02 21:32:53 +00:00
Potuz
85f334b663 Return parent root from forkchoice (#14293) 2024-08-02 14:36:37 +00:00
Potuz
10f520accb process Electra operations in the right order (#14294)
* process Electra operations in the right order

* godoc
2024-08-02 14:30:52 +00:00
Sammy Rosso
836608537e HTTP endpoint for GetValidatorParticipation (#14261)
* add endpoint

* remove canonicalFetcher

* Add replayerBuilder to coreService

* fix endpoint template

* fix string query params

* gaz

* fix linter

* test fix

* Radek' review

* remove unused request struct + gaz

* linter

* gaz

---------

Co-authored-by: rkapka <radoslaw.kapka@gmail.com>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2024-08-02 13:55:17 +00:00
Radosław Kapka
13e09c58f6 Committee-aware attestation packing (#14245)
* initial algorithm

* ready for review

* feature flag

* typo

* review

* comment fix

* fix TestProposer_sort_DifferentCommittees

* flag usage
2024-08-02 11:53:43 +00:00
Potuz
600ca08aa8 Use the beacon block root to verify the prestate (#14290) 2024-08-01 20:22:37 +00:00
james-prysm
0ed74b3c4a removing explicit deadlines on push proposer settings (#14285)
* removing deadlines

* fixing mock interface implementation
2024-07-31 18:28:50 +00:00
Potuz
7c69a9aa1c Refactor CommitteeAssignments to get all committees at once (#14284) 2024-07-31 16:01:19 +00:00
Potuz
c50cfb044a Get all beacon committees at once (#14282)
* Get all beacon committees at once

* Update beacon-chain/core/helpers/beacon_committee.go

---------

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2024-07-31 12:52:23 +00:00
ethfanWilliam
38d4e179ba chore: fix unfound image (#14167)
* Remove Feature Flag From Prater (#12082)

* Use Epoch boundary cache to retrieve balances (#12083)

* Use Epoch boundary cache to retrieve balances

* save boundary states before inserting to forkchoice

* move up last block save

* remove boundary checks on balances

* fix ordering

---------

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>

* chore: fix unfound image

* fix link

---------

Co-authored-by: Nishant Das <nishdas93@gmail.com>
Co-authored-by: Potuz <potuz@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2024-07-31 12:10:18 +00:00
shangchengbabaiban
be80728320 chore: fix some comments (#14270)
Signed-off-by: shangchengbabaiban <shuang.cui@live.cn>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2024-07-31 11:06:15 +00:00
james-prysm
09028033c0 adding corrected returns under handle error (#14280) 2024-07-30 22:02:25 +00:00
james-prysm
52c036c3ab missed adding electra process epoch on state replay - devnet 1 (#14272)
* missed adding electra process epoch on state replay

* abstracting common functionality as to not forget in processing

* removing unnessesary error check

* breaking out core process slots logic out

* fixing small bug in epoch processing

* adding small log to fix linting and make use of logrus in the package

* adding some unit tests for process epoch

* potuz's review feedback

* reversing processing order based on feedback
2024-07-30 20:52:55 +00:00
james-prysm
2fc7cdeba7 Push proposer settings every slot (#14155)
* test push settings every slot

* updating comments

* changing design a little bit based on feedback

* adding corrected deadline
2024-07-30 19:30:56 +00:00
james-prysm
6f7976766d moving cloner functions to beacon_block.go (#14265)
* moving cloner functions for beacon block and adding fuzzing tests

* fixing test
2024-07-29 15:13:21 +00:00
164 changed files with 4694 additions and 4209 deletions

View File

@@ -225,3 +225,19 @@ type IndividualVote struct {
InclusionDistance string `json:"inclusion_distance"`
InactivityScore string `json:"inactivity_score"`
}
type ChainHead struct {
HeadSlot string `json:"head_slot"`
HeadEpoch string `json:"head_epoch"`
HeadBlockRoot string `json:"head_block_root"`
FinalizedSlot string `json:"finalized_slot"`
FinalizedEpoch string `json:"finalized_epoch"`
FinalizedBlockRoot string `json:"finalized_block_root"`
JustifiedSlot string `json:"justified_slot"`
JustifiedEpoch string `json:"justified_epoch"`
JustifiedBlockRoot string `json:"justified_block_root"`
PreviousJustifiedSlot string `json:"previous_justified_slot"`
PreviousJustifiedEpoch string `json:"previous_justified_epoch"`
PreviousJustifiedBlockRoot string `json:"previous_justified_block_root"`
OptimisticStatus bool `json:"optimistic_status"`
}

View File

@@ -118,3 +118,34 @@ type GetValidatorPerformanceResponse struct {
MissingValidators [][]byte `json:"missing_validators,omitempty"`
InactivityScores []uint64 `json:"inactivity_scores,omitempty"`
}
type GetValidatorParticipationResponse struct {
Epoch string `json:"epoch"`
Finalized bool `json:"finalized"`
Participation *ValidatorParticipation `json:"participation"`
}
type ValidatorParticipation struct {
GlobalParticipationRate string `json:"global_participation_rate" deprecated:"true"`
VotedEther string `json:"voted_ether" deprecated:"true"`
EligibleEther string `json:"eligible_ether" deprecated:"true"`
CurrentEpochActiveGwei string `json:"current_epoch_active_gwei"`
CurrentEpochAttestingGwei string `json:"current_epoch_attesting_gwei"`
CurrentEpochTargetAttestingGwei string `json:"current_epoch_target_attesting_gwei"`
PreviousEpochActiveGwei string `json:"previous_epoch_active_gwei"`
PreviousEpochAttestingGwei string `json:"previous_epoch_attesting_gwei"`
PreviousEpochTargetAttestingGwei string `json:"previous_epoch_target_attesting_gwei"`
PreviousEpochHeadAttestingGwei string `json:"previous_epoch_head_attesting_gwei"`
}
type ActiveSetChanges struct {
Epoch string `json:"epoch"`
ActivatedPublicKeys []string `json:"activated_public_keys"`
ActivatedIndices []string `json:"activated_indices"`
ExitedPublicKeys []string `json:"exited_public_keys"`
ExitedIndices []string `json:"exited_indices"`
SlashedPublicKeys []string `json:"slashed_public_keys"`
SlashedIndices []string `json:"slashed_indices"`
EjectedPublicKeys []string `json:"ejected_public_keys"`
EjectedIndices []string `json:"ejected_indices"`
}

View File

@@ -99,3 +99,10 @@ func (s *Service) FinalizedBlockHash() [32]byte {
defer s.cfg.ForkChoiceStore.RUnlock()
return s.cfg.ForkChoiceStore.FinalizedPayloadBlockHash()
}
// ParentRoot wraps a call to the corresponding method in forkchoice
func (s *Service) ParentRoot(root [32]byte) ([32]byte, error) {
s.cfg.ForkChoiceStore.RLock()
defer s.cfg.ForkChoiceStore.RUnlock()
return s.cfg.ForkChoiceStore.ParentRoot(root)
}

View File

@@ -142,7 +142,7 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []consensusblocks.ROBlo
b := blks[0].Block()
// Retrieve incoming block's pre state.
if err := s.verifyBlkPreState(ctx, b); err != nil {
if err := s.verifyBlkPreState(ctx, b.ParentRoot()); err != nil {
return err
}
preState, err := s.cfg.StateGen.StateByRootInitialSync(ctx, b.ParentRoot())

View File

@@ -14,6 +14,7 @@ import (
forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/features"
field_params "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
@@ -285,7 +286,7 @@ func (s *Service) getBlockPreState(ctx context.Context, b interfaces.ReadOnlyBea
defer span.End()
// Verify incoming block has a valid pre state.
if err := s.verifyBlkPreState(ctx, b); err != nil {
if err := s.verifyBlkPreState(ctx, b.ParentRoot()); err != nil {
return nil, err
}
@@ -311,11 +312,10 @@ func (s *Service) getBlockPreState(ctx context.Context, b interfaces.ReadOnlyBea
}
// verifyBlkPreState validates input block has a valid pre-state.
func (s *Service) verifyBlkPreState(ctx context.Context, b interfaces.ReadOnlyBeaconBlock) error {
func (s *Service) verifyBlkPreState(ctx context.Context, parentRoot [field_params.RootLength]byte) error {
ctx, span := trace.StartSpan(ctx, "blockChain.verifyBlkPreState")
defer span.End()
parentRoot := b.ParentRoot()
// Loosen the check to HasBlock because state summary gets saved in batches
// during initial syncing. There's no risk given a state summary object is just a
// subset of the block object.

View File

@@ -117,7 +117,7 @@ func TestCachedPreState_CanGetFromStateSummary(t *testing.T) {
require.NoError(t, service.cfg.BeaconDB.SaveStateSummary(ctx, &ethpb.StateSummary{Slot: 1, Root: root[:]}))
require.NoError(t, service.cfg.StateGen.SaveState(ctx, root, st))
require.NoError(t, service.verifyBlkPreState(ctx, wsb.Block()))
require.NoError(t, service.verifyBlkPreState(ctx, wsb.Block().ParentRoot()))
}
func TestFillForkChoiceMissingBlocks_CanSave(t *testing.T) {

View File

@@ -77,59 +77,20 @@ func (s *Service) ReceiveBlock(ctx context.Context, block interfaces.ReadOnlySig
if err != nil {
return err
}
rob, err := blocks.NewROBlockWithRoot(block, blockRoot)
if err != nil {
return err
}
preState, err := s.getBlockPreState(ctx, blockCopy.Block())
if err != nil {
return errors.Wrap(err, "could not get block's prestate")
}
// Save current justified and finalized epochs for future use.
currStoreJustifiedEpoch := s.CurrentJustifiedCheckpt().Epoch
currStoreFinalizedEpoch := s.FinalizedCheckpt().Epoch
currentEpoch := coreTime.CurrentEpoch(preState)
preStateVersion, preStateHeader, err := getStateVersionAndPayload(preState)
currentCheckpoints := s.saveCurrentCheckpoints(preState)
postState, isValidPayload, err := s.validateExecutionAndConsensus(ctx, preState, blockCopy, blockRoot)
if err != nil {
return err
}
eg, _ := errgroup.WithContext(ctx)
var postState state.BeaconState
eg.Go(func() error {
var err error
postState, err = s.validateStateTransition(ctx, preState, blockCopy)
if err != nil {
return errors.Wrap(err, "failed to validate consensus state transition function")
}
return nil
})
var isValidPayload bool
eg.Go(func() error {
var err error
isValidPayload, err = s.validateExecutionOnBlock(ctx, preStateVersion, preStateHeader, blockCopy, blockRoot)
if err != nil {
return errors.Wrap(err, "could not notify the engine of the new payload")
}
return nil
})
if err := eg.Wait(); err != nil {
daWaitedTime, err := s.handleDA(ctx, blockCopy, blockRoot, avs)
if err != nil {
return err
}
daStartTime := time.Now()
if avs != nil {
if err := avs.IsDataAvailable(ctx, s.CurrentSlot(), rob); err != nil {
return errors.Wrap(err, "could not validate blob data availability (AvailabilityStore.IsDataAvailable)")
}
} else {
if err := s.isDataAvailable(ctx, blockRoot, blockCopy); err != nil {
return errors.Wrap(err, "could not validate blob data availability")
}
}
daWaitedTime := time.Since(daStartTime)
dataAvailWaitedTime.Observe(float64(daWaitedTime.Milliseconds()))
// Defragment the state before continuing block processing.
s.defragmentState(postState)
@@ -151,29 +112,9 @@ func (s *Service) ReceiveBlock(ctx context.Context, block interfaces.ReadOnlySig
tracing.AnnotateError(span, err)
return err
}
if coreTime.CurrentEpoch(postState) > currentEpoch && s.cfg.ForkChoiceStore.IsCanonical(blockRoot) {
headSt, err := s.HeadState(ctx)
if err != nil {
return errors.Wrap(err, "could not get head state")
}
if err := reportEpochMetrics(ctx, postState, headSt); err != nil {
log.WithError(err).Error("could not report epoch metrics")
}
if err := s.updateCheckpoints(ctx, currentCheckpoints, preState, postState, blockRoot); err != nil {
return err
}
if err := s.updateJustificationOnBlock(ctx, preState, postState, currStoreJustifiedEpoch); err != nil {
return errors.Wrap(err, "could not update justified checkpoint")
}
newFinalized, err := s.updateFinalizationOnBlock(ctx, preState, postState, currStoreFinalizedEpoch)
if err != nil {
return errors.Wrap(err, "could not update finalized checkpoint")
}
// Send finalized events and finalized deposits in the background
if newFinalized {
// hook to process all post state finalization tasks
s.executePostFinalizationTasks(ctx, postState)
}
// If slasher is configured, forward the attestations in the block via an event feed for processing.
if features.Get().EnableSlasher {
go s.sendBlockAttestationsToSlasher(blockCopy, preState)
@@ -193,31 +134,140 @@ func (s *Service) ReceiveBlock(ctx context.Context, block interfaces.ReadOnlySig
if err := s.handleCaches(); err != nil {
return err
}
s.reportPostBlockProcessing(blockCopy, blockRoot, receivedTime, daWaitedTime)
return nil
}
type ffgCheckpoints struct {
j, f, c primitives.Epoch
}
func (s *Service) saveCurrentCheckpoints(state state.BeaconState) (cp ffgCheckpoints) {
// Save current justified and finalized epochs for future use.
cp.j = s.CurrentJustifiedCheckpt().Epoch
cp.f = s.FinalizedCheckpt().Epoch
cp.c = coreTime.CurrentEpoch(state)
return
}
func (s *Service) updateCheckpoints(
ctx context.Context,
cp ffgCheckpoints,
preState, postState state.BeaconState,
blockRoot [32]byte,
) error {
if coreTime.CurrentEpoch(postState) > cp.c && s.cfg.ForkChoiceStore.IsCanonical(blockRoot) {
headSt, err := s.HeadState(ctx)
if err != nil {
return errors.Wrap(err, "could not get head state")
}
if err := reportEpochMetrics(ctx, postState, headSt); err != nil {
log.WithError(err).Error("could not report epoch metrics")
}
}
if err := s.updateJustificationOnBlock(ctx, preState, postState, cp.j); err != nil {
return errors.Wrap(err, "could not update justified checkpoint")
}
newFinalized, err := s.updateFinalizationOnBlock(ctx, preState, postState, cp.f)
if err != nil {
return errors.Wrap(err, "could not update finalized checkpoint")
}
// Send finalized events and finalized deposits in the background
if newFinalized {
// hook to process all post state finalization tasks
s.executePostFinalizationTasks(ctx, postState)
}
return nil
}
func (s *Service) validateExecutionAndConsensus(
ctx context.Context,
preState state.BeaconState,
block interfaces.SignedBeaconBlock,
blockRoot [32]byte,
) (state.BeaconState, bool, error) {
preStateVersion, preStateHeader, err := getStateVersionAndPayload(preState)
if err != nil {
return nil, false, err
}
eg, _ := errgroup.WithContext(ctx)
var postState state.BeaconState
eg.Go(func() error {
var err error
postState, err = s.validateStateTransition(ctx, preState, block)
if err != nil {
return errors.Wrap(err, "failed to validate consensus state transition function")
}
return nil
})
var isValidPayload bool
eg.Go(func() error {
var err error
isValidPayload, err = s.validateExecutionOnBlock(ctx, preStateVersion, preStateHeader, block, blockRoot)
if err != nil {
return errors.Wrap(err, "could not notify the engine of the new payload")
}
return nil
})
if err := eg.Wait(); err != nil {
return nil, false, err
}
return postState, isValidPayload, nil
}
func (s *Service) handleDA(
ctx context.Context,
block interfaces.SignedBeaconBlock,
blockRoot [32]byte,
avs das.AvailabilityStore,
) (time.Duration, error) {
daStartTime := time.Now()
if avs != nil {
rob, err := blocks.NewROBlockWithRoot(block, blockRoot)
if err != nil {
return 0, err
}
if err := avs.IsDataAvailable(ctx, s.CurrentSlot(), rob); err != nil {
return 0, errors.Wrap(err, "could not validate blob data availability (AvailabilityStore.IsDataAvailable)")
}
} else {
if err := s.isDataAvailable(ctx, blockRoot, block); err != nil {
return 0, errors.Wrap(err, "could not validate blob data availability")
}
}
daWaitedTime := time.Since(daStartTime)
dataAvailWaitedTime.Observe(float64(daWaitedTime.Milliseconds()))
return daWaitedTime, nil
}
func (s *Service) reportPostBlockProcessing(
block interfaces.SignedBeaconBlock,
blockRoot [32]byte,
receivedTime time.Time,
daWaitedTime time.Duration,
) {
// Reports on block and fork choice metrics.
cp := s.cfg.ForkChoiceStore.FinalizedCheckpoint()
finalized := &ethpb.Checkpoint{Epoch: cp.Epoch, Root: bytesutil.SafeCopyBytes(cp.Root[:])}
reportSlotMetrics(blockCopy.Block().Slot(), s.HeadSlot(), s.CurrentSlot(), finalized)
reportSlotMetrics(block.Block().Slot(), s.HeadSlot(), s.CurrentSlot(), finalized)
// Log block sync status.
cp = s.cfg.ForkChoiceStore.JustifiedCheckpoint()
justified := &ethpb.Checkpoint{Epoch: cp.Epoch, Root: bytesutil.SafeCopyBytes(cp.Root[:])}
if err := logBlockSyncStatus(blockCopy.Block(), blockRoot, justified, finalized, receivedTime, uint64(s.genesisTime.Unix()), daWaitedTime); err != nil {
if err := logBlockSyncStatus(block.Block(), blockRoot, justified, finalized, receivedTime, uint64(s.genesisTime.Unix()), daWaitedTime); err != nil {
log.WithError(err).Error("Unable to log block sync status")
}
// Log payload data
if err := logPayload(blockCopy.Block()); err != nil {
if err := logPayload(block.Block()); err != nil {
log.WithError(err).Error("Unable to log debug block payload data")
}
// Log state transition data.
if err := logStateTransitionData(blockCopy.Block()); err != nil {
if err := logStateTransitionData(block.Block()); err != nil {
log.WithError(err).Error("Unable to log state transition data")
}
timeWithoutDaWait := time.Since(receivedTime) - daWaitedTime
chainServiceProcessingTime.Observe(float64(timeWithoutDaWait.Milliseconds()))
return nil
}
func (s *Service) executePostFinalizationTasks(ctx context.Context, finalizedState state.BeaconState) {

View File

@@ -1,3 +1,5 @@
package blocks
var ProcessBLSToExecutionChange = processBLSToExecutionChange
var VerifyBlobCommitmentCount = verifyBlobCommitmentCount

View File

@@ -2,11 +2,13 @@ package blocks
import (
"bytes"
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
field_params "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
consensus_types "github.com/prysmaticlabs/prysm/v5/consensus-types"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
@@ -200,13 +202,13 @@ func ValidatePayload(st state.BeaconState, payload interfaces.ExecutionData) err
// block_hash=payload.block_hash,
// transactions_root=hash_tree_root(payload.transactions),
// )
func ProcessPayload(st state.BeaconState, payload interfaces.ExecutionData) (state.BeaconState, error) {
var err error
if st.Version() >= version.Capella {
st, err = ProcessWithdrawals(st, payload)
if err != nil {
return nil, errors.Wrap(err, "could not process withdrawals")
}
func ProcessPayload(st state.BeaconState, body interfaces.ReadOnlyBeaconBlockBody) (state.BeaconState, error) {
payload, err := body.Execution()
if err != nil {
return nil, err
}
if err := verifyBlobCommitmentCount(body); err != nil {
return nil, err
}
if err := ValidatePayloadWhenMergeCompletes(st, payload); err != nil {
return nil, err
@@ -220,70 +222,20 @@ func ProcessPayload(st state.BeaconState, payload interfaces.ExecutionData) (sta
return st, nil
}
// ValidatePayloadHeaderWhenMergeCompletes validates the payload header when the merge completes.
func ValidatePayloadHeaderWhenMergeCompletes(st state.BeaconState, header interfaces.ExecutionData) error {
// Skip validation if the state is not merge compatible.
complete, err := IsMergeTransitionComplete(st)
if err != nil {
return err
}
if !complete {
func verifyBlobCommitmentCount(body interfaces.ReadOnlyBeaconBlockBody) error {
if body.Version() < version.Deneb {
return nil
}
// Validate current header's parent hash matches state header's block hash.
h, err := st.LatestExecutionPayloadHeader()
kzgs, err := body.BlobKzgCommitments()
if err != nil {
return err
}
if !bytes.Equal(header.ParentHash(), h.BlockHash()) {
return ErrInvalidPayloadBlockHash
if len(kzgs) > field_params.MaxBlobsPerBlock {
return fmt.Errorf("too many kzg commitments in block: %d", len(kzgs))
}
return nil
}
// ValidatePayloadHeader validates the payload header.
func ValidatePayloadHeader(st state.BeaconState, header interfaces.ExecutionData) error {
// Validate header's random mix matches with state in current epoch
random, err := helpers.RandaoMix(st, time.CurrentEpoch(st))
if err != nil {
return err
}
if !bytes.Equal(header.PrevRandao(), random) {
return ErrInvalidPayloadPrevRandao
}
// Validate header's timestamp matches with state in current slot.
t, err := slots.ToTime(st.GenesisTime(), st.Slot())
if err != nil {
return err
}
if header.Timestamp() != uint64(t.Unix()) {
return ErrInvalidPayloadTimeStamp
}
return nil
}
// ProcessPayloadHeader processes the payload header.
func ProcessPayloadHeader(st state.BeaconState, header interfaces.ExecutionData) (state.BeaconState, error) {
var err error
if st.Version() >= version.Capella {
st, err = ProcessWithdrawals(st, header)
if err != nil {
return nil, errors.Wrap(err, "could not process withdrawals")
}
}
if err := ValidatePayloadHeaderWhenMergeCompletes(st, header); err != nil {
return nil, err
}
if err := ValidatePayloadHeader(st, header); err != nil {
return nil, err
}
if err := st.SetLatestExecutionPayloadHeader(header); err != nil {
return nil, err
}
return st, nil
}
// GetBlockPayloadHash returns the hash of the execution payload of the block
func GetBlockPayloadHash(blk interfaces.ReadOnlyBeaconBlock) ([32]byte, error) {
var payloadHash [32]byte

View File

@@ -1,6 +1,7 @@
package blocks_test
import (
"fmt"
"testing"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
@@ -13,6 +14,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v5/encoding/ssz"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
"github.com/prysmaticlabs/prysm/v5/time/slots"
@@ -581,14 +583,18 @@ func Test_ProcessPayload(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
wrappedPayload, err := consensusblocks.WrappedExecutionPayload(tt.payload)
body, err := consensusblocks.NewBeaconBlockBody(&ethpb.BeaconBlockBodyBellatrix{
ExecutionPayload: tt.payload,
})
require.NoError(t, err)
st, err := blocks.ProcessPayload(st, wrappedPayload)
st, err := blocks.ProcessPayload(st, body)
if err != nil {
require.Equal(t, tt.err.Error(), err.Error())
} else {
require.Equal(t, tt.err, err)
want, err := consensusblocks.PayloadToHeader(wrappedPayload)
payload, err := body.Execution()
require.NoError(t, err)
want, err := consensusblocks.PayloadToHeader(payload)
require.Equal(t, tt.err, err)
h, err := st.LatestExecutionPayloadHeader()
require.NoError(t, err)
@@ -609,13 +615,15 @@ func Test_ProcessPayloadCapella(t *testing.T) {
random, err := helpers.RandaoMix(st, time.CurrentEpoch(st))
require.NoError(t, err)
payload.PrevRandao = random
wrapped, err := consensusblocks.WrappedExecutionPayloadCapella(payload)
body, err := consensusblocks.NewBeaconBlockBody(&ethpb.BeaconBlockBodyCapella{
ExecutionPayload: payload,
})
require.NoError(t, err)
_, err = blocks.ProcessPayload(st, wrapped)
_, err = blocks.ProcessPayload(st, body)
require.NoError(t, err)
}
func Test_ProcessPayloadHeader(t *testing.T) {
func Test_ProcessPayload_Blinded(t *testing.T) {
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
random, err := helpers.RandaoMix(st, time.CurrentEpoch(st))
require.NoError(t, err)
@@ -663,7 +671,13 @@ func Test_ProcessPayloadHeader(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
st, err := blocks.ProcessPayloadHeader(st, tt.header)
p, ok := tt.header.Proto().(*enginev1.ExecutionPayloadHeader)
require.Equal(t, true, ok)
body, err := consensusblocks.NewBeaconBlockBody(&ethpb.BlindedBeaconBlockBodyBellatrix{
ExecutionPayloadHeader: p,
})
require.NoError(t, err)
st, err := blocks.ProcessPayload(st, body)
if err != nil {
require.Equal(t, tt.err.Error(), err.Error())
} else {
@@ -728,7 +742,7 @@ func Test_ValidatePayloadHeader(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err = blocks.ValidatePayloadHeader(st, tt.header)
err = blocks.ValidatePayload(st, tt.header)
require.Equal(t, tt.err, err)
})
}
@@ -785,7 +799,7 @@ func Test_ValidatePayloadHeaderWhenMergeCompletes(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err = blocks.ValidatePayloadHeaderWhenMergeCompletes(tt.state, tt.header)
err = blocks.ValidatePayloadWhenMergeCompletes(tt.state, tt.header)
require.Equal(t, tt.err, err)
})
}
@@ -906,3 +920,15 @@ func emptyPayloadCapella() *enginev1.ExecutionPayloadCapella {
Withdrawals: make([]*enginev1.Withdrawal, 0),
}
}
func TestVerifyBlobCommitmentCount(t *testing.T) {
b := &ethpb.BeaconBlockDeneb{Body: &ethpb.BeaconBlockBodyDeneb{}}
rb, err := consensusblocks.NewBeaconBlock(b)
require.NoError(t, err)
require.NoError(t, blocks.VerifyBlobCommitmentCount(rb.Body()))
b = &ethpb.BeaconBlockDeneb{Body: &ethpb.BeaconBlockBodyDeneb{BlobKzgCommitments: make([][]byte, fieldparams.MaxBlobsPerBlock+1)}}
rb, err = consensusblocks.NewBeaconBlock(b)
require.NoError(t, err)
require.ErrorContains(t, fmt.Sprintf("too many kzg commitments in block: %d", fieldparams.MaxBlobsPerBlock+1), blocks.VerifyBlobCommitmentCount(rb.Body()))
}

View File

@@ -22,29 +22,31 @@ var (
//
// Spec definition:
//
// def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
// # [Modified in Electra:EIP6110]
// # Disable former deposit mechanism once all prior deposits are processed
// eth1_deposit_index_limit = min(state.eth1_data.deposit_count, state.deposit_requests_start_index)
// if state.eth1_deposit_index < eth1_deposit_index_limit:
// assert len(body.deposits) == min(MAX_DEPOSITS, eth1_deposit_index_limit - state.eth1_deposit_index)
// else:
// assert len(body.deposits) == 0
// def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
// # [Modified in Electra:EIP6110]
// # Disable former deposit mechanism once all prior deposits are processed
// eth1_deposit_index_limit = min(state.eth1_data.deposit_count, state.deposit_requests_start_index)
// if state.eth1_deposit_index < eth1_deposit_index_limit:
// assert len(body.deposits) == min(MAX_DEPOSITS, eth1_deposit_index_limit - state.eth1_deposit_index)
// else:
// assert len(body.deposits) == 0
//
// def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None:
// for operation in operations:
// fn(state, operation)
// def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None:
// for operation in operations:
// fn(state, operation)
//
// for_ops(body.proposer_slashings, process_proposer_slashing)
// for_ops(body.attester_slashings, process_attester_slashing)
// for_ops(body.attestations, process_attestation) # [Modified in Electra:EIP7549]
// for_ops(body.deposits, process_deposit) # [Modified in Electra:EIP7251]
// for_ops(body.voluntary_exits, process_voluntary_exit) # [Modified in Electra:EIP7251]
// for_ops(body.bls_to_execution_changes, process_bls_to_execution_change)
// # [New in Electra:EIP7002:EIP7251]
// for_ops(body.execution_payload.withdrawal_requests, process_execution_layer_withdrawal_request)
// for_ops(body.execution_payload.deposit_requests, process_deposit_requests) # [New in Electra:EIP6110]
// for_ops(body.consolidations, process_consolidation) # [New in Electra:EIP7251]
// for_ops(body.proposer_slashings, process_proposer_slashing)
// for_ops(body.attester_slashings, process_attester_slashing)
// for_ops(body.attestations, process_attestation) # [Modified in Electra:EIP7549]
// for_ops(body.deposits, process_deposit) # [Modified in Electra:EIP7251]
// for_ops(body.voluntary_exits, process_voluntary_exit) # [Modified in Electra:EIP7251]
// for_ops(body.bls_to_execution_changes, process_bls_to_execution_change)
// for_ops(body.execution_payload.deposit_requests, process_deposit_request) # [New in Electra:EIP6110]
// # [New in Electra:EIP7002:EIP7251]
// for_ops(body.execution_payload.withdrawal_requests, process_withdrawal_request)
// # [New in Electra:EIP7251]
// for_ops(body.execution_payload.consolidation_requests, process_consolidation_request)
func ProcessOperations(
ctx context.Context,
st state.BeaconState,
@@ -84,16 +86,14 @@ func ProcessOperations(
if !ok {
return nil, errors.New("could not cast execution data to electra execution data")
}
st, err = ProcessWithdrawalRequests(ctx, st, exe.WithdrawalRequests())
if err != nil {
return nil, errors.Wrap(err, "could not process execution layer withdrawal requests")
}
st, err = ProcessDepositRequests(ctx, st, exe.DepositRequests())
if err != nil {
return nil, errors.Wrap(err, "could not process deposit receipts")
}
st, err = ProcessWithdrawalRequests(ctx, st, exe.WithdrawalRequests())
if err != nil {
return nil, errors.Wrap(err, "could not process execution layer withdrawal requests")
}
if err := ProcessConsolidationRequests(ctx, st, exe.ConsolidationRequests()); err != nil {
return nil, fmt.Errorf("could not process consolidation requests: %w", err)
}

View File

@@ -1,11 +1,15 @@
package electra_test
import (
"context"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/electra"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/config/params"
consensusblocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
@@ -47,3 +51,68 @@ func TestVerifyOperationLengths_Electra(t *testing.T) {
require.ErrorContains(t, "incorrect outstanding deposits in block body", err)
})
}
func TestProcessEpoch_CanProcessElectra(t *testing.T) {
st, _ := util.DeterministicGenesisStateElectra(t, params.BeaconConfig().MaxValidatorsPerCommittee)
require.NoError(t, st.SetSlot(10*params.BeaconConfig().SlotsPerEpoch))
require.NoError(t, st.SetDepositBalanceToConsume(100))
amountAvailForProcessing := helpers.ActivationExitChurnLimit(1_000 * 1e9)
deps := make([]*ethpb.PendingBalanceDeposit, 20)
for i := 0; i < len(deps); i += 1 {
deps[i] = &ethpb.PendingBalanceDeposit{
Amount: uint64(amountAvailForProcessing) / 10,
Index: primitives.ValidatorIndex(i),
}
}
require.NoError(t, st.SetPendingBalanceDeposits(deps))
require.NoError(t, st.SetPendingConsolidations([]*ethpb.PendingConsolidation{
{
SourceIndex: 2,
TargetIndex: 3,
},
{
SourceIndex: 0,
TargetIndex: 1,
},
}))
err := electra.ProcessEpoch(context.Background(), st)
require.NoError(t, err)
require.Equal(t, uint64(0), st.Slashings()[2], "Unexpected slashed balance")
b := st.Balances()
require.Equal(t, params.BeaconConfig().MaxValidatorsPerCommittee, uint64(len(b)))
require.Equal(t, uint64(44799839993), b[0])
s, err := st.InactivityScores()
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MaxValidatorsPerCommittee, uint64(len(s)))
p, err := st.PreviousEpochParticipation()
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MaxValidatorsPerCommittee, uint64(len(p)))
p, err = st.CurrentEpochParticipation()
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MaxValidatorsPerCommittee, uint64(len(p)))
sc, err := st.CurrentSyncCommittee()
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().SyncCommitteeSize, uint64(len(sc.Pubkeys)))
sc, err = st.NextSyncCommittee()
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().SyncCommitteeSize, uint64(len(sc.Pubkeys)))
res, err := st.DepositBalanceToConsume()
require.NoError(t, err)
require.Equal(t, primitives.Gwei(100), res)
// Half of the balance deposits should have been processed.
remaining, err := st.PendingBalanceDeposits()
require.NoError(t, err)
require.Equal(t, 10, len(remaining))
num, err := st.NumPendingConsolidations()
require.NoError(t, err)
require.Equal(t, uint64(2), num)
}

View File

@@ -82,6 +82,47 @@ func AttestationCommittees(ctx context.Context, st state.ReadOnlyBeaconState, at
return committees, nil
}
// BeaconCommittees returns the list of all beacon committees for a given state at a given slot.
func BeaconCommittees(ctx context.Context, state state.ReadOnlyBeaconState, slot primitives.Slot) ([][]primitives.ValidatorIndex, error) {
epoch := slots.ToEpoch(slot)
activeCount, err := ActiveValidatorCount(ctx, state, epoch)
if err != nil {
return nil, errors.Wrap(err, "could not compute active validator count")
}
committeesPerSlot := SlotCommitteeCount(activeCount)
seed, err := Seed(state, epoch, params.BeaconConfig().DomainBeaconAttester)
if err != nil {
return nil, errors.Wrap(err, "could not get seed")
}
committees := make([][]primitives.ValidatorIndex, committeesPerSlot)
var activeIndices []primitives.ValidatorIndex
for idx := primitives.CommitteeIndex(0); idx < primitives.CommitteeIndex(len(committees)); idx++ {
committee, err := committeeCache.Committee(ctx, slot, seed, idx)
if err != nil {
return nil, errors.Wrap(err, "could not interface with committee cache")
}
if committee != nil {
committees[idx] = committee
continue
}
if len(activeIndices) == 0 {
activeIndices, err = ActiveValidatorIndices(ctx, state, epoch)
if err != nil {
return nil, errors.Wrap(err, "could not get active indices")
}
}
committee, err = BeaconCommittee(ctx, activeIndices, seed, slot, idx)
if err != nil {
return nil, errors.Wrap(err, "could not compute beacon committee")
}
committees[idx] = committee
}
return committees, nil
}
// BeaconCommitteeFromState returns the crosslink committee of a given slot and committee index. This
// is a spec implementation where state is used as an argument. In case of state retrieval
// becomes expensive, consider using BeaconCommittee below.
@@ -253,36 +294,22 @@ func CommitteeAssignments(ctx context.Context, state state.BeaconState, epoch pr
if err := verifyAssignmentEpoch(epoch, state); err != nil {
return nil, err
}
// Retrieve active validator count for the specified epoch.
activeValidatorCount, err := ActiveValidatorCount(ctx, state, epoch)
if err != nil {
return nil, err
}
// Determine the number of committees per slot based on the number of active validator indices.
numCommitteesPerSlot := SlotCommitteeCount(activeValidatorCount)
startSlot, err := slots.EpochStart(epoch)
if err != nil {
return nil, err
}
assignments := make(map[primitives.ValidatorIndex]*CommitteeAssignment)
vals := make(map[primitives.ValidatorIndex]struct{})
for _, v := range validators {
vals[v] = struct{}{}
}
assignments := make(map[primitives.ValidatorIndex]*CommitteeAssignment)
// Compute committee assignments for each slot in the epoch.
for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ {
// Compute committees for the current slot.
for j := uint64(0); j < numCommitteesPerSlot; j++ {
committee, err := BeaconCommitteeFromState(ctx, state, slot, primitives.CommitteeIndex(j))
if err != nil {
return nil, err
}
committees, err := BeaconCommittees(ctx, state, slot)
if err != nil {
return nil, errors.Wrap(err, "could not compute beacon committees")
}
for j, committee := range committees {
for _, vIndex := range committee {
if _, ok := vals[vIndex]; !ok { // Skip if the validator is not in the provided validators slice.
continue
@@ -296,7 +323,6 @@ func CommitteeAssignments(ctx context.Context, state state.BeaconState, epoch pr
}
}
}
return assignments, nil
}

View File

@@ -18,6 +18,7 @@ import (
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/assert"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
"github.com/prysmaticlabs/prysm/v5/time/slots"
)
@@ -749,3 +750,27 @@ func TestAttestationCommittees(t *testing.T) {
assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(committees[1])))
})
}
func TestBeaconCommittees(t *testing.T) {
prevConfig := params.BeaconConfig().Copy()
defer params.OverrideBeaconConfig(prevConfig)
c := params.BeaconConfig().Copy()
c.MinGenesisActiveValidatorCount = 128
c.SlotsPerEpoch = 4
c.TargetCommitteeSize = 16
params.OverrideBeaconConfig(c)
state, _ := util.DeterministicGenesisState(t, 256)
activeCount, err := helpers.ActiveValidatorCount(context.Background(), state, 0)
require.NoError(t, err)
committeesPerSlot := helpers.SlotCommitteeCount(activeCount)
committees, err := helpers.BeaconCommittees(context.Background(), state, 0)
require.NoError(t, err)
require.Equal(t, committeesPerSlot, uint64(len(committees)))
for idx := primitives.CommitteeIndex(0); idx < primitives.CommitteeIndex(len(committees)); idx++ {
committee, err := helpers.BeaconCommitteeFromState(context.Background(), state, 0, idx)
require.NoError(t, err)
require.DeepEqual(t, committees[idx], committee)
}
}

View File

@@ -29,6 +29,8 @@ import (
"go.opencensus.io/trace"
)
type customProcessingFn func(context.Context, state.BeaconState) error
// ExecuteStateTransition defines the procedure for a state transition function.
//
// Note: This method differs from the spec pseudocode as it uses a batch signature verification.
@@ -173,18 +175,7 @@ func ProcessSlotsIfPossible(ctx context.Context, state state.BeaconState, target
return state, nil
}
// ProcessSlots process through skip slots and apply epoch transition when it's needed
//
// Spec pseudocode definition:
//
// def process_slots(state: BeaconState, slot: Slot) -> None:
// assert state.slot < slot
// while state.slot < slot:
// process_slot(state)
// # Process epoch on the start slot of the next epoch
// if (state.slot + 1) % SLOTS_PER_EPOCH == 0:
// process_epoch(state)
// state.slot = Slot(state.slot + 1)
// ProcessSlots includes core slot processing as well as a cache
func ProcessSlots(ctx context.Context, state state.BeaconState, slot primitives.Slot) (state.BeaconState, error) {
ctx, span := trace.StartSpan(ctx, "core.state.ProcessSlots")
defer span.End()
@@ -231,42 +222,63 @@ func ProcessSlots(ctx context.Context, state state.BeaconState, slot primitives.
defer func() {
SkipSlotCache.MarkNotInProgress(key)
}()
state, err = ProcessSlotsCore(ctx, span, state, slot, cacheBestBeaconStateOnErrFn(highestSlot, key))
if err != nil {
return nil, err
}
if highestSlot < state.Slot() {
SkipSlotCache.Put(ctx, key, state)
}
for state.Slot() < slot {
return state, nil
}
func cacheBestBeaconStateOnErrFn(highestSlot primitives.Slot, key [32]byte) customProcessingFn {
return func(ctx context.Context, state state.BeaconState) error {
if ctx.Err() != nil {
tracing.AnnotateError(span, ctx.Err())
// Cache last best value.
if highestSlot < state.Slot() {
if SkipSlotCache.Put(ctx, key, state); err != nil {
log.WithError(err).Error("Failed to put skip slot cache value")
}
SkipSlotCache.Put(ctx, key, state)
}
return ctx.Err()
}
return nil
}
}
// ProcessSlotsCore process through skip slots and apply epoch transition when it's needed
//
// Spec pseudocode definition:
//
// def process_slots(state: BeaconState, slot: Slot) -> None:
// assert state.slot < slot
// while state.slot < slot:
// process_slot(state)
// # Process epoch on the start slot of the next epoch
// if (state.slot + 1) % SLOTS_PER_EPOCH == 0:
// process_epoch(state)
// state.slot = Slot(state.slot + 1)
func ProcessSlotsCore(ctx context.Context, span *trace.Span, state state.BeaconState, slot primitives.Slot, fn customProcessingFn) (state.BeaconState, error) {
var err error
for state.Slot() < slot {
if fn != nil {
if err = fn(ctx, state); err != nil {
tracing.AnnotateError(span, err)
return nil, err
}
return nil, ctx.Err()
}
state, err = ProcessSlot(ctx, state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, errors.Wrap(err, "could not process slot")
}
if time.CanProcessEpoch(state) {
if state.Version() == version.Phase0 {
state, err = ProcessEpochPrecompute(ctx, state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, errors.Wrap(err, "could not process epoch with optimizations")
}
} else if state.Version() <= version.Deneb {
if err = altair.ProcessEpoch(ctx, state); err != nil {
tracing.AnnotateError(span, err)
return nil, errors.Wrap(err, fmt.Sprintf("could not process %s epoch", version.String(state.Version())))
}
} else {
if err = electra.ProcessEpoch(ctx, state); err != nil {
tracing.AnnotateError(span, err)
return nil, errors.Wrap(err, fmt.Sprintf("could not process %s epoch", version.String(state.Version())))
}
}
state, err = ProcessEpoch(ctx, state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, err
}
if err := state.SetSlot(state.Slot() + 1); err != nil {
tracing.AnnotateError(span, err)
return nil, errors.Wrap(err, "failed to increment state slot")
@@ -278,25 +290,46 @@ func ProcessSlots(ctx context.Context, state state.BeaconState, slot primitives.
return nil, errors.Wrap(err, "failed to upgrade state")
}
}
if highestSlot < state.Slot() {
SkipSlotCache.Put(ctx, key, state)
}
return state, nil
}
// ProcessEpoch is a wrapper on fork specific epoch processing
func ProcessEpoch(ctx context.Context, state state.BeaconState) (state.BeaconState, error) {
var err error
if time.CanProcessEpoch(state) {
if state.Version() == version.Electra {
if err = electra.ProcessEpoch(ctx, state); err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("could not process %s epoch", version.String(state.Version())))
}
} else if state.Version() >= version.Altair {
if err = altair.ProcessEpoch(ctx, state); err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("could not process %s epoch", version.String(state.Version())))
}
} else {
state, err = ProcessEpochPrecompute(ctx, state)
if err != nil {
return nil, errors.Wrap(err, "could not process epoch with optimizations")
}
}
}
return state, err
}
// UpgradeState upgrades the state to the next version if possible.
func UpgradeState(ctx context.Context, state state.BeaconState) (state.BeaconState, error) {
ctx, span := trace.StartSpan(ctx, "core.state.UpgradeState")
defer span.End()
var err error
upgraded := false
if time.CanUpgradeToAltair(state.Slot()) {
state, err = altair.UpgradeToAltair(ctx, state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, err
}
upgraded = true
}
if time.CanUpgradeToBellatrix(state.Slot()) {
@@ -305,6 +338,7 @@ func UpgradeState(ctx context.Context, state state.BeaconState) (state.BeaconSta
tracing.AnnotateError(span, err)
return nil, err
}
upgraded = true
}
if time.CanUpgradeToCapella(state.Slot()) {
@@ -313,6 +347,7 @@ func UpgradeState(ctx context.Context, state state.BeaconState) (state.BeaconSta
tracing.AnnotateError(span, err)
return nil, err
}
upgraded = true
}
if time.CanUpgradeToDeneb(state.Slot()) {
@@ -321,6 +356,7 @@ func UpgradeState(ctx context.Context, state state.BeaconState) (state.BeaconSta
tracing.AnnotateError(span, err)
return nil, err
}
upgraded = true
}
if time.CanUpgradeToElectra(state.Slot()) {
@@ -329,7 +365,13 @@ func UpgradeState(ctx context.Context, state state.BeaconState) (state.BeaconSta
tracing.AnnotateError(span, err)
return nil, err
}
upgraded = true
}
if upgraded {
log.Debugf("upgraded state to %s", version.String(state.Version()))
}
return state, nil
}

View File

@@ -12,7 +12,6 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition/interop"
v "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
field_params "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
@@ -328,20 +327,18 @@ func ProcessBlockForStateRoot(
if err != nil {
return nil, err
}
if blk.IsBlinded() {
state, err = b.ProcessPayloadHeader(state, executionData)
} else {
state, err = b.ProcessPayload(state, executionData)
if state.Version() >= version.Capella {
state, err = b.ProcessWithdrawals(state, executionData)
if err != nil {
return nil, errors.Wrap(err, "could not process withdrawals")
}
}
state, err = b.ProcessPayload(state, blk.Body())
if err != nil {
return nil, errors.Wrap(err, "could not process execution data")
}
}
if err := VerifyBlobCommitmentCount(blk); err != nil {
return nil, err
}
randaoReveal := signed.Block().Body().RandaoReveal()
state, err = b.ProcessRandaoNoVerify(state, randaoReveal[:])
if err != nil {
@@ -377,20 +374,6 @@ func ProcessBlockForStateRoot(
return state, nil
}
func VerifyBlobCommitmentCount(blk interfaces.ReadOnlyBeaconBlock) error {
if blk.Version() < version.Deneb {
return nil
}
kzgs, err := blk.Body().BlobKzgCommitments()
if err != nil {
return err
}
if len(kzgs) > field_params.MaxBlobsPerBlock {
return fmt.Errorf("too many kzg commitments in block: %d", len(kzgs))
}
return nil
}
// This calls altair block operations.
func altairOperations(
ctx context.Context,

View File

@@ -2,13 +2,11 @@ package transition_test
import (
"context"
"fmt"
"testing"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition"
field_params "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
@@ -212,15 +210,3 @@ func TestProcessBlockDifferentVersion(t *testing.T) {
_, _, err = transition.ProcessBlockNoVerifyAnySig(context.Background(), beaconState, wsb)
require.ErrorContains(t, "state and block are different version. 0 != 1", err)
}
func TestVerifyBlobCommitmentCount(t *testing.T) {
b := &ethpb.BeaconBlockDeneb{Body: &ethpb.BeaconBlockBodyDeneb{}}
rb, err := blocks.NewBeaconBlock(b)
require.NoError(t, err)
require.NoError(t, transition.VerifyBlobCommitmentCount(rb))
b = &ethpb.BeaconBlockDeneb{Body: &ethpb.BeaconBlockBodyDeneb{BlobKzgCommitments: make([][]byte, field_params.MaxBlobsPerBlock+1)}}
rb, err = blocks.NewBeaconBlock(b)
require.NoError(t, err)
require.ErrorContains(t, fmt.Sprintf("too many kzg commitments in block: %d", field_params.MaxBlobsPerBlock+1), transition.VerifyBlobCommitmentCount(rb))
}

View File

@@ -84,11 +84,15 @@ func (s *mockEngine) callCount(method string) int {
}
func mockParseUintList(t *testing.T, data json.RawMessage) []uint64 {
var list []uint64
var list []string
if err := json.Unmarshal(data, &list); err != nil {
t.Fatalf("failed to parse uint list: %v", err)
}
return list
uints := make([]uint64, len(list))
for i, u := range list {
uints[i] = hexutil.MustDecodeUint64(u)
}
return uints
}
func mockParseHexByteList(t *testing.T, data json.RawMessage) []hexutil.Bytes {
@@ -117,7 +121,7 @@ func TestParseRequest(t *testing.T) {
ctx := context.Background()
cases := []struct {
method string
uintArgs []uint64
hexArgs []string // uint64 as hex
byteArgs []hexutil.Bytes
}{
{
@@ -135,26 +139,28 @@ func TestParseRequest(t *testing.T) {
},
},
{
method: GetPayloadBodiesByRangeV1,
uintArgs: []uint64{0, 1},
method: GetPayloadBodiesByRangeV1,
hexArgs: []string{hexutil.EncodeUint64(0), hexutil.EncodeUint64(1)},
},
{
method: GetPayloadBodiesByRangeV2,
uintArgs: []uint64{math.MaxUint64, 1},
method: GetPayloadBodiesByRangeV2,
hexArgs: []string{hexutil.EncodeUint64(math.MaxUint64), hexutil.EncodeUint64(1)},
},
}
for _, c := range cases {
t.Run(c.method, func(t *testing.T) {
cli, srv := newMockEngine(t)
srv.register(c.method, func(msg *jsonrpcMessage, w http.ResponseWriter, r *http.Request) {
srv.register(c.method, func(msg *jsonrpcMessage, w http.ResponseWriter, _ *http.Request) {
require.Equal(t, c.method, msg.Method)
nr := uint64(len(c.byteArgs))
if len(c.byteArgs) > 0 {
require.DeepEqual(t, c.byteArgs, mockParseHexByteList(t, msg.Params))
}
if len(c.uintArgs) > 0 {
if len(c.hexArgs) > 0 {
rang := mockParseUintList(t, msg.Params)
require.DeepEqual(t, c.uintArgs, rang)
for i, r := range rang {
require.Equal(t, c.hexArgs[i], hexutil.EncodeUint64(r))
}
nr = rang[1]
}
mockWriteResult(t, w, msg, make([]*pb.ExecutionPayloadBody, nr))
@@ -165,18 +171,18 @@ func TestParseRequest(t *testing.T) {
if len(c.byteArgs) > 0 {
args = []interface{}{c.byteArgs}
}
if len(c.uintArgs) > 0 {
args = make([]interface{}, len(c.uintArgs))
for i := range c.uintArgs {
args[i] = c.uintArgs[i]
if len(c.hexArgs) > 0 {
args = make([]interface{}, len(c.hexArgs))
for i := range c.hexArgs {
args[i] = c.hexArgs[i]
}
}
require.NoError(t, cli.CallContext(ctx, &result, c.method, args...))
if len(c.byteArgs) > 0 {
require.Equal(t, len(c.byteArgs), len(result))
}
if len(c.uintArgs) > 0 {
require.Equal(t, int(c.uintArgs[1]), len(result))
if len(c.hexArgs) > 0 {
require.Equal(t, int(hexutil.MustDecodeUint64(c.hexArgs[1])), len(result))
}
})
}
@@ -203,7 +209,7 @@ func TestCallCount(t *testing.T) {
for _, c := range cases {
t.Run(c.method, func(t *testing.T) {
cli, srv := newMockEngine(t)
srv.register(c.method, func(msg *jsonrpcMessage, w http.ResponseWriter, r *http.Request) {
srv.register(c.method, func(msg *jsonrpcMessage, w http.ResponseWriter, _ *http.Request) {
mockWriteResult(t, w, msg, nil)
})
for i := 0; i < c.count; i++ {

View File

@@ -5,6 +5,7 @@ import (
"sort"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
@@ -160,7 +161,7 @@ func computeRanges(hbns []hashBlockNumber) []byRangeReq {
func (r *blindedBlockReconstructor) requestBodiesByRange(ctx context.Context, client RPCClient, method string, req byRangeReq) error {
result := make([]*pb.ExecutionPayloadBody, 0)
if err := client.CallContext(ctx, &result, method, req.start, req.count); err != nil {
if err := client.CallContext(ctx, &result, method, hexutil.EncodeUint64(req.start), hexutil.EncodeUint64(req.count)); err != nil {
return err
}
if uint64(len(result)) != req.count {

View File

@@ -676,3 +676,18 @@ func (f *ForkChoice) TargetRootForEpoch(root [32]byte, epoch primitives.Epoch) (
}
return f.TargetRootForEpoch(targetNode.root, epoch)
}
// ParentRoot returns the block root of the parent node if it is in forkchoice.
// The exception is for the finalized checkpoint root which we return the zero
// hash.
func (f *ForkChoice) ParentRoot(root [32]byte) ([32]byte, error) {
n, ok := f.store.nodeByRoot[root]
if !ok || n == nil {
return [32]byte{}, ErrNilNode
}
// Return the zero hash for the tree root
if n.parent == nil {
return [32]byte{}, nil
}
return n.parent.root, nil
}

View File

@@ -861,3 +861,29 @@ func TestForkChoiceSlot(t *testing.T) {
require.NoError(t, err)
require.Equal(t, primitives.Slot(3), slot)
}
func TestForkchoiceParentRoot(t *testing.T) {
f := setup(0, 0)
ctx := context.Background()
root1 := [32]byte{'a'}
st, root, err := prepareForkchoiceState(ctx, 3, root1, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, st, root))
root2 := [32]byte{'b'}
st, root, err = prepareForkchoiceState(ctx, 3, root2, root1, [32]byte{'A'}, 0, 0)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, st, root))
root, err = f.ParentRoot(root2)
require.NoError(t, err)
require.Equal(t, root1, root)
_, err = f.ParentRoot([32]byte{'c'})
require.ErrorIs(t, err, ErrNilNode)
zeroHash := [32]byte{}
root, err = f.ParentRoot(zeroHash)
require.NoError(t, err)
require.Equal(t, zeroHash, root)
}

View File

@@ -80,6 +80,7 @@ type FastGetter interface {
TargetRootForEpoch([32]byte, primitives.Epoch) ([32]byte, error)
UnrealizedJustifiedPayloadBlockHash() [32]byte
Weight(root [32]byte) (uint64, error)
ParentRoot(root [32]byte) ([32]byte, error)
}
// Setter allows to set forkchoice information

View File

@@ -169,3 +169,10 @@ func (ro *ROForkChoice) TargetRootForEpoch(root [32]byte, epoch primitives.Epoch
defer ro.l.RUnlock()
return ro.getter.TargetRootForEpoch(root, epoch)
}
// ParentRoot delegates to the underlying forkchoice call, under a lock.
func (ro *ROForkChoice) ParentRoot(root [32]byte) ([32]byte, error) {
ro.l.RLock()
defer ro.l.RUnlock()
return ro.getter.ParentRoot(root)
}

View File

@@ -37,6 +37,7 @@ const (
slotCalled
lastRootCalled
targetRootForEpochCalled
parentRootCalled
)
func _discard(t *testing.T, e error) {
@@ -291,3 +292,8 @@ func (ro *mockROForkchoice) TargetRootForEpoch(_ [32]byte, _ primitives.Epoch) (
ro.calls = append(ro.calls, targetRootForEpochCalled)
return [32]byte{}, nil
}
func (ro *mockROForkchoice) ParentRoot(_ [32]byte) ([32]byte, error) {
ro.calls = append(ro.calls, parentRootCalled)
return [32]byte{}, nil
}

View File

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

View File

@@ -3,6 +3,7 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"beacon.go",
"errors.go",
"log.go",
"service.go",
@@ -20,6 +21,8 @@ go_library(
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/time:go_default_library",
"//beacon-chain/core/transition:go_default_library",
"//beacon-chain/core/validators:go_default_library",
"//beacon-chain/db:go_default_library",
"//beacon-chain/forkchoice/types:go_default_library",
"//beacon-chain/operations/synccommittee:go_default_library",
"//beacon-chain/p2p:go_default_library",
@@ -28,6 +31,7 @@ go_library(
"//beacon-chain/sync:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/primitives:go_default_library",
"//consensus-types/validator:go_default_library",
"//crypto/bls:go_default_library",

View File

@@ -0,0 +1,128 @@
package core
import (
"context"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/config/params"
consensusblocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/time/slots"
)
// Retrieve chain head information from the DB and the current beacon state.
func (s *Service) ChainHead(ctx context.Context) (*ethpb.ChainHead, *RpcError) {
headBlock, err := s.HeadFetcher.HeadBlock(ctx)
if err != nil {
return nil, &RpcError{
Err: errors.Wrapf(err, "could not get head block"),
Reason: Internal,
}
}
if err := consensusblocks.BeaconBlockIsNil(headBlock); err != nil {
return nil, &RpcError{
Err: errors.Wrapf(err, "head block of chain was nil"),
Reason: NotFound,
}
}
optimisticStatus, err := s.OptimisticModeFetcher.IsOptimistic(ctx)
if err != nil {
return nil, &RpcError{
Err: errors.Wrapf(err, "could not get optimistic status"),
Reason: Internal,
}
}
headBlockRoot, err := headBlock.Block().HashTreeRoot()
if err != nil {
return nil, &RpcError{
Err: errors.Wrapf(err, "could not get head block root"),
Reason: Internal,
}
}
validGenesis := false
validateCP := func(cp *ethpb.Checkpoint, name string) error {
if bytesutil.ToBytes32(cp.Root) == params.BeaconConfig().ZeroHash && cp.Epoch == 0 {
if validGenesis {
return nil
}
// Retrieve genesis block in the event we have genesis checkpoints.
genBlock, err := s.BeaconDB.GenesisBlock(ctx)
if err != nil || consensusblocks.BeaconBlockIsNil(genBlock) != nil {
return errors.New("could not get genesis block")
}
validGenesis = true
return nil
}
b, err := s.BeaconDB.Block(ctx, bytesutil.ToBytes32(cp.Root))
if err != nil {
return errors.Errorf("could not get %s block: %v", name, err)
}
if err := consensusblocks.BeaconBlockIsNil(b); err != nil {
return errors.Errorf("could not get %s block: %v", name, err)
}
return nil
}
finalizedCheckpoint := s.FinalizedFetcher.FinalizedCheckpt()
if err := validateCP(finalizedCheckpoint, "finalized"); err != nil {
return nil, &RpcError{
Err: errors.Wrap(err, "could not get finalized checkpoint"),
Reason: Internal,
}
}
justifiedCheckpoint := s.FinalizedFetcher.CurrentJustifiedCheckpt()
if err := validateCP(justifiedCheckpoint, "justified"); err != nil {
return nil, &RpcError{
Err: errors.Wrap(err, "could not get current justified checkpoint"),
Reason: Internal,
}
}
prevJustifiedCheckpoint := s.FinalizedFetcher.PreviousJustifiedCheckpt()
if err := validateCP(prevJustifiedCheckpoint, "prev justified"); err != nil {
return nil, &RpcError{
Err: errors.Wrap(err, "could not get previous justified checkpoint"),
Reason: Internal,
}
}
fSlot, err := slots.EpochStart(finalizedCheckpoint.Epoch)
if err != nil {
return nil, &RpcError{
Err: errors.Wrapf(err, "could not get epoch start slot from finalized checkpoint epoch"),
Reason: Internal,
}
}
jSlot, err := slots.EpochStart(justifiedCheckpoint.Epoch)
if err != nil {
return nil, &RpcError{
Err: errors.Wrapf(err, "could not get epoch start slot from justified checkpoint epoch"),
Reason: Internal,
}
}
pjSlot, err := slots.EpochStart(prevJustifiedCheckpoint.Epoch)
if err != nil {
return nil, &RpcError{
Err: errors.Wrapf(err, "could not get epoch start slot from prev justified checkpoint epoch"),
Reason: Internal,
}
}
return &ethpb.ChainHead{
HeadSlot: headBlock.Block().Slot(),
HeadEpoch: slots.ToEpoch(headBlock.Block().Slot()),
HeadBlockRoot: headBlockRoot[:],
FinalizedSlot: fSlot,
FinalizedEpoch: finalizedCheckpoint.Epoch,
FinalizedBlockRoot: finalizedCheckpoint.Root,
JustifiedSlot: jSlot,
JustifiedEpoch: justifiedCheckpoint.Epoch,
JustifiedBlockRoot: justifiedCheckpoint.Root,
PreviousJustifiedSlot: pjSlot,
PreviousJustifiedEpoch: prevJustifiedCheckpoint.Epoch,
PreviousJustifiedBlockRoot: prevJustifiedCheckpoint.Root,
OptimisticStatus: optimisticStatus,
}, nil
}

View File

@@ -4,6 +4,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/cache"
opfeed "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed/operation"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/db"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/synccommittee"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/p2p"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stategen"
@@ -11,6 +12,8 @@ import (
)
type Service struct {
BeaconDB db.ReadOnlyDatabase
ChainInfoFetcher blockchain.ChainInfoFetcher
HeadFetcher blockchain.HeadFetcher
FinalizedFetcher blockchain.FinalizationFetcher
GenesisTimeFetcher blockchain.TimeFetcher

View File

@@ -16,6 +16,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
coreTime "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators"
forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types"
beaconState "github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
@@ -752,3 +753,177 @@ func subnetsFromCommittee(pubkey []byte, comm *ethpb.SyncCommittee) []uint64 {
}
return positions
}
// ValidatorParticipation retrieves the validator participation information for a given epoch,
// it returns the information about validator's participation rate in voting on the proof of stake
// rules based on their balance compared to the total active validator balance.
func (s *Service) ValidatorParticipation(
ctx context.Context,
requestedEpoch primitives.Epoch,
) (
*ethpb.ValidatorParticipationResponse,
*RpcError,
) {
currentSlot := s.GenesisTimeFetcher.CurrentSlot()
currentEpoch := slots.ToEpoch(currentSlot)
if requestedEpoch > currentEpoch {
return nil, &RpcError{
Err: fmt.Errorf("cannot retrieve information about an epoch greater than current epoch, current epoch %d, requesting %d", currentEpoch, requestedEpoch),
Reason: BadRequest,
}
}
// Use the last slot of requested epoch to obtain current and previous epoch attestations.
// This ensures that we don't miss previous attestations when input requested epochs.
endSlot, err := slots.EpochEnd(requestedEpoch)
if err != nil {
return nil, &RpcError{Reason: Internal, Err: errors.Wrap(err, "could not get slot from requested epoch")}
}
// 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.
if endSlot > currentSlot {
endSlot = currentSlot
}
// ReplayerBuilder ensures that a canonical chain is followed to the slot
beaconSt, err := s.ReplayerBuilder.ReplayerForSlot(endSlot).ReplayBlocks(ctx)
if err != nil {
return nil, &RpcError{Reason: Internal, Err: errors.Wrapf(err, "error replaying blocks for state at slot %d", endSlot)}
}
var v []*precompute.Validator
var b *precompute.Balance
if beaconSt.Version() == version.Phase0 {
v, b, err = precompute.New(ctx, beaconSt)
if err != nil {
return nil, &RpcError{Reason: Internal, Err: errors.Wrap(err, "could not set up pre compute instance")}
}
_, b, err = precompute.ProcessAttestations(ctx, beaconSt, v, b)
if err != nil {
return nil, &RpcError{Reason: Internal, Err: errors.Wrap(err, "could not pre compute attestations")}
}
} else if beaconSt.Version() >= version.Altair {
v, b, err = altair.InitializePrecomputeValidators(ctx, beaconSt)
if err != nil {
return nil, &RpcError{Reason: Internal, Err: errors.Wrap(err, "could not set up altair pre compute instance")}
}
_, b, err = altair.ProcessEpochParticipation(ctx, beaconSt, b, v)
if err != nil {
return nil, &RpcError{Reason: Internal, Err: errors.Wrap(err, "could not pre compute attestations: %v")}
}
} else {
return nil, &RpcError{Reason: Internal, Err: fmt.Errorf("invalid state type retrieved with a version of %s", version.String(beaconSt.Version()))}
}
cp := s.FinalizedFetcher.FinalizedCheckpt()
p := &ethpb.ValidatorParticipationResponse{
Epoch: requestedEpoch,
Finalized: requestedEpoch <= cp.Epoch,
Participation: &ethpb.ValidatorParticipation{
// TODO(7130): Remove these three deprecated fields.
GlobalParticipationRate: float32(b.PrevEpochTargetAttested) / float32(b.ActivePrevEpoch),
VotedEther: b.PrevEpochTargetAttested,
EligibleEther: b.ActivePrevEpoch,
CurrentEpochActiveGwei: b.ActiveCurrentEpoch,
CurrentEpochAttestingGwei: b.CurrentEpochAttested,
CurrentEpochTargetAttestingGwei: b.CurrentEpochTargetAttested,
PreviousEpochActiveGwei: b.ActivePrevEpoch,
PreviousEpochAttestingGwei: b.PrevEpochAttested,
PreviousEpochTargetAttestingGwei: b.PrevEpochTargetAttested,
PreviousEpochHeadAttestingGwei: b.PrevEpochHeadAttested,
},
}
return p, nil
}
// ValidatorActiveSetChanges retrieves the active set changes for a given epoch.
//
// This data includes any activations, voluntary exits, and involuntary
// ejections.
func (s *Service) ValidatorActiveSetChanges(
ctx context.Context,
requestedEpoch primitives.Epoch,
) (
*ethpb.ActiveSetChanges,
*RpcError,
) {
currentEpoch := slots.ToEpoch(s.GenesisTimeFetcher.CurrentSlot())
if requestedEpoch > currentEpoch {
return nil, &RpcError{
Err: errors.Errorf("cannot retrieve information about an epoch in the future, current epoch %d, requesting %d", currentEpoch, requestedEpoch),
Reason: BadRequest,
}
}
slot, err := slots.EpochStart(requestedEpoch)
if err != nil {
return nil, &RpcError{Err: err, Reason: BadRequest}
}
requestedState, err := s.ReplayerBuilder.ReplayerForSlot(slot).ReplayBlocks(ctx)
if err != nil {
return nil, &RpcError{
Err: errors.Wrapf(err, "error replaying blocks for state at slot %d", slot),
Reason: Internal,
}
}
activeValidatorCount, err := helpers.ActiveValidatorCount(ctx, requestedState, coreTime.CurrentEpoch(requestedState))
if err != nil {
return nil, &RpcError{
Err: errors.Wrap(err, "could not get active validator count"),
Reason: Internal,
}
}
vs := requestedState.Validators()
activatedIndices := validators.ActivatedValidatorIndices(coreTime.CurrentEpoch(requestedState), vs)
exitedIndices, err := validators.ExitedValidatorIndices(coreTime.CurrentEpoch(requestedState), vs, activeValidatorCount)
if err != nil {
return nil, &RpcError{
Err: errors.Wrap(err, "could not determine exited validator indices"),
Reason: Internal,
}
}
slashedIndices := validators.SlashedValidatorIndices(coreTime.CurrentEpoch(requestedState), vs)
ejectedIndices, err := validators.EjectedValidatorIndices(coreTime.CurrentEpoch(requestedState), vs, activeValidatorCount)
if err != nil {
return nil, &RpcError{
Err: errors.Wrap(err, "could not determine ejected validator indices"),
Reason: Internal,
}
}
// Retrieve public keys for the indices.
activatedKeys := make([][]byte, len(activatedIndices))
exitedKeys := make([][]byte, len(exitedIndices))
slashedKeys := make([][]byte, len(slashedIndices))
ejectedKeys := make([][]byte, len(ejectedIndices))
for i, idx := range activatedIndices {
pubkey := requestedState.PubkeyAtIndex(idx)
activatedKeys[i] = pubkey[:]
}
for i, idx := range exitedIndices {
pubkey := requestedState.PubkeyAtIndex(idx)
exitedKeys[i] = pubkey[:]
}
for i, idx := range slashedIndices {
pubkey := requestedState.PubkeyAtIndex(idx)
slashedKeys[i] = pubkey[:]
}
for i, idx := range ejectedIndices {
pubkey := requestedState.PubkeyAtIndex(idx)
ejectedKeys[i] = pubkey[:]
}
return &ethpb.ActiveSetChanges{
Epoch: requestedEpoch,
ActivatedPublicKeys: activatedKeys,
ActivatedIndices: activatedIndices,
ExitedPublicKeys: exitedKeys,
ExitedIndices: exitedIndices,
SlashedPublicKeys: slashedKeys,
SlashedIndices: slashedIndices,
EjectedPublicKeys: ejectedKeys,
EjectedIndices: ejectedIndices,
}, nil
}

View File

@@ -70,7 +70,7 @@ func (s *Service) endpoints(
endpoints = append(endpoints, s.eventsEndpoints()...)
endpoints = append(endpoints, s.prysmBeaconEndpoints(ch, stater, coreService)...)
endpoints = append(endpoints, s.prysmNodeEndpoints()...)
endpoints = append(endpoints, s.prysmValidatorEndpoints(coreService)...)
endpoints = append(endpoints, s.prysmValidatorEndpoints(stater, coreService)...)
if enableDebug {
endpoints = append(endpoints, s.debugEndpoints(stater)...)
}
@@ -983,6 +983,15 @@ func (s *Service) prysmBeaconEndpoints(
handler: server.GetIndividualVotes,
methods: []string{http.MethodPost},
},
{
template: "/prysm/v1/beacon/chain_head",
name: namespace + ".GetChainHead",
middleware: []mux.MiddlewareFunc{
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
},
handler: server.GetChainHead,
methods: []string{http.MethodGet},
},
}
}
@@ -1060,32 +1069,52 @@ func (s *Service) prysmNodeEndpoints() []endpoint {
}
}
func (*Service) prysmValidatorEndpoints(coreService *core.Service) []endpoint {
func (s *Service) prysmValidatorEndpoints(stater lookup.Stater, coreService *core.Service) []endpoint {
server := &validatorprysm.Server{
CoreService: coreService,
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
Stater: stater,
CoreService: coreService,
}
const namespace = "prysm.validator"
return []endpoint{
{
template: "/prysm/validators/performance",
name: namespace + ".GetValidatorPerformance",
name: namespace + ".GetPerformance",
middleware: []mux.MiddlewareFunc{
middleware.ContentTypeHandler([]string{api.JsonMediaType}),
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
},
handler: server.GetValidatorPerformance,
handler: server.GetPerformance,
methods: []string{http.MethodPost},
},
{
template: "/prysm/v1/validators/performance",
name: namespace + ".GetValidatorPerformance",
name: namespace + ".GetPerformance",
middleware: []mux.MiddlewareFunc{
middleware.ContentTypeHandler([]string{api.JsonMediaType}),
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
},
handler: server.GetValidatorPerformance,
handler: server.GetPerformance,
methods: []string{http.MethodPost},
},
{
template: "/prysm/v1/validators/participation",
name: namespace + ".GetParticipation",
middleware: []mux.MiddlewareFunc{
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
},
handler: server.GetParticipation,
methods: []string{http.MethodGet},
},
{
template: "/prysm/v1/validators/active_set_changes",
name: namespace + ".GetActiveSetChanges",
middleware: []mux.MiddlewareFunc{
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
},
handler: server.GetActiveSetChanges,
methods: []string{http.MethodGet},
},
}
}

View File

@@ -114,6 +114,7 @@ func Test_endpoints(t *testing.T) {
"/prysm/v1/beacon/weak_subjectivity": {http.MethodGet},
"/eth/v1/beacon/states/{state_id}/validator_count": {http.MethodGet},
"/prysm/v1/beacon/states/{state_id}/validator_count": {http.MethodGet},
"/prysm/v1/beacon/chain_head": {http.MethodGet},
}
prysmNodeRoutes := map[string][]string{
@@ -124,8 +125,10 @@ func Test_endpoints(t *testing.T) {
}
prysmValidatorRoutes := map[string][]string{
"/prysm/validators/performance": {http.MethodPost},
"/prysm/v1/validators/performance": {http.MethodPost},
"/prysm/validators/performance": {http.MethodPost},
"/prysm/v1/validators/performance": {http.MethodPost},
"/prysm/v1/validators/participation": {http.MethodGet},
"/prysm/v1/validators/active_set_changes": {http.MethodGet},
}
s := &Service{cfg: &Config{}}

View File

@@ -116,9 +116,11 @@ func (s *Server) getBlockV2Ssz(w http.ResponseWriter, blk interfaces.ReadOnlySig
result, err := s.getBlockResponseBodySsz(blk)
if err != nil {
httputil.HandleError(w, "Could not get signed beacon block: "+err.Error(), http.StatusInternalServerError)
return
}
if result == nil {
httputil.HandleError(w, fmt.Sprintf("Unknown block type %T", blk), http.StatusInternalServerError)
return
}
w.Header().Set(api.VersionHeader, version.String(blk.Version()))
httputil.WriteSsz(w, result, "beacon_block.ssz")
@@ -149,6 +151,11 @@ func (s *Server) getBlockV2Json(ctx context.Context, w http.ResponseWriter, blk
result, err := s.getBlockResponseBodyJson(ctx, blk)
if err != nil {
httputil.HandleError(w, "Error processing request: "+err.Error(), http.StatusInternalServerError)
return
}
if result == nil {
httputil.HandleError(w, fmt.Sprintf("Unknown block type %T", blk), http.StatusInternalServerError)
return
}
w.Header().Set(api.VersionHeader, result.Version)
httputil.WriteJson(w, result)

View File

@@ -52,10 +52,14 @@ go_test(
"//beacon-chain/rpc/lookup:go_default_library",
"//beacon-chain/rpc/testutil:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/state-native:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//beacon-chain/state/stategen/mock:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//network/httputil:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",

View File

@@ -154,3 +154,32 @@ func (s *Server) GetIndividualVotes(w http.ResponseWriter, r *http.Request) {
}
httputil.WriteJson(w, response)
}
// GetChainHead retrieves information about the head of the beacon chain from
// the view of the beacon chain node.
func (s *Server) GetChainHead(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.GetChainHead")
defer span.End()
ch, rpcError := s.CoreService.ChainHead(ctx)
if rpcError != nil {
httputil.HandleError(w, rpcError.Err.Error(), core.ErrorReasonToHTTP(rpcError.Reason))
return
}
response := &structs.ChainHead{
HeadSlot: fmt.Sprintf("%d", ch.HeadSlot),
HeadEpoch: fmt.Sprintf("%d", ch.HeadEpoch),
HeadBlockRoot: hexutil.Encode(ch.HeadBlockRoot),
FinalizedSlot: fmt.Sprintf("%d", ch.FinalizedSlot),
FinalizedEpoch: fmt.Sprintf("%d", ch.FinalizedEpoch),
FinalizedBlockRoot: hexutil.Encode(ch.FinalizedBlockRoot),
JustifiedSlot: fmt.Sprintf("%d", ch.JustifiedSlot),
JustifiedEpoch: fmt.Sprintf("%d", ch.JustifiedEpoch),
JustifiedBlockRoot: hexutil.Encode(ch.JustifiedBlockRoot),
PreviousJustifiedSlot: fmt.Sprintf("%d", ch.PreviousJustifiedSlot),
PreviousJustifiedEpoch: fmt.Sprintf("%d", ch.PreviousJustifiedEpoch),
PreviousJustifiedBlockRoot: hexutil.Encode(ch.PreviousJustifiedBlockRoot),
OptimisticStatus: ch.OptimisticStatus,
}
httputil.WriteJson(w, response)
}

View File

@@ -19,11 +19,15 @@ import (
dbTest "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing"
doublylinkedtree "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/doubly-linked-tree"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/core"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stategen"
mockstategen "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stategen/mock"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/assert"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
@@ -227,11 +231,11 @@ func TestServer_GetIndividualVotes_Working(t *testing.T) {
require.NoError(t, beaconState.SetBlockRoots(br))
att2.Data.Target.Root = rt[:]
att2.Data.BeaconBlockRoot = newRt[:]
err = beaconState.AppendPreviousEpochAttestations(&eth.PendingAttestation{
err = beaconState.AppendPreviousEpochAttestations(&ethpb.PendingAttestation{
Data: att1.Data, AggregationBits: bf, InclusionDelay: 1,
})
require.NoError(t, err)
err = beaconState.AppendCurrentEpochAttestations(&eth.PendingAttestation{
err = beaconState.AppendCurrentEpochAttestations(&ethpb.PendingAttestation{
Data: att2.Data, AggregationBits: bf, InclusionDelay: 1,
})
require.NoError(t, err)
@@ -658,3 +662,211 @@ func TestServer_GetIndividualVotes_CapellaEndOfEpoch(t *testing.T) {
}
assert.DeepEqual(t, want, resp, "Unexpected response")
}
// ensures that if any of the checkpoints are zero-valued, an error will be generated without genesis being present
func TestServer_GetChainHead_NoGenesis(t *testing.T) {
db := dbTest.SetupDB(t)
s, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, s.SetSlot(1))
genBlock := util.NewBeaconBlock()
genBlock.Block.ParentRoot = bytesutil.PadTo([]byte{'G'}, fieldparams.RootLength)
util.SaveBlock(t, context.Background(), db, genBlock)
gRoot, err := genBlock.Block.HashTreeRoot()
require.NoError(t, err)
cases := []struct {
name string
zeroSetter func(val *ethpb.Checkpoint) error
}{
{
name: "zero-value prev justified",
zeroSetter: s.SetPreviousJustifiedCheckpoint,
},
{
name: "zero-value current justified",
zeroSetter: s.SetCurrentJustifiedCheckpoint,
},
{
name: "zero-value finalized",
zeroSetter: s.SetFinalizedCheckpoint,
},
}
finalized := &ethpb.Checkpoint{Epoch: 1, Root: gRoot[:]}
prevJustified := &ethpb.Checkpoint{Epoch: 2, Root: gRoot[:]}
justified := &ethpb.Checkpoint{Epoch: 3, Root: gRoot[:]}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
require.NoError(t, s.SetPreviousJustifiedCheckpoint(prevJustified))
require.NoError(t, s.SetCurrentJustifiedCheckpoint(justified))
require.NoError(t, s.SetFinalizedCheckpoint(finalized))
require.NoError(t, c.zeroSetter(&ethpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]}))
})
wsb, err := blocks.NewSignedBeaconBlock(genBlock)
require.NoError(t, err)
s := &Server{
CoreService: &core.Service{
BeaconDB: db,
HeadFetcher: &chainMock.ChainService{Block: wsb, State: s},
FinalizedFetcher: &chainMock.ChainService{
FinalizedCheckPoint: s.FinalizedCheckpoint(),
CurrentJustifiedCheckPoint: s.CurrentJustifiedCheckpoint(),
PreviousJustifiedCheckPoint: s.PreviousJustifiedCheckpoint(),
},
OptimisticModeFetcher: &chainMock.ChainService{},
},
}
url := "http://example.com"
request := httptest.NewRequest(http.MethodGet, url, nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetChainHead(writer, request)
require.Equal(t, http.StatusInternalServerError, writer.Code)
require.StringContains(t, "could not get genesis block", writer.Body.String())
}
}
func TestServer_GetChainHead_NoFinalizedBlock(t *testing.T) {
db := dbTest.SetupDB(t)
bs, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, bs.SetSlot(1))
require.NoError(t, bs.SetPreviousJustifiedCheckpoint(&ethpb.Checkpoint{Epoch: 3, Root: bytesutil.PadTo([]byte{'A'}, fieldparams.RootLength)}))
require.NoError(t, bs.SetCurrentJustifiedCheckpoint(&ethpb.Checkpoint{Epoch: 2, Root: bytesutil.PadTo([]byte{'B'}, fieldparams.RootLength)}))
require.NoError(t, bs.SetFinalizedCheckpoint(&ethpb.Checkpoint{Epoch: 1, Root: bytesutil.PadTo([]byte{'C'}, fieldparams.RootLength)}))
genBlock := util.NewBeaconBlock()
genBlock.Block.ParentRoot = bytesutil.PadTo([]byte{'G'}, fieldparams.RootLength)
util.SaveBlock(t, context.Background(), db, genBlock)
gRoot, err := genBlock.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, db.SaveGenesisBlockRoot(context.Background(), gRoot))
wsb, err := blocks.NewSignedBeaconBlock(genBlock)
require.NoError(t, err)
s := &Server{
CoreService: &core.Service{
BeaconDB: db,
HeadFetcher: &chainMock.ChainService{Block: wsb, State: bs},
FinalizedFetcher: &chainMock.ChainService{
FinalizedCheckPoint: bs.FinalizedCheckpoint(),
CurrentJustifiedCheckPoint: bs.CurrentJustifiedCheckpoint(),
PreviousJustifiedCheckPoint: bs.PreviousJustifiedCheckpoint()},
OptimisticModeFetcher: &chainMock.ChainService{},
},
}
url := "http://example.com"
request := httptest.NewRequest(http.MethodGet, url, nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetChainHead(writer, request)
require.Equal(t, http.StatusInternalServerError, writer.Code)
require.StringContains(t, "ould not get finalized block", writer.Body.String())
}
func TestServer_GetChainHead_NoHeadBlock(t *testing.T) {
s := &Server{
CoreService: &core.Service{
HeadFetcher: &chainMock.ChainService{Block: nil},
OptimisticModeFetcher: &chainMock.ChainService{},
},
}
url := "http://example.com"
request := httptest.NewRequest(http.MethodGet, url, nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetChainHead(writer, request)
require.Equal(t, http.StatusNotFound, writer.Code)
require.StringContains(t, "head block of chain was nil", writer.Body.String())
}
func TestServer_GetChainHead(t *testing.T) {
params.SetupTestConfigCleanup(t)
params.OverrideBeaconConfig(params.MinimalSpecConfig())
db := dbTest.SetupDB(t)
genBlock := util.NewBeaconBlock()
genBlock.Block.ParentRoot = bytesutil.PadTo([]byte{'G'}, fieldparams.RootLength)
util.SaveBlock(t, context.Background(), db, genBlock)
gRoot, err := genBlock.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, db.SaveGenesisBlockRoot(context.Background(), gRoot))
finalizedBlock := util.NewBeaconBlock()
finalizedBlock.Block.Slot = 1
finalizedBlock.Block.ParentRoot = bytesutil.PadTo([]byte{'A'}, fieldparams.RootLength)
util.SaveBlock(t, context.Background(), db, finalizedBlock)
fRoot, err := finalizedBlock.Block.HashTreeRoot()
require.NoError(t, err)
justifiedBlock := util.NewBeaconBlock()
justifiedBlock.Block.Slot = 2
justifiedBlock.Block.ParentRoot = bytesutil.PadTo([]byte{'B'}, fieldparams.RootLength)
util.SaveBlock(t, context.Background(), db, justifiedBlock)
jRoot, err := justifiedBlock.Block.HashTreeRoot()
require.NoError(t, err)
prevJustifiedBlock := util.NewBeaconBlock()
prevJustifiedBlock.Block.Slot = 3
prevJustifiedBlock.Block.ParentRoot = bytesutil.PadTo([]byte{'C'}, fieldparams.RootLength)
util.SaveBlock(t, context.Background(), db, prevJustifiedBlock)
pjRoot, err := prevJustifiedBlock.Block.HashTreeRoot()
require.NoError(t, err)
st, err := state_native.InitializeFromProtoPhase0(&ethpb.BeaconState{
Slot: 1,
PreviousJustifiedCheckpoint: &ethpb.Checkpoint{Epoch: 3, Root: pjRoot[:]},
CurrentJustifiedCheckpoint: &ethpb.Checkpoint{Epoch: 2, Root: jRoot[:]},
FinalizedCheckpoint: &ethpb.Checkpoint{Epoch: 1, Root: fRoot[:]},
})
require.NoError(t, err)
b := util.NewBeaconBlock()
b.Block.Slot, err = slots.EpochStart(st.PreviousJustifiedCheckpoint().Epoch)
require.NoError(t, err)
b.Block.Slot++
wsb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
s := &Server{
CoreService: &core.Service{
BeaconDB: db,
HeadFetcher: &chainMock.ChainService{Block: wsb, State: st},
OptimisticModeFetcher: &chainMock.ChainService{},
FinalizedFetcher: &chainMock.ChainService{
FinalizedCheckPoint: st.FinalizedCheckpoint(),
CurrentJustifiedCheckPoint: st.CurrentJustifiedCheckpoint(),
PreviousJustifiedCheckPoint: st.PreviousJustifiedCheckpoint()},
},
}
url := "http://example.com"
request := httptest.NewRequest(http.MethodGet, url, nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetChainHead(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
var ch *structs.ChainHead
err = json.NewDecoder(writer.Body).Decode(&ch)
require.NoError(t, err)
assert.Equal(t, "3", ch.PreviousJustifiedEpoch, "Unexpected PreviousJustifiedEpoch")
assert.Equal(t, "2", ch.JustifiedEpoch, "Unexpected JustifiedEpoch")
assert.Equal(t, "1", ch.FinalizedEpoch, "Unexpected FinalizedEpoch")
assert.Equal(t, "24", ch.PreviousJustifiedSlot, "Unexpected PreviousJustifiedSlot")
assert.Equal(t, "16", ch.JustifiedSlot, "Unexpected JustifiedSlot")
assert.Equal(t, "8", ch.FinalizedSlot, "Unexpected FinalizedSlot")
assert.DeepEqual(t, hexutil.Encode(pjRoot[:]), ch.PreviousJustifiedBlockRoot, "Unexpected PreviousJustifiedBlockRoot")
assert.DeepEqual(t, hexutil.Encode(jRoot[:]), ch.JustifiedBlockRoot, "Unexpected JustifiedBlockRoot")
assert.DeepEqual(t, hexutil.Encode(fRoot[:]), ch.FinalizedBlockRoot, "Unexpected FinalizedBlockRoot")
assert.Equal(t, false, ch.OptimisticStatus)
}

View File

@@ -19,15 +19,12 @@ go_library(
"//api/pagination:go_default_library",
"//beacon-chain/blockchain:go_default_library",
"//beacon-chain/cache:go_default_library",
"//beacon-chain/core/altair:go_default_library",
"//beacon-chain/core/epoch/precompute:go_default_library",
"//beacon-chain/core/feed/block:go_default_library",
"//beacon-chain/core/feed/operation:go_default_library",
"//beacon-chain/core/feed/state:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/time:go_default_library",
"//beacon-chain/core/transition:go_default_library",
"//beacon-chain/core/validators:go_default_library",
"//beacon-chain/db:go_default_library",
"//beacon-chain/db/filters:go_default_library",
"//beacon-chain/execution:go_default_library",

View File

@@ -7,13 +7,12 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/api/pagination"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/db/filters"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/core"
"github.com/prysmaticlabs/prysm/v5/cmd"
"github.com/prysmaticlabs/prysm/v5/config/params"
consensusblocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/time/slots"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
@@ -244,91 +243,9 @@ func (bs *Server) listBlocksForGenesis(ctx context.Context, _ *ethpb.ListBlocksR
// the most recent finalized and justified slots.
// DEPRECATED: This endpoint is superseded by the /eth/v1/beacon API endpoint
func (bs *Server) GetChainHead(ctx context.Context, _ *emptypb.Empty) (*ethpb.ChainHead, error) {
return bs.chainHeadRetrieval(ctx)
}
// Retrieve chain head information from the DB and the current beacon state.
func (bs *Server) chainHeadRetrieval(ctx context.Context) (*ethpb.ChainHead, error) {
headBlock, err := bs.HeadFetcher.HeadBlock(ctx)
if err != nil {
return nil, status.Error(codes.Internal, "Could not get head block")
}
optimisticStatus, err := bs.OptimisticModeFetcher.IsOptimistic(ctx)
if err != nil {
return nil, status.Error(codes.Internal, "Could not get optimistic status")
}
if err := consensusblocks.BeaconBlockIsNil(headBlock); err != nil {
return nil, status.Errorf(codes.NotFound, "Head block of chain was nil: %v", err)
}
headBlockRoot, err := headBlock.Block().HashTreeRoot()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get head block root: %v", err)
}
validGenesis := false
validateCP := func(cp *ethpb.Checkpoint, name string) error {
if bytesutil.ToBytes32(cp.Root) == params.BeaconConfig().ZeroHash && cp.Epoch == 0 {
if validGenesis {
return nil
}
// Retrieve genesis block in the event we have genesis checkpoints.
genBlock, err := bs.BeaconDB.GenesisBlock(ctx)
if err != nil || consensusblocks.BeaconBlockIsNil(genBlock) != nil {
return status.Error(codes.Internal, "Could not get genesis block")
}
validGenesis = true
return nil
}
b, err := bs.BeaconDB.Block(ctx, bytesutil.ToBytes32(cp.Root))
if err != nil {
return status.Errorf(codes.Internal, "Could not get %s block: %v", name, err)
}
if err := consensusblocks.BeaconBlockIsNil(b); err != nil {
return status.Errorf(codes.Internal, "Could not get %s block: %v", name, err)
}
return nil
}
finalizedCheckpoint := bs.FinalizationFetcher.FinalizedCheckpt()
if err := validateCP(finalizedCheckpoint, "finalized"); err != nil {
return nil, err
}
justifiedCheckpoint := bs.FinalizationFetcher.CurrentJustifiedCheckpt()
if err := validateCP(justifiedCheckpoint, "justified"); err != nil {
return nil, err
}
prevJustifiedCheckpoint := bs.FinalizationFetcher.PreviousJustifiedCheckpt()
if err := validateCP(prevJustifiedCheckpoint, "prev justified"); err != nil {
return nil, err
}
fSlot, err := slots.EpochStart(finalizedCheckpoint.Epoch)
if err != nil {
return nil, errors.Wrap(err, "could not get epoch start slot from finalized checkpoint epoch")
}
jSlot, err := slots.EpochStart(justifiedCheckpoint.Epoch)
if err != nil {
return nil, errors.Wrap(err, "could not get epoch start slot from justified checkpoint epoch")
}
pjSlot, err := slots.EpochStart(prevJustifiedCheckpoint.Epoch)
if err != nil {
return nil, errors.Wrap(err, "could not get epoch start slot from prev justified checkpoint epoch")
}
return &ethpb.ChainHead{
HeadSlot: headBlock.Block().Slot(),
HeadEpoch: slots.ToEpoch(headBlock.Block().Slot()),
HeadBlockRoot: headBlockRoot[:],
FinalizedSlot: fSlot,
FinalizedEpoch: finalizedCheckpoint.Epoch,
FinalizedBlockRoot: finalizedCheckpoint.Root,
JustifiedSlot: jSlot,
JustifiedEpoch: justifiedCheckpoint.Epoch,
JustifiedBlockRoot: justifiedCheckpoint.Root,
PreviousJustifiedSlot: pjSlot,
PreviousJustifiedEpoch: prevJustifiedCheckpoint.Epoch,
PreviousJustifiedBlockRoot: prevJustifiedCheckpoint.Root,
OptimisticStatus: optimisticStatus,
}, nil
ch, err := bs.CoreService.ChainHead(ctx)
if err != nil {
return nil, status.Errorf(core.ErrorReasonToGRPC(err.Reason), "Could not retrieve chain head: %v", err.Err)
}
return ch, nil
}

View File

@@ -8,6 +8,7 @@ import (
chainMock "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/testing"
dbTest "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/core"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v5/config/features"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
@@ -68,16 +69,19 @@ func TestServer_GetChainHead_NoGenesis(t *testing.T) {
wsb, err := blocks.NewSignedBeaconBlock(genBlock)
require.NoError(t, err)
bs := &Server{
BeaconDB: db,
HeadFetcher: &chainMock.ChainService{Block: wsb, State: s},
FinalizationFetcher: &chainMock.ChainService{
FinalizedCheckPoint: s.FinalizedCheckpoint(),
CurrentJustifiedCheckPoint: s.CurrentJustifiedCheckpoint(),
PreviousJustifiedCheckPoint: s.PreviousJustifiedCheckpoint()},
OptimisticModeFetcher: &chainMock.ChainService{},
CoreService: &core.Service{
BeaconDB: db,
HeadFetcher: &chainMock.ChainService{Block: wsb, State: s},
FinalizedFetcher: &chainMock.ChainService{
FinalizedCheckPoint: s.FinalizedCheckpoint(),
CurrentJustifiedCheckPoint: s.CurrentJustifiedCheckpoint(),
PreviousJustifiedCheckPoint: s.PreviousJustifiedCheckpoint(),
},
OptimisticModeFetcher: &chainMock.ChainService{},
},
}
_, err = bs.GetChainHead(context.Background(), nil)
require.ErrorContains(t, "Could not get genesis block", err)
require.ErrorContains(t, "could not get genesis block", err)
}
}
@@ -102,26 +106,30 @@ func TestServer_GetChainHead_NoFinalizedBlock(t *testing.T) {
require.NoError(t, err)
bs := &Server{
BeaconDB: db,
HeadFetcher: &chainMock.ChainService{Block: wsb, State: s},
FinalizationFetcher: &chainMock.ChainService{
FinalizedCheckPoint: s.FinalizedCheckpoint(),
CurrentJustifiedCheckPoint: s.CurrentJustifiedCheckpoint(),
PreviousJustifiedCheckPoint: s.PreviousJustifiedCheckpoint()},
OptimisticModeFetcher: &chainMock.ChainService{},
CoreService: &core.Service{
BeaconDB: db,
HeadFetcher: &chainMock.ChainService{Block: wsb, State: s},
FinalizedFetcher: &chainMock.ChainService{
FinalizedCheckPoint: s.FinalizedCheckpoint(),
CurrentJustifiedCheckPoint: s.CurrentJustifiedCheckpoint(),
PreviousJustifiedCheckPoint: s.PreviousJustifiedCheckpoint()},
OptimisticModeFetcher: &chainMock.ChainService{},
},
}
_, err = bs.GetChainHead(context.Background(), nil)
require.ErrorContains(t, "Could not get finalized block", err)
require.ErrorContains(t, "could not get finalized block", err)
}
func TestServer_GetChainHead_NoHeadBlock(t *testing.T) {
bs := &Server{
HeadFetcher: &chainMock.ChainService{Block: nil},
OptimisticModeFetcher: &chainMock.ChainService{},
CoreService: &core.Service{
HeadFetcher: &chainMock.ChainService{Block: nil},
OptimisticModeFetcher: &chainMock.ChainService{},
},
}
_, err := bs.GetChainHead(context.Background(), nil)
assert.ErrorContains(t, "Head block of chain was nil", err)
assert.ErrorContains(t, "head block of chain was nil", err)
}
func TestServer_GetChainHead(t *testing.T) {
@@ -172,13 +180,15 @@ func TestServer_GetChainHead(t *testing.T) {
wsb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
bs := &Server{
BeaconDB: db,
HeadFetcher: &chainMock.ChainService{Block: wsb, State: s},
OptimisticModeFetcher: &chainMock.ChainService{},
FinalizationFetcher: &chainMock.ChainService{
FinalizedCheckPoint: s.FinalizedCheckpoint(),
CurrentJustifiedCheckPoint: s.CurrentJustifiedCheckpoint(),
PreviousJustifiedCheckPoint: s.PreviousJustifiedCheckpoint()},
CoreService: &core.Service{
BeaconDB: db,
HeadFetcher: &chainMock.ChainService{Block: wsb, State: s},
OptimisticModeFetcher: &chainMock.ChainService{},
FinalizedFetcher: &chainMock.ChainService{
FinalizedCheckPoint: s.FinalizedCheckpoint(),
CurrentJustifiedCheckPoint: s.CurrentJustifiedCheckpoint(),
PreviousJustifiedCheckPoint: s.PreviousJustifiedCheckpoint()},
},
}
head, err := bs.GetChainHead(context.Background(), nil)

View File

@@ -7,12 +7,9 @@ import (
"strconv"
"github.com/prysmaticlabs/prysm/v5/api/pagination"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epoch/precompute"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
coreTime "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/core"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/cmd"
@@ -392,7 +389,7 @@ func (bs *Server) GetValidator(
func (bs *Server) GetValidatorActiveSetChanges(
ctx context.Context, req *ethpb.GetValidatorActiveSetChangesRequest,
) (*ethpb.ActiveSetChanges, error) {
currentEpoch := slots.ToEpoch(bs.GenesisTimeFetcher.CurrentSlot())
currentEpoch := slots.ToEpoch(bs.CoreService.GenesisTimeFetcher.CurrentSlot())
var requestedEpoch primitives.Epoch
switch q := req.QueryFilter.(type) {
@@ -403,72 +400,12 @@ func (bs *Server) GetValidatorActiveSetChanges(
default:
requestedEpoch = currentEpoch
}
if requestedEpoch > currentEpoch {
return nil, status.Errorf(
codes.InvalidArgument,
errEpoch,
currentEpoch,
requestedEpoch,
)
}
s, err := slots.EpochStart(requestedEpoch)
as, err := bs.CoreService.ValidatorActiveSetChanges(ctx, requestedEpoch)
if err != nil {
return nil, err
return nil, status.Errorf(core.ErrorReasonToGRPC(err.Reason), "Could not retrieve validator active set changes: %v", err.Err)
}
requestedState, err := bs.ReplayerBuilder.ReplayerForSlot(s).ReplayBlocks(ctx)
if err != nil {
return nil, status.Error(codes.Internal, fmt.Sprintf("error replaying blocks for state at slot %d: %v", s, err))
}
activeValidatorCount, err := helpers.ActiveValidatorCount(ctx, requestedState, coreTime.CurrentEpoch(requestedState))
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get active validator count: %v", err)
}
vs := requestedState.Validators()
activatedIndices := validators.ActivatedValidatorIndices(coreTime.CurrentEpoch(requestedState), vs)
exitedIndices, err := validators.ExitedValidatorIndices(coreTime.CurrentEpoch(requestedState), vs, activeValidatorCount)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not determine exited validator indices: %v", err)
}
slashedIndices := validators.SlashedValidatorIndices(coreTime.CurrentEpoch(requestedState), vs)
ejectedIndices, err := validators.EjectedValidatorIndices(coreTime.CurrentEpoch(requestedState), vs, activeValidatorCount)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not determine ejected validator indices: %v", err)
}
// Retrieve public keys for the indices.
activatedKeys := make([][]byte, len(activatedIndices))
exitedKeys := make([][]byte, len(exitedIndices))
slashedKeys := make([][]byte, len(slashedIndices))
ejectedKeys := make([][]byte, len(ejectedIndices))
for i, idx := range activatedIndices {
pubkey := requestedState.PubkeyAtIndex(idx)
activatedKeys[i] = pubkey[:]
}
for i, idx := range exitedIndices {
pubkey := requestedState.PubkeyAtIndex(idx)
exitedKeys[i] = pubkey[:]
}
for i, idx := range slashedIndices {
pubkey := requestedState.PubkeyAtIndex(idx)
slashedKeys[i] = pubkey[:]
}
for i, idx := range ejectedIndices {
pubkey := requestedState.PubkeyAtIndex(idx)
ejectedKeys[i] = pubkey[:]
}
return &ethpb.ActiveSetChanges{
Epoch: requestedEpoch,
ActivatedPublicKeys: activatedKeys,
ActivatedIndices: activatedIndices,
ExitedPublicKeys: exitedKeys,
ExitedIndices: exitedIndices,
SlashedPublicKeys: slashedKeys,
SlashedIndices: slashedIndices,
EjectedPublicKeys: ejectedKeys,
EjectedIndices: ejectedIndices,
}, nil
return as, nil
}
// GetValidatorParticipation retrieves the validator participation information for a given epoch,
@@ -477,7 +414,7 @@ func (bs *Server) GetValidatorActiveSetChanges(
func (bs *Server) GetValidatorParticipation(
ctx context.Context, req *ethpb.GetValidatorParticipationRequest,
) (*ethpb.ValidatorParticipationResponse, error) {
currentSlot := bs.GenesisTimeFetcher.CurrentSlot()
currentSlot := bs.CoreService.GenesisTimeFetcher.CurrentSlot()
currentEpoch := slots.ToEpoch(currentSlot)
var requestedEpoch primitives.Epoch
@@ -489,79 +426,11 @@ func (bs *Server) GetValidatorParticipation(
default:
requestedEpoch = currentEpoch
}
if requestedEpoch > currentEpoch {
return nil, status.Errorf(
codes.InvalidArgument,
"Cannot retrieve information about an epoch greater than current epoch, current epoch %d, requesting %d",
currentEpoch,
requestedEpoch,
)
}
// Use the last slot of requested epoch to obtain current and previous epoch attestations.
// This ensures that we don't miss previous attestations when input requested epochs.
endSlot, err := slots.EpochEnd(requestedEpoch)
vp, err := bs.CoreService.ValidatorParticipation(ctx, requestedEpoch)
if err != nil {
return nil, err
return nil, status.Errorf(core.ErrorReasonToGRPC(err.Reason), "Could not retrieve validator participation: %v", err.Err)
}
// 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.
if endSlot > currentSlot {
endSlot = currentSlot
}
// ReplayerBuilder ensures that a canonical chain is followed to the slot
beaconState, err := bs.ReplayerBuilder.ReplayerForSlot(endSlot).ReplayBlocks(ctx)
if err != nil {
return nil, status.Error(codes.Internal, fmt.Sprintf("error replaying blocks for state at slot %d: %v", endSlot, err))
}
var v []*precompute.Validator
var b *precompute.Balance
if beaconState.Version() == version.Phase0 {
v, b, err = precompute.New(ctx, beaconState)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not set up pre compute instance: %v", err)
}
_, b, err = precompute.ProcessAttestations(ctx, beaconState, v, b)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not pre compute attestations: %v", err)
}
} else if beaconState.Version() >= version.Altair {
v, b, err = altair.InitializePrecomputeValidators(ctx, beaconState)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not set up altair pre compute instance: %v", err)
}
_, b, err = altair.ProcessEpochParticipation(ctx, beaconState, b, v)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not pre compute attestations: %v", err)
}
} else {
return nil, status.Errorf(codes.Internal, "Invalid state type retrieved with a version of %d", beaconState.Version())
}
cp := bs.FinalizationFetcher.FinalizedCheckpt()
p := &ethpb.ValidatorParticipationResponse{
Epoch: requestedEpoch,
Finalized: requestedEpoch <= cp.Epoch,
Participation: &ethpb.ValidatorParticipation{
// TODO(7130): Remove these three deprecated fields.
GlobalParticipationRate: float32(b.PrevEpochTargetAttested) / float32(b.ActivePrevEpoch),
VotedEther: b.PrevEpochTargetAttested,
EligibleEther: b.ActivePrevEpoch,
CurrentEpochActiveGwei: b.ActiveCurrentEpoch,
CurrentEpochAttestingGwei: b.CurrentEpochAttested,
CurrentEpochTargetAttestingGwei: b.CurrentEpochTargetAttested,
PreviousEpochActiveGwei: b.ActivePrevEpoch,
PreviousEpochAttestingGwei: b.PrevEpochAttested,
PreviousEpochTargetAttestingGwei: b.PrevEpochTargetAttested,
PreviousEpochHeadAttestingGwei: b.PrevEpochHeadAttested,
},
}
return p, nil
return vp, nil
}
// GetValidatorQueue retrieves the current validator queue information.

View File

@@ -54,11 +54,13 @@ func TestServer_GetValidatorActiveSetChanges_CannotRequestFutureEpoch(t *testing
require.NoError(t, err)
require.NoError(t, st.SetSlot(0))
bs := &Server{
GenesisTimeFetcher: &mock.ChainService{},
HeadFetcher: &mock.ChainService{
State: st,
CoreService: &core.Service{
BeaconDB: beaconDB,
GenesisTimeFetcher: &mock.ChainService{},
HeadFetcher: &mock.ChainService{
State: st,
},
},
BeaconDB: beaconDB,
}
wanted := errNoEpochInfoError
@@ -66,7 +68,7 @@ func TestServer_GetValidatorActiveSetChanges_CannotRequestFutureEpoch(t *testing
ctx,
&ethpb.GetValidatorActiveSetChangesRequest{
QueryFilter: &ethpb.GetValidatorActiveSetChangesRequest_Epoch{
Epoch: slots.ToEpoch(bs.GenesisTimeFetcher.CurrentSlot()) + 1,
Epoch: slots.ToEpoch(bs.CoreService.GenesisTimeFetcher.CurrentSlot()) + 1,
},
},
)
@@ -1029,7 +1031,7 @@ func TestServer_ListValidators_FromOldEpoch(t *testing.T) {
ctx := context.Background()
slot := primitives.Slot(0)
epochs := 10
epochs := primitives.Epoch(10)
numVals := uint64(10)
beaconDB := dbTest.SetupDB(t)
@@ -1065,7 +1067,7 @@ func TestServer_ListValidators_FromOldEpoch(t *testing.T) {
}
res, err := bs.ListValidators(context.Background(), req)
require.NoError(t, err)
assert.Equal(t, epochs, len(res.ValidatorList))
assert.Equal(t, int(numVals), len(res.ValidatorList))
vals := st.Validators()
want := make([]*ethpb.Validators_ValidatorContainer, 0)
@@ -1077,7 +1079,7 @@ func TestServer_ListValidators_FromOldEpoch(t *testing.T) {
}
req = &ethpb.ListValidatorsRequest{
QueryFilter: &ethpb.ListValidatorsRequest_Epoch{
Epoch: 10,
Epoch: epochs,
},
}
res, err = bs.ListValidators(context.Background(), req)
@@ -1283,10 +1285,12 @@ func TestServer_GetValidatorActiveSetChanges(t *testing.T) {
require.NoError(t, beaconDB.SaveState(ctx, headState, gRoot))
bs := &Server{
FinalizationFetcher: &mock.ChainService{
FinalizedCheckPoint: &ethpb.Checkpoint{Epoch: 0, Root: make([]byte, fieldparams.RootLength)},
CoreService: &core.Service{
FinalizedFetcher: &mock.ChainService{
FinalizedCheckPoint: &ethpb.Checkpoint{Epoch: 0, Root: make([]byte, fieldparams.RootLength)},
},
GenesisTimeFetcher: &mock.ChainService{},
},
GenesisTimeFetcher: &mock.ChainService{},
}
addDefaultReplayerBuilder(bs, beaconDB)
res, err := bs.GetValidatorActiveSetChanges(ctx, &ethpb.GetValidatorActiveSetChangesRequest{
@@ -1476,27 +1480,25 @@ func TestServer_GetValidatorQueue_PendingExit(t *testing.T) {
}
func TestServer_GetValidatorParticipation_CannotRequestFutureEpoch(t *testing.T) {
beaconDB := dbTest.SetupDB(t)
ctx := context.Background()
headState, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, headState.SetSlot(0))
bs := &Server{
BeaconDB: beaconDB,
HeadFetcher: &mock.ChainService{
State: headState,
CoreService: &core.Service{
HeadFetcher: &mock.ChainService{
State: headState,
},
GenesisTimeFetcher: &mock.ChainService{},
},
GenesisTimeFetcher: &mock.ChainService{},
StateGen: stategen.New(beaconDB, doublylinkedtree.New()),
}
wanted := "Cannot retrieve information about an epoch"
wanted := "cannot retrieve information about an epoch"
_, err = bs.GetValidatorParticipation(
ctx,
&ethpb.GetValidatorParticipationRequest{
QueryFilter: &ethpb.GetValidatorParticipationRequest_Epoch{
Epoch: slots.ToEpoch(bs.GenesisTimeFetcher.CurrentSlot()) + 1,
Epoch: slots.ToEpoch(bs.CoreService.GenesisTimeFetcher.CurrentSlot()) + 1,
},
},
)
@@ -1549,18 +1551,20 @@ func TestServer_GetValidatorParticipation_CurrentAndPrevEpoch(t *testing.T) {
m := &mock.ChainService{State: headState}
offset := int64(params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot))
bs := &Server{
BeaconDB: beaconDB,
HeadFetcher: m,
StateGen: stategen.New(beaconDB, doublylinkedtree.New()),
GenesisTimeFetcher: &mock.ChainService{
Genesis: prysmTime.Now().Add(time.Duration(-1*offset) * time.Second),
BeaconDB: beaconDB,
StateGen: stategen.New(beaconDB, doublylinkedtree.New()),
CoreService: &core.Service{
HeadFetcher: m,
GenesisTimeFetcher: &mock.ChainService{
Genesis: prysmTime.Now().Add(time.Duration(-1*offset) * time.Second),
},
FinalizedFetcher: &mock.ChainService{FinalizedCheckPoint: &ethpb.Checkpoint{Epoch: 100}},
},
CanonicalFetcher: &mock.ChainService{
CanonicalRoots: map[[32]byte]bool{
bRoot: true,
},
},
FinalizationFetcher: &mock.ChainService{FinalizedCheckPoint: &ethpb.Checkpoint{Epoch: 100}},
}
addDefaultReplayerBuilder(bs, beaconDB)
@@ -1628,18 +1632,20 @@ func TestServer_GetValidatorParticipation_OrphanedUntilGenesis(t *testing.T) {
m := &mock.ChainService{State: headState}
offset := int64(params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot))
bs := &Server{
BeaconDB: beaconDB,
HeadFetcher: m,
StateGen: stategen.New(beaconDB, doublylinkedtree.New()),
GenesisTimeFetcher: &mock.ChainService{
Genesis: prysmTime.Now().Add(time.Duration(-1*offset) * time.Second),
BeaconDB: beaconDB,
StateGen: stategen.New(beaconDB, doublylinkedtree.New()),
CoreService: &core.Service{
HeadFetcher: m,
GenesisTimeFetcher: &mock.ChainService{
Genesis: prysmTime.Now().Add(time.Duration(-1*offset) * time.Second),
},
FinalizedFetcher: &mock.ChainService{FinalizedCheckPoint: &ethpb.Checkpoint{Epoch: 100}},
},
CanonicalFetcher: &mock.ChainService{
CanonicalRoots: map[[32]byte]bool{
bRoot: true,
},
},
FinalizationFetcher: &mock.ChainService{FinalizedCheckPoint: &ethpb.Checkpoint{Epoch: 100}},
}
addDefaultReplayerBuilder(bs, beaconDB)
@@ -1744,13 +1750,15 @@ func runGetValidatorParticipationCurrentAndPrevEpoch(t *testing.T, genState stat
m := &mock.ChainService{State: genState}
offset := int64(params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot))
bs := &Server{
BeaconDB: beaconDB,
BeaconDB: beaconDB,
CoreService: &core.Service{
GenesisTimeFetcher: &mock.ChainService{
Genesis: prysmTime.Now().Add(time.Duration(-1*offset) * time.Second),
},
FinalizedFetcher: &mock.ChainService{FinalizedCheckPoint: &ethpb.Checkpoint{Epoch: 100}},
},
HeadFetcher: m,
StateGen: stategen.New(beaconDB, doublylinkedtree.New()),
GenesisTimeFetcher: &mock.ChainService{
Genesis: prysmTime.Now().Add(time.Duration(-1*offset) * time.Second),
},
FinalizationFetcher: &mock.ChainService{FinalizedCheckPoint: &ethpb.Checkpoint{Epoch: 100}},
}
addDefaultReplayerBuilder(bs, beaconDB)

View File

@@ -11,6 +11,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/features"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
@@ -112,18 +113,12 @@ func (vs *Server) packAttestations(ctx context.Context, latestState state.Beacon
if err != nil {
return nil, err
}
sorted, err := deduped.sortByProfitability()
sorted, err := deduped.sort()
if err != nil {
return nil, err
}
atts = sorted.limitToMaxAttestations()
atts, err = vs.filterAttestationBySignature(ctx, atts, latestState)
if err != nil {
return nil, err
}
return atts, nil
return vs.filterAttestationBySignature(ctx, atts, latestState)
}
// filter separates attestation list into two groups: valid and invalid attestations.
@@ -143,14 +138,6 @@ func (a proposerAtts) filter(ctx context.Context, st state.BeaconState) (propose
return validAtts, invalidAtts
}
// sortByProfitability orders attestations by highest slot and by highest aggregation bit count.
func (a proposerAtts) sortByProfitability() (proposerAtts, error) {
if len(a) < 2 {
return a, nil
}
return a.sortByProfitabilityUsingMaxCover()
}
// sortByProfitabilityUsingMaxCover orders attestations by highest slot and by highest aggregation bit count.
// Duplicate bits are counted only once, using max-cover algorithm.
func (a proposerAtts) sortByProfitabilityUsingMaxCover() (proposerAtts, error) {
@@ -218,6 +205,143 @@ func (a proposerAtts) sortByProfitabilityUsingMaxCover() (proposerAtts, error) {
return sortedAtts, nil
}
// sort attestations as follows:
//
// - all attestations selected by max-cover are taken, leftover attestations are discarded
// (with current parameters all bits of a leftover attestation are already covered by selected attestations)
// - selected attestations are ordered by slot, with higher slot coming first
// - within a slot, all top attestations (one per committee) are ordered before any second-best attestations, second-best before third-best etc.
// - within top/second-best/etc. attestations (one per committee), attestations are ordered by bit count, with higher bit count coming first
func (a proposerAtts) sort() (proposerAtts, error) {
if len(a) < 2 {
return a, nil
}
if features.Get().EnableCommitteeAwarePacking {
return a.sortBySlotAndCommittee()
}
return a.sortByProfitabilityUsingMaxCover()
}
// Separate attestations by slot, as slot number takes higher precedence when sorting.
// Also separate by committee index because maxcover will prefer attestations for the same
// committee with disjoint bits over attestations for different committees with overlapping
// bits, even though same bits for different committees are separate votes.
func (a proposerAtts) sortBySlotAndCommittee() (proposerAtts, error) {
type slotAtts struct {
candidates map[primitives.CommitteeIndex]proposerAtts
selected map[primitives.CommitteeIndex]proposerAtts
leftover map[primitives.CommitteeIndex]proposerAtts
}
var slots []primitives.Slot
attsBySlot := map[primitives.Slot]*slotAtts{}
for _, att := range a {
slot := att.GetData().Slot
ci := att.GetData().CommitteeIndex
if _, ok := attsBySlot[slot]; !ok {
attsBySlot[slot] = &slotAtts{}
attsBySlot[slot].candidates = make(map[primitives.CommitteeIndex]proposerAtts)
slots = append(slots, slot)
}
attsBySlot[slot].candidates[ci] = append(attsBySlot[slot].candidates[ci], att)
}
var err error
for _, sa := range attsBySlot {
sa.selected = make(map[primitives.CommitteeIndex]proposerAtts)
sa.leftover = make(map[primitives.CommitteeIndex]proposerAtts)
for ci, committeeAtts := range sa.candidates {
sa.selected[ci], err = committeeAtts.sortByProfitabilityUsingMaxCover_committeeAwarePacking()
if err != nil {
return nil, err
}
}
}
var sortedAtts proposerAtts
sort.Slice(slots, func(i, j int) bool {
return slots[i] > slots[j]
})
for _, slot := range slots {
sortedAtts = append(sortedAtts, sortSlotAttestations(attsBySlot[slot].selected)...)
}
for _, slot := range slots {
sortedAtts = append(sortedAtts, sortSlotAttestations(attsBySlot[slot].leftover)...)
}
return sortedAtts, nil
}
// sortByProfitabilityUsingMaxCover orders attestations by highest aggregation bit count.
// Duplicate bits are counted only once, using max-cover algorithm.
func (a proposerAtts) sortByProfitabilityUsingMaxCover_committeeAwarePacking() (proposerAtts, error) {
if len(a) < 2 {
return a, nil
}
candidates := make([]*bitfield.Bitlist64, len(a))
for i := 0; i < len(a); i++ {
var err error
candidates[i], err = a[i].GetAggregationBits().ToBitlist64()
if err != nil {
return nil, err
}
}
// Add selected candidates on top, those that are not selected - append at bottom.
selectedKeys, _, err := aggregation.MaxCover(candidates, len(candidates), true /* allowOverlaps */)
if err != nil {
log.WithError(err).Debug("MaxCover aggregation failed")
return a, nil
}
// Pick selected attestations first, leftover attestations will be appended at the end.
// Both lists will be sorted by number of bits set.
selected := make(proposerAtts, selectedKeys.Count())
for i, key := range selectedKeys.BitIndices() {
selected[i] = a[key]
}
sort.Slice(selected, func(i, j int) bool {
return selected[i].GetAggregationBits().Count() > selected[j].GetAggregationBits().Count()
})
return selected, nil
}
// sortSlotAttestations assumes each proposerAtts value in the map is ordered by profitability.
// The function takes the first attestation from each value, orders these attestations by bit count
// and places them at the start of the resulting slice. It then takes the second attestation for each value,
// orders these attestations by bit count and appends them to the end.
// It continues this pattern until all attestations are processed.
func sortSlotAttestations(slotAtts map[primitives.CommitteeIndex]proposerAtts) proposerAtts {
attCount := 0
for _, committeeAtts := range slotAtts {
attCount += len(committeeAtts)
}
sorted := make([]ethpb.Att, 0, attCount)
processedCount := 0
index := 0
for processedCount < attCount {
var atts []ethpb.Att
for _, committeeAtts := range slotAtts {
if len(committeeAtts) > index {
atts = append(atts, committeeAtts[index])
}
}
sort.Slice(atts, func(i, j int) bool {
return atts[i].GetAggregationBits().Count() > atts[j].GetAggregationBits().Count()
})
sorted = append(sorted, atts...)
processedCount += len(atts)
index++
}
return sorted
}
// limitToMaxAttestations limits attestations to maximum attestations per block.
func (a proposerAtts) limitToMaxAttestations() proposerAtts {
if len(a) == 0 {

View File

@@ -9,6 +9,7 @@ import (
"github.com/prysmaticlabs/go-bitfield"
chainMock "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/attestations"
"github.com/prysmaticlabs/prysm/v5/config/features"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/crypto/bls/blst"
@@ -19,31 +20,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/time/slots"
)
func TestProposer_ProposerAtts_sortByProfitability(t *testing.T) {
atts := proposerAtts([]ethpb.Att{
util.HydrateAttestation(&ethpb.Attestation{Data: &ethpb.AttestationData{Slot: 4}, AggregationBits: bitfield.Bitlist{0b11100000}}),
util.HydrateAttestation(&ethpb.Attestation{Data: &ethpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b11000000}}),
util.HydrateAttestation(&ethpb.Attestation{Data: &ethpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b11100000}}),
util.HydrateAttestation(&ethpb.Attestation{Data: &ethpb.AttestationData{Slot: 4}, AggregationBits: bitfield.Bitlist{0b11110000}}),
util.HydrateAttestation(&ethpb.Attestation{Data: &ethpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b11100000}}),
util.HydrateAttestation(&ethpb.Attestation{Data: &ethpb.AttestationData{Slot: 3}, AggregationBits: bitfield.Bitlist{0b11000000}}),
})
want := proposerAtts([]ethpb.Att{
util.HydrateAttestation(&ethpb.Attestation{Data: &ethpb.AttestationData{Slot: 4}, AggregationBits: bitfield.Bitlist{0b11110000}}),
util.HydrateAttestation(&ethpb.Attestation{Data: &ethpb.AttestationData{Slot: 4}, AggregationBits: bitfield.Bitlist{0b11100000}}),
util.HydrateAttestation(&ethpb.Attestation{Data: &ethpb.AttestationData{Slot: 3}, AggregationBits: bitfield.Bitlist{0b11000000}}),
util.HydrateAttestation(&ethpb.Attestation{Data: &ethpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b11100000}}),
util.HydrateAttestation(&ethpb.Attestation{Data: &ethpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b11100000}}),
util.HydrateAttestation(&ethpb.Attestation{Data: &ethpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b11000000}}),
})
atts, err := atts.sortByProfitability()
if err != nil {
t.Error(err)
}
require.DeepEqual(t, want, atts)
}
func TestProposer_ProposerAtts_sortByProfitabilityUsingMaxCover(t *testing.T) {
func TestProposer_ProposerAtts_sort(t *testing.T) {
type testData struct {
slot primitives.Slot
bits bitfield.Bitlist
@@ -60,7 +37,7 @@ func TestProposer_ProposerAtts_sortByProfitabilityUsingMaxCover(t *testing.T) {
t.Run("no atts", func(t *testing.T) {
atts := getAtts([]testData{})
want := getAtts([]testData{})
atts, err := atts.sortByProfitability()
atts, err := atts.sort()
if err != nil {
t.Error(err)
}
@@ -74,7 +51,7 @@ func TestProposer_ProposerAtts_sortByProfitabilityUsingMaxCover(t *testing.T) {
want := getAtts([]testData{
{4, bitfield.Bitlist{0b11100000, 0b1}},
})
atts, err := atts.sortByProfitability()
atts, err := atts.sort()
if err != nil {
t.Error(err)
}
@@ -90,7 +67,7 @@ func TestProposer_ProposerAtts_sortByProfitabilityUsingMaxCover(t *testing.T) {
{4, bitfield.Bitlist{0b11100000, 0b1}},
{1, bitfield.Bitlist{0b11000000, 0b1}},
})
atts, err := atts.sortByProfitability()
atts, err := atts.sort()
if err != nil {
t.Error(err)
}
@@ -108,7 +85,7 @@ func TestProposer_ProposerAtts_sortByProfitabilityUsingMaxCover(t *testing.T) {
{4, bitfield.Bitlist{0b11100000, 0b1}},
{1, bitfield.Bitlist{0b11000000, 0b1}},
})
atts, err := atts.sortByProfitability()
atts, err := atts.sort()
if err != nil {
t.Error(err)
}
@@ -129,7 +106,7 @@ func TestProposer_ProposerAtts_sortByProfitabilityUsingMaxCover(t *testing.T) {
{1, bitfield.Bitlist{0b00001100, 0b1}},
{1, bitfield.Bitlist{0b11001000, 0b1}},
})
atts, err := atts.sortByProfitability()
atts, err := atts.sort()
if err != nil {
t.Error(err)
}
@@ -154,14 +131,14 @@ func TestProposer_ProposerAtts_sortByProfitabilityUsingMaxCover(t *testing.T) {
{1, bitfield.Bitlist{0b11100000, 0b1}},
{1, bitfield.Bitlist{0b11000000, 0b1}},
})
atts, err := atts.sortByProfitability()
atts, err := atts.sort()
if err != nil {
t.Error(err)
}
require.DeepEqual(t, want, atts)
})
t.Run("selected and non selected atts sorted by bit count", func(t *testing.T) {
t.Run("follows max-cover", func(t *testing.T) {
// Items at slot 4, must be first split into two lists by max-cover, with
// 0b10000011 scoring higher (as it provides more info in addition to already selected
// attestations) than 0b11100001 (despite naive bit count suggesting otherwise). Then,
@@ -186,7 +163,7 @@ func TestProposer_ProposerAtts_sortByProfitabilityUsingMaxCover(t *testing.T) {
{1, bitfield.Bitlist{0b11100000, 0b1}},
{1, bitfield.Bitlist{0b11000000, 0b1}},
})
atts, err := atts.sortByProfitability()
atts, err := atts.sort()
if err != nil {
t.Error(err)
}
@@ -194,6 +171,241 @@ func TestProposer_ProposerAtts_sortByProfitabilityUsingMaxCover(t *testing.T) {
})
}
func TestProposer_ProposerAtts_committeeAwareSort(t *testing.T) {
type testData struct {
slot primitives.Slot
bits bitfield.Bitlist
}
getAtts := func(data []testData) proposerAtts {
var atts proposerAtts
for _, att := range data {
atts = append(atts, util.HydrateAttestation(&ethpb.Attestation{
Data: &ethpb.AttestationData{Slot: att.slot}, AggregationBits: att.bits}))
}
return atts
}
t.Run("no atts", func(t *testing.T) {
feat := features.Get()
feat.EnableCommitteeAwarePacking = true
reset := features.InitWithReset(feat)
defer reset()
atts := getAtts([]testData{})
want := getAtts([]testData{})
atts, err := atts.sort()
if err != nil {
t.Error(err)
}
require.DeepEqual(t, want, atts)
})
t.Run("single att", func(t *testing.T) {
feat := features.Get()
feat.EnableCommitteeAwarePacking = true
reset := features.InitWithReset(feat)
defer reset()
atts := getAtts([]testData{
{4, bitfield.Bitlist{0b11100000, 0b1}},
})
want := getAtts([]testData{
{4, bitfield.Bitlist{0b11100000, 0b1}},
})
atts, err := atts.sort()
if err != nil {
t.Error(err)
}
require.DeepEqual(t, want, atts)
})
t.Run("single att per slot", func(t *testing.T) {
feat := features.Get()
feat.EnableCommitteeAwarePacking = true
reset := features.InitWithReset(feat)
defer reset()
atts := getAtts([]testData{
{1, bitfield.Bitlist{0b11000000, 0b1}},
{4, bitfield.Bitlist{0b11100000, 0b1}},
})
want := getAtts([]testData{
{4, bitfield.Bitlist{0b11100000, 0b1}},
{1, bitfield.Bitlist{0b11000000, 0b1}},
})
atts, err := atts.sort()
if err != nil {
t.Error(err)
}
require.DeepEqual(t, want, atts)
})
t.Run("two atts on one of the slots", func(t *testing.T) {
feat := features.Get()
feat.EnableCommitteeAwarePacking = true
reset := features.InitWithReset(feat)
defer reset()
atts := getAtts([]testData{
{1, bitfield.Bitlist{0b11000000, 0b1}},
{4, bitfield.Bitlist{0b11100000, 0b1}},
{4, bitfield.Bitlist{0b11110000, 0b1}},
})
want := getAtts([]testData{
{4, bitfield.Bitlist{0b11110000, 0b1}},
{1, bitfield.Bitlist{0b11000000, 0b1}},
})
atts, err := atts.sort()
if err != nil {
t.Error(err)
}
require.DeepEqual(t, want, atts)
})
t.Run("compare to native sort", func(t *testing.T) {
feat := features.Get()
feat.EnableCommitteeAwarePacking = true
reset := features.InitWithReset(feat)
defer reset()
// The max-cover based approach will select 0b00001100 instead, despite lower bit count
// (since it has two new/unknown bits).
t.Run("max-cover", func(t *testing.T) {
atts := getAtts([]testData{
{1, bitfield.Bitlist{0b11000011, 0b1}},
{1, bitfield.Bitlist{0b11001000, 0b1}},
{1, bitfield.Bitlist{0b00001100, 0b1}},
})
want := getAtts([]testData{
{1, bitfield.Bitlist{0b11000011, 0b1}},
{1, bitfield.Bitlist{0b00001100, 0b1}},
})
atts, err := atts.sort()
if err != nil {
t.Error(err)
}
require.DeepEqual(t, want, atts)
})
})
t.Run("multiple slots", func(t *testing.T) {
feat := features.Get()
feat.EnableCommitteeAwarePacking = true
reset := features.InitWithReset(feat)
defer reset()
atts := getAtts([]testData{
{2, bitfield.Bitlist{0b11100000, 0b1}},
{4, bitfield.Bitlist{0b11100000, 0b1}},
{1, bitfield.Bitlist{0b11000000, 0b1}},
{4, bitfield.Bitlist{0b11110000, 0b1}},
{1, bitfield.Bitlist{0b11100000, 0b1}},
{3, bitfield.Bitlist{0b11000000, 0b1}},
})
want := getAtts([]testData{
{4, bitfield.Bitlist{0b11110000, 0b1}},
{3, bitfield.Bitlist{0b11000000, 0b1}},
{2, bitfield.Bitlist{0b11100000, 0b1}},
{1, bitfield.Bitlist{0b11100000, 0b1}},
})
atts, err := atts.sort()
if err != nil {
t.Error(err)
}
require.DeepEqual(t, want, atts)
})
t.Run("follows max-cover", func(t *testing.T) {
feat := features.Get()
feat.EnableCommitteeAwarePacking = true
reset := features.InitWithReset(feat)
defer reset()
// Items at slot 4 must be first split into two lists by max-cover, with
// 0b10000011 being selected and 0b11100001 being leftover (despite naive bit count suggesting otherwise).
atts := getAtts([]testData{
{4, bitfield.Bitlist{0b00000001, 0b1}},
{4, bitfield.Bitlist{0b11100001, 0b1}},
{1, bitfield.Bitlist{0b11000000, 0b1}},
{2, bitfield.Bitlist{0b11100000, 0b1}},
{4, bitfield.Bitlist{0b10000011, 0b1}},
{4, bitfield.Bitlist{0b11111000, 0b1}},
{1, bitfield.Bitlist{0b11100000, 0b1}},
{3, bitfield.Bitlist{0b11000000, 0b1}},
})
want := getAtts([]testData{
{4, bitfield.Bitlist{0b11111000, 0b1}},
{4, bitfield.Bitlist{0b10000011, 0b1}},
{3, bitfield.Bitlist{0b11000000, 0b1}},
{2, bitfield.Bitlist{0b11100000, 0b1}},
{1, bitfield.Bitlist{0b11100000, 0b1}},
})
atts, err := atts.sort()
if err != nil {
t.Error(err)
}
require.DeepEqual(t, want, atts)
})
}
func TestProposer_sort_DifferentCommittees(t *testing.T) {
t.Run("one att per committee", func(t *testing.T) {
feat := features.Get()
feat.EnableCommitteeAwarePacking = true
reset := features.InitWithReset(feat)
defer reset()
c1_a1 := util.HydrateAttestation(&ethpb.Attestation{AggregationBits: bitfield.Bitlist{0b11111000, 0b1}, Data: &ethpb.AttestationData{CommitteeIndex: 1}})
c2_a1 := util.HydrateAttestation(&ethpb.Attestation{AggregationBits: bitfield.Bitlist{0b11100000, 0b1}, Data: &ethpb.AttestationData{CommitteeIndex: 2}})
atts := proposerAtts{c1_a1, c2_a1}
atts, err := atts.sort()
require.NoError(t, err)
want := proposerAtts{c1_a1, c2_a1}
assert.DeepEqual(t, want, atts)
})
t.Run("multiple atts per committee", func(t *testing.T) {
feat := features.Get()
feat.EnableCommitteeAwarePacking = true
reset := features.InitWithReset(feat)
defer reset()
c1_a1 := util.HydrateAttestation(&ethpb.Attestation{AggregationBits: bitfield.Bitlist{0b11111100, 0b1}, Data: &ethpb.AttestationData{CommitteeIndex: 1}})
c1_a2 := util.HydrateAttestation(&ethpb.Attestation{AggregationBits: bitfield.Bitlist{0b10000010, 0b1}, Data: &ethpb.AttestationData{CommitteeIndex: 1}})
c2_a1 := util.HydrateAttestation(&ethpb.Attestation{AggregationBits: bitfield.Bitlist{0b11110000, 0b1}, Data: &ethpb.AttestationData{CommitteeIndex: 2}})
c2_a2 := util.HydrateAttestation(&ethpb.Attestation{AggregationBits: bitfield.Bitlist{0b11100000, 0b1}, Data: &ethpb.AttestationData{CommitteeIndex: 2}})
atts := proposerAtts{c1_a1, c1_a2, c2_a1, c2_a2}
atts, err := atts.sort()
require.NoError(t, err)
want := proposerAtts{c1_a1, c2_a1, c1_a2}
assert.DeepEqual(t, want, atts)
})
t.Run("multiple atts per committee, multiple slots", func(t *testing.T) {
feat := features.Get()
feat.EnableCommitteeAwarePacking = true
reset := features.InitWithReset(feat)
defer reset()
s2_c1_a1 := util.HydrateAttestation(&ethpb.Attestation{AggregationBits: bitfield.Bitlist{0b11111100, 0b1}, Data: &ethpb.AttestationData{Slot: 2, CommitteeIndex: 1}})
s2_c1_a2 := util.HydrateAttestation(&ethpb.Attestation{AggregationBits: bitfield.Bitlist{0b10000010, 0b1}, Data: &ethpb.AttestationData{Slot: 2, CommitteeIndex: 1}})
s2_c2_a1 := util.HydrateAttestation(&ethpb.Attestation{AggregationBits: bitfield.Bitlist{0b11110000, 0b1}, Data: &ethpb.AttestationData{Slot: 2, CommitteeIndex: 2}})
s2_c2_a2 := util.HydrateAttestation(&ethpb.Attestation{AggregationBits: bitfield.Bitlist{0b11000000, 0b1}, Data: &ethpb.AttestationData{Slot: 2, CommitteeIndex: 2}})
s1_c1_a1 := util.HydrateAttestation(&ethpb.Attestation{AggregationBits: bitfield.Bitlist{0b11111100, 0b1}, Data: &ethpb.AttestationData{Slot: 1, CommitteeIndex: 1}})
s1_c1_a2 := util.HydrateAttestation(&ethpb.Attestation{AggregationBits: bitfield.Bitlist{0b10000010, 0b1}, Data: &ethpb.AttestationData{Slot: 1, CommitteeIndex: 1}})
s1_c2_a1 := util.HydrateAttestation(&ethpb.Attestation{AggregationBits: bitfield.Bitlist{0b11110000, 0b1}, Data: &ethpb.AttestationData{Slot: 1, CommitteeIndex: 2}})
s1_c2_a2 := util.HydrateAttestation(&ethpb.Attestation{AggregationBits: bitfield.Bitlist{0b11000000, 0b1}, Data: &ethpb.AttestationData{Slot: 1, CommitteeIndex: 2}})
// Arrange in some random order
atts := proposerAtts{s1_c1_a1, s2_c1_a2, s1_c2_a2, s2_c2_a2, s1_c2_a1, s2_c2_a1, s1_c1_a2, s2_c1_a1}
atts, err := atts.sort()
require.NoError(t, err)
want := proposerAtts{s2_c1_a1, s2_c2_a1, s2_c1_a2, s1_c1_a1, s1_c2_a1, s1_c1_a2}
assert.DeepEqual(t, want, atts)
})
}
func TestProposer_ProposerAtts_dedup(t *testing.T) {
data1 := util.HydrateAttestationData(&ethpb.AttestationData{
Slot: 4,

View File

@@ -96,6 +96,27 @@ func setExecutionData(ctx context.Context, blk interfaces.SignedBeaconBlock, loc
// Compare payload values between local and builder. Default to the local value if it is higher.
localValueGwei := primitives.WeiToGwei(local.Bid)
builderValueGwei := primitives.WeiToGwei(bid.Value())
minBid := primitives.Gwei(params.BeaconConfig().MinBuilderBid)
// Use local block if min bid is not attained
if builderValueGwei < minBid {
log.WithFields(logrus.Fields{
"minBuilderBid": minBid,
"builderGweiValue": builderValueGwei,
}).Warn("Proposer: using local execution payload because min bid not attained")
return local.Bid, local.BlobsBundle, setLocalExecution(blk, local)
}
// Use local block if min difference is not attained
minDiff := localValueGwei + primitives.Gwei(params.BeaconConfig().MinBuilderDiff)
if builderValueGwei < minDiff {
log.WithFields(logrus.Fields{
"localGweiValue": localValueGwei,
"minBidDiff": minDiff,
"builderGweiValue": builderValueGwei,
}).Warn("Proposer: using local execution payload because min difference with local value was not attained")
return local.Bid, local.BlobsBundle, setLocalExecution(blk, local)
}
// Use builder payload if the following in true:
// builder_bid_value * builderBoostFactor(default 100) > local_block_value * (local-block-value-boost + 100)
boost := primitives.Gwei(params.BeaconConfig().LocalBlockValueBoost)

View File

@@ -420,7 +420,41 @@ func TestServer_setExecutionData(t *testing.T) {
require.NoError(t, err)
require.Equal(t, uint64(3), e.BlockNumber()) // Local block
require.LogsContain(t, hook, "builderGweiValue=1 localBoostPercentage=0 localGweiValue=2")
require.LogsContain(t, hook, "\"Proposer: using local execution payload because min difference with local value was not attained\" builderGweiValue=1 localGweiValue=2")
})
t.Run("Builder configured. Builder block does not achieve min bid", func(t *testing.T) {
cfg := params.BeaconConfig().Copy()
cfg.MinBuilderBid = 5
params.OverrideBeaconConfig(cfg)
blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
require.NoError(t, err)
elBid := primitives.Uint64ToWei(2 * 1e9)
ed, err := blocks.NewWrappedExecutionData(&v1.ExecutionPayloadCapella{BlockNumber: 3})
require.NoError(t, err)
vs.ExecutionEngineCaller = &powtesting.EngineClient{PayloadIDBytes: id, GetPayloadResponse: &blocks.GetPayloadResponse{ExecutionData: ed, Bid: elBid}}
b := blk.Block()
res, err := vs.getLocalPayload(ctx, b, capellaTransitionState)
require.NoError(t, err)
builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
require.NoError(t, err)
_, err = builderBid.Header()
require.NoError(t, err)
builderKzgCommitments, err := builderBid.BlobKzgCommitments()
if builderBid.Version() >= version.Deneb {
require.NoError(t, err)
}
require.DeepEqual(t, [][]uint8{}, builderKzgCommitments)
_, bundle, err := setExecutionData(context.Background(), blk, res, builderBid, defaultBuilderBoostFactor)
require.NoError(t, err)
require.IsNil(t, bundle)
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(3), e.BlockNumber()) // Local block
require.LogsContain(t, hook, "\"Proposer: using local execution payload because min bid not attained\" builderGweiValue=1 minBuilderBid=5")
cfg.MinBuilderBid = 0
params.OverrideBeaconConfig(cfg)
})
t.Run("Builder configured. Local block and local boost has higher value", func(t *testing.T) {
cfg := params.BeaconConfig().Copy()

View File

@@ -49,7 +49,7 @@ func BenchmarkProposerAtts_sortByProfitability(b *testing.B) {
for i, att := range atts {
attsCopy[i] = att.(*ethpb.Attestation).Copy()
}
_, err := attsCopy.sortByProfitability()
_, err := attsCopy.sort()
require.NoError(b, err, "Could not sort attestations by profitability")
}

View File

@@ -3,6 +3,7 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"handlers.go",
"server.go",
"validator_performance.go",
],
@@ -10,9 +11,17 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//api/server/structs:go_default_library",
"//beacon-chain/blockchain:go_default_library",
"//beacon-chain/db:go_default_library",
"//beacon-chain/rpc/core:go_default_library",
"//beacon-chain/rpc/eth/shared:go_default_library",
"//beacon-chain/rpc/lookup:go_default_library",
"//consensus-types/primitives:go_default_library",
"//network/httputil:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_gorilla_mux//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
],
@@ -20,23 +29,42 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["validator_performance_test.go"],
srcs = [
"handlers_test.go",
"validator_performance_test.go",
],
embed = [":go_default_library"],
deps = [
"//api/server/structs:go_default_library",
"//beacon-chain/blockchain/testing:go_default_library",
"//beacon-chain/core/altair:go_default_library",
"//beacon-chain/core/epoch/precompute:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/transition:go_default_library",
"//beacon-chain/db/testing:go_default_library",
"//beacon-chain/forkchoice/doubly-linked-tree:go_default_library",
"//beacon-chain/rpc/core:go_default_library",
"//beacon-chain/rpc/testutil:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//beacon-chain/state/stategen/mock:go_default_library",
"//beacon-chain/sync/initial-sync/testing:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/blocks/testing:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"//time:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_gorilla_mux//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
],
)

View File

@@ -0,0 +1,117 @@
package validator
import (
"fmt"
"net/http"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/gorilla/mux"
"github.com/prysmaticlabs/prysm/v5/api/server/structs"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/core"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/shared"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/network/httputil"
"github.com/prysmaticlabs/prysm/v5/time/slots"
"go.opencensus.io/trace"
)
// GetParticipation retrieves the validator participation information for a given epoch,
// it returns the information about validator's participation rate in voting on the proof of stake
// rules based on their balance compared to the total active validator balance.
func (s *Server) GetParticipation(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "validator.GetParticipation")
defer span.End()
stateId := mux.Vars(r)["state_id"]
if stateId == "" {
httputil.HandleError(w, "state_id is required in URL params", http.StatusBadRequest)
return
}
st, err := s.Stater.State(ctx, []byte(stateId))
if err != nil {
shared.WriteStateFetchError(w, err)
return
}
stEpoch := slots.ToEpoch(st.Slot())
vp, rpcError := s.CoreService.ValidatorParticipation(ctx, stEpoch)
if rpcError != nil {
httputil.HandleError(w, rpcError.Err.Error(), core.ErrorReasonToHTTP(rpcError.Reason))
return
}
response := &structs.GetValidatorParticipationResponse{
Epoch: fmt.Sprintf("%d", vp.Epoch),
Finalized: vp.Finalized,
Participation: &structs.ValidatorParticipation{
GlobalParticipationRate: fmt.Sprintf("%f", vp.Participation.GlobalParticipationRate),
VotedEther: fmt.Sprintf("%d", vp.Participation.VotedEther),
EligibleEther: fmt.Sprintf("%d", vp.Participation.EligibleEther),
CurrentEpochActiveGwei: fmt.Sprintf("%d", vp.Participation.CurrentEpochActiveGwei),
CurrentEpochAttestingGwei: fmt.Sprintf("%d", vp.Participation.CurrentEpochAttestingGwei),
CurrentEpochTargetAttestingGwei: fmt.Sprintf("%d", vp.Participation.CurrentEpochTargetAttestingGwei),
PreviousEpochActiveGwei: fmt.Sprintf("%d", vp.Participation.PreviousEpochActiveGwei),
PreviousEpochAttestingGwei: fmt.Sprintf("%d", vp.Participation.PreviousEpochAttestingGwei),
PreviousEpochTargetAttestingGwei: fmt.Sprintf("%d", vp.Participation.PreviousEpochTargetAttestingGwei),
PreviousEpochHeadAttestingGwei: fmt.Sprintf("%d", vp.Participation.PreviousEpochHeadAttestingGwei),
},
}
httputil.WriteJson(w, response)
}
// GetActiveSetChanges retrieves the active set changes for a given epoch.
//
// This data includes any activations, voluntary exits, and involuntary
// ejections.
func (s *Server) GetActiveSetChanges(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "validator.GetActiveSetChanges")
defer span.End()
stateId := mux.Vars(r)["state_id"]
if stateId == "" {
httputil.HandleError(w, "state_id is required in URL params", http.StatusBadRequest)
return
}
st, err := s.Stater.State(ctx, []byte(stateId))
if err != nil {
shared.WriteStateFetchError(w, err)
return
}
stEpoch := slots.ToEpoch(st.Slot())
as, rpcError := s.CoreService.ValidatorActiveSetChanges(ctx, stEpoch)
if rpcError != nil {
httputil.HandleError(w, rpcError.Err.Error(), core.ErrorReasonToHTTP(rpcError.Reason))
return
}
response := &structs.ActiveSetChanges{
Epoch: fmt.Sprintf("%d", as.Epoch),
ActivatedPublicKeys: byteSlice2dToStringSlice(as.ActivatedPublicKeys),
ActivatedIndices: uint64SliceToStringSlice(as.ActivatedIndices),
ExitedPublicKeys: byteSlice2dToStringSlice(as.ExitedPublicKeys),
ExitedIndices: uint64SliceToStringSlice(as.ExitedIndices),
SlashedPublicKeys: byteSlice2dToStringSlice(as.SlashedPublicKeys),
SlashedIndices: uint64SliceToStringSlice(as.SlashedIndices),
EjectedPublicKeys: byteSlice2dToStringSlice(as.EjectedPublicKeys),
EjectedIndices: uint64SliceToStringSlice(as.EjectedIndices),
}
httputil.WriteJson(w, response)
}
func byteSlice2dToStringSlice(byteArrays [][]byte) []string {
s := make([]string, len(byteArrays))
for i, b := range byteArrays {
s[i] = hexutil.Encode(b)
}
return s
}
func uint64SliceToStringSlice(indices []primitives.ValidatorIndex) []string {
s := make([]string, len(indices))
for i, u := range indices {
s[i] = fmt.Sprintf("%d", u)
}
return s
}

View File

@@ -0,0 +1,558 @@
package validator
import (
"bytes"
"context"
"encoding/binary"
"encoding/json"
"fmt"
"math"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/gorilla/mux"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/v5/api/server/structs"
mock "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition"
dbTest "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing"
doublylinkedtree "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/doubly-linked-tree"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/core"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/testutil"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stategen"
mockstategen "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stategen/mock"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
blocktest "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks/testing"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/assert"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
prysmTime "github.com/prysmaticlabs/prysm/v5/time"
"github.com/prysmaticlabs/prysm/v5/time/slots"
)
func addDefaultReplayerBuilder(s *Server, h stategen.HistoryAccessor) {
cc := &mockstategen.CanonicalChecker{Is: true, Err: nil}
cs := &mockstategen.CurrentSlotter{Slot: math.MaxUint64 - 1}
s.CoreService.ReplayerBuilder = stategen.NewCanonicalHistory(h, cc, cs)
}
func TestServer_GetValidatorParticipation_NoState(t *testing.T) {
headState, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, headState.SetSlot(0))
var st state.BeaconState
st, _ = util.DeterministicGenesisState(t, 4)
s := &Server{
Stater: &testutil.MockStater{
BeaconState: st,
},
CoreService: &core.Service{
HeadFetcher: &mock.ChainService{
State: headState,
},
GenesisTimeFetcher: &mock.ChainService{},
},
}
url := "http://example.com" + fmt.Sprintf("%d", slots.ToEpoch(s.CoreService.GenesisTimeFetcher.CurrentSlot())+1)
request := httptest.NewRequest(http.MethodGet, url, nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetParticipation(writer, request)
require.Equal(t, http.StatusBadRequest, writer.Code)
require.StringContains(t, "state_id is required in URL params", writer.Body.String())
}
func TestServer_GetValidatorParticipation_CurrentAndPrevEpoch(t *testing.T) {
helpers.ClearCache()
beaconDB := dbTest.SetupDB(t)
ctx := context.Background()
validatorCount := uint64(32)
validators := make([]*ethpb.Validator, validatorCount)
balances := make([]uint64, validatorCount)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
PublicKey: bytesutil.ToBytes(uint64(i), 48),
WithdrawalCredentials: make([]byte, 32),
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
}
balances[i] = params.BeaconConfig().MaxEffectiveBalance
}
atts := []*ethpb.PendingAttestation{{
Data: util.HydrateAttestationData(&ethpb.AttestationData{}),
InclusionDelay: 1,
AggregationBits: bitfield.NewBitlist(validatorCount / uint64(params.BeaconConfig().SlotsPerEpoch)),
}}
headState, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, headState.SetSlot(8))
require.NoError(t, headState.SetValidators(validators))
require.NoError(t, headState.SetBalances(balances))
require.NoError(t, headState.AppendCurrentEpochAttestations(atts[0]))
require.NoError(t, headState.AppendPreviousEpochAttestations(atts[0]))
b := util.NewBeaconBlock()
b.Block.Slot = 8
util.SaveBlock(t, ctx, beaconDB, b)
bRoot, err := b.Block.HashTreeRoot()
require.NoError(t, beaconDB.SaveStateSummary(ctx, &ethpb.StateSummary{Root: bRoot[:]}))
require.NoError(t, beaconDB.SaveStateSummary(ctx, &ethpb.StateSummary{Root: params.BeaconConfig().ZeroHash[:]}))
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, bRoot))
require.NoError(t, err)
require.NoError(t, beaconDB.SaveState(ctx, headState, bRoot))
require.NoError(t, beaconDB.SaveState(ctx, headState, params.BeaconConfig().ZeroHash))
m := &mock.ChainService{State: headState}
offset := int64(params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot))
var st state.BeaconState
st, _ = util.DeterministicGenesisState(t, 4)
s := &Server{
Stater: &testutil.MockStater{
BeaconState: st,
},
BeaconDB: beaconDB,
CoreService: &core.Service{
HeadFetcher: m,
StateGen: stategen.New(beaconDB, doublylinkedtree.New()),
GenesisTimeFetcher: &mock.ChainService{
Genesis: prysmTime.Now().Add(time.Duration(-1*offset) * time.Second),
},
FinalizedFetcher: &mock.ChainService{FinalizedCheckPoint: &ethpb.Checkpoint{Epoch: 100}},
},
CanonicalFetcher: &mock.ChainService{
CanonicalRoots: map[[32]byte]bool{
bRoot: true,
},
},
}
addDefaultReplayerBuilder(s, beaconDB)
url := "http://example.com"
request := httptest.NewRequest(http.MethodGet, url, nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetParticipation(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
want := &structs.GetValidatorParticipationResponse{
Participation: &structs.ValidatorParticipation{
GlobalParticipationRate: fmt.Sprintf("%f", float32(params.BeaconConfig().EffectiveBalanceIncrement)/float32(validatorCount*params.BeaconConfig().MaxEffectiveBalance)),
VotedEther: fmt.Sprintf("%d", params.BeaconConfig().EffectiveBalanceIncrement),
EligibleEther: fmt.Sprintf("%d", validatorCount*params.BeaconConfig().MaxEffectiveBalance),
CurrentEpochActiveGwei: fmt.Sprintf("%d", validatorCount*params.BeaconConfig().MaxEffectiveBalance),
CurrentEpochAttestingGwei: fmt.Sprintf("%d", params.BeaconConfig().EffectiveBalanceIncrement),
CurrentEpochTargetAttestingGwei: fmt.Sprintf("%d", params.BeaconConfig().EffectiveBalanceIncrement),
PreviousEpochActiveGwei: fmt.Sprintf("%d", validatorCount*params.BeaconConfig().MaxEffectiveBalance),
PreviousEpochAttestingGwei: fmt.Sprintf("%d", params.BeaconConfig().EffectiveBalanceIncrement),
PreviousEpochTargetAttestingGwei: fmt.Sprintf("%d", params.BeaconConfig().EffectiveBalanceIncrement),
PreviousEpochHeadAttestingGwei: fmt.Sprintf("%d", params.BeaconConfig().EffectiveBalanceIncrement),
},
}
var vp *structs.GetValidatorParticipationResponse
err = json.NewDecoder(writer.Body).Decode(&vp)
require.NoError(t, err)
// Compare the response with the expected values
assert.Equal(t, true, vp.Finalized, "Incorrect validator participation response")
assert.Equal(t, *want.Participation, *vp.Participation, "Incorrect validator participation response")
}
func TestServer_GetValidatorParticipation_OrphanedUntilGenesis(t *testing.T) {
helpers.ClearCache()
params.SetupTestConfigCleanup(t)
params.OverrideBeaconConfig(params.BeaconConfig())
beaconDB := dbTest.SetupDB(t)
ctx := context.Background()
validatorCount := uint64(100)
validators := make([]*ethpb.Validator, validatorCount)
balances := make([]uint64, validatorCount)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
PublicKey: bytesutil.ToBytes(uint64(i), 48),
WithdrawalCredentials: make([]byte, 32),
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
}
balances[i] = params.BeaconConfig().MaxEffectiveBalance
}
atts := []*ethpb.PendingAttestation{{
Data: util.HydrateAttestationData(&ethpb.AttestationData{}),
InclusionDelay: 1,
AggregationBits: bitfield.NewBitlist(validatorCount / uint64(params.BeaconConfig().SlotsPerEpoch)),
}}
headState, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, headState.SetSlot(0))
require.NoError(t, headState.SetValidators(validators))
require.NoError(t, headState.SetBalances(balances))
require.NoError(t, headState.AppendCurrentEpochAttestations(atts[0]))
require.NoError(t, headState.AppendPreviousEpochAttestations(atts[0]))
b := util.NewBeaconBlock()
util.SaveBlock(t, ctx, beaconDB, b)
bRoot, err := b.Block.HashTreeRoot()
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, bRoot))
require.NoError(t, err)
require.NoError(t, beaconDB.SaveState(ctx, headState, bRoot))
require.NoError(t, beaconDB.SaveState(ctx, headState, params.BeaconConfig().ZeroHash))
m := &mock.ChainService{State: headState}
offset := int64(params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot))
var st state.BeaconState
st, _ = util.DeterministicGenesisState(t, 4)
s := &Server{
BeaconDB: beaconDB,
Stater: &testutil.MockStater{
BeaconState: st,
},
CoreService: &core.Service{
HeadFetcher: m,
StateGen: stategen.New(beaconDB, doublylinkedtree.New()),
GenesisTimeFetcher: &mock.ChainService{
Genesis: prysmTime.Now().Add(time.Duration(-1*offset) * time.Second),
},
FinalizedFetcher: &mock.ChainService{FinalizedCheckPoint: &ethpb.Checkpoint{Epoch: 100}},
},
CanonicalFetcher: &mock.ChainService{
CanonicalRoots: map[[32]byte]bool{
bRoot: true,
},
},
}
addDefaultReplayerBuilder(s, beaconDB)
url := "http://example.com"
request := httptest.NewRequest(http.MethodGet, url, nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetParticipation(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
want := &structs.GetValidatorParticipationResponse{
Participation: &structs.ValidatorParticipation{
GlobalParticipationRate: fmt.Sprintf("%f", float32(params.BeaconConfig().EffectiveBalanceIncrement)/float32(validatorCount*params.BeaconConfig().MaxEffectiveBalance)),
VotedEther: fmt.Sprintf("%d", params.BeaconConfig().EffectiveBalanceIncrement),
EligibleEther: fmt.Sprintf("%d", validatorCount*params.BeaconConfig().MaxEffectiveBalance),
CurrentEpochActiveGwei: fmt.Sprintf("%d", validatorCount*params.BeaconConfig().MaxEffectiveBalance),
CurrentEpochAttestingGwei: fmt.Sprintf("%d", params.BeaconConfig().EffectiveBalanceIncrement),
CurrentEpochTargetAttestingGwei: fmt.Sprintf("%d", params.BeaconConfig().EffectiveBalanceIncrement),
PreviousEpochActiveGwei: fmt.Sprintf("%d", validatorCount*params.BeaconConfig().MaxEffectiveBalance),
PreviousEpochAttestingGwei: fmt.Sprintf("%d", params.BeaconConfig().EffectiveBalanceIncrement),
PreviousEpochTargetAttestingGwei: fmt.Sprintf("%d", params.BeaconConfig().EffectiveBalanceIncrement),
PreviousEpochHeadAttestingGwei: fmt.Sprintf("%d", params.BeaconConfig().EffectiveBalanceIncrement),
},
}
var vp *structs.GetValidatorParticipationResponse
err = json.NewDecoder(writer.Body).Decode(&vp)
require.NoError(t, err)
assert.DeepEqual(t, true, vp.Finalized, "Incorrect validator participation respond")
assert.DeepEqual(t, want.Participation, vp.Participation, "Incorrect validator participation respond")
}
func TestServer_GetValidatorParticipation_CurrentAndPrevEpochWithBits(t *testing.T) {
params.SetupTestConfigCleanup(t)
params.OverrideBeaconConfig(params.BeaconConfig())
transition.SkipSlotCache.Disable()
t.Run("altair", func(t *testing.T) {
validatorCount := uint64(32)
genState, _ := util.DeterministicGenesisStateAltair(t, validatorCount)
c, err := altair.NextSyncCommittee(context.Background(), genState)
require.NoError(t, err)
require.NoError(t, genState.SetCurrentSyncCommittee(c))
bits := make([]byte, validatorCount)
for i := range bits {
bits[i] = 0xff
}
require.NoError(t, genState.SetCurrentParticipationBits(bits))
require.NoError(t, genState.SetPreviousParticipationBits(bits))
gb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockAltair())
assert.NoError(t, err)
runGetValidatorParticipationCurrentEpoch(t, genState, gb)
})
t.Run("bellatrix", func(t *testing.T) {
validatorCount := uint64(32)
genState, _ := util.DeterministicGenesisStateBellatrix(t, validatorCount)
c, err := altair.NextSyncCommittee(context.Background(), genState)
require.NoError(t, err)
require.NoError(t, genState.SetCurrentSyncCommittee(c))
bits := make([]byte, validatorCount)
for i := range bits {
bits[i] = 0xff
}
require.NoError(t, genState.SetCurrentParticipationBits(bits))
require.NoError(t, genState.SetPreviousParticipationBits(bits))
gb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockBellatrix())
assert.NoError(t, err)
runGetValidatorParticipationCurrentEpoch(t, genState, gb)
})
t.Run("capella", func(t *testing.T) {
validatorCount := uint64(32)
genState, _ := util.DeterministicGenesisStateCapella(t, validatorCount)
c, err := altair.NextSyncCommittee(context.Background(), genState)
require.NoError(t, err)
require.NoError(t, genState.SetCurrentSyncCommittee(c))
bits := make([]byte, validatorCount)
for i := range bits {
bits[i] = 0xff
}
require.NoError(t, genState.SetCurrentParticipationBits(bits))
require.NoError(t, genState.SetPreviousParticipationBits(bits))
gb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
assert.NoError(t, err)
runGetValidatorParticipationCurrentEpoch(t, genState, gb)
})
}
func runGetValidatorParticipationCurrentEpoch(t *testing.T, genState state.BeaconState, gb interfaces.SignedBeaconBlock) {
helpers.ClearCache()
beaconDB := dbTest.SetupDB(t)
ctx := context.Background()
validatorCount := uint64(32)
gsr, err := genState.HashTreeRoot(ctx)
require.NoError(t, err)
gb, err = blocktest.SetBlockStateRoot(gb, gsr)
require.NoError(t, err)
require.NoError(t, err)
gRoot, err := gb.Block().HashTreeRoot()
require.NoError(t, err)
require.NoError(t, beaconDB.SaveState(ctx, genState, gRoot))
require.NoError(t, beaconDB.SaveBlock(ctx, gb))
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, gRoot))
m := &mock.ChainService{State: genState}
offset := int64(params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot))
s := &Server{
BeaconDB: beaconDB,
Stater: &testutil.MockStater{
BeaconState: genState,
},
CoreService: &core.Service{
HeadFetcher: m,
StateGen: stategen.New(beaconDB, doublylinkedtree.New()),
GenesisTimeFetcher: &mock.ChainService{
Genesis: prysmTime.Now().Add(time.Duration(-1*offset) * time.Second),
},
FinalizedFetcher: &mock.ChainService{FinalizedCheckPoint: &ethpb.Checkpoint{Epoch: 100}},
},
}
addDefaultReplayerBuilder(s, beaconDB)
url := "http://example.com"
request := httptest.NewRequest(http.MethodGet, url, nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetParticipation(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
want := &structs.GetValidatorParticipationResponse{
Participation: &structs.ValidatorParticipation{
GlobalParticipationRate: "1.000000",
VotedEther: fmt.Sprintf("%d", validatorCount*params.BeaconConfig().MaxEffectiveBalance),
EligibleEther: fmt.Sprintf("%d", validatorCount*params.BeaconConfig().MaxEffectiveBalance),
CurrentEpochActiveGwei: fmt.Sprintf("%d", validatorCount*params.BeaconConfig().MaxEffectiveBalance),
CurrentEpochAttestingGwei: fmt.Sprintf("%d", validatorCount*params.BeaconConfig().MaxEffectiveBalance),
CurrentEpochTargetAttestingGwei: fmt.Sprintf("%d", validatorCount*params.BeaconConfig().MaxEffectiveBalance),
PreviousEpochActiveGwei: fmt.Sprintf("%d", validatorCount*params.BeaconConfig().MaxEffectiveBalance),
PreviousEpochAttestingGwei: fmt.Sprintf("%d", validatorCount*params.BeaconConfig().MaxEffectiveBalance),
PreviousEpochTargetAttestingGwei: fmt.Sprintf("%d", validatorCount*params.BeaconConfig().MaxEffectiveBalance),
PreviousEpochHeadAttestingGwei: fmt.Sprintf("%d", validatorCount*params.BeaconConfig().MaxEffectiveBalance),
},
}
var vp *structs.GetValidatorParticipationResponse
err = json.NewDecoder(writer.Body).Decode(&vp)
require.NoError(t, err)
assert.DeepEqual(t, true, vp.Finalized, "Incorrect validator participation respond")
assert.DeepEqual(t, *want.Participation, *vp.Participation, "Incorrect validator participation respond")
}
func TestServer_GetValidatorActiveSetChanges_NoState(t *testing.T) {
beaconDB := dbTest.SetupDB(t)
var st state.BeaconState
st, _ = util.DeterministicGenesisState(t, 4)
s := &Server{
Stater: &testutil.MockStater{
BeaconState: st,
},
CoreService: &core.Service{
BeaconDB: beaconDB,
GenesisTimeFetcher: &mock.ChainService{},
HeadFetcher: &mock.ChainService{
State: st,
},
},
}
url := "http://example.com" + fmt.Sprintf("%d", slots.ToEpoch(s.CoreService.GenesisTimeFetcher.CurrentSlot())+1)
request := httptest.NewRequest(http.MethodGet, url, nil)
request = mux.SetURLVars(request, map[string]string{"state_id": ""})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetActiveSetChanges(writer, request)
require.Equal(t, http.StatusBadRequest, writer.Code)
require.StringContains(t, "state_id is required in URL params", writer.Body.String())
}
func TestServer_GetValidatorActiveSetChanges(t *testing.T) {
beaconDB := dbTest.SetupDB(t)
ctx := context.Background()
validators := make([]*ethpb.Validator, 8)
headState, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, headState.SetSlot(0))
require.NoError(t, headState.SetValidators(validators))
for i := 0; i < len(validators); i++ {
activationEpoch := params.BeaconConfig().FarFutureEpoch
withdrawableEpoch := params.BeaconConfig().FarFutureEpoch
exitEpoch := params.BeaconConfig().FarFutureEpoch
slashed := false
balance := params.BeaconConfig().MaxEffectiveBalance
// Mark indices divisible by two as activated.
if i%2 == 0 {
activationEpoch = 0
} else if i%3 == 0 {
// Mark indices divisible by 3 as slashed.
withdrawableEpoch = params.BeaconConfig().EpochsPerSlashingsVector
slashed = true
} else if i%5 == 0 {
// Mark indices divisible by 5 as exited.
exitEpoch = 0
withdrawableEpoch = params.BeaconConfig().MinValidatorWithdrawabilityDelay
} else if i%7 == 0 {
// Mark indices divisible by 7 as ejected.
exitEpoch = 0
withdrawableEpoch = params.BeaconConfig().MinValidatorWithdrawabilityDelay
balance = params.BeaconConfig().EjectionBalance
}
err := headState.UpdateValidatorAtIndex(primitives.ValidatorIndex(i), &ethpb.Validator{
ActivationEpoch: activationEpoch,
PublicKey: pubKey(uint64(i)),
EffectiveBalance: balance,
WithdrawalCredentials: make([]byte, 32),
WithdrawableEpoch: withdrawableEpoch,
Slashed: slashed,
ExitEpoch: exitEpoch,
})
require.NoError(t, err)
}
b := util.NewBeaconBlock()
util.SaveBlock(t, ctx, beaconDB, b)
gRoot, err := b.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, gRoot))
require.NoError(t, beaconDB.SaveState(ctx, headState, gRoot))
var st state.BeaconState
st, _ = util.DeterministicGenesisState(t, 4)
s := &Server{
Stater: &testutil.MockStater{
BeaconState: st,
},
CoreService: &core.Service{
FinalizedFetcher: &mock.ChainService{
FinalizedCheckPoint: &ethpb.Checkpoint{Epoch: 0, Root: make([]byte, fieldparams.RootLength)},
},
GenesisTimeFetcher: &mock.ChainService{},
},
}
addDefaultReplayerBuilder(s, beaconDB)
url := "http://example.com"
request := httptest.NewRequest(http.MethodGet, url, nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "genesis"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetActiveSetChanges(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
wantedActive := []string{
hexutil.Encode(pubKey(0)),
hexutil.Encode(pubKey(2)),
hexutil.Encode(pubKey(4)),
hexutil.Encode(pubKey(6)),
}
wantedActiveIndices := []string{"0", "2", "4", "6"}
wantedExited := []string{
hexutil.Encode(pubKey(5)),
}
wantedExitedIndices := []string{"5"}
wantedSlashed := []string{
hexutil.Encode(pubKey(3)),
}
wantedSlashedIndices := []string{"3"}
wantedEjected := []string{
hexutil.Encode(pubKey(7)),
}
wantedEjectedIndices := []string{"7"}
want := &structs.ActiveSetChanges{
Epoch: "0",
ActivatedPublicKeys: wantedActive,
ActivatedIndices: wantedActiveIndices,
ExitedPublicKeys: wantedExited,
ExitedIndices: wantedExitedIndices,
SlashedPublicKeys: wantedSlashed,
SlashedIndices: wantedSlashedIndices,
EjectedPublicKeys: wantedEjected,
EjectedIndices: wantedEjectedIndices,
}
var as *structs.ActiveSetChanges
err = json.NewDecoder(writer.Body).Decode(&as)
require.NoError(t, err)
require.DeepEqual(t, *want, *as)
}
func pubKey(i uint64) []byte {
pubKey := make([]byte, params.BeaconConfig().BLSPubkeyLength)
binary.LittleEndian.PutUint64(pubKey, i)
return pubKey
}

View File

@@ -1,9 +1,17 @@
package validator
import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/db"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/core"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/lookup"
)
type Server struct {
CoreService *core.Service
BeaconDB db.ReadOnlyDatabase
Stater lookup.Stater
CanonicalFetcher blockchain.CanonicalFetcher
FinalizationFetcher blockchain.FinalizationFetcher
ChainInfoFetcher blockchain.ChainInfoFetcher
CoreService *core.Service
}

View File

@@ -13,9 +13,9 @@ import (
"go.opencensus.io/trace"
)
// GetValidatorPerformance is an HTTP handler for GetValidatorPerformance.
func (s *Server) GetValidatorPerformance(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "validator.GetValidatorPerformance")
// GetPerformance is an HTTP handler for GetPerformance.
func (s *Server) GetPerformance(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "validator.GetPerformance")
defer span.End()
var req structs.GetValidatorPerformanceRequest

View File

@@ -35,7 +35,7 @@ func TestServer_GetValidatorPerformance(t *testing.T) {
},
}
srv := httptest.NewServer(http.HandlerFunc(vs.GetValidatorPerformance))
srv := httptest.NewServer(http.HandlerFunc(vs.GetPerformance))
req := httptest.NewRequest("POST", "/foo", nil)
client := &http.Client{}
@@ -86,7 +86,7 @@ func TestServer_GetValidatorPerformance(t *testing.T) {
err = json.NewEncoder(&buf).Encode(request)
require.NoError(t, err)
srv := httptest.NewServer(http.HandlerFunc(vs.GetValidatorPerformance))
srv := httptest.NewServer(http.HandlerFunc(vs.GetPerformance))
req := httptest.NewRequest("POST", "/foo", &buf)
client := &http.Client{}
rawResp, err := client.Post(srv.URL, "application/json", req.Body)
@@ -151,7 +151,7 @@ func TestServer_GetValidatorPerformance(t *testing.T) {
err = json.NewEncoder(&buf).Encode(request)
require.NoError(t, err)
srv := httptest.NewServer(http.HandlerFunc(vs.GetValidatorPerformance))
srv := httptest.NewServer(http.HandlerFunc(vs.GetPerformance))
req := httptest.NewRequest("POST", "/foo", &buf)
client := &http.Client{}
rawResp, err := client.Post(srv.URL, "application/json", req.Body)
@@ -216,7 +216,7 @@ func TestServer_GetValidatorPerformance(t *testing.T) {
err = json.NewEncoder(&buf).Encode(request)
require.NoError(t, err)
srv := httptest.NewServer(http.HandlerFunc(vs.GetValidatorPerformance))
srv := httptest.NewServer(http.HandlerFunc(vs.GetPerformance))
req := httptest.NewRequest("POST", "/foo", &buf)
client := &http.Client{}
rawResp, err := client.Post(srv.URL, "application/json", req.Body)
@@ -278,7 +278,7 @@ func TestServer_GetValidatorPerformance(t *testing.T) {
err := json.NewEncoder(&buf).Encode(request)
require.NoError(t, err)
srv := httptest.NewServer(http.HandlerFunc(vs.GetValidatorPerformance))
srv := httptest.NewServer(http.HandlerFunc(vs.GetPerformance))
req := httptest.NewRequest("POST", "/foo", &buf)
client := &http.Client{}
rawResp, err := client.Post(srv.URL, "application/json", req.Body)
@@ -340,7 +340,7 @@ func TestServer_GetValidatorPerformance(t *testing.T) {
err := json.NewEncoder(&buf).Encode(request)
require.NoError(t, err)
srv := httptest.NewServer(http.HandlerFunc(vs.GetValidatorPerformance))
srv := httptest.NewServer(http.HandlerFunc(vs.GetPerformance))
req := httptest.NewRequest("POST", "/foo", &buf)
client := &http.Client{}
rawResp, err := client.Post(srv.URL, "application/json", req.Body)
@@ -402,7 +402,7 @@ func TestServer_GetValidatorPerformance(t *testing.T) {
err := json.NewEncoder(&buf).Encode(request)
require.NoError(t, err)
srv := httptest.NewServer(http.HandlerFunc(vs.GetValidatorPerformance))
srv := httptest.NewServer(http.HandlerFunc(vs.GetPerformance))
req := httptest.NewRequest("POST", "/foo", &buf)
client := &http.Client{}
rawResp, err := client.Post(srv.URL, "application/json", req.Body)

View File

@@ -215,6 +215,7 @@ func NewService(ctx context.Context, cfg *Config) *Service {
}
rewardFetcher := &rewards.BlockRewardService{Replayer: ch, DB: s.cfg.BeaconDB}
coreService := &core.Service{
BeaconDB: s.cfg.BeaconDB,
HeadFetcher: s.cfg.HeadFetcher,
GenesisTimeFetcher: s.cfg.GenesisTimeFetcher,
SyncChecker: s.cfg.SyncService,
@@ -225,6 +226,7 @@ func NewService(ctx context.Context, cfg *Config) *Service {
StateGen: s.cfg.StateGen,
P2P: s.cfg.Broadcaster,
FinalizedFetcher: s.cfg.FinalizationFetcher,
ReplayerBuilder: ch,
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
}
validatorServer := &validatorv1alpha1.Server{

View File

@@ -43,17 +43,17 @@ func TestBeaconStateMerkleProofs_altair(t *testing.T) {
t.Run("current sync committee", func(t *testing.T) {
cscp, err := altair.CurrentSyncCommitteeProof(ctx)
require.NoError(t, err)
require.Equal(t, len(cscp), 5)
require.Equal(t, 5, len(cscp))
for i, bytes := range cscp {
require.Equal(t, hexutil.Encode(bytes), results[i])
require.Equal(t, results[i], hexutil.Encode(bytes))
}
})
t.Run("next sync committee", func(t *testing.T) {
nscp, err := altair.NextSyncCommitteeProof(ctx)
require.NoError(t, err)
require.Equal(t, len(nscp), 5)
require.Equal(t, 5, len(nscp))
for i, bytes := range nscp {
require.Equal(t, hexutil.Encode(bytes), results[i])
require.Equal(t, results[i], hexutil.Encode(bytes))
}
})
t.Run("finalized root", func(t *testing.T) {
@@ -112,17 +112,17 @@ func TestBeaconStateMerkleProofs_bellatrix(t *testing.T) {
t.Run("current sync committee", func(t *testing.T) {
cscp, err := bellatrix.CurrentSyncCommitteeProof(ctx)
require.NoError(t, err)
require.Equal(t, len(cscp), 5)
require.Equal(t, 5, len(cscp))
for i, bytes := range cscp {
require.Equal(t, hexutil.Encode(bytes), results[i])
require.Equal(t, results[i], hexutil.Encode(bytes))
}
})
t.Run("next sync committee", func(t *testing.T) {
nscp, err := bellatrix.NextSyncCommitteeProof(ctx)
require.NoError(t, err)
require.Equal(t, len(nscp), 5)
require.Equal(t, 5, len(nscp))
for i, bytes := range nscp {
require.Equal(t, hexutil.Encode(bytes), results[i])
require.Equal(t, results[i], hexutil.Encode(bytes))
}
})
t.Run("finalized root", func(t *testing.T) {

View File

@@ -48,7 +48,7 @@ func (b *BeaconState) SetPendingConsolidations(val []*ethpb.PendingConsolidation
return nil
}
// SetEarliestConsolidationEpoch is a mutating call to the beacon state which sets the earlest
// SetEarliestConsolidationEpoch is a mutating call to the beacon state which sets the earliest
// consolidation epoch value. This method requires access to the Lock on the state and only applies
// in electra or later.
func (b *BeaconState) SetEarliestConsolidationEpoch(epoch primitives.Epoch) error {

View File

@@ -20,11 +20,6 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stategen",
visibility = ["//visibility:public"],
deps = [
"//beacon-chain/core/altair:go_default_library",
"//beacon-chain/core/capella:go_default_library",
"//beacon-chain/core/deneb:go_default_library",
"//beacon-chain/core/electra:go_default_library",
"//beacon-chain/core/execution:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/time:go_default_library",
"//beacon-chain/core/transition:go_default_library",
@@ -40,9 +35,7 @@ go_library(
"//consensus-types/primitives:go_default_library",
"//crypto/bls:go_default_library",
"//encoding/bytesutil:go_default_library",
"//monitoring/tracing:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
"//time/slots:go_default_library",
"@com_github_hashicorp_golang_lru//:go_default_library",
"@com_github_pkg_errors//:go_default_library",

View File

@@ -6,20 +6,12 @@ import (
"time"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/capella"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/deneb"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/electra"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/execution"
prysmtime "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/db/filters"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
@@ -185,7 +177,6 @@ func ReplayProcessSlots(ctx context.Context, state state.BeaconState, slot primi
if state == nil || state.IsNil() {
return nil, errUnknownState
}
if state.Slot() > slot {
err := fmt.Errorf("expected state.slot %d <= slot %d", state.Slot(), slot)
return nil, err
@@ -195,74 +186,7 @@ func ReplayProcessSlots(ctx context.Context, state state.BeaconState, slot primi
return state, nil
}
var err error
for state.Slot() < slot {
state, err = transition.ProcessSlot(ctx, state)
if err != nil {
return nil, errors.Wrap(err, "could not process slot")
}
if prysmtime.CanProcessEpoch(state) {
if state.Version() == version.Phase0 {
state, err = transition.ProcessEpochPrecompute(ctx, state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, errors.Wrap(err, "could not process epoch with optimizations")
}
} else {
err = altair.ProcessEpoch(ctx, state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, errors.Wrap(err, "could not process epoch")
}
}
}
if err := state.SetSlot(state.Slot() + 1); err != nil {
tracing.AnnotateError(span, err)
return nil, errors.Wrap(err, "failed to increment state slot")
}
if prysmtime.CanUpgradeToAltair(state.Slot()) {
state, err = altair.UpgradeToAltair(ctx, state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, err
}
}
if prysmtime.CanUpgradeToBellatrix(state.Slot()) {
state, err = execution.UpgradeToBellatrix(state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, err
}
}
if prysmtime.CanUpgradeToCapella(state.Slot()) {
state, err = capella.UpgradeToCapella(state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, err
}
}
if prysmtime.CanUpgradeToDeneb(state.Slot()) {
state, err = deneb.UpgradeToDeneb(state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, err
}
}
if prysmtime.CanUpgradeToElectra(state.Slot()) {
state, err = electra.UpgradeToElectra(state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, err
}
}
}
return state, nil
return transition.ProcessSlotsCore(ctx, span, state, slot, nil)
}
// Given the start slot and the end slot, this returns the finalized beacon blocks in between.

View File

@@ -5,12 +5,14 @@ import (
"testing"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/db"
testDB "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing"
doublylinkedtree "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/doubly-linked-tree"
"github.com/prysmaticlabs/prysm/v5/config/params"
consensusblocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
@@ -197,6 +199,52 @@ func TestReplayBlocks_ThroughFutureForkBoundaries(t *testing.T) {
assert.Equal(t, version.Electra, newState.Version())
}
func TestReplayBlocks_ProcessEpoch_Electra(t *testing.T) {
params.SetupTestConfigCleanup(t)
bCfg := params.BeaconConfig().Copy()
bCfg.ElectraForkEpoch = 1
bCfg.ForkVersionSchedule[bytesutil.ToBytes4(bCfg.ElectraForkVersion)] = 1
params.OverrideBeaconConfig(bCfg)
beaconState, _ := util.DeterministicGenesisStateElectra(t, 1)
require.NoError(t, beaconState.SetDepositBalanceToConsume(100))
amountAvailForProcessing := helpers.ActivationExitChurnLimit(1_000 * 1e9)
require.NoError(t, beaconState.SetPendingBalanceDeposits([]*ethpb.PendingBalanceDeposit{
{
Amount: uint64(amountAvailForProcessing) / 10,
Index: primitives.ValidatorIndex(0),
},
}))
genesisBlock := util.NewBeaconBlockElectra()
bodyRoot, err := genesisBlock.Block.HashTreeRoot()
require.NoError(t, err)
err = beaconState.SetLatestBlockHeader(&ethpb.BeaconBlockHeader{
Slot: genesisBlock.Block.Slot,
ParentRoot: genesisBlock.Block.ParentRoot,
StateRoot: params.BeaconConfig().ZeroHash[:],
BodyRoot: bodyRoot[:],
})
require.NoError(t, err)
assert.Equal(t, version.Electra, beaconState.Version())
require.Equal(t, params.BeaconConfig().MinActivationBalance, beaconState.Balances()[0])
service := New(testDB.SetupDB(t), doublylinkedtree.New())
targetSlot := (params.BeaconConfig().SlotsPerEpoch * 2) - 1
newState, err := service.replayBlocks(context.Background(), beaconState, []interfaces.ReadOnlySignedBeaconBlock{}, targetSlot)
require.NoError(t, err)
require.Equal(t, version.Electra, newState.Version())
res, err := newState.DepositBalanceToConsume()
require.NoError(t, err)
require.Equal(t, primitives.Gwei(0), res)
remaining, err := newState.PendingBalanceDeposits()
require.NoError(t, err)
require.Equal(t, 0, len(remaining))
require.Equal(t, params.BeaconConfig().MinActivationBalance+(uint64(amountAvailForProcessing)/10), newState.Balances()[0])
}
func TestLoadBlocks_FirstBranch(t *testing.T) {
beaconDB := testDB.SetupDB(t)
ctx := context.Background()

View File

@@ -684,9 +684,9 @@ func TestService_AddPendingBlockToQueueOverMax(t *testing.T) {
}
b := util.NewBeaconBlock()
b1 := ethpb.CopySignedBeaconBlock(b)
b1 := b.Copy()
b1.Block.StateRoot = []byte{'a'}
b2 := ethpb.CopySignedBeaconBlock(b)
b2 := b.Copy()
b2.Block.StateRoot = []byte{'b'}
wsb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
@@ -698,7 +698,7 @@ func TestService_AddPendingBlockToQueueOverMax(t *testing.T) {
require.NoError(t, err)
require.NoError(t, r.insertBlockToPendingQueue(0, wsb, [32]byte{2}))
b3 := ethpb.CopySignedBeaconBlock(b)
b3 := b.Copy()
b3.Block.StateRoot = []byte{'c'}
wsb, err = blocks.NewSignedBeaconBlock(b2)
require.NoError(t, err)

View File

@@ -113,46 +113,19 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(ctx context.Context, p
committeeIndex = data.CommitteeIndex
}
if features.Get().EnableSlasher {
// Feed the indexed attestation to slasher if enabled. This action
// is done in the background to avoid adding more load to this critical code path.
go func() {
// Using a different context to prevent timeouts as this operation can be expensive
// and we want to avoid affecting the critical code path.
ctx := context.TODO()
preState, err := s.cfg.chain.AttestationTargetState(ctx, data.Target)
if err != nil {
log.WithError(err).Error("Could not retrieve pre state")
tracing.AnnotateError(span, err)
return
}
committee, err := helpers.BeaconCommitteeFromState(ctx, preState, data.Slot, committeeIndex)
if err != nil {
log.WithError(err).Error("Could not get attestation committee")
tracing.AnnotateError(span, err)
return
}
indexedAtt, err := attestation.ConvertToIndexed(ctx, att, committee)
if err != nil {
log.WithError(err).Error("Could not convert to indexed attestation")
tracing.AnnotateError(span, err)
return
}
s.cfg.slasherAttestationsFeed.Send(&types.WrappedIndexedAtt{IndexedAtt: indexedAtt})
}()
}
if !features.Get().EnableSlasher {
// Verify this the first attestation received for the participating validator for the slot.
if s.hasSeenCommitteeIndicesSlot(data.Slot, data.CommitteeIndex, att.GetAggregationBits()) {
return pubsub.ValidationIgnore, nil
}
// Verify this the first attestation received for the participating validator for the slot.
if s.hasSeenCommitteeIndicesSlot(data.Slot, data.CommitteeIndex, att.GetAggregationBits()) {
return pubsub.ValidationIgnore, nil
}
// Reject an attestation if it references an invalid block.
if s.hasBadBlock(bytesutil.ToBytes32(data.BeaconBlockRoot)) ||
s.hasBadBlock(bytesutil.ToBytes32(data.Target.Root)) ||
s.hasBadBlock(bytesutil.ToBytes32(data.Source.Root)) {
attBadBlockCount.Inc()
return pubsub.ValidationReject, errors.New("attestation data references bad block root")
// Reject an attestation if it references an invalid block.
if s.hasBadBlock(bytesutil.ToBytes32(data.BeaconBlockRoot)) ||
s.hasBadBlock(bytesutil.ToBytes32(data.Target.Root)) ||
s.hasBadBlock(bytesutil.ToBytes32(data.Source.Root)) {
attBadBlockCount.Inc()
return pubsub.ValidationReject, errors.New("attestation data references bad block root")
}
}
// Verify the block being voted and the processed state is in beaconDB and the block has passed validation if it's in the beaconDB.
@@ -203,6 +176,35 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(ctx context.Context, p
return validationRes, err
}
if features.Get().EnableSlasher {
// Feed the indexed attestation to slasher if enabled. This action
// is done in the background to avoid adding more load to this critical code path.
go func() {
// Using a different context to prevent timeouts as this operation can be expensive
// and we want to avoid affecting the critical code path.
ctx := context.TODO()
preState, err := s.cfg.chain.AttestationTargetState(ctx, data.Target)
if err != nil {
log.WithError(err).Error("Could not retrieve pre state")
tracing.AnnotateError(span, err)
return
}
committee, err := helpers.BeaconCommitteeFromState(ctx, preState, data.Slot, committeeIndex)
if err != nil {
log.WithError(err).Error("Could not get attestation committee")
tracing.AnnotateError(span, err)
return
}
indexedAtt, err := attestation.ConvertToIndexed(ctx, att, committee)
if err != nil {
log.WithError(err).Error("Could not convert to indexed attestation")
tracing.AnnotateError(span, err)
return
}
s.cfg.slasherAttestationsFeed.Send(&types.WrappedIndexedAtt{IndexedAtt: indexedAtt})
}()
}
s.setSeenCommitteeIndicesSlot(data.Slot, data.CommitteeIndex, att.GetAggregationBits())
msg.ValidatorData = att

View File

@@ -31,6 +31,23 @@ var (
"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)",
Value: 10,
}
// MinBuilderBid sets an absolute value for the builder bid that this
// node will accept without reverting to local building
MinBuilderBid = &cli.Uint64Flag{
Name: "min-builder-bid",
Usage: "An absolute value in Gwei that the builder bid has to have in order for this beacon node to use the builder's block. Anything less than this value" +
" and the beacon will revert to local building.",
Value: 0,
}
// MinBuilderDiff sets an absolute value for the difference between the
// builder's bid and the local block value that this node will accept
// without reverting to local building
MinBuilderDiff = &cli.Uint64Flag{
Name: "min-builder-to-local-difference",
Usage: "An absolute value in Gwei that the builder bid has to have in order for this beacon node to use the builder's block. Anything less than this value" +
" and the beacon will revert to local building.",
Value: 0,
}
// ExecutionEngineEndpoint provides an HTTP access endpoint to connect to an execution client on the execution layer
ExecutionEngineEndpoint = &cli.StringFlag{
Name: "execution-endpoint",

View File

@@ -82,6 +82,8 @@ var appFlags = []cli.Flag{
flags.MaxBuilderConsecutiveMissedSlots,
flags.EngineEndpointTimeoutSeconds,
flags.LocalBlockValueBoost,
flags.MinBuilderBid,
flags.MinBuilderDiff,
cmd.BackupWebhookOutputDir,
cmd.MinimalConfigFlag,
cmd.E2EConfigFlag,

View File

@@ -131,6 +131,8 @@ var appHelpFlagGroups = []flagGroup{
flags.EngineEndpointTimeoutSeconds,
flags.SlasherDirFlag,
flags.LocalBlockValueBoost,
flags.MinBuilderBid,
flags.MinBuilderDiff,
flags.JwtId,
checkpoint.BlockPath,
checkpoint.StatePath,

View File

@@ -48,6 +48,7 @@ type Flags struct {
EnableDoppelGanger bool // EnableDoppelGanger enables doppelganger protection on startup for the validator.
EnableHistoricalSpaceRepresentation bool // EnableHistoricalSpaceRepresentation enables the saving of registry validators in separate buckets to save space
EnableBeaconRESTApi bool // EnableBeaconRESTApi enables experimental usage of the beacon REST API by the validator when querying a beacon node
EnableCommitteeAwarePacking bool // EnableCommitteeAwarePacking TODO
// Logging related toggles.
DisableGRPCConnectionLogs bool // Disables logging when a new grpc client has connected.
EnableFullSSZDataLogging bool // Enables logging for full ssz data on rejected gossip messages
@@ -254,6 +255,10 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
logEnabled(EnableQUIC)
cfg.EnableQUIC = true
}
if ctx.IsSet(EnableCommitteeAwarePacking.Name) {
logEnabled(EnableCommitteeAwarePacking)
cfg.EnableCommitteeAwarePacking = true
}
cfg.AggregateIntervals = [3]time.Duration{aggregateFirstInterval.Value, aggregateSecondInterval.Value, aggregateThirdInterval.Value}
Init(cfg)

View File

@@ -166,6 +166,10 @@ var (
Name: "enable-quic",
Usage: "Enables connection using the QUIC protocol for peers which support it.",
}
EnableCommitteeAwarePacking = &cli.BoolFlag{
Name: "enable-committee-aware-packing",
Usage: "Changes the attestation packing algorithm to one that is aware of attesting committees.",
}
)
// devModeFlags holds list of flags that are set when development mode is on.
@@ -223,6 +227,7 @@ var BeaconChainFlags = append(deprecatedBeaconFlags, append(deprecatedFlags, []c
EnableLightClient,
BlobSaveFsync,
EnableQUIC,
EnableCommitteeAwarePacking,
}...)...)
// E2EBeaconChainFlags contains a list of the beacon chain feature flags to be tested in E2E.

View File

@@ -222,7 +222,8 @@ type BeaconChainConfig struct {
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 defining 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.
MinBuilderBid uint64 // MinBuilderBid is the minimum value that the builder's block can have to be considered by this node.
MinBuilderDiff uint64 // MinBuilderDiff is the minimum value above the local block value that the builder has to bid to be considered by this node
// 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

@@ -108,7 +108,7 @@ type Settings struct {
DefaultConfig *Option
}
// ShouldBeSaved goes through checks to see if the value should be savable
// ShouldBeSaved goes through checks to see if the value should be saveable
// Pseudocode: conditions for being saved into the database
// 1. settings are not nil
// 2. proposeconfig is not nil (this defines specific settings for each validator key), default config can be nil in this case and fall back to beacon node settings

View File

@@ -52,40 +52,29 @@ func (b *SignedBeaconBlock) Copy() (interfaces.SignedBeaconBlock, error) {
}
switch b.version {
case version.Phase0:
cp := eth.CopySignedBeaconBlock(pb.(*eth.SignedBeaconBlock))
return initSignedBlockFromProtoPhase0(cp)
return initSignedBlockFromProtoPhase0(pb.(*eth.SignedBeaconBlock).Copy())
case version.Altair:
cp := eth.CopySignedBeaconBlockAltair(pb.(*eth.SignedBeaconBlockAltair))
return initSignedBlockFromProtoAltair(cp)
return initSignedBlockFromProtoAltair(pb.(*eth.SignedBeaconBlockAltair).Copy())
case version.Bellatrix:
if b.IsBlinded() {
cp := eth.CopySignedBlindedBeaconBlockBellatrix(pb.(*eth.SignedBlindedBeaconBlockBellatrix))
return initBlindedSignedBlockFromProtoBellatrix(cp)
return initBlindedSignedBlockFromProtoBellatrix(pb.(*eth.SignedBlindedBeaconBlockBellatrix).Copy())
}
cp := eth.CopySignedBeaconBlockBellatrix(pb.(*eth.SignedBeaconBlockBellatrix))
return initSignedBlockFromProtoBellatrix(cp)
return initSignedBlockFromProtoBellatrix(pb.(*eth.SignedBeaconBlockBellatrix).Copy())
case version.Capella:
if b.IsBlinded() {
cp := eth.CopySignedBlindedBeaconBlockCapella(pb.(*eth.SignedBlindedBeaconBlockCapella))
return initBlindedSignedBlockFromProtoCapella(cp)
return initBlindedSignedBlockFromProtoCapella(pb.(*eth.SignedBlindedBeaconBlockCapella).Copy())
}
cp := eth.CopySignedBeaconBlockCapella(pb.(*eth.SignedBeaconBlockCapella))
return initSignedBlockFromProtoCapella(cp)
return initSignedBlockFromProtoCapella(pb.(*eth.SignedBeaconBlockCapella).Copy())
case version.Deneb:
if b.IsBlinded() {
cp := eth.CopySignedBlindedBeaconBlockDeneb(pb.(*eth.SignedBlindedBeaconBlockDeneb))
return initBlindedSignedBlockFromProtoDeneb(cp)
return initBlindedSignedBlockFromProtoDeneb(pb.(*eth.SignedBlindedBeaconBlockDeneb).Copy())
}
cp := eth.CopySignedBeaconBlockDeneb(pb.(*eth.SignedBeaconBlockDeneb))
return initSignedBlockFromProtoDeneb(cp)
return initSignedBlockFromProtoDeneb(pb.(*eth.SignedBeaconBlockDeneb).Copy())
case version.Electra:
if b.IsBlinded() {
cp := eth.CopySignedBlindedBeaconBlockElectra(pb.(*eth.SignedBlindedBeaconBlockElectra))
return initBlindedSignedBlockFromProtoElectra(cp)
return initBlindedSignedBlockFromProtoElectra(pb.(*eth.SignedBlindedBeaconBlockElectra).Copy())
}
cp := eth.CopySignedBeaconBlockElectra(pb.(*eth.SignedBeaconBlockElectra))
return initSignedBlockFromProtoElectra(cp)
return initSignedBlockFromProtoElectra(pb.(*eth.SignedBeaconBlockElectra).Copy())
default:
return nil, errIncorrectBlockVersion
}
@@ -972,39 +961,29 @@ func (b *BeaconBlock) Copy() (interfaces.ReadOnlyBeaconBlock, error) {
}
switch b.version {
case version.Phase0:
cp := eth.CopyBeaconBlock(pb.(*eth.BeaconBlock))
return initBlockFromProtoPhase0(cp)
return initBlockFromProtoPhase0(pb.(*eth.BeaconBlock).Copy())
case version.Altair:
cp := eth.CopyBeaconBlockAltair(pb.(*eth.BeaconBlockAltair))
return initBlockFromProtoAltair(cp)
return initBlockFromProtoAltair(pb.(*eth.BeaconBlockAltair).Copy())
case version.Bellatrix:
if b.IsBlinded() {
cp := eth.CopyBlindedBeaconBlockBellatrix(pb.(*eth.BlindedBeaconBlockBellatrix))
return initBlindedBlockFromProtoBellatrix(cp)
return initBlindedBlockFromProtoBellatrix(pb.(*eth.BlindedBeaconBlockBellatrix).Copy())
}
cp := eth.CopyBeaconBlockBellatrix(pb.(*eth.BeaconBlockBellatrix))
return initBlockFromProtoBellatrix(cp)
return initBlockFromProtoBellatrix(pb.(*eth.BeaconBlockBellatrix).Copy())
case version.Capella:
if b.IsBlinded() {
cp := eth.CopyBlindedBeaconBlockCapella(pb.(*eth.BlindedBeaconBlockCapella))
return initBlindedBlockFromProtoCapella(cp)
return initBlindedBlockFromProtoCapella(pb.(*eth.BlindedBeaconBlockCapella).Copy())
}
cp := eth.CopyBeaconBlockCapella(pb.(*eth.BeaconBlockCapella))
return initBlockFromProtoCapella(cp)
return initBlockFromProtoCapella(pb.(*eth.BeaconBlockCapella).Copy())
case version.Deneb:
if b.IsBlinded() {
cp := eth.CopyBlindedBeaconBlockDeneb(pb.(*eth.BlindedBeaconBlockDeneb))
return initBlindedBlockFromProtoDeneb(cp)
return initBlindedBlockFromProtoDeneb(pb.(*eth.BlindedBeaconBlockDeneb).Copy())
}
cp := eth.CopyBeaconBlockDeneb(pb.(*eth.BeaconBlockDeneb))
return initBlockFromProtoDeneb(cp)
return initBlockFromProtoDeneb(pb.(*eth.BeaconBlockDeneb).Copy())
case version.Electra:
if b.IsBlinded() {
cp := eth.CopyBlindedBeaconBlockElectra(pb.(*eth.BlindedBeaconBlockElectra))
return initBlindedBlockFromProtoElectra(cp)
return initBlindedBlockFromProtoElectra(pb.(*eth.BlindedBeaconBlockElectra).Copy())
}
cp := eth.CopyBeaconBlockElectra(pb.(*eth.BeaconBlockElectra))
return initBlockFromProtoElectra(cp)
return initBlockFromProtoElectra(pb.(*eth.BeaconBlockElectra).Copy())
default:
return nil, errIncorrectBlockVersion
}

View File

@@ -35,7 +35,7 @@ $ go tool trace trace.out
#### How to collect additional traces
We use the OpenCensus library to create traces. To trace the execution of a p2p
message through the system, we must define [spans](https://godoc.org/go.opencensus.io/trace#Span) around the code that handles the message. To correlate the trace with other spans defined for the same message, use the context passed inside the [Message](https://godoc.org/github.com/prysmaticlabs/prysm/shared/deprecated-p2p#Message) struct to create a span:
message through the system, we must define [spans](https://godoc.org/go.opencensus.io/trace#Span) around the code that handles the message. To correlate the trace with other spans defined for the same message, use the context passed inside the Message struct to create a span:
```go
var msg p2p.Message

View File

@@ -32,7 +32,7 @@ func (e Endpoint) Equals(other Endpoint) bool {
return e.Url == other.Url && e.Auth.Equals(other.Auth)
}
// HttpClient creates a http client object dependant
// HttpClient creates a http client object dependent
// on the properties of the network endpoint.
func (e Endpoint) HttpClient() *http.Client {
if e.Auth.Method != authorization.Bearer {

View File

@@ -663,6 +663,18 @@ func TestMaxCover_MaxCover(t *testing.T) {
}},
wantedErr: "empty bitlists: invalid max_cover problem",
},
{
name: "doesn't select bitlist which is a subset of another bitlist",
args: args{k: 3, allowOverlaps: true, candidates: []*bitfield.Bitlist64{
bitfield.NewBitlist64From([]uint64{0b00011100}),
bitfield.NewBitlist64From([]uint64{0b00011110}),
bitfield.NewBitlist64From([]uint64{0b00000001}),
}},
want: &BitSetAggregation{
Coverage: bitfield.NewBitlist64From([]uint64{0b00011111}),
Keys: []int{1, 2},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@@ -2,6 +2,458 @@ package eth
import "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
// Copy --
func (sigBlock *SignedBeaconBlock) Copy() *SignedBeaconBlock {
if sigBlock == nil {
return nil
}
return &SignedBeaconBlock{
Block: sigBlock.Block.Copy(),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// Copy --
func (block *BeaconBlock) Copy() *BeaconBlock {
if block == nil {
return nil
}
return &BeaconBlock{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: block.Body.Copy(),
}
}
// Copy --
func (body *BeaconBlockBody) Copy() *BeaconBlockBody {
if body == nil {
return nil
}
return &BeaconBlockBody{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
}
}
// Copy --
func (sigBlock *SignedBeaconBlockAltair) Copy() *SignedBeaconBlockAltair {
if sigBlock == nil {
return nil
}
return &SignedBeaconBlockAltair{
Block: sigBlock.Block.Copy(),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// Copy --
func (block *BeaconBlockAltair) Copy() *BeaconBlockAltair {
if block == nil {
return nil
}
return &BeaconBlockAltair{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: block.Body.Copy(),
}
}
// Copy --
func (body *BeaconBlockBodyAltair) Copy() *BeaconBlockBodyAltair {
if body == nil {
return nil
}
return &BeaconBlockBodyAltair{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
SyncAggregate: body.SyncAggregate.Copy(),
}
}
// Copy --
func (sigBlock *SignedBeaconBlockBellatrix) Copy() *SignedBeaconBlockBellatrix {
if sigBlock == nil {
return nil
}
return &SignedBeaconBlockBellatrix{
Block: sigBlock.Block.Copy(),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// Copy --
func (block *BeaconBlockBellatrix) Copy() *BeaconBlockBellatrix {
if block == nil {
return nil
}
return &BeaconBlockBellatrix{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: block.Body.Copy(),
}
}
// Copy --
func (body *BeaconBlockBodyBellatrix) Copy() *BeaconBlockBodyBellatrix {
if body == nil {
return nil
}
return &BeaconBlockBodyBellatrix{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
SyncAggregate: body.SyncAggregate.Copy(),
ExecutionPayload: body.ExecutionPayload.Copy(),
}
}
// Copy --
func (sigBlock *SignedBeaconBlockCapella) Copy() *SignedBeaconBlockCapella {
if sigBlock == nil {
return nil
}
return &SignedBeaconBlockCapella{
Block: sigBlock.Block.Copy(),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// Copy --
func (block *BeaconBlockCapella) Copy() *BeaconBlockCapella {
if block == nil {
return nil
}
return &BeaconBlockCapella{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: block.Body.Copy(),
}
}
// Copy --
func (body *BeaconBlockBodyCapella) Copy() *BeaconBlockBodyCapella {
if body == nil {
return nil
}
return &BeaconBlockBodyCapella{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
SyncAggregate: body.SyncAggregate.Copy(),
ExecutionPayload: body.ExecutionPayload.Copy(),
BlsToExecutionChanges: CopySlice(body.BlsToExecutionChanges),
}
}
// Copy --
func (sigBlock *SignedBlindedBeaconBlockCapella) Copy() *SignedBlindedBeaconBlockCapella {
if sigBlock == nil {
return nil
}
return &SignedBlindedBeaconBlockCapella{
Block: sigBlock.Block.Copy(),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// Copy --
func (block *BlindedBeaconBlockCapella) Copy() *BlindedBeaconBlockCapella {
if block == nil {
return nil
}
return &BlindedBeaconBlockCapella{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: block.Body.Copy(),
}
}
// Copy --
func (body *BlindedBeaconBlockBodyCapella) Copy() *BlindedBeaconBlockBodyCapella {
if body == nil {
return nil
}
return &BlindedBeaconBlockBodyCapella{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
SyncAggregate: body.SyncAggregate.Copy(),
ExecutionPayloadHeader: body.ExecutionPayloadHeader.Copy(),
BlsToExecutionChanges: CopySlice(body.BlsToExecutionChanges),
}
}
// Copy --
func (sigBlock *SignedBlindedBeaconBlockDeneb) Copy() *SignedBlindedBeaconBlockDeneb {
if sigBlock == nil {
return nil
}
return &SignedBlindedBeaconBlockDeneb{
Message: sigBlock.Message.Copy(),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// Copy --
func (block *BlindedBeaconBlockDeneb) Copy() *BlindedBeaconBlockDeneb {
if block == nil {
return nil
}
return &BlindedBeaconBlockDeneb{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: block.Body.Copy(),
}
}
// Copy --
func (body *BlindedBeaconBlockBodyDeneb) Copy() *BlindedBeaconBlockBodyDeneb {
if body == nil {
return nil
}
return &BlindedBeaconBlockBodyDeneb{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
SyncAggregate: body.SyncAggregate.Copy(),
ExecutionPayloadHeader: body.ExecutionPayloadHeader.Copy(),
BlsToExecutionChanges: CopySlice(body.BlsToExecutionChanges),
BlobKzgCommitments: CopyBlobKZGs(body.BlobKzgCommitments),
}
}
// Copy --
func (sigBlock *SignedBlindedBeaconBlockElectra) Copy() *SignedBlindedBeaconBlockElectra {
if sigBlock == nil {
return nil
}
return &SignedBlindedBeaconBlockElectra{
Message: sigBlock.Message.Copy(),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// Copy --
func (block *BlindedBeaconBlockElectra) Copy() *BlindedBeaconBlockElectra {
if block == nil {
return nil
}
return &BlindedBeaconBlockElectra{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: block.Body.Copy(),
}
}
// Copy --
func (body *BlindedBeaconBlockBodyElectra) Copy() *BlindedBeaconBlockBodyElectra {
if body == nil {
return nil
}
return &BlindedBeaconBlockBodyElectra{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
SyncAggregate: body.SyncAggregate.Copy(),
ExecutionPayloadHeader: body.ExecutionPayloadHeader.Copy(),
BlsToExecutionChanges: CopySlice(body.BlsToExecutionChanges),
BlobKzgCommitments: CopyBlobKZGs(body.BlobKzgCommitments),
}
}
// Copy --
func (sigBlock *SignedBlindedBeaconBlockBellatrix) Copy() *SignedBlindedBeaconBlockBellatrix {
if sigBlock == nil {
return nil
}
return &SignedBlindedBeaconBlockBellatrix{
Block: sigBlock.Block.Copy(),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// Copy --
func (block *BlindedBeaconBlockBellatrix) Copy() *BlindedBeaconBlockBellatrix {
if block == nil {
return nil
}
return &BlindedBeaconBlockBellatrix{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: block.Body.Copy(),
}
}
// Copy --
func (body *BlindedBeaconBlockBodyBellatrix) Copy() *BlindedBeaconBlockBodyBellatrix {
if body == nil {
return nil
}
return &BlindedBeaconBlockBodyBellatrix{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
SyncAggregate: body.SyncAggregate.Copy(),
ExecutionPayloadHeader: body.ExecutionPayloadHeader.Copy(),
}
}
// CopyBlobKZGs copies the provided blob kzgs object.
func CopyBlobKZGs(b [][]byte) [][]byte {
return bytesutil.SafeCopy2dBytes(b)
}
// Copy --
func (sigBlock *SignedBeaconBlockDeneb) Copy() *SignedBeaconBlockDeneb {
if sigBlock == nil {
return nil
}
return &SignedBeaconBlockDeneb{
Block: sigBlock.Block.Copy(),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// Copy --
func (block *BeaconBlockDeneb) Copy() *BeaconBlockDeneb {
if block == nil {
return nil
}
return &BeaconBlockDeneb{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: block.Body.Copy(),
}
}
// Copy --
func (body *BeaconBlockBodyDeneb) Copy() *BeaconBlockBodyDeneb {
if body == nil {
return nil
}
return &BeaconBlockBodyDeneb{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
SyncAggregate: body.SyncAggregate.Copy(),
ExecutionPayload: body.ExecutionPayload.Copy(),
BlsToExecutionChanges: CopySlice(body.BlsToExecutionChanges),
BlobKzgCommitments: CopyBlobKZGs(body.BlobKzgCommitments),
}
}
// Copy --
func (sigBlock *SignedBeaconBlockElectra) Copy() *SignedBeaconBlockElectra {
if sigBlock == nil {
return nil
}
return &SignedBeaconBlockElectra{
Block: sigBlock.Block.Copy(),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// Copy --
func (block *BeaconBlockElectra) Copy() *BeaconBlockElectra {
if block == nil {
return nil
}
return &BeaconBlockElectra{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: block.Body.Copy(),
}
}
// Copy --
func (body *BeaconBlockBodyElectra) Copy() *BeaconBlockBodyElectra {
if body == nil {
return nil
}
return &BeaconBlockBodyElectra{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
SyncAggregate: body.SyncAggregate.Copy(),
ExecutionPayload: body.ExecutionPayload.Copy(),
BlsToExecutionChanges: CopySlice(body.BlsToExecutionChanges),
BlobKzgCommitments: CopyBlobKZGs(body.BlobKzgCommitments),
}
}
// Copy --
func (data *Eth1Data) Copy() *Eth1Data {
if data == nil {

View File

@@ -6,6 +6,49 @@ import (
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
)
func TestBeaconBlock_Fuzz(t *testing.T) {
// Phase 0 Full
fuzzCopies(t, &eth.SignedBeaconBlock{})
fuzzCopies(t, &eth.BeaconBlock{})
fuzzCopies(t, &eth.BeaconBlockBody{})
// Altair Full
fuzzCopies(t, &eth.SignedBeaconBlockAltair{})
fuzzCopies(t, &eth.BeaconBlockAltair{})
fuzzCopies(t, &eth.BeaconBlockBodyAltair{})
// Bellatrix Full
fuzzCopies(t, &eth.SignedBeaconBlockBellatrix{})
fuzzCopies(t, &eth.BeaconBlockBellatrix{})
fuzzCopies(t, &eth.BeaconBlockBodyBellatrix{})
// Bellatrix Blinded
fuzzCopies(t, &eth.SignedBlindedBeaconBlockBellatrix{})
fuzzCopies(t, &eth.BlindedBeaconBlockBellatrix{})
fuzzCopies(t, &eth.BlindedBeaconBlockBodyBellatrix{})
// Capella Full
fuzzCopies(t, &eth.SignedBeaconBlockCapella{})
fuzzCopies(t, &eth.BeaconBlockCapella{})
fuzzCopies(t, &eth.BeaconBlockBodyCapella{})
// Capella Blinded
fuzzCopies(t, &eth.SignedBlindedBeaconBlockCapella{})
fuzzCopies(t, &eth.BlindedBeaconBlockCapella{})
fuzzCopies(t, &eth.BlindedBeaconBlockBodyCapella{})
// Deneb Full
fuzzCopies(t, &eth.SignedBeaconBlockDeneb{})
fuzzCopies(t, &eth.BeaconBlockDeneb{})
fuzzCopies(t, &eth.BeaconBlockBodyDeneb{})
// Deneb Blinded
fuzzCopies(t, &eth.SignedBlindedBeaconBlockDeneb{})
fuzzCopies(t, &eth.BlindedBeaconBlockDeneb{})
fuzzCopies(t, &eth.BlindedBeaconBlockBodyDeneb{})
// Electra Full
fuzzCopies(t, &eth.SignedBeaconBlockElectra{})
fuzzCopies(t, &eth.BeaconBlockElectra{})
fuzzCopies(t, &eth.BeaconBlockBodyElectra{})
// Electra Blinded
fuzzCopies(t, &eth.SignedBlindedBeaconBlockElectra{})
fuzzCopies(t, &eth.BlindedBeaconBlockElectra{})
fuzzCopies(t, &eth.BlindedBeaconBlockBodyElectra{})
}
func TestCopyBeaconBlockFields_Fuzz(t *testing.T) {
fuzzCopies(t, &eth.Eth1Data{})
fuzzCopies(t, &eth.ProposerSlashing{})

View File

@@ -17,91 +17,6 @@ func CopySlice[T any, C copier[T]](original []C) []T {
return newSlice
}
// CopySignedBeaconBlock copies the provided SignedBeaconBlock.
func CopySignedBeaconBlock(sigBlock *SignedBeaconBlock) *SignedBeaconBlock {
if sigBlock == nil {
return nil
}
return &SignedBeaconBlock{
Block: CopyBeaconBlock(sigBlock.Block),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// CopyBeaconBlock copies the provided BeaconBlock.
func CopyBeaconBlock(block *BeaconBlock) *BeaconBlock {
if block == nil {
return nil
}
return &BeaconBlock{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: CopyBeaconBlockBody(block.Body),
}
}
// CopyBeaconBlockBody copies the provided BeaconBlockBody.
func CopyBeaconBlockBody(body *BeaconBlockBody) *BeaconBlockBody {
if body == nil {
return nil
}
return &BeaconBlockBody{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
}
}
// CopySignedBeaconBlockAltair copies the provided SignedBeaconBlock.
func CopySignedBeaconBlockAltair(sigBlock *SignedBeaconBlockAltair) *SignedBeaconBlockAltair {
if sigBlock == nil {
return nil
}
return &SignedBeaconBlockAltair{
Block: CopyBeaconBlockAltair(sigBlock.Block),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// CopyBeaconBlockAltair copies the provided BeaconBlock.
func CopyBeaconBlockAltair(block *BeaconBlockAltair) *BeaconBlockAltair {
if block == nil {
return nil
}
return &BeaconBlockAltair{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: CopyBeaconBlockBodyAltair(block.Body),
}
}
// CopyBeaconBlockBodyAltair copies the provided BeaconBlockBody.
func CopyBeaconBlockBodyAltair(body *BeaconBlockBodyAltair) *BeaconBlockBodyAltair {
if body == nil {
return nil
}
return &BeaconBlockBodyAltair{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
SyncAggregate: body.SyncAggregate.Copy(),
}
}
// CopyValidator copies the provided validator.
func CopyValidator(val *Validator) *Validator {
pubKey := make([]byte, len(val.PublicKey))
@@ -146,370 +61,3 @@ func CopySyncCommitteeContribution(c *SyncCommitteeContribution) *SyncCommitteeC
Signature: bytesutil.SafeCopyBytes(c.Signature),
}
}
// CopySignedBeaconBlockBellatrix copies the provided SignedBeaconBlockBellatrix.
func CopySignedBeaconBlockBellatrix(sigBlock *SignedBeaconBlockBellatrix) *SignedBeaconBlockBellatrix {
if sigBlock == nil {
return nil
}
return &SignedBeaconBlockBellatrix{
Block: CopyBeaconBlockBellatrix(sigBlock.Block),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// CopyBeaconBlockBellatrix copies the provided BeaconBlockBellatrix.
func CopyBeaconBlockBellatrix(block *BeaconBlockBellatrix) *BeaconBlockBellatrix {
if block == nil {
return nil
}
return &BeaconBlockBellatrix{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: CopyBeaconBlockBodyBellatrix(block.Body),
}
}
// CopyBeaconBlockBodyBellatrix copies the provided BeaconBlockBodyBellatrix.
func CopyBeaconBlockBodyBellatrix(body *BeaconBlockBodyBellatrix) *BeaconBlockBodyBellatrix {
if body == nil {
return nil
}
return &BeaconBlockBodyBellatrix{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
SyncAggregate: body.SyncAggregate.Copy(),
ExecutionPayload: body.ExecutionPayload.Copy(),
}
}
// CopySignedBeaconBlockCapella copies the provided SignedBeaconBlockCapella.
func CopySignedBeaconBlockCapella(sigBlock *SignedBeaconBlockCapella) *SignedBeaconBlockCapella {
if sigBlock == nil {
return nil
}
return &SignedBeaconBlockCapella{
Block: CopyBeaconBlockCapella(sigBlock.Block),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// CopyBeaconBlockCapella copies the provided BeaconBlockCapella.
func CopyBeaconBlockCapella(block *BeaconBlockCapella) *BeaconBlockCapella {
if block == nil {
return nil
}
return &BeaconBlockCapella{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: CopyBeaconBlockBodyCapella(block.Body),
}
}
// CopyBeaconBlockBodyCapella copies the provided BeaconBlockBodyCapella.
func CopyBeaconBlockBodyCapella(body *BeaconBlockBodyCapella) *BeaconBlockBodyCapella {
if body == nil {
return nil
}
return &BeaconBlockBodyCapella{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
SyncAggregate: body.SyncAggregate.Copy(),
ExecutionPayload: body.ExecutionPayload.Copy(),
BlsToExecutionChanges: CopySlice(body.BlsToExecutionChanges),
}
}
// CopySignedBlindedBeaconBlockCapella copies the provided SignedBlindedBeaconBlockCapella.
func CopySignedBlindedBeaconBlockCapella(sigBlock *SignedBlindedBeaconBlockCapella) *SignedBlindedBeaconBlockCapella {
if sigBlock == nil {
return nil
}
return &SignedBlindedBeaconBlockCapella{
Block: CopyBlindedBeaconBlockCapella(sigBlock.Block),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// CopyBlindedBeaconBlockCapella copies the provided BlindedBeaconBlockCapella.
func CopyBlindedBeaconBlockCapella(block *BlindedBeaconBlockCapella) *BlindedBeaconBlockCapella {
if block == nil {
return nil
}
return &BlindedBeaconBlockCapella{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: CopyBlindedBeaconBlockBodyCapella(block.Body),
}
}
// CopyBlindedBeaconBlockBodyCapella copies the provided BlindedBeaconBlockBodyCapella.
func CopyBlindedBeaconBlockBodyCapella(body *BlindedBeaconBlockBodyCapella) *BlindedBeaconBlockBodyCapella {
if body == nil {
return nil
}
return &BlindedBeaconBlockBodyCapella{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
SyncAggregate: body.SyncAggregate.Copy(),
ExecutionPayloadHeader: body.ExecutionPayloadHeader.Copy(),
BlsToExecutionChanges: CopySlice(body.BlsToExecutionChanges),
}
}
// CopySignedBlindedBeaconBlockDeneb copies the provided SignedBlindedBeaconBlockDeneb.
func CopySignedBlindedBeaconBlockDeneb(sigBlock *SignedBlindedBeaconBlockDeneb) *SignedBlindedBeaconBlockDeneb {
if sigBlock == nil {
return nil
}
return &SignedBlindedBeaconBlockDeneb{
Message: CopyBlindedBeaconBlockDeneb(sigBlock.Message),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// CopyBlindedBeaconBlockDeneb copies the provided BlindedBeaconBlockDeneb.
func CopyBlindedBeaconBlockDeneb(block *BlindedBeaconBlockDeneb) *BlindedBeaconBlockDeneb {
if block == nil {
return nil
}
return &BlindedBeaconBlockDeneb{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: CopyBlindedBeaconBlockBodyDeneb(block.Body),
}
}
// CopyBlindedBeaconBlockBodyDeneb copies the provided BlindedBeaconBlockBodyDeneb.
func CopyBlindedBeaconBlockBodyDeneb(body *BlindedBeaconBlockBodyDeneb) *BlindedBeaconBlockBodyDeneb {
if body == nil {
return nil
}
return &BlindedBeaconBlockBodyDeneb{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
SyncAggregate: body.SyncAggregate.Copy(),
ExecutionPayloadHeader: body.ExecutionPayloadHeader.Copy(),
BlsToExecutionChanges: CopySlice(body.BlsToExecutionChanges),
BlobKzgCommitments: CopyBlobKZGs(body.BlobKzgCommitments),
}
}
// CopySignedBlindedBeaconBlockElectra copies the provided SignedBlindedBeaconBlockElectra.
func CopySignedBlindedBeaconBlockElectra(sigBlock *SignedBlindedBeaconBlockElectra) *SignedBlindedBeaconBlockElectra {
if sigBlock == nil {
return nil
}
return &SignedBlindedBeaconBlockElectra{
Message: CopyBlindedBeaconBlockElectra(sigBlock.Message),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// CopyBlindedBeaconBlockElectra copies the provided BlindedBeaconBlockElectra.
func CopyBlindedBeaconBlockElectra(block *BlindedBeaconBlockElectra) *BlindedBeaconBlockElectra {
if block == nil {
return nil
}
return &BlindedBeaconBlockElectra{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: CopyBlindedBeaconBlockBodyElectra(block.Body),
}
}
// CopyBlindedBeaconBlockBodyElectra copies the provided BlindedBeaconBlockBodyElectra.
func CopyBlindedBeaconBlockBodyElectra(body *BlindedBeaconBlockBodyElectra) *BlindedBeaconBlockBodyElectra {
if body == nil {
return nil
}
return &BlindedBeaconBlockBodyElectra{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
SyncAggregate: body.SyncAggregate.Copy(),
ExecutionPayloadHeader: body.ExecutionPayloadHeader.Copy(),
BlsToExecutionChanges: CopySlice(body.BlsToExecutionChanges),
BlobKzgCommitments: CopyBlobKZGs(body.BlobKzgCommitments),
}
}
// CopySignedBlindedBeaconBlockBellatrix copies the provided SignedBlindedBeaconBlockBellatrix.
func CopySignedBlindedBeaconBlockBellatrix(sigBlock *SignedBlindedBeaconBlockBellatrix) *SignedBlindedBeaconBlockBellatrix {
if sigBlock == nil {
return nil
}
return &SignedBlindedBeaconBlockBellatrix{
Block: CopyBlindedBeaconBlockBellatrix(sigBlock.Block),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// CopyBlindedBeaconBlockBellatrix copies the provided BlindedBeaconBlockBellatrix.
func CopyBlindedBeaconBlockBellatrix(block *BlindedBeaconBlockBellatrix) *BlindedBeaconBlockBellatrix {
if block == nil {
return nil
}
return &BlindedBeaconBlockBellatrix{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: CopyBlindedBeaconBlockBodyBellatrix(block.Body),
}
}
// CopyBlindedBeaconBlockBodyBellatrix copies the provided BlindedBeaconBlockBodyBellatrix.
func CopyBlindedBeaconBlockBodyBellatrix(body *BlindedBeaconBlockBodyBellatrix) *BlindedBeaconBlockBodyBellatrix {
if body == nil {
return nil
}
return &BlindedBeaconBlockBodyBellatrix{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
SyncAggregate: body.SyncAggregate.Copy(),
ExecutionPayloadHeader: body.ExecutionPayloadHeader.Copy(),
}
}
// CopyBlobKZGs copies the provided blob kzgs object.
func CopyBlobKZGs(b [][]byte) [][]byte {
return bytesutil.SafeCopy2dBytes(b)
}
// CopySignedBeaconBlockDeneb copies the provided SignedBeaconBlockDeneb.
func CopySignedBeaconBlockDeneb(sigBlock *SignedBeaconBlockDeneb) *SignedBeaconBlockDeneb {
if sigBlock == nil {
return nil
}
return &SignedBeaconBlockDeneb{
Block: CopyBeaconBlockDeneb(sigBlock.Block),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// CopyBeaconBlockDeneb copies the provided BeaconBlockDeneb.
func CopyBeaconBlockDeneb(block *BeaconBlockDeneb) *BeaconBlockDeneb {
if block == nil {
return nil
}
return &BeaconBlockDeneb{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: CopyBeaconBlockBodyDeneb(block.Body),
}
}
// CopyBeaconBlockBodyDeneb copies the provided BeaconBlockBodyDeneb.
func CopyBeaconBlockBodyDeneb(body *BeaconBlockBodyDeneb) *BeaconBlockBodyDeneb {
if body == nil {
return nil
}
return &BeaconBlockBodyDeneb{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
SyncAggregate: body.SyncAggregate.Copy(),
ExecutionPayload: body.ExecutionPayload.Copy(),
BlsToExecutionChanges: CopySlice(body.BlsToExecutionChanges),
BlobKzgCommitments: CopyBlobKZGs(body.BlobKzgCommitments),
}
}
// CopySignedBeaconBlockElectra copies the provided SignedBeaconBlockElectra.
func CopySignedBeaconBlockElectra(sigBlock *SignedBeaconBlockElectra) *SignedBeaconBlockElectra {
if sigBlock == nil {
return nil
}
return &SignedBeaconBlockElectra{
Block: CopyBeaconBlockElectra(sigBlock.Block),
Signature: bytesutil.SafeCopyBytes(sigBlock.Signature),
}
}
// CopyBeaconBlockElectra copies the provided BeaconBlockElectra.
func CopyBeaconBlockElectra(block *BeaconBlockElectra) *BeaconBlockElectra {
if block == nil {
return nil
}
return &BeaconBlockElectra{
Slot: block.Slot,
ProposerIndex: block.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(block.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(block.StateRoot),
Body: CopyBeaconBlockBodyElectra(block.Body),
}
}
// CopyBeaconBlockBodyElectra copies the provided BeaconBlockBodyElectra.
func CopyBeaconBlockBodyElectra(body *BeaconBlockBodyElectra) *BeaconBlockBodyElectra {
if body == nil {
return nil
}
return &BeaconBlockBodyElectra{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Eth1Data: body.Eth1Data.Copy(),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
ProposerSlashings: CopySlice(body.ProposerSlashings),
AttesterSlashings: CopySlice(body.AttesterSlashings),
Attestations: CopySlice(body.Attestations),
Deposits: CopySlice(body.Deposits),
VoluntaryExits: CopySlice(body.VoluntaryExits),
SyncAggregate: body.SyncAggregate.Copy(),
ExecutionPayload: body.ExecutionPayload.Copy(),
BlsToExecutionChanges: CopySlice(body.BlsToExecutionChanges),
BlobKzgCommitments: CopyBlobKZGs(body.BlobKzgCommitments),
}
}

View File

@@ -13,7 +13,7 @@ import (
func TestCopySignedBeaconBlock(t *testing.T) {
blk := genSignedBeaconBlock()
got := v1alpha1.CopySignedBeaconBlock(blk)
got := blk.Copy()
if !reflect.DeepEqual(got, blk) {
t.Errorf("CopySignedBeaconBlock() = %v, want %v", got, blk)
}
@@ -23,7 +23,7 @@ func TestCopySignedBeaconBlock(t *testing.T) {
func TestCopyBeaconBlock(t *testing.T) {
blk := genBeaconBlock()
got := v1alpha1.CopyBeaconBlock(blk)
got := blk.Copy()
if !reflect.DeepEqual(got, blk) {
t.Errorf("CopyBeaconBlock() = %v, want %v", got, blk)
}
@@ -33,7 +33,7 @@ func TestCopyBeaconBlock(t *testing.T) {
func TestCopyBeaconBlockBody(t *testing.T) {
body := genBeaconBlockBody()
got := v1alpha1.CopyBeaconBlockBody(body)
got := body.Copy()
if !reflect.DeepEqual(got, body) {
t.Errorf("CopyBeaconBlockBody() = %v, want %v", got, body)
}
@@ -43,7 +43,7 @@ func TestCopyBeaconBlockBody(t *testing.T) {
func TestCopySignedBeaconBlockAltair(t *testing.T) {
sbb := genSignedBeaconBlockAltair()
got := v1alpha1.CopySignedBeaconBlockAltair(sbb)
got := sbb.Copy()
if !reflect.DeepEqual(got, sbb) {
t.Errorf("CopySignedBeaconBlockAltair() = %v, want %v", got, sbb)
}
@@ -53,7 +53,7 @@ func TestCopySignedBeaconBlockAltair(t *testing.T) {
func TestCopyBeaconBlockAltair(t *testing.T) {
b := genBeaconBlockAltair()
got := v1alpha1.CopyBeaconBlockAltair(b)
got := b.Copy()
if !reflect.DeepEqual(got, b) {
t.Errorf("CopyBeaconBlockAltair() = %v, want %v", got, b)
}
@@ -63,7 +63,7 @@ func TestCopyBeaconBlockAltair(t *testing.T) {
func TestCopyBeaconBlockBodyAltair(t *testing.T) {
bb := genBeaconBlockBodyAltair()
got := v1alpha1.CopyBeaconBlockBodyAltair(bb)
got := bb.Copy()
if !reflect.DeepEqual(got, bb) {
t.Errorf("CopyBeaconBlockBodyAltair() = %v, want %v", got, bb)
}
@@ -133,7 +133,7 @@ func TestCopyPendingAttestationSlice(t *testing.T) {
func TestCopySignedBeaconBlockBellatrix(t *testing.T) {
sbb := genSignedBeaconBlockBellatrix()
got := v1alpha1.CopySignedBeaconBlockBellatrix(sbb)
got := sbb.Copy()
if !reflect.DeepEqual(got, sbb) {
t.Errorf("CopySignedBeaconBlockBellatrix() = %v, want %v", got, sbb)
}
@@ -143,7 +143,7 @@ func TestCopySignedBeaconBlockBellatrix(t *testing.T) {
func TestCopyBeaconBlockBellatrix(t *testing.T) {
b := genBeaconBlockBellatrix()
got := v1alpha1.CopyBeaconBlockBellatrix(b)
got := b.Copy()
if !reflect.DeepEqual(got, b) {
t.Errorf("CopyBeaconBlockBellatrix() = %v, want %v", got, b)
}
@@ -153,7 +153,7 @@ func TestCopyBeaconBlockBellatrix(t *testing.T) {
func TestCopyBeaconBlockBodyBellatrix(t *testing.T) {
bb := genBeaconBlockBodyBellatrix()
got := v1alpha1.CopyBeaconBlockBodyBellatrix(bb)
got := bb.Copy()
if !reflect.DeepEqual(got, bb) {
t.Errorf("CopyBeaconBlockBodyBellatrix() = %v, want %v", got, bb)
}
@@ -163,7 +163,7 @@ func TestCopyBeaconBlockBodyBellatrix(t *testing.T) {
func TestCopySignedBlindedBeaconBlockBellatrix(t *testing.T) {
sbb := genSignedBeaconBlockBellatrix()
got := v1alpha1.CopySignedBeaconBlockBellatrix(sbb)
got := sbb.Copy()
if !reflect.DeepEqual(got, sbb) {
t.Errorf("CopySignedBeaconBlockBellatrix() = %v, want %v", got, sbb)
}
@@ -173,7 +173,7 @@ func TestCopySignedBlindedBeaconBlockBellatrix(t *testing.T) {
func TestCopyBlindedBeaconBlockBellatrix(t *testing.T) {
b := genBeaconBlockBellatrix()
got := v1alpha1.CopyBeaconBlockBellatrix(b)
got := b.Copy()
if !reflect.DeepEqual(got, b) {
t.Errorf("CopyBeaconBlockBellatrix() = %v, want %v", got, b)
}
@@ -183,7 +183,7 @@ func TestCopyBlindedBeaconBlockBellatrix(t *testing.T) {
func TestCopyBlindedBeaconBlockBodyBellatrix(t *testing.T) {
bb := genBeaconBlockBodyBellatrix()
got := v1alpha1.CopyBeaconBlockBodyBellatrix(bb)
got := bb.Copy()
if !reflect.DeepEqual(got, bb) {
t.Errorf("CopyBeaconBlockBodyBellatrix() = %v, want %v", got, bb)
}
@@ -193,7 +193,7 @@ func TestCopyBlindedBeaconBlockBodyBellatrix(t *testing.T) {
func TestCopySignedBeaconBlockCapella(t *testing.T) {
sbb := genSignedBeaconBlockCapella()
got := v1alpha1.CopySignedBeaconBlockCapella(sbb)
got := sbb.Copy()
if !reflect.DeepEqual(got, sbb) {
t.Errorf("CopySignedBeaconBlockCapella() = %v, want %v", got, sbb)
}
@@ -203,7 +203,7 @@ func TestCopySignedBeaconBlockCapella(t *testing.T) {
func TestCopyBeaconBlockCapella(t *testing.T) {
b := genBeaconBlockCapella()
got := v1alpha1.CopyBeaconBlockCapella(b)
got := b.Copy()
if !reflect.DeepEqual(got, b) {
t.Errorf("CopyBeaconBlockCapella() = %v, want %v", got, b)
}
@@ -213,7 +213,7 @@ func TestCopyBeaconBlockCapella(t *testing.T) {
func TestCopyBeaconBlockBodyCapella(t *testing.T) {
bb := genBeaconBlockBodyCapella()
got := v1alpha1.CopyBeaconBlockBodyCapella(bb)
got := bb.Copy()
if !reflect.DeepEqual(got, bb) {
t.Errorf("CopyBeaconBlockBodyCapella() = %v, want %v", got, bb)
}
@@ -223,7 +223,7 @@ func TestCopyBeaconBlockBodyCapella(t *testing.T) {
func TestCopySignedBlindedBeaconBlockCapella(t *testing.T) {
sbb := genSignedBlindedBeaconBlockCapella()
got := v1alpha1.CopySignedBlindedBeaconBlockCapella(sbb)
got := sbb.Copy()
if !reflect.DeepEqual(got, sbb) {
t.Errorf("CopySignedBlindedBeaconBlockCapella() = %v, want %v", got, sbb)
}
@@ -233,7 +233,7 @@ func TestCopySignedBlindedBeaconBlockCapella(t *testing.T) {
func TestCopyBlindedBeaconBlockCapella(t *testing.T) {
b := genBlindedBeaconBlockCapella()
got := v1alpha1.CopyBlindedBeaconBlockCapella(b)
got := b.Copy()
if !reflect.DeepEqual(got, b) {
t.Errorf("CopyBlindedBeaconBlockCapella() = %v, want %v", got, b)
}
@@ -243,7 +243,7 @@ func TestCopyBlindedBeaconBlockCapella(t *testing.T) {
func TestCopyBlindedBeaconBlockBodyCapella(t *testing.T) {
bb := genBlindedBeaconBlockBodyCapella()
got := v1alpha1.CopyBlindedBeaconBlockBodyCapella(bb)
got := bb.Copy()
if !reflect.DeepEqual(got, bb) {
t.Errorf("CopyBlindedBeaconBlockBodyCapella() = %v, want %v", got, bb)
}
@@ -253,7 +253,7 @@ func TestCopyBlindedBeaconBlockBodyCapella(t *testing.T) {
func TestCopySignedBeaconBlockDeneb(t *testing.T) {
sbb := genSignedBeaconBlockDeneb()
got := v1alpha1.CopySignedBeaconBlockDeneb(sbb)
got := sbb.Copy()
if !reflect.DeepEqual(got, sbb) {
t.Errorf("CopySignedBeaconBlockDeneb() = %v, want %v", got, sbb)
}
@@ -263,7 +263,7 @@ func TestCopySignedBeaconBlockDeneb(t *testing.T) {
func TestCopyBeaconBlockDeneb(t *testing.T) {
b := genBeaconBlockDeneb()
got := v1alpha1.CopyBeaconBlockDeneb(b)
got := b.Copy()
if !reflect.DeepEqual(got, b) {
t.Errorf("CopyBeaconBlockDeneb() = %v, want %v", got, b)
}
@@ -273,7 +273,7 @@ func TestCopyBeaconBlockDeneb(t *testing.T) {
func TestCopyBeaconBlockBodyDeneb(t *testing.T) {
bb := genBeaconBlockBodyDeneb()
got := v1alpha1.CopyBeaconBlockBodyDeneb(bb)
got := bb.Copy()
if !reflect.DeepEqual(got, bb) {
t.Errorf("CopyBeaconBlockBodyDeneb() = %v, want %v", got, bb)
}
@@ -283,7 +283,7 @@ func TestCopyBeaconBlockBodyDeneb(t *testing.T) {
func TestCopySignedBlindedBeaconBlockDeneb(t *testing.T) {
sbb := genSignedBlindedBeaconBlockDeneb()
got := v1alpha1.CopySignedBlindedBeaconBlockDeneb(sbb)
got := sbb.Copy()
if !reflect.DeepEqual(got, sbb) {
t.Errorf("CopySignedBlindedBeaconBlockDeneb() = %v, want %v", got, sbb)
}
@@ -293,7 +293,7 @@ func TestCopySignedBlindedBeaconBlockDeneb(t *testing.T) {
func TestCopyBlindedBeaconBlockDeneb(t *testing.T) {
b := genBlindedBeaconBlockDeneb()
got := v1alpha1.CopyBlindedBeaconBlockDeneb(b)
got := b.Copy()
if !reflect.DeepEqual(got, b) {
t.Errorf("CopyBlindedBeaconBlockDeneb() = %v, want %v", got, b)
}
@@ -303,7 +303,7 @@ func TestCopyBlindedBeaconBlockDeneb(t *testing.T) {
func TestCopyBlindedBeaconBlockBodyDeneb(t *testing.T) {
bb := genBlindedBeaconBlockBodyDeneb()
got := v1alpha1.CopyBlindedBeaconBlockBodyDeneb(bb)
got := bb.Copy()
if !reflect.DeepEqual(got, bb) {
t.Errorf("CopyBlindedBeaconBlockBodyDeneb() = %v, want %v", got, bb)
}
@@ -321,7 +321,7 @@ func bytes(length int) []byte {
func TestCopySignedBlindedBeaconBlockElectra(t *testing.T) {
sbb := genSignedBlindedBeaconBlockElectra()
got := v1alpha1.CopySignedBlindedBeaconBlockElectra(sbb)
got := sbb.Copy()
if !reflect.DeepEqual(got, sbb) {
t.Errorf("TestCopySignedBlindedBeaconBlockElectra() = %v, want %v", got, sbb)
}
@@ -330,7 +330,7 @@ func TestCopySignedBlindedBeaconBlockElectra(t *testing.T) {
func TestCopyBlindedBeaconBlockElectra(t *testing.T) {
b := genBlindedBeaconBlockElectra()
got := v1alpha1.CopyBlindedBeaconBlockElectra(b)
got := b.Copy()
if !reflect.DeepEqual(got, b) {
t.Errorf("TestCopyBlindedBeaconBlockElectra() = %v, want %v", got, b)
}
@@ -339,7 +339,7 @@ func TestCopyBlindedBeaconBlockElectra(t *testing.T) {
func TestCopyBlindedBeaconBlockBodyElectra(t *testing.T) {
bb := genBlindedBeaconBlockBodyElectra()
got := v1alpha1.CopyBlindedBeaconBlockBodyElectra(bb)
got := bb.Copy()
if !reflect.DeepEqual(got, bb) {
t.Errorf("TestCopyBlindedBeaconBlockBodyElectra() = %v, want %v", got, bb)
}
@@ -348,7 +348,7 @@ func TestCopyBlindedBeaconBlockBodyElectra(t *testing.T) {
func TestCopySignedBeaconBlockElectra(t *testing.T) {
sbb := genSignedBeaconBlockElectra()
got := v1alpha1.CopySignedBeaconBlockElectra(sbb)
got := sbb.Copy()
if !reflect.DeepEqual(got, sbb) {
t.Errorf("TestCopySignedBeaconBlockElectra() = %v, want %v", got, sbb)
}
@@ -357,7 +357,7 @@ func TestCopySignedBeaconBlockElectra(t *testing.T) {
func TestCopyBeaconBlockElectra(t *testing.T) {
b := genBeaconBlockElectra()
got := v1alpha1.CopyBeaconBlockElectra(b)
got := b.Copy()
if !reflect.DeepEqual(got, b) {
t.Errorf("TestCopyBeaconBlockElectra() = %v, want %v", got, b)
}
@@ -366,7 +366,7 @@ func TestCopyBeaconBlockElectra(t *testing.T) {
func TestCopyBeaconBlockBodyElectra(t *testing.T) {
bb := genBeaconBlockBodyElectra()
got := v1alpha1.CopyBeaconBlockBodyElectra(bb)
got := bb.Copy()
if !reflect.DeepEqual(got, bb) {
t.Errorf("TestCopyBeaconBlockBodyElectra() = %v, want %v", got, bb)
}

View File

@@ -6,7 +6,7 @@ Originally from [github.com/x-cray/logrus-prefixed-formatter(https://github.com/
modified colored output and support for log entry prefixes, e.g. message source followed by a colon. In addition, custom
color themes are supported.
![Formatter screenshot](http://cl.ly/image/1w0B3F233F3z/formatter-screenshot@2x.png)
![Formatter screenshot](http://i.imgur.com/PY7qMwd.png)
Just like with the original `logrus.TextFormatter` when a TTY is not attached, the output is compatible with the
[logfmt](http://godoc.org/github.com/kr/logfmt) format:

View File

@@ -17,21 +17,13 @@ go_library(
visibility = ["//testing/spectest:__subpackages__"],
deps = [
"//beacon-chain/core/altair:go_default_library",
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/validators:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/state-native:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/require:go_default_library",
"//testing/spectest/utils:go_default_library",
"//runtime/version:go_default_library",
"//testing/spectest/shared/common/operations:go_default_library",
"//testing/util:go_default_library",
"@com_github_golang_snappy//:go_default_library",
"@com_github_google_go_cmp//cmp:go_default_library",
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
"@org_golang_google_protobuf//testing/protocmp:go_default_library",
],
)

View File

@@ -1,59 +1,27 @@
package operations
import (
"context"
"errors"
"path"
"testing"
"github.com/golang/snappy"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
b "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func RunAttestationTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "altair", "operations/attestation/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "altair", "operations/attestation/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
attestationFile, err := util.BazelFileBytes(folderPath, "attestation.ssz_snappy")
require.NoError(t, err)
attestationSSZ, err := snappy.Decode(nil /* dst */, attestationFile)
require.NoError(t, err, "Failed to decompress")
att := &ethpb.Attestation{}
require.NoError(t, att.UnmarshalSSZ(attestationSSZ), "Failed to unmarshal")
body := &ethpb.BeaconBlockBodyAltair{Attestations: []*ethpb.Attestation{att}}
processAtt := func(ctx context.Context, st state.BeaconState, blk interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) {
st, err = altair.ProcessAttestationsNoVerifySignature(ctx, st, blk.Block())
if err != nil {
return nil, err
}
aSet, err := b.AttestationSignatureBatch(ctx, st, blk.Block().Body().Attestations())
if err != nil {
return nil, err
}
verified, err := aSet.Verify()
if err != nil {
return nil, err
}
if !verified {
return nil, errors.New("could not batch verify attestation signature")
}
return st, nil
}
RunBlockOperationTest(t, folderPath, body, processAtt)
})
func blockWithAttestation(attestationSSZ []byte) (interfaces.SignedBeaconBlock, error) {
att := &ethpb.Attestation{}
if err := att.UnmarshalSSZ(attestationSSZ); err != nil {
return nil, err
}
b := util.NewBeaconBlockAltair()
b.Block.Body = &ethpb.BeaconBlockBodyAltair{Attestations: []*ethpb.Attestation{att}}
return blocks.NewSignedBeaconBlock(b)
}
func RunAttestationTest(t *testing.T, config string) {
common.RunAttestationTest(t, config, version.String(version.Altair), blockWithAttestation, altair.ProcessAttestationsNoVerifySignature, sszToState)
}

View File

@@ -1,41 +1,26 @@
package operations
import (
"context"
"path"
"testing"
"github.com/golang/snappy"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func RunAttesterSlashingTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "altair", "operations/attester_slashing/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "altair", "operations/attester_slashing/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
attSlashingFile, err := util.BazelFileBytes(folderPath, "attester_slashing.ssz_snappy")
require.NoError(t, err)
attSlashingSSZ, err := snappy.Decode(nil /* dst */, attSlashingFile)
require.NoError(t, err, "Failed to decompress")
attSlashing := &ethpb.AttesterSlashing{}
require.NoError(t, attSlashing.UnmarshalSSZ(attSlashingSSZ), "Failed to unmarshal")
body := &ethpb.BeaconBlockBodyAltair{AttesterSlashings: []*ethpb.AttesterSlashing{attSlashing}}
RunBlockOperationTest(t, folderPath, body, func(ctx context.Context, s state.BeaconState, b interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) {
return blocks.ProcessAttesterSlashings(ctx, s, b.Block().Body().AttesterSlashings(), validators.SlashValidator)
})
})
func blockWithAttesterSlashing(asSSZ []byte) (interfaces.SignedBeaconBlock, error) {
as := &ethpb.AttesterSlashing{}
if err := as.UnmarshalSSZ(asSSZ); err != nil {
return nil, err
}
b := util.NewBeaconBlockAltair()
b.Block.Body = &ethpb.BeaconBlockBodyAltair{AttesterSlashings: []*ethpb.AttesterSlashing{as}}
return blocks.NewSignedBeaconBlock(b)
}
func RunAttesterSlashingTest(t *testing.T, config string) {
common.RunAttesterSlashingTest(t, config, version.String(version.Altair), blockWithAttesterSlashing, sszToState)
}

View File

@@ -1,90 +1,12 @@
package operations
import (
"context"
"os"
"path"
"strings"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/golang/snappy"
"github.com/google/go-cmp/cmp"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/testing/util"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
)
func RunBlockHeaderTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "altair", "operations/block_header/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "altair", "operations/block_header/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
helpers.ClearCache()
blockFile, err := util.BazelFileBytes(testsFolderPath, folder.Name(), "block.ssz_snappy")
require.NoError(t, err)
blockSSZ, err := snappy.Decode(nil /* dst */, blockFile)
require.NoError(t, err, "Failed to decompress")
block := &ethpb.BeaconBlockAltair{}
require.NoError(t, block.UnmarshalSSZ(blockSSZ), "Failed to unmarshal")
preBeaconStateFile, err := util.BazelFileBytes(testsFolderPath, folder.Name(), "pre.ssz_snappy")
require.NoError(t, err)
preBeaconStateSSZ, err := snappy.Decode(nil /* dst */, preBeaconStateFile)
require.NoError(t, err, "Failed to decompress")
preBeaconStateBase := &ethpb.BeaconStateAltair{}
require.NoError(t, preBeaconStateBase.UnmarshalSSZ(preBeaconStateSSZ), "Failed to unmarshal")
preBeaconState, err := state_native.InitializeFromProtoAltair(preBeaconStateBase)
require.NoError(t, err)
// If the post.ssz is not present, it means the test should fail on our end.
postSSZFilepath, err := bazel.Runfile(path.Join(testsFolderPath, folder.Name(), "post.ssz_snappy"))
postSSZExists := true
if err != nil && strings.Contains(err.Error(), "could not locate file") {
postSSZExists = false
} else {
require.NoError(t, err)
}
// Spectest blocks are not signed, so we'll call NoVerify to skip sig verification.
bodyRoot, err := block.Body.HashTreeRoot()
require.NoError(t, err)
beaconState, err := blocks.ProcessBlockHeaderNoVerify(context.Background(), preBeaconState, block.Slot, block.ProposerIndex, block.ParentRoot, bodyRoot[:])
if postSSZExists {
require.NoError(t, err)
postBeaconStateFile, err := os.ReadFile(postSSZFilepath) // #nosec G304
require.NoError(t, err)
postBeaconStateSSZ, err := snappy.Decode(nil /* dst */, postBeaconStateFile)
require.NoError(t, err, "Failed to decompress")
postBeaconState := &ethpb.BeaconStateAltair{}
require.NoError(t, postBeaconState.UnmarshalSSZ(postBeaconStateSSZ), "Failed to unmarshal")
pbState, err := state_native.ProtobufBeaconStateAltair(beaconState.ToProto())
require.NoError(t, err)
if !proto.Equal(pbState, postBeaconState) {
t.Log(cmp.Diff(postBeaconState, pbState, protocmp.Transform()))
t.Fatal("Post state does not match expected")
}
} else {
// Note: This doesn't test anything worthwhile. It essentially tests
// that *any* error has occurred, not any specific error.
if err == nil {
t.Fatal("Did not fail when expected")
}
t.Logf("Expected failure; failure reason = %v", err)
return
}
})
}
common.RunBlockHeaderTest(t, config, version.String(version.Altair), sszToBlock, sszToState)
}

View File

@@ -1,41 +1,27 @@
package operations
import (
"context"
"path"
"testing"
"github.com/golang/snappy"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func RunDepositTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "altair", "operations/deposit/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "altair", "operations/deposit/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
depositFile, err := util.BazelFileBytes(folderPath, "deposit.ssz_snappy")
require.NoError(t, err)
depositSSZ, err := snappy.Decode(nil /* dst */, depositFile)
require.NoError(t, err, "Failed to decompress")
deposit := &ethpb.Deposit{}
require.NoError(t, deposit.UnmarshalSSZ(depositSSZ), "Failed to unmarshal")
body := &ethpb.BeaconBlockBodyAltair{Deposits: []*ethpb.Deposit{deposit}}
processDepositsFunc := func(ctx context.Context, s state.BeaconState, b interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) {
return altair.ProcessDeposits(ctx, s, b.Block().Body().Deposits())
}
RunBlockOperationTest(t, folderPath, body, processDepositsFunc)
})
func blockWithDeposit(ssz []byte) (interfaces.SignedBeaconBlock, error) {
d := &ethpb.Deposit{}
if err := d.UnmarshalSSZ(ssz); err != nil {
return nil, err
}
b := util.NewBeaconBlockAltair()
b.Block.Body = &ethpb.BeaconBlockBodyAltair{Deposits: []*ethpb.Deposit{d}}
return blocks.NewSignedBeaconBlock(b)
}
func RunDepositTest(t *testing.T, config string) {
common.RunDepositTest(t, config, version.String(version.Altair), blockWithDeposit, altair.ProcessDeposits, sszToState)
}

View File

@@ -1,88 +1,25 @@
package operations
import (
"context"
"os"
"path"
"strings"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/golang/snappy"
"github.com/google/go-cmp/cmp"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
)
type blockOperation func(context.Context, state.BeaconState, interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error)
// RunBlockOperationTest takes in the prestate and the beacon block body, processes it through the
// passed in block operation function and checks the post state with the expected post state.
func RunBlockOperationTest(
t *testing.T,
folderPath string,
body *ethpb.BeaconBlockBodyAltair,
operationFn blockOperation,
) {
preBeaconStateFile, err := util.BazelFileBytes(path.Join(folderPath, "pre.ssz_snappy"))
require.NoError(t, err)
preBeaconStateSSZ, err := snappy.Decode(nil /* dst */, preBeaconStateFile)
require.NoError(t, err, "Failed to decompress")
preStateBase := &ethpb.BeaconStateAltair{}
if err := preStateBase.UnmarshalSSZ(preBeaconStateSSZ); err != nil {
t.Fatalf("Failed to unmarshal: %v", err)
}
preState, err := state_native.InitializeFromProtoAltair(preStateBase)
require.NoError(t, err)
// If the post.ssz is not present, it means the test should fail on our end.
postSSZFilepath, err := bazel.Runfile(path.Join(folderPath, "post.ssz_snappy"))
postSSZExists := true
if err != nil && strings.Contains(err.Error(), "could not locate file") {
postSSZExists = false
} else if err != nil {
t.Fatal(err)
}
helpers.ClearCache()
b := util.NewBeaconBlockAltair()
b.Block.Body = body
wsb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
beaconState, err := operationFn(context.Background(), preState, wsb)
if postSSZExists {
require.NoError(t, err)
postBeaconStateFile, err := os.ReadFile(postSSZFilepath) // #nosec G304
require.NoError(t, err)
postBeaconStateSSZ, err := snappy.Decode(nil /* dst */, postBeaconStateFile)
require.NoError(t, err, "Failed to decompress")
postBeaconState := &ethpb.BeaconStateAltair{}
if err := postBeaconState.UnmarshalSSZ(postBeaconStateSSZ); err != nil {
t.Fatalf("Failed to unmarshal: %v", err)
}
pbState, err := state_native.ProtobufBeaconStateAltair(beaconState.ToProtoUnsafe())
require.NoError(t, err)
if !proto.Equal(pbState, postBeaconState) {
t.Log(cmp.Diff(postBeaconState, pbState, protocmp.Transform()))
t.Fatal("Post state does not match expected")
}
} else {
// Note: This doesn't test anything worthwhile. It essentially tests
// that *any* error has occurred, not any specific error.
if err == nil {
t.Fatal("Did not fail when expected")
}
t.Logf("Expected failure; failure reason = %v", err)
return
func sszToState(b []byte) (state.BeaconState, error) {
base := &ethpb.BeaconStateAltair{}
if err := base.UnmarshalSSZ(b); err != nil {
return nil, err
}
return state_native.InitializeFromProtoAltair(base)
}
func sszToBlock(b []byte) (interfaces.SignedBeaconBlock, error) {
base := &ethpb.BeaconBlockAltair{}
if err := base.UnmarshalSSZ(b); err != nil {
return nil, err
}
return blocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockAltair{Block: base})
}

View File

@@ -1,41 +1,26 @@
package operations
import (
"context"
"path"
"testing"
"github.com/golang/snappy"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func RunProposerSlashingTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "altair", "operations/proposer_slashing/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "altair", "operations/proposer_slashing/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
proposerSlashingFile, err := util.BazelFileBytes(folderPath, "proposer_slashing.ssz_snappy")
require.NoError(t, err)
proposerSlashingSSZ, err := snappy.Decode(nil /* dst */, proposerSlashingFile)
require.NoError(t, err, "Failed to decompress")
proposerSlashing := &ethpb.ProposerSlashing{}
require.NoError(t, proposerSlashing.UnmarshalSSZ(proposerSlashingSSZ), "Failed to unmarshal")
body := &ethpb.BeaconBlockBodyAltair{ProposerSlashings: []*ethpb.ProposerSlashing{proposerSlashing}}
RunBlockOperationTest(t, folderPath, body, func(ctx context.Context, s state.BeaconState, b interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) {
return blocks.ProcessProposerSlashings(ctx, s, b.Block().Body().ProposerSlashings(), validators.SlashValidator)
})
})
func blockWithProposerSlashing(ssz []byte) (interfaces.SignedBeaconBlock, error) {
ps := &ethpb.ProposerSlashing{}
if err := ps.UnmarshalSSZ(ssz); err != nil {
return nil, err
}
b := util.NewBeaconBlockAltair()
b.Block.Body = &ethpb.BeaconBlockBodyAltair{ProposerSlashings: []*ethpb.ProposerSlashing{ps}}
return blocks.NewSignedBeaconBlock(b)
}
func RunProposerSlashingTest(t *testing.T, config string) {
common.RunProposerSlashingTest(t, config, version.String(version.Altair), blockWithProposerSlashing, sszToState)
}

View File

@@ -1,44 +1,26 @@
package operations
import (
"context"
"path"
"testing"
"github.com/golang/snappy"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func RunSyncCommitteeTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "altair", "operations/sync_aggregate/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "altair", "operations/sync_aggregate/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
syncCommitteeFile, err := util.BazelFileBytes(folderPath, "sync_aggregate.ssz_snappy")
require.NoError(t, err)
syncCommitteeSSZ, err := snappy.Decode(nil /* dst */, syncCommitteeFile)
require.NoError(t, err, "Failed to decompress")
sc := &ethpb.SyncAggregate{}
require.NoError(t, sc.UnmarshalSSZ(syncCommitteeSSZ), "Failed to unmarshal")
body := &ethpb.BeaconBlockBodyAltair{SyncAggregate: sc}
RunBlockOperationTest(t, folderPath, body, func(ctx context.Context, s state.BeaconState, b interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) {
st, _, err := altair.ProcessSyncAggregate(context.Background(), s, body.SyncAggregate)
if err != nil {
return nil, err
}
return st, nil
})
})
func blockWithSyncAggregate(ssz []byte) (interfaces.SignedBeaconBlock, error) {
sa := &ethpb.SyncAggregate{}
if err := sa.UnmarshalSSZ(ssz); err != nil {
return nil, err
}
b := util.NewBeaconBlockAltair()
b.Block.Body = &ethpb.BeaconBlockBodyAltair{SyncAggregate: sa}
return blocks.NewSignedBeaconBlock(b)
}
func RunSyncCommitteeTest(t *testing.T, config string) {
common.RunSyncCommitteeTest(t, config, version.String(version.Altair), blockWithSyncAggregate, sszToState)
}

View File

@@ -1,40 +1,26 @@
package operations
import (
"context"
"path"
"testing"
"github.com/golang/snappy"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func RunVoluntaryExitTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "altair", "operations/voluntary_exit/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "altair", "operations/voluntary_exit/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
exitFile, err := util.BazelFileBytes(folderPath, "voluntary_exit.ssz_snappy")
require.NoError(t, err)
exitSSZ, err := snappy.Decode(nil /* dst */, exitFile)
require.NoError(t, err, "Failed to decompress")
voluntaryExit := &ethpb.SignedVoluntaryExit{}
require.NoError(t, voluntaryExit.UnmarshalSSZ(exitSSZ), "Failed to unmarshal")
body := &ethpb.BeaconBlockBodyAltair{VoluntaryExits: []*ethpb.SignedVoluntaryExit{voluntaryExit}}
RunBlockOperationTest(t, folderPath, body, func(ctx context.Context, s state.BeaconState, b interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) {
return blocks.ProcessVoluntaryExits(ctx, s, b.Block().Body().VoluntaryExits())
})
})
func blockWithVoluntaryExit(ssz []byte) (interfaces.SignedBeaconBlock, error) {
e := &ethpb.SignedVoluntaryExit{}
if err := e.UnmarshalSSZ(ssz); err != nil {
return nil, err
}
b := util.NewBeaconBlockAltair()
b.Block.Body = &ethpb.BeaconBlockBodyAltair{VoluntaryExits: []*ethpb.SignedVoluntaryExit{e}}
return blocks.NewSignedBeaconBlock(b)
}
func RunVoluntaryExitTest(t *testing.T, config string) {
common.RunVoluntaryExitTest(t, config, version.String(version.Altair), blockWithVoluntaryExit, sszToState)
}

View File

@@ -18,21 +18,13 @@ go_library(
visibility = ["//testing/spectest:__subpackages__"],
deps = [
"//beacon-chain/core/altair:go_default_library",
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/validators:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/state-native:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/require:go_default_library",
"//testing/spectest/utils:go_default_library",
"//runtime/version:go_default_library",
"//testing/spectest/shared/common/operations:go_default_library",
"//testing/util:go_default_library",
"@com_github_golang_snappy//:go_default_library",
"@com_github_google_go_cmp//cmp:go_default_library",
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
"@org_golang_google_protobuf//testing/protocmp:go_default_library",
],
)

View File

@@ -1,59 +1,27 @@
package operations
import (
"context"
"errors"
"path"
"testing"
"github.com/golang/snappy"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
b "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func RunAttestationTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "bellatrix", "operations/attestation/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "bellatrix", "operations/attestation/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
attestationFile, err := util.BazelFileBytes(folderPath, "attestation.ssz_snappy")
require.NoError(t, err)
attestationSSZ, err := snappy.Decode(nil /* dst */, attestationFile)
require.NoError(t, err, "Failed to decompress")
att := &ethpb.Attestation{}
require.NoError(t, att.UnmarshalSSZ(attestationSSZ), "Failed to unmarshal")
body := &ethpb.BeaconBlockBodyBellatrix{Attestations: []*ethpb.Attestation{att}}
processAtt := func(ctx context.Context, st state.BeaconState, blk interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) {
st, err = altair.ProcessAttestationsNoVerifySignature(ctx, st, blk.Block())
if err != nil {
return nil, err
}
aSet, err := b.AttestationSignatureBatch(ctx, st, blk.Block().Body().Attestations())
if err != nil {
return nil, err
}
verified, err := aSet.Verify()
if err != nil {
return nil, err
}
if !verified {
return nil, errors.New("could not batch verify attestation signature")
}
return st, nil
}
RunBlockOperationTest(t, folderPath, body, processAtt)
})
func blockWithAttestation(attestationSSZ []byte) (interfaces.SignedBeaconBlock, error) {
att := &ethpb.Attestation{}
if err := att.UnmarshalSSZ(attestationSSZ); err != nil {
return nil, err
}
b := util.NewBeaconBlockBellatrix()
b.Block.Body = &ethpb.BeaconBlockBodyBellatrix{Attestations: []*ethpb.Attestation{att}}
return blocks.NewSignedBeaconBlock(b)
}
func RunAttestationTest(t *testing.T, config string) {
common.RunAttestationTest(t, config, version.String(version.Bellatrix), blockWithAttestation, altair.ProcessAttestationsNoVerifySignature, sszToState)
}

View File

@@ -1,41 +1,26 @@
package operations
import (
"context"
"path"
"testing"
"github.com/golang/snappy"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func RunAttesterSlashingTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "bellatrix", "operations/attester_slashing/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "bellatrix", "operations/attester_slashing/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
attSlashingFile, err := util.BazelFileBytes(folderPath, "attester_slashing.ssz_snappy")
require.NoError(t, err)
attSlashingSSZ, err := snappy.Decode(nil /* dst */, attSlashingFile)
require.NoError(t, err, "Failed to decompress")
attSlashing := &ethpb.AttesterSlashing{}
require.NoError(t, attSlashing.UnmarshalSSZ(attSlashingSSZ), "Failed to unmarshal")
body := &ethpb.BeaconBlockBodyBellatrix{AttesterSlashings: []*ethpb.AttesterSlashing{attSlashing}}
RunBlockOperationTest(t, folderPath, body, func(ctx context.Context, s state.BeaconState, b interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) {
return blocks.ProcessAttesterSlashings(ctx, s, b.Block().Body().AttesterSlashings(), validators.SlashValidator)
})
})
func blockWithAttesterSlashing(asSSZ []byte) (interfaces.SignedBeaconBlock, error) {
as := &ethpb.AttesterSlashing{}
if err := as.UnmarshalSSZ(asSSZ); err != nil {
return nil, err
}
b := util.NewBeaconBlockBellatrix()
b.Block.Body = &ethpb.BeaconBlockBodyBellatrix{AttesterSlashings: []*ethpb.AttesterSlashing{as}}
return blocks.NewSignedBeaconBlock(b)
}
func RunAttesterSlashingTest(t *testing.T, config string) {
common.RunAttesterSlashingTest(t, config, version.String(version.Bellatrix), blockWithAttesterSlashing, sszToState)
}

View File

@@ -1,90 +1,12 @@
package operations
import (
"context"
"os"
"path"
"strings"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/golang/snappy"
"github.com/google/go-cmp/cmp"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/testing/util"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
)
func RunBlockHeaderTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "bellatrix", "operations/block_header/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "bellatrix", "operations/block_header/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
helpers.ClearCache()
blockFile, err := util.BazelFileBytes(testsFolderPath, folder.Name(), "block.ssz_snappy")
require.NoError(t, err)
blockSSZ, err := snappy.Decode(nil /* dst */, blockFile)
require.NoError(t, err, "Failed to decompress")
block := &ethpb.BeaconBlockBellatrix{}
require.NoError(t, block.UnmarshalSSZ(blockSSZ), "Failed to unmarshal")
preBeaconStateFile, err := util.BazelFileBytes(testsFolderPath, folder.Name(), "pre.ssz_snappy")
require.NoError(t, err)
preBeaconStateSSZ, err := snappy.Decode(nil /* dst */, preBeaconStateFile)
require.NoError(t, err, "Failed to decompress")
preBeaconStateBase := &ethpb.BeaconStateBellatrix{}
require.NoError(t, preBeaconStateBase.UnmarshalSSZ(preBeaconStateSSZ), "Failed to unmarshal")
preBeaconState, err := state_native.InitializeFromProtoBellatrix(preBeaconStateBase)
require.NoError(t, err)
// If the post.ssz is not present, it means the test should fail on our end.
postSSZFilepath, err := bazel.Runfile(path.Join(testsFolderPath, folder.Name(), "post.ssz_snappy"))
postSSZExists := true
if err != nil && strings.Contains(err.Error(), "could not locate file") {
postSSZExists = false
} else {
require.NoError(t, err)
}
// Spectest blocks are not signed, so we'll call NoVerify to skip sig verification.
bodyRoot, err := block.Body.HashTreeRoot()
require.NoError(t, err)
beaconState, err := blocks.ProcessBlockHeaderNoVerify(context.Background(), preBeaconState, block.Slot, block.ProposerIndex, block.ParentRoot, bodyRoot[:])
if postSSZExists {
require.NoError(t, err)
postBeaconStateFile, err := os.ReadFile(postSSZFilepath) // #nosec G304
require.NoError(t, err)
postBeaconStateSSZ, err := snappy.Decode(nil /* dst */, postBeaconStateFile)
require.NoError(t, err, "Failed to decompress")
postBeaconState := &ethpb.BeaconStateBellatrix{}
require.NoError(t, postBeaconState.UnmarshalSSZ(postBeaconStateSSZ), "Failed to unmarshal")
pbState, err := state_native.ProtobufBeaconStateBellatrix(beaconState.ToProto())
require.NoError(t, err)
if !proto.Equal(pbState, postBeaconState) {
t.Log(cmp.Diff(postBeaconState, pbState, protocmp.Transform()))
t.Fatal("Post state does not match expected")
}
} else {
// Note: This doesn't test anything worthwhile. It essentially tests
// that *any* error has occurred, not any specific error.
if err == nil {
t.Fatal("Did not fail when expected")
}
t.Logf("Expected failure; failure reason = %v", err)
return
}
})
}
common.RunBlockHeaderTest(t, config, version.String(version.Bellatrix), sszToBlock, sszToState)
}

View File

@@ -1,41 +1,27 @@
package operations
import (
"context"
"path"
"testing"
"github.com/golang/snappy"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func RunDepositTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "bellatrix", "operations/deposit/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "bellatrix", "operations/deposit/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
depositFile, err := util.BazelFileBytes(folderPath, "deposit.ssz_snappy")
require.NoError(t, err)
depositSSZ, err := snappy.Decode(nil /* dst */, depositFile)
require.NoError(t, err, "Failed to decompress")
deposit := &ethpb.Deposit{}
require.NoError(t, deposit.UnmarshalSSZ(depositSSZ), "Failed to unmarshal")
body := &ethpb.BeaconBlockBodyBellatrix{Deposits: []*ethpb.Deposit{deposit}}
processDepositsFunc := func(ctx context.Context, s state.BeaconState, b interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) {
return altair.ProcessDeposits(ctx, s, b.Block().Body().Deposits())
}
RunBlockOperationTest(t, folderPath, body, processDepositsFunc)
})
func blockWithDeposit(ssz []byte) (interfaces.SignedBeaconBlock, error) {
d := &ethpb.Deposit{}
if err := d.UnmarshalSSZ(ssz); err != nil {
return nil, err
}
b := util.NewBeaconBlockBellatrix()
b.Block.Body = &ethpb.BeaconBlockBodyBellatrix{Deposits: []*ethpb.Deposit{d}}
return blocks.NewSignedBeaconBlock(b)
}
func RunDepositTest(t *testing.T, config string) {
common.RunDepositTest(t, config, version.String(version.Bellatrix), blockWithDeposit, altair.ProcessDeposits, sszToState)
}

View File

@@ -1,98 +1,12 @@
package operations
import (
"os"
"path"
"strings"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/golang/snappy"
"github.com/google/go-cmp/cmp"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
blocks2 "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/testing/util"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
)
func RunExecutionPayloadTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "bellatrix", "operations/execution_payload/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "bellatrix", "operations/execution_payload/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
helpers.ClearCache()
blockBodyFile, err := util.BazelFileBytes(testsFolderPath, folder.Name(), "body.ssz_snappy")
require.NoError(t, err)
blockSSZ, err := snappy.Decode(nil /* dst */, blockBodyFile)
require.NoError(t, err, "Failed to decompress")
block := &ethpb.BeaconBlockBodyBellatrix{}
require.NoError(t, block.UnmarshalSSZ(blockSSZ), "Failed to unmarshal")
preBeaconStateFile, err := util.BazelFileBytes(testsFolderPath, folder.Name(), "pre.ssz_snappy")
require.NoError(t, err)
preBeaconStateSSZ, err := snappy.Decode(nil /* dst */, preBeaconStateFile)
require.NoError(t, err, "Failed to decompress")
preBeaconStateBase := &ethpb.BeaconStateBellatrix{}
require.NoError(t, preBeaconStateBase.UnmarshalSSZ(preBeaconStateSSZ), "Failed to unmarshal")
preBeaconState, err := state_native.InitializeFromProtoBellatrix(preBeaconStateBase)
require.NoError(t, err)
postSSZFilepath, err := bazel.Runfile(path.Join(testsFolderPath, folder.Name(), "post.ssz_snappy"))
postSSZExists := true
if err != nil && strings.Contains(err.Error(), "could not locate file") {
postSSZExists = false
} else {
require.NoError(t, err)
}
payload, err := blocks2.WrappedExecutionPayload(block.ExecutionPayload)
require.NoError(t, err)
file, err := util.BazelFileBytes(testsFolderPath, folder.Name(), "execution.yaml")
require.NoError(t, err)
config := &ExecutionConfig{}
require.NoError(t, utils.UnmarshalYaml(file, config), "Failed to Unmarshal")
gotState, err := blocks.ProcessPayload(preBeaconState, payload)
if postSSZExists {
require.NoError(t, err)
postBeaconStateFile, err := os.ReadFile(postSSZFilepath) // #nosec G304
require.NoError(t, err)
postBeaconStateSSZ, err := snappy.Decode(nil /* dst */, postBeaconStateFile)
require.NoError(t, err, "Failed to decompress")
postBeaconState := &ethpb.BeaconStateBellatrix{}
require.NoError(t, postBeaconState.UnmarshalSSZ(postBeaconStateSSZ), "Failed to unmarshal")
pbState, err := state_native.ProtobufBeaconStateBellatrix(gotState.ToProto())
require.NoError(t, err)
if !proto.Equal(pbState, postBeaconState) {
t.Log(cmp.Diff(postBeaconState, pbState, protocmp.Transform()))
t.Fatal("Post state does not match expected")
}
} else if config.Valid {
// Note: This doesn't test anything worthwhile. It essentially tests
// that *any* error has occurred, not any specific error.
if err == nil {
t.Fatal("Did not fail when expected")
}
t.Logf("Expected failure; failure reason = %v", err)
return
}
})
}
}
type ExecutionConfig struct {
Valid bool `json:"execution_valid"`
common.RunExecutionPayloadTest(t, config, version.String(version.Bellatrix), sszToBlockBody, sszToState)
}

View File

@@ -1,88 +1,33 @@
package operations
import (
"context"
"os"
"path"
"strings"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/golang/snappy"
"github.com/google/go-cmp/cmp"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
)
type blockOperation func(context.Context, state.BeaconState, interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error)
// RunBlockOperationTest takes in the prestate and the beacon block body, processes it through the
// passed in block operation function and checks the post state with the expected post state.
func RunBlockOperationTest(
t *testing.T,
folderPath string,
body *ethpb.BeaconBlockBodyBellatrix,
operationFn blockOperation,
) {
preBeaconStateFile, err := util.BazelFileBytes(path.Join(folderPath, "pre.ssz_snappy"))
require.NoError(t, err)
preBeaconStateSSZ, err := snappy.Decode(nil /* dst */, preBeaconStateFile)
require.NoError(t, err, "Failed to decompress")
preStateBase := &ethpb.BeaconStateBellatrix{}
if err := preStateBase.UnmarshalSSZ(preBeaconStateSSZ); err != nil {
t.Fatalf("Failed to unmarshal: %v", err)
}
preState, err := state_native.InitializeFromProtoBellatrix(preStateBase)
require.NoError(t, err)
// If the post.ssz is not present, it means the test should fail on our end.
postSSZFilepath, err := bazel.Runfile(path.Join(folderPath, "post.ssz_snappy"))
postSSZExists := true
if err != nil && strings.Contains(err.Error(), "could not locate file") {
postSSZExists = false
} else if err != nil {
t.Fatal(err)
}
helpers.ClearCache()
b := util.NewBeaconBlockBellatrix()
b.Block.Body = body
wsb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
beaconState, err := operationFn(context.Background(), preState, wsb)
if postSSZExists {
require.NoError(t, err)
postBeaconStateFile, err := os.ReadFile(postSSZFilepath) // #nosec G304
require.NoError(t, err)
postBeaconStateSSZ, err := snappy.Decode(nil /* dst */, postBeaconStateFile)
require.NoError(t, err, "Failed to decompress")
postBeaconState := &ethpb.BeaconStateBellatrix{}
if err := postBeaconState.UnmarshalSSZ(postBeaconStateSSZ); err != nil {
t.Fatalf("Failed to unmarshal: %v", err)
}
pbState, err := state_native.ProtobufBeaconStateBellatrix(beaconState.ToProtoUnsafe())
require.NoError(t, err)
if !proto.Equal(pbState, postBeaconState) {
t.Log(cmp.Diff(postBeaconState, pbState, protocmp.Transform()))
t.Fatal("Post state does not match expected")
}
} else {
// Note: This doesn't test anything worthwhile. It essentially tests
// that *any* error has occurred, not any specific error.
if err == nil {
t.Fatal("Did not fail when expected")
}
t.Logf("Expected failure; failure reason = %v", err)
return
func sszToState(b []byte) (state.BeaconState, error) {
base := &ethpb.BeaconStateBellatrix{}
if err := base.UnmarshalSSZ(b); err != nil {
return nil, err
}
return state_native.InitializeFromProtoBellatrix(base)
}
func sszToBlock(b []byte) (interfaces.SignedBeaconBlock, error) {
base := &ethpb.BeaconBlockBellatrix{}
if err := base.UnmarshalSSZ(b); err != nil {
return nil, err
}
return blocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockBellatrix{Block: base})
}
func sszToBlockBody(b []byte) (interfaces.ReadOnlyBeaconBlockBody, error) {
base := &ethpb.BeaconBlockBodyBellatrix{}
if err := base.UnmarshalSSZ(b); err != nil {
return nil, err
}
return blocks.NewBeaconBlockBody(base)
}

View File

@@ -1,41 +1,26 @@
package operations
import (
"context"
"path"
"testing"
"github.com/golang/snappy"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func RunProposerSlashingTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "bellatrix", "operations/proposer_slashing/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "bellatrix", "operations/proposer_slashing/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
proposerSlashingFile, err := util.BazelFileBytes(folderPath, "proposer_slashing.ssz_snappy")
require.NoError(t, err)
proposerSlashingSSZ, err := snappy.Decode(nil /* dst */, proposerSlashingFile)
require.NoError(t, err, "Failed to decompress")
proposerSlashing := &ethpb.ProposerSlashing{}
require.NoError(t, proposerSlashing.UnmarshalSSZ(proposerSlashingSSZ), "Failed to unmarshal")
body := &ethpb.BeaconBlockBodyBellatrix{ProposerSlashings: []*ethpb.ProposerSlashing{proposerSlashing}}
RunBlockOperationTest(t, folderPath, body, func(ctx context.Context, s state.BeaconState, b interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) {
return blocks.ProcessProposerSlashings(ctx, s, b.Block().Body().ProposerSlashings(), validators.SlashValidator)
})
})
func blockWithProposerSlashing(ssz []byte) (interfaces.SignedBeaconBlock, error) {
ps := &ethpb.ProposerSlashing{}
if err := ps.UnmarshalSSZ(ssz); err != nil {
return nil, err
}
b := util.NewBeaconBlockBellatrix()
b.Block.Body = &ethpb.BeaconBlockBodyBellatrix{ProposerSlashings: []*ethpb.ProposerSlashing{ps}}
return blocks.NewSignedBeaconBlock(b)
}
func RunProposerSlashingTest(t *testing.T, config string) {
common.RunProposerSlashingTest(t, config, version.String(version.Bellatrix), blockWithProposerSlashing, sszToState)
}

View File

@@ -1,44 +1,26 @@
package operations
import (
"context"
"path"
"testing"
"github.com/golang/snappy"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func RunSyncCommitteeTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "bellatrix", "operations/sync_aggregate/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "bellatrix", "operations/sync_aggregate/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
syncCommitteeFile, err := util.BazelFileBytes(folderPath, "sync_aggregate.ssz_snappy")
require.NoError(t, err)
syncCommitteeSSZ, err := snappy.Decode(nil /* dst */, syncCommitteeFile)
require.NoError(t, err, "Failed to decompress")
sc := &ethpb.SyncAggregate{}
require.NoError(t, sc.UnmarshalSSZ(syncCommitteeSSZ), "Failed to unmarshal")
body := &ethpb.BeaconBlockBodyBellatrix{SyncAggregate: sc}
RunBlockOperationTest(t, folderPath, body, func(ctx context.Context, s state.BeaconState, b interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) {
st, _, err := altair.ProcessSyncAggregate(context.Background(), s, body.SyncAggregate)
if err != nil {
return nil, err
}
return st, nil
})
})
func blockWithSyncAggregate(ssz []byte) (interfaces.SignedBeaconBlock, error) {
sa := &ethpb.SyncAggregate{}
if err := sa.UnmarshalSSZ(ssz); err != nil {
return nil, err
}
b := util.NewBeaconBlockBellatrix()
b.Block.Body = &ethpb.BeaconBlockBodyBellatrix{SyncAggregate: sa}
return blocks.NewSignedBeaconBlock(b)
}
func RunSyncCommitteeTest(t *testing.T, config string) {
common.RunSyncCommitteeTest(t, config, version.String(version.Bellatrix), blockWithSyncAggregate, sszToState)
}

View File

@@ -1,40 +1,26 @@
package operations
import (
"context"
"path"
"testing"
"github.com/golang/snappy"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func RunVoluntaryExitTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "bellatrix", "operations/voluntary_exit/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "bellatrix", "operations/voluntary_exit/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
exitFile, err := util.BazelFileBytes(folderPath, "voluntary_exit.ssz_snappy")
require.NoError(t, err)
exitSSZ, err := snappy.Decode(nil /* dst */, exitFile)
require.NoError(t, err, "Failed to decompress")
voluntaryExit := &ethpb.SignedVoluntaryExit{}
require.NoError(t, voluntaryExit.UnmarshalSSZ(exitSSZ), "Failed to unmarshal")
body := &ethpb.BeaconBlockBodyBellatrix{VoluntaryExits: []*ethpb.SignedVoluntaryExit{voluntaryExit}}
RunBlockOperationTest(t, folderPath, body, func(ctx context.Context, s state.BeaconState, b interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) {
return blocks.ProcessVoluntaryExits(ctx, s, b.Block().Body().VoluntaryExits())
})
})
func blockWithVoluntaryExit(ssz []byte) (interfaces.SignedBeaconBlock, error) {
e := &ethpb.SignedVoluntaryExit{}
if err := e.UnmarshalSSZ(ssz); err != nil {
return nil, err
}
b := util.NewBeaconBlockBellatrix()
b.Block.Body = &ethpb.BeaconBlockBodyBellatrix{VoluntaryExits: []*ethpb.SignedVoluntaryExit{e}}
return blocks.NewSignedBeaconBlock(b)
}
func RunVoluntaryExitTest(t *testing.T, config string) {
common.RunVoluntaryExitTest(t, config, version.String(version.Bellatrix), blockWithVoluntaryExit, sszToState)
}

View File

@@ -20,23 +20,14 @@ go_library(
visibility = ["//testing/spectest:__subpackages__"],
deps = [
"//beacon-chain/core/altair:go_default_library",
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/validators:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/state-native:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/require:go_default_library",
"//testing/spectest/utils:go_default_library",
"//runtime/version:go_default_library",
"//testing/spectest/shared/common/operations:go_default_library",
"//testing/util:go_default_library",
"@com_github_golang_snappy//:go_default_library",
"@com_github_google_go_cmp//cmp:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
"@org_golang_google_protobuf//testing/protocmp:go_default_library",
],
)

View File

@@ -1,59 +1,27 @@
package operations
import (
"context"
"errors"
"path"
"testing"
"github.com/golang/snappy"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
b "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func RunAttestationTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "capella", "operations/attestation/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "capella", "operations/attestation/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
attestationFile, err := util.BazelFileBytes(folderPath, "attestation.ssz_snappy")
require.NoError(t, err)
attestationSSZ, err := snappy.Decode(nil /* dst */, attestationFile)
require.NoError(t, err, "Failed to decompress")
att := &ethpb.Attestation{}
require.NoError(t, att.UnmarshalSSZ(attestationSSZ), "Failed to unmarshal")
body := &ethpb.BeaconBlockBodyCapella{Attestations: []*ethpb.Attestation{att}}
processAtt := func(ctx context.Context, st state.BeaconState, blk interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) {
st, err = altair.ProcessAttestationsNoVerifySignature(ctx, st, blk.Block())
if err != nil {
return nil, err
}
aSet, err := b.AttestationSignatureBatch(ctx, st, blk.Block().Body().Attestations())
if err != nil {
return nil, err
}
verified, err := aSet.Verify()
if err != nil {
return nil, err
}
if !verified {
return nil, errors.New("could not batch verify attestation signature")
}
return st, nil
}
RunBlockOperationTest(t, folderPath, body, processAtt)
})
func blockWithAttestation(attestationSSZ []byte) (interfaces.SignedBeaconBlock, error) {
att := &ethpb.Attestation{}
if err := att.UnmarshalSSZ(attestationSSZ); err != nil {
return nil, err
}
b := util.NewBeaconBlockCapella()
b.Block.Body = &ethpb.BeaconBlockBodyCapella{Attestations: []*ethpb.Attestation{att}}
return blocks.NewSignedBeaconBlock(b)
}
func RunAttestationTest(t *testing.T, config string) {
common.RunAttestationTest(t, config, version.String(version.Capella), blockWithAttestation, altair.ProcessAttestationsNoVerifySignature, sszToState)
}

View File

@@ -1,41 +1,26 @@
package operations
import (
"context"
"path"
"testing"
"github.com/golang/snappy"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func RunAttesterSlashingTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "capella", "operations/attester_slashing/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "capella", "operations/attester_slashing/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
attSlashingFile, err := util.BazelFileBytes(folderPath, "attester_slashing.ssz_snappy")
require.NoError(t, err)
attSlashingSSZ, err := snappy.Decode(nil /* dst */, attSlashingFile)
require.NoError(t, err, "Failed to decompress")
attSlashing := &ethpb.AttesterSlashing{}
require.NoError(t, attSlashing.UnmarshalSSZ(attSlashingSSZ), "Failed to unmarshal")
body := &ethpb.BeaconBlockBodyCapella{AttesterSlashings: []*ethpb.AttesterSlashing{attSlashing}}
RunBlockOperationTest(t, folderPath, body, func(ctx context.Context, s state.BeaconState, b interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) {
return blocks.ProcessAttesterSlashings(ctx, s, b.Block().Body().AttesterSlashings(), validators.SlashValidator)
})
})
func blockWithAttesterSlashing(asSSZ []byte) (interfaces.SignedBeaconBlock, error) {
as := &ethpb.AttesterSlashing{}
if err := as.UnmarshalSSZ(asSSZ); err != nil {
return nil, err
}
b := util.NewBeaconBlockCapella()
b.Block.Body = &ethpb.BeaconBlockBodyCapella{AttesterSlashings: []*ethpb.AttesterSlashing{as}}
return blocks.NewSignedBeaconBlock(b)
}
func RunAttesterSlashingTest(t *testing.T, config string) {
common.RunAttesterSlashingTest(t, config, version.String(version.Capella), blockWithAttesterSlashing, sszToState)
}

View File

@@ -1,90 +1,12 @@
package operations
import (
"context"
"os"
"path"
"strings"
"testing"
"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/golang/snappy"
"github.com/google/go-cmp/cmp"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/testing/util"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
)
func RunBlockHeaderTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "capella", "operations/block_header/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "capella", "operations/block_header/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
helpers.ClearCache()
blockFile, err := util.BazelFileBytes(testsFolderPath, folder.Name(), "block.ssz_snappy")
require.NoError(t, err)
blockSSZ, err := snappy.Decode(nil /* dst */, blockFile)
require.NoError(t, err, "Failed to decompress")
block := &ethpb.BeaconBlockCapella{}
require.NoError(t, block.UnmarshalSSZ(blockSSZ), "Failed to unmarshal")
preBeaconStateFile, err := util.BazelFileBytes(testsFolderPath, folder.Name(), "pre.ssz_snappy")
require.NoError(t, err)
preBeaconStateSSZ, err := snappy.Decode(nil /* dst */, preBeaconStateFile)
require.NoError(t, err, "Failed to decompress")
preBeaconStateBase := &ethpb.BeaconStateCapella{}
require.NoError(t, preBeaconStateBase.UnmarshalSSZ(preBeaconStateSSZ), "Failed to unmarshal")
preBeaconState, err := state_native.InitializeFromProtoCapella(preBeaconStateBase)
require.NoError(t, err)
// If the post.ssz is not present, it means the test should fail on our end.
postSSZFilepath, err := bazel.Runfile(path.Join(testsFolderPath, folder.Name(), "post.ssz_snappy"))
postSSZExists := true
if err != nil && strings.Contains(err.Error(), "could not locate file") {
postSSZExists = false
} else {
require.NoError(t, err)
}
// Spectest blocks are not signed, so we'll call NoVerify to skip sig verification.
bodyRoot, err := block.Body.HashTreeRoot()
require.NoError(t, err)
beaconState, err := blocks.ProcessBlockHeaderNoVerify(context.Background(), preBeaconState, block.Slot, block.ProposerIndex, block.ParentRoot, bodyRoot[:])
if postSSZExists {
require.NoError(t, err)
postBeaconStateFile, err := os.ReadFile(postSSZFilepath) // #nosec G304
require.NoError(t, err)
postBeaconStateSSZ, err := snappy.Decode(nil /* dst */, postBeaconStateFile)
require.NoError(t, err, "Failed to decompress")
postBeaconState := &ethpb.BeaconStateCapella{}
require.NoError(t, postBeaconState.UnmarshalSSZ(postBeaconStateSSZ), "Failed to unmarshal")
pbState, err := state_native.ProtobufBeaconStateCapella(beaconState.ToProto())
require.NoError(t, err)
if !proto.Equal(pbState, postBeaconState) {
t.Log(cmp.Diff(postBeaconState, pbState, protocmp.Transform()))
t.Fatal("Post state does not match expected")
}
} else {
// Note: This doesn't test anything worthwhile. It essentially tests
// that *any* error has occurred, not any specific error.
if err == nil {
t.Fatal("Did not fail when expected")
}
t.Logf("Expected failure; failure reason = %v", err)
return
}
})
}
common.RunBlockHeaderTest(t, config, version.String(version.Capella), sszToBlock, sszToState)
}

View File

@@ -1,62 +1,26 @@
package operations
import (
"context"
"path"
"testing"
"github.com/golang/snappy"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func RunBLSToExecutionChangeTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "capella", "operations/bls_to_execution_change/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "capella", "operations/bls_to_execution_change/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
changeFile, err := util.BazelFileBytes(folderPath, "address_change.ssz_snappy")
require.NoError(t, err)
changeSSZ, err := snappy.Decode(nil /* dst */, changeFile)
require.NoError(t, err, "Failed to decompress")
change := &ethpb.SignedBLSToExecutionChange{}
require.NoError(t, change.UnmarshalSSZ(changeSSZ), "Failed to unmarshal")
body := &ethpb.BeaconBlockBodyCapella{
BlsToExecutionChanges: []*ethpb.SignedBLSToExecutionChange{change},
}
RunBlockOperationTest(t, folderPath, body, func(ctx context.Context, s state.BeaconState, b interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) {
st, err := blocks.ProcessBLSToExecutionChanges(s, b.Block())
if err != nil {
return nil, err
}
changes, err := b.Block().Body().BLSToExecutionChanges()
if err != nil {
return nil, err
}
cSet, err := blocks.BLSChangesSignatureBatch(st, changes)
if err != nil {
return nil, err
}
ok, err := cSet.Verify()
if err != nil {
return nil, err
}
if !ok {
return nil, errors.New("signature did not verify")
}
return st, nil
})
})
func blockWithBlsChange(ssz []byte) (interfaces.SignedBeaconBlock, error) {
c := &ethpb.SignedBLSToExecutionChange{}
if err := c.UnmarshalSSZ(ssz); err != nil {
return nil, err
}
b := util.NewBeaconBlockCapella()
b.Block.Body = &ethpb.BeaconBlockBodyCapella{BlsToExecutionChanges: []*ethpb.SignedBLSToExecutionChange{c}}
return blocks.NewSignedBeaconBlock(b)
}
func RunBLSToExecutionChangeTest(t *testing.T, config string) {
common.RunBLSToExecutionChangeTest(t, config, version.String(version.Capella), blockWithBlsChange, sszToState)
}

View File

@@ -1,41 +1,27 @@
package operations
import (
"context"
"path"
"testing"
"github.com/golang/snappy"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/operations"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func RunDepositTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "capella", "operations/deposit/pyspec_tests")
if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/%s", config, "capella", "operations/deposit/pyspec_tests")
}
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
depositFile, err := util.BazelFileBytes(folderPath, "deposit.ssz_snappy")
require.NoError(t, err)
depositSSZ, err := snappy.Decode(nil /* dst */, depositFile)
require.NoError(t, err, "Failed to decompress")
deposit := &ethpb.Deposit{}
require.NoError(t, deposit.UnmarshalSSZ(depositSSZ), "Failed to unmarshal")
body := &ethpb.BeaconBlockBodyCapella{Deposits: []*ethpb.Deposit{deposit}}
processDepositsFunc := func(ctx context.Context, s state.BeaconState, b interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) {
return altair.ProcessDeposits(ctx, s, b.Block().Body().Deposits())
}
RunBlockOperationTest(t, folderPath, body, processDepositsFunc)
})
func blockWithDeposit(ssz []byte) (interfaces.SignedBeaconBlock, error) {
d := &ethpb.Deposit{}
if err := d.UnmarshalSSZ(ssz); err != nil {
return nil, err
}
b := util.NewBeaconBlockCapella()
b.Block.Body = &ethpb.BeaconBlockBodyCapella{Deposits: []*ethpb.Deposit{d}}
return blocks.NewSignedBeaconBlock(b)
}
func RunDepositTest(t *testing.T, config string) {
common.RunDepositTest(t, config, version.String(version.Capella), blockWithDeposit, altair.ProcessDeposits, sszToState)
}

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