diff --git a/cl/aggregation/pool_impl.go b/cl/aggregation/pool_impl.go index 2eff66ffa5c..62f1b704e4e 100644 --- a/cl/aggregation/pool_impl.go +++ b/cl/aggregation/pool_impl.go @@ -47,11 +47,6 @@ func NewAggregationPool( } func (p *aggregationPoolImpl) AddAttestation(inAtt *solid.Attestation) error { - // check if it's single attestation - if utils.BitsOnCount(inAtt.AggregationBits()) != 1 { - return fmt.Errorf("exactly one aggregation bit should be set") - } - // use hash of attestation data as key hashRoot, err := inAtt.AttestantionData().HashSSZ() if err != nil { diff --git a/cl/beacon/handler/block_production.go b/cl/beacon/handler/block_production.go index dcf70b756c6..fb4ddcbfb9f 100644 --- a/cl/beacon/handler/block_production.go +++ b/cl/beacon/handler/block_production.go @@ -1,12 +1,14 @@ package handler import ( + "bytes" "context" "encoding/hex" "encoding/json" "fmt" "io" "net/http" + "sort" "strconv" "sync" "time" @@ -18,6 +20,7 @@ import ( "github.com/ledgerwatch/erigon-lib/common/length" "github.com/ledgerwatch/erigon-lib/gointerfaces/sentinel" "github.com/ledgerwatch/erigon-lib/kv" + "github.com/ledgerwatch/erigon/cl/abstract" "github.com/ledgerwatch/erigon/cl/beacon/beaconhttp" "github.com/ledgerwatch/erigon/cl/clparams" "github.com/ledgerwatch/erigon/cl/cltypes" @@ -27,9 +30,12 @@ import ( "github.com/ledgerwatch/erigon/cl/phase1/core/state" "github.com/ledgerwatch/erigon/cl/transition" "github.com/ledgerwatch/erigon/cl/transition/impl/eth2" + "github.com/ledgerwatch/erigon/cl/transition/machine" + "github.com/ledgerwatch/erigon/cl/utils" "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/turbo/engineapi/engine_types" "github.com/ledgerwatch/log/v3" + "golang.org/x/exp/slices" ) type BlockPublishingValidation string @@ -42,7 +48,10 @@ const ( var defaultGraffitiString = "Caplin" -func (a *ApiHandler) GetEthV1ValidatorAttestationData(w http.ResponseWriter, r *http.Request) (*beaconhttp.BeaconResponse, error) { +func (a *ApiHandler) GetEthV1ValidatorAttestationData( + w http.ResponseWriter, + r *http.Request, +) (*beaconhttp.BeaconResponse, error) { slot, err := beaconhttp.Uint64FromQueryParams(r, "slot") if err != nil { return nil, beaconhttp.NewEndpointError(http.StatusBadRequest, err) @@ -52,28 +61,44 @@ func (a *ApiHandler) GetEthV1ValidatorAttestationData(w http.ResponseWriter, r * return nil, beaconhttp.NewEndpointError(http.StatusBadRequest, err) } if slot == nil || committeeIndex == nil { - return nil, beaconhttp.NewEndpointError(http.StatusBadRequest, fmt.Errorf("slot and committee_index url params are required")) + return nil, beaconhttp.NewEndpointError( + http.StatusBadRequest, + fmt.Errorf("slot and committee_index url params are required"), + ) } headState := a.syncedData.HeadState() if headState == nil { - return nil, beaconhttp.NewEndpointError(http.StatusServiceUnavailable, fmt.Errorf("beacon node is still syncing")) + return nil, beaconhttp.NewEndpointError( + http.StatusServiceUnavailable, + fmt.Errorf("beacon node is still syncing"), + ) } - attestationData, err := a.attestationProducer.ProduceAndCacheAttestationData(headState, *slot, *committeeIndex) + attestationData, err := a.attestationProducer.ProduceAndCacheAttestationData( + headState, + *slot, + *committeeIndex, + ) if err != nil { return nil, beaconhttp.NewEndpointError(http.StatusInternalServerError, err) } return newBeaconResponse(attestationData), nil } -func (a *ApiHandler) GetEthV3ValidatorBlock(w http.ResponseWriter, r *http.Request) (*beaconhttp.BeaconResponse, error) { +func (a *ApiHandler) GetEthV3ValidatorBlock( + w http.ResponseWriter, + r *http.Request, +) (*beaconhttp.BeaconResponse, error) { ctx := r.Context() // parse request data randaoRevealString := r.URL.Query().Get("randao_reveal") var randaoReveal common.Bytes96 if err := randaoReveal.UnmarshalText([]byte(randaoRevealString)); err != nil { - return nil, beaconhttp.NewEndpointError(http.StatusBadRequest, fmt.Errorf("invalid randao_reveal: %v", err)) + return nil, beaconhttp.NewEndpointError( + http.StatusBadRequest, + fmt.Errorf("invalid randao_reveal: %v", err), + ) } if r.URL.Query().Has("skip_randao_verification") { randaoReveal = common.Bytes96{0xc0} // infinity bls signature @@ -92,12 +117,18 @@ func (a *ApiHandler) GetEthV3ValidatorBlock(w http.ResponseWriter, r *http.Reque targetSlotStr := chi.URLParam(r, "slot") targetSlot, err := strconv.ParseUint(targetSlotStr, 10, 64) if err != nil { - return nil, beaconhttp.NewEndpointError(http.StatusBadRequest, fmt.Errorf("invalid slot: %v", err)) + return nil, beaconhttp.NewEndpointError( + http.StatusBadRequest, + fmt.Errorf("invalid slot: %v", err), + ) } s := a.syncedData.HeadState() if s == nil { - return nil, beaconhttp.NewEndpointError(http.StatusServiceUnavailable, fmt.Errorf("node is syncing")) + return nil, beaconhttp.NewEndpointError( + http.StatusServiceUnavailable, + fmt.Errorf("node is syncing"), + ) } baseBlockRoot, err := s.BlockRoot() @@ -110,21 +141,42 @@ func (a *ApiHandler) GetEthV3ValidatorBlock(w http.ResponseWriter, r *http.Reque return nil, err } if sourceBlock == nil { - return nil, beaconhttp.NewEndpointError(http.StatusNotFound, fmt.Errorf("block not found %x", baseBlockRoot)) - } - baseState, err := a.forkchoiceStore.GetStateAtBlockRoot(baseBlockRoot, true) // we start the block production from this state + return nil, beaconhttp.NewEndpointError( + http.StatusNotFound, + fmt.Errorf("block not found %x", baseBlockRoot), + ) + } + baseState, err := a.forkchoiceStore.GetStateAtBlockRoot( + baseBlockRoot, + true, + ) // we start the block production from this state if err != nil { return nil, err } if baseState == nil { - return nil, beaconhttp.NewEndpointError(http.StatusNotFound, fmt.Errorf("state not found %x", baseBlockRoot)) + return nil, beaconhttp.NewEndpointError( + http.StatusNotFound, + fmt.Errorf("state not found %x", baseBlockRoot), + ) + } + if err := transition.DefaultMachine.ProcessSlots(baseState, targetSlot); err != nil { + return nil, err } - beaconBody, executionValue, err := a.produceBeaconBody(ctx, 3, sourceBlock.Block, baseState, targetSlot, randaoReveal, graffiti) + + beaconBody, executionValue, err := a.produceBeaconBody( + ctx, + 3, + sourceBlock.Block, + baseState, + targetSlot, + randaoReveal, + graffiti, + ) if err != nil { return nil, err } - proposerIndex, err := baseState.GetBeaconProposerIndexForSlot(targetSlot) + proposerIndex, err := baseState.GetBeaconProposerIndex() if err != nil { return nil, err } @@ -136,12 +188,18 @@ func (a *ApiHandler) GetEthV3ValidatorBlock(w http.ResponseWriter, r *http.Reque ParentRoot: baseBlockRoot, Body: beaconBody, } - log.Info("BlockProduction: Computing HashSSZ block", "slot", targetSlot, "execution_value", executionValue, "proposerIndex", proposerIndex) + log.Info( + "BlockProduction: Computing HashSSZ block", + "slot", + targetSlot, + "execution_value", + executionValue, + "proposerIndex", + proposerIndex, + ) // compute the state root now - if err := transition.TransitionState(baseState, &cltypes.SignedBeaconBlock{ - Block: block, - }, rewardsCollector, false); err != nil { + if err := machine.ProcessBlock(transition.DefaultMachine, baseState, &cltypes.SignedBeaconBlock{Block: block}); err != nil { return nil, err } block.StateRoot, err = baseState.HashSSZ() @@ -150,7 +208,13 @@ func (a *ApiHandler) GetEthV3ValidatorBlock(w http.ResponseWriter, r *http.Reque } consensusValue := rewardsCollector.Attestations + rewardsCollector.ProposerSlashings + rewardsCollector.AttesterSlashings + rewardsCollector.SyncAggregate isSSZBlinded := false - a.setupHeaderReponseForBlockProduction(w, block.Version(), isSSZBlinded, executionValue, consensusValue) + a.setupHeaderReponseForBlockProduction( + w, + block.Version(), + isSSZBlinded, + executionValue, + consensusValue, + ) return newBeaconResponse(block). With("execution_payload_blinded", isSSZBlinded). @@ -158,12 +222,26 @@ func (a *ApiHandler) GetEthV3ValidatorBlock(w http.ResponseWriter, r *http.Reque With("consensus_block_value", strconv.FormatUint(consensusValue, 10)), nil } -func (a *ApiHandler) produceBeaconBody(ctx context.Context, apiVersion int, baseBlock *cltypes.BeaconBlock, baseState *state.CachingBeaconState, targetSlot uint64, randaoReveal common.Bytes96, graffiti common.Hash) (*cltypes.BeaconBody, uint64, error) { +func (a *ApiHandler) produceBeaconBody( + ctx context.Context, + apiVersion int, + baseBlock *cltypes.BeaconBlock, + baseState *state.CachingBeaconState, + targetSlot uint64, + randaoReveal common.Bytes96, + graffiti common.Hash, +) (*cltypes.BeaconBody, uint64, error) { if targetSlot <= baseBlock.Slot { - return nil, 0, fmt.Errorf("target slot %d must be greater than base block slot %d", targetSlot, baseBlock.Slot) + return nil, 0, fmt.Errorf( + "target slot %d must be greater than base block slot %d", + targetSlot, + baseBlock.Slot, + ) } var wg sync.WaitGroup - stateVersion := a.beaconChainCfg.GetCurrentStateVersion(targetSlot / a.beaconChainCfg.SlotsPerEpoch) + stateVersion := a.beaconChainCfg.GetCurrentStateVersion( + targetSlot / a.beaconChainCfg.SlotsPerEpoch, + ) beaconBody := cltypes.NewBeaconBody(&clparams.MainnetBeaconConfig) // Setup body. beaconBody.RandaoReveal = randaoReveal @@ -200,7 +278,10 @@ func (a *ApiHandler) produceBeaconBody(ctx context.Context, apiVersion int, base secsDiff := (targetSlot - baseBlock.Slot) * a.beaconChainCfg.SecondsPerSlot feeRecipient, _ := a.validatorParams.GetFeeRecipient(proposerIndex) var withdrawals []*types.Withdrawal - clWithdrawals := state.ExpectedWithdrawals(baseState, targetSlot/a.beaconChainCfg.SlotsPerEpoch) + clWithdrawals := state.ExpectedWithdrawals( + baseState, + targetSlot/a.beaconChainCfg.SlotsPerEpoch, + ) for _, w := range clWithdrawals { withdrawals = append(withdrawals, &types.Withdrawal{ Index: w.Index, @@ -210,13 +291,18 @@ func (a *ApiHandler) produceBeaconBody(ctx context.Context, apiVersion int, base }) } - idBytes, err := a.engine.ForkChoiceUpdate(ctx, finalizedHash, head, &engine_types.PayloadAttributes{ - Timestamp: hexutil.Uint64(latestExecutionPayload.Time + secsDiff), - PrevRandao: random, - SuggestedFeeRecipient: feeRecipient, - Withdrawals: withdrawals, - ParentBeaconBlockRoot: (*libcommon.Hash)(&blockRoot), - }) + idBytes, err := a.engine.ForkChoiceUpdate( + ctx, + finalizedHash, + head, + &engine_types.PayloadAttributes{ + Timestamp: hexutil.Uint64(latestExecutionPayload.Time + secsDiff), + PrevRandao: random, + SuggestedFeeRecipient: feeRecipient, + Withdrawals: withdrawals, + ParentBeaconBlockRoot: (*libcommon.Hash)(&blockRoot), + }, + ) if err != nil { log.Error("BlockProduction: Failed to get payload id", "err", err) return @@ -246,7 +332,8 @@ func (a *ApiHandler) produceBeaconBody(ctx context.Context, apiVersion int, base executionValue = blockValue.Uint64() } - if len(bundles.Blobs) != len(bundles.Proofs) || len(bundles.Commitments) != len(bundles.Proofs) { + if len(bundles.Blobs) != len(bundles.Proofs) || + len(bundles.Commitments) != len(bundles.Proofs) { log.Error("BlockProduction: Invalid bundle") return } @@ -274,6 +361,7 @@ func (a *ApiHandler) produceBeaconBody(ctx context.Context, apiVersion int, base copy(c[:], bundles.Commitments[i]) beaconBody.BlobKzgCommitments.Append(&c) } + // Setup executionPayload executionPayload = cltypes.NewEth1Block(beaconBody.Version, a.beaconChainCfg) executionPayload.BlockHash = payload.BlockHash executionPayload.ParentHash = payload.ParentHash @@ -292,11 +380,16 @@ func (a *ApiHandler) produceBeaconBody(ctx context.Context, apiVersion int, base executionPayload.FeeRecipient = payload.FeeRecipient executionPayload.PrevRandao = payload.PrevRandao // Reset the limit of withdrawals - executionPayload.Withdrawals = solid.NewStaticListSSZ[*cltypes.Withdrawal](int(a.beaconChainCfg.MaxWithdrawalsPerPayload), 44) - payload.Withdrawals.Range(func(index int, value *cltypes.Withdrawal, length int) bool { - executionPayload.Withdrawals.Append(value) - return true - }) + executionPayload.Withdrawals = solid.NewStaticListSSZ[*cltypes.Withdrawal]( + int(a.beaconChainCfg.MaxWithdrawalsPerPayload), + 44, + ) + payload.Withdrawals.Range( + func(index int, value *cltypes.Withdrawal, length int) bool { + executionPayload.Withdrawals.Append(value) + return true + }, + ) executionPayload.Transactions = payload.Transactions return @@ -312,6 +405,17 @@ func (a *ApiHandler) produceBeaconBody(ctx context.Context, apiVersion int, base log.Error("BlockProduction: Failed to get sync aggregate", "err", err) } }() + // Process operations all in parallel with each other. + wg.Add(1) + go func() { + defer wg.Done() + beaconBody.AttesterSlashings, beaconBody.ProposerSlashings, beaconBody.VoluntaryExits, beaconBody.ExecutionChanges = a.getBlockOperations( + baseState, + targetSlot, + ) + beaconBody.Attestations = a.findBestAttestationsForBlockProduction(baseState) + }() + wg.Wait() if executionPayload == nil { return nil, 0, fmt.Errorf("failed to produce execution payload") @@ -320,7 +424,113 @@ func (a *ApiHandler) produceBeaconBody(ctx context.Context, apiVersion int, base return beaconBody, executionValue, nil } -func (a *ApiHandler) setupHeaderReponseForBlockProduction(w http.ResponseWriter, consensusVersion clparams.StateVersion, blinded bool, executionBlockValue, consensusBlockValue uint64) { +func (a *ApiHandler) getBlockOperations(s *state.CachingBeaconState, targetSlot uint64) ( + *solid.ListSSZ[*cltypes.AttesterSlashing], + *solid.ListSSZ[*cltypes.ProposerSlashing], + *solid.ListSSZ[*cltypes.SignedVoluntaryExit], + *solid.ListSSZ[*cltypes.SignedBLSToExecutionChange]) { + + attesterSlashings := solid.NewDynamicListSSZ[*cltypes.AttesterSlashing]( + int(a.beaconChainCfg.MaxAttesterSlashings), + ) + slashedIndicies := []uint64{} + // AttesterSlashings +AttLoop: + for _, slashing := range a.operationsPool.AttesterSlashingsPool.Raw() { + idxs := slashing.Attestation_1.AttestingIndices + rawIdxs := []uint64{} + for i := 0; i < idxs.Length(); i++ { + currentValidatorIndex := idxs.Get(i) + if slices.Contains(slashedIndicies, currentValidatorIndex) || slices.Contains(rawIdxs, currentValidatorIndex) { + continue AttLoop + } + v := s.ValidatorSet().Get(int(currentValidatorIndex)) + if !v.IsSlashable(targetSlot / a.beaconChainCfg.SlotsPerEpoch) { + continue AttLoop + } + rawIdxs = append(rawIdxs, currentValidatorIndex) + } + slashedIndicies = append(slashedIndicies, rawIdxs...) + attesterSlashings.Append(slashing) + if attesterSlashings.Len() >= int(a.beaconChainCfg.MaxAttesterSlashings) { + break + } + } + // ProposerSlashings + proposerSlashings := solid.NewStaticListSSZ[*cltypes.ProposerSlashing]( + int(a.beaconChainCfg.MaxProposerSlashings), + 416, + ) + for _, slashing := range a.operationsPool.ProposerSlashingsPool.Raw() { + proposerIndex := slashing.Header1.Header.ProposerIndex + if slices.Contains(slashedIndicies, proposerIndex) { + continue + } + v := s.ValidatorSet().Get(int(proposerIndex)) + if !v.IsSlashable(targetSlot / a.beaconChainCfg.SlotsPerEpoch) { + continue + } + proposerSlashings.Append(slashing) + slashedIndicies = append(slashedIndicies, proposerIndex) + if proposerSlashings.Len() >= int(a.beaconChainCfg.MaxProposerSlashings) { + break + } + } + // Voluntary Exits + voluntaryExits := solid.NewStaticListSSZ[*cltypes.SignedVoluntaryExit]( + int(a.beaconChainCfg.MaxVoluntaryExits), + 112, + ) + for _, exit := range a.operationsPool.VoluntaryExitsPool.Raw() { + if slices.Contains(slashedIndicies, exit.VoluntaryExit.ValidatorIndex) { + continue + } + if err := eth2.IsVoluntaryExitApplicable(s, exit.VoluntaryExit); err != nil { + continue // Not applicable right now, skip. + } + voluntaryExits.Append(exit) + slashedIndicies = append(slashedIndicies, exit.VoluntaryExit.ValidatorIndex) + if voluntaryExits.Len() >= int(a.beaconChainCfg.MaxVoluntaryExits) { + break + } + } + // BLS Executions Changes + blsToExecutionChanges := solid.NewStaticListSSZ[*cltypes.SignedBLSToExecutionChange]( + int(a.beaconChainCfg.MaxBlsToExecutionChanges), + 172, + ) + for _, blsExecutionChange := range a.operationsPool.BLSToExecutionChangesPool.Raw() { + if slices.Contains(slashedIndicies, blsExecutionChange.Message.ValidatorIndex) { + continue + } + if blsExecutionChange.Message.ValidatorIndex >= uint64(s.ValidatorLength()) { + continue + } + wc := s.ValidatorSet(). + Get(int(blsExecutionChange.Message.ValidatorIndex)). + WithdrawalCredentials() + // Check the validator's withdrawal credentials prefix. + if wc[0] != byte(a.beaconChainCfg.ETH1AddressWithdrawalPrefixByte) { + continue + } + + // Check the validator's withdrawal credentials against the provided message. + hashedFrom := utils.Sha256(blsExecutionChange.Message.From[:]) + if !bytes.Equal(hashedFrom[1:], wc[1:]) { + continue + } + blsToExecutionChanges.Append(blsExecutionChange) + slashedIndicies = append(slashedIndicies, blsExecutionChange.Message.ValidatorIndex) + } + return attesterSlashings, proposerSlashings, voluntaryExits, blsToExecutionChanges +} + +func (a *ApiHandler) setupHeaderReponseForBlockProduction( + w http.ResponseWriter, + consensusVersion clparams.StateVersion, + blinded bool, + executionBlockValue, consensusBlockValue uint64, +) { w.Header().Set("Eth-Execution-Payload-Value", strconv.FormatUint(executionBlockValue, 10)) w.Header().Set("Eth-Consensus-Block-Value", strconv.FormatUint(consensusBlockValue, 10)) w.Header().Set("Eth-Consensus-Version", clparams.ClVersionToString(consensusVersion)) @@ -359,7 +569,10 @@ func (a *ApiHandler) postBeaconBlocks(w http.ResponseWriter, r *http.Request, ap } -func (a *ApiHandler) parseEthConsensusVersion(str string, apiVersion int) (clparams.StateVersion, error) { +func (a *ApiHandler) parseEthConsensusVersion( + str string, + apiVersion int, +) (clparams.StateVersion, error) { if str == "" && apiVersion == 2 { return 0, fmt.Errorf("Eth-Consensus-Version header is required") } @@ -370,7 +583,11 @@ func (a *ApiHandler) parseEthConsensusVersion(str string, apiVersion int) (clpar return clparams.StringToClVersion(str) } -func (a *ApiHandler) parseBlockPublishingValidation(w http.ResponseWriter, r *http.Request, apiVersion int) BlockPublishingValidation { +func (a *ApiHandler) parseBlockPublishingValidation( + w http.ResponseWriter, + r *http.Request, + apiVersion int, +) BlockPublishingValidation { str := r.URL.Query().Get("broadcast_validation") if apiVersion == 1 || str == string(BlockPublishingValidationGossip) { return BlockPublishingValidationGossip @@ -379,7 +596,10 @@ func (a *ApiHandler) parseBlockPublishingValidation(w http.ResponseWriter, r *ht return BlockPublishingValidationConsensus } -func (a *ApiHandler) parseRequestBeaconBlock(version clparams.StateVersion, r *http.Request) (*cltypes.SignedBeaconBlock, error) { +func (a *ApiHandler) parseRequestBeaconBlock( + version clparams.StateVersion, + r *http.Request, +) (*cltypes.SignedBeaconBlock, error) { block := cltypes.NewSignedBeaconBlock(a.beaconChainCfg) block.Block.Body.Version = version // check content type @@ -445,7 +665,13 @@ func (a *ApiHandler) broadcastBlock(ctx context.Context, blk *cltypes.SignedBeac } }() - log.Info("BlockPublishing: publishing block and blobs", "slot", blk.Block.Slot, "blobs", len(blobsSidecars)) + log.Info( + "BlockPublishing: publishing block and blobs", + "slot", + blk.Block.Slot, + "blobs", + len(blobsSidecars), + ) // Broadcast the block and its blobs if _, err := a.sentinel.PublishGossip(ctx, &sentinel.GossipData{ Name: gossip.TopicNameBeaconBlock, @@ -468,7 +694,11 @@ func (a *ApiHandler) broadcastBlock(ctx context.Context, blk *cltypes.SignedBeac return nil } -func (a *ApiHandler) storeBlockAndBlobs(ctx context.Context, block *cltypes.SignedBeaconBlock, sidecars []*cltypes.BlobSidecar) error { +func (a *ApiHandler) storeBlockAndBlobs( + ctx context.Context, + block *cltypes.SignedBeaconBlock, + sidecars []*cltypes.BlobSidecar, +) error { blockRoot, err := block.Block.HashSSZ() if err != nil { return err @@ -487,3 +717,124 @@ func (a *ApiHandler) storeBlockAndBlobs(ctx context.Context, block *cltypes.Sign return a.forkchoiceStore.OnBlock(ctx, block, true, false, false) } + +type attestationCandidate struct { + attestation *solid.Attestation + reward uint64 +} + +func (a *ApiHandler) findBestAttestationsForBlockProduction( + s abstract.BeaconState, +) *solid.ListSSZ[*solid.Attestation] { + + ret := solid.NewDynamicListSSZ[*solid.Attestation](int(a.beaconChainCfg.MaxAttestations)) + attestationCandidates := []attestationCandidate{} + + for _, attestation := range a.operationsPool.AttestationsPool.Raw() { + if err := eth2.IsAttestationApplicable(s, attestation); err != nil { + continue // attestation not applicable skip + } + expectedReward, err := computeAttestationReward(s, attestation) + if err != nil { + log.Warn( + "[Block Production] Could not compute expected attestation reward", + "reason", + err, + ) + continue + } + if expectedReward == 0 { + continue + } + attestationCandidates = append(attestationCandidates, attestationCandidate{ + attestation: attestation, + reward: expectedReward, + }) + } + // Rank by reward in descending order. + sort.Slice(attestationCandidates, func(i, j int) bool { + return attestationCandidates[i].reward > attestationCandidates[j].reward + }) + // Some aggregates can be supersets of existing ones so let's filter out the supersets + // this MAP is HashTreeRoot(AttestationData) => AggregationBits + aggregationBitsByAttestationData := make(map[libcommon.Hash][]byte) + for _, candidate := range attestationCandidates { + // Check if it is a superset of a pre-included attestation with higher reward + attestationDataRoot, err := candidate.attestation.AttestantionData().HashSSZ() + if err != nil { + log.Warn("[Block Production] Cannot compute attestation data root", "err", err) + continue + } + currAggregationBits, exists := aggregationBitsByAttestationData[attestationDataRoot] + if exists { + if utils.IsNonStrictSupersetBitlist( + currAggregationBits, + candidate.attestation.AggregationBits(), + ) { + continue + } + utils.MergeBitlists(currAggregationBits, candidate.attestation.AggregationBits()) + } else { + currAggregationBits = candidate.attestation.AggregationBits() + } + // Update the currently built superset + aggregationBitsByAttestationData[attestationDataRoot] = currAggregationBits + + ret.Append(candidate.attestation) + if ret.Len() >= int(a.beaconChainCfg.MaxAttestations) { + break + } + } + return ret +} + +// computeAttestationReward computes the reward for a specific attestation. +func computeAttestationReward( + s abstract.BeaconState, + attestation *solid.Attestation) (uint64, error) { + + baseRewardPerIncrement := s.BaseRewardPerIncrement() + data := attestation.AttestantionData() + currentEpoch := state.Epoch(s) + stateSlot := s.Slot() + beaconConfig := s.BeaconConfig() + + participationFlagsIndicies, err := s.GetAttestationParticipationFlagIndicies( + data, + stateSlot-data.Slot(), + false, + ) + if err != nil { + return 0, err + } + attestingIndicies, err := s.GetAttestingIndicies(data, attestation.AggregationBits(), true) + if err != nil { + return 0, err + } + var proposerRewardNumerator uint64 + + isCurrentEpoch := data.Target().Epoch() == currentEpoch + + for _, attesterIndex := range attestingIndicies { + val, err := s.ValidatorEffectiveBalance(int(attesterIndex)) + if err != nil { + return 0, err + } + + baseReward := (val / beaconConfig.EffectiveBalanceIncrement) * baseRewardPerIncrement + for flagIndex, weight := range beaconConfig.ParticipationWeights() { + flagParticipation := s.EpochParticipationForValidatorIndex( + isCurrentEpoch, + int(attesterIndex), + ) + if !slices.Contains(participationFlagsIndicies, uint8(flagIndex)) || + flagParticipation.HasFlag(flagIndex) { + continue + } + proposerRewardNumerator += baseReward * weight + } + } + proposerRewardDenominator := (beaconConfig.WeightDenominator - beaconConfig.ProposerWeight) * beaconConfig.WeightDenominator / beaconConfig.ProposerWeight + reward := proposerRewardNumerator / proposerRewardDenominator + return reward, nil +} diff --git a/cl/phase1/core/state/cache_accessors.go b/cl/phase1/core/state/cache_accessors.go index 602bf727aef..e72bcec31d0 100644 --- a/cl/phase1/core/state/cache_accessors.go +++ b/cl/phase1/core/state/cache_accessors.go @@ -48,7 +48,11 @@ func (b *CachingBeaconState) GetTotalActiveBalance() uint64 { } // ComputeCommittee uses cache to compute compittee -func (b *CachingBeaconState) ComputeCommittee(indicies []uint64, slot uint64, index, count uint64) ([]uint64, error) { +func (b *CachingBeaconState) ComputeCommittee( + indicies []uint64, + slot uint64, + index, count uint64, +) ([]uint64, error) { lenIndicies := uint64(len(indicies)) start := (lenIndicies * index) / count end := (lenIndicies * (index + 1)) / count @@ -108,7 +112,6 @@ func (b *CachingBeaconState) GetBeaconProposerIndexForSlot(slot uint64) (uint64, // Write the seed to an array. seedArray := [32]byte{} copy(seedArray[:], seed) - b.proposerIndex = new(uint64) return shuffling2.ComputeProposerIndex(b.BeaconState, indices, seedArray) } @@ -152,7 +155,9 @@ func (b *CachingBeaconState) SyncRewards() (proposerReward, participantReward ui // CommitteeCount returns current number of committee for epoch. func (b *CachingBeaconState) CommitteeCount(epoch uint64) uint64 { - committeCount := uint64(len(b.GetActiveValidatorsIndices(epoch))) / b.BeaconConfig().SlotsPerEpoch / b.BeaconConfig().TargetCommitteeSize + committeCount := uint64( + len(b.GetActiveValidatorsIndices(epoch)), + ) / b.BeaconConfig().SlotsPerEpoch / b.BeaconConfig().TargetCommitteeSize if b.BeaconConfig().MaxCommitteesPerSlot < committeCount { committeCount = b.BeaconConfig().MaxCommitteesPerSlot } @@ -162,7 +167,11 @@ func (b *CachingBeaconState) CommitteeCount(epoch uint64) uint64 { return committeCount } -func (b *CachingBeaconState) GetAttestationParticipationFlagIndicies(data solid.AttestationData, inclusionDelay uint64, skipAssert bool) ([]uint8, error) { +func (b *CachingBeaconState) GetAttestationParticipationFlagIndicies( + data solid.AttestationData, + inclusionDelay uint64, + skipAssert bool, +) ([]uint8, error) { var justifiedCheckpoint solid.Checkpoint // get checkpoint from epoch @@ -187,16 +196,29 @@ func (b *CachingBeaconState) GetAttestationParticipationFlagIndicies(data solid. matchingHead := matchingTarget && data.BeaconBlockRoot() == headRoot participationFlagIndicies := []uint8{} if inclusionDelay <= utils.IntegerSquareRoot(b.BeaconConfig().SlotsPerEpoch) { - participationFlagIndicies = append(participationFlagIndicies, b.BeaconConfig().TimelySourceFlagIndex) + participationFlagIndicies = append( + participationFlagIndicies, + b.BeaconConfig().TimelySourceFlagIndex, + ) } - if b.Version() < clparams.DenebVersion && matchingTarget && inclusionDelay <= b.BeaconConfig().SlotsPerEpoch { - participationFlagIndicies = append(participationFlagIndicies, b.BeaconConfig().TimelyTargetFlagIndex) + if b.Version() < clparams.DenebVersion && matchingTarget && + inclusionDelay <= b.BeaconConfig().SlotsPerEpoch { + participationFlagIndicies = append( + participationFlagIndicies, + b.BeaconConfig().TimelyTargetFlagIndex, + ) } if b.Version() >= clparams.DenebVersion && matchingTarget { - participationFlagIndicies = append(participationFlagIndicies, b.BeaconConfig().TimelyTargetFlagIndex) + participationFlagIndicies = append( + participationFlagIndicies, + b.BeaconConfig().TimelyTargetFlagIndex, + ) } if matchingHead && inclusionDelay == b.BeaconConfig().MinAttestationInclusionDelay { - participationFlagIndicies = append(participationFlagIndicies, b.BeaconConfig().TimelyHeadFlagIndex) + participationFlagIndicies = append( + participationFlagIndicies, + b.BeaconConfig().TimelyHeadFlagIndex, + ) } return participationFlagIndicies, nil } @@ -283,14 +305,22 @@ func (b *CachingBeaconState) ComputeNextSyncCommittee() (*solid.SyncCommittee, e // GetAttestingIndicies retrieves attesting indicies for a specific attestation. however some tests will not expect the aggregation bits check. // thus, it is a flag now. -func (b *CachingBeaconState) GetAttestingIndicies(attestation solid.AttestationData, aggregationBits []byte, checkBitsLength bool) ([]uint64, error) { +func (b *CachingBeaconState) GetAttestingIndicies( + attestation solid.AttestationData, + aggregationBits []byte, + checkBitsLength bool, +) ([]uint64, error) { committee, err := b.GetBeaconCommitee(attestation.Slot(), attestation.CommitteeIndex()) if err != nil { return nil, err } aggregationBitsLen := utils.GetBitlistLength(aggregationBits) if checkBitsLength && utils.GetBitlistLength(aggregationBits) != len(committee) { - return nil, fmt.Errorf("GetAttestingIndicies: invalid aggregation bits. agg bits size: %d, expect: %d", aggregationBitsLen, len(committee)) + return nil, fmt.Errorf( + "GetAttestingIndicies: invalid aggregation bits. agg bits size: %d, expect: %d", + aggregationBitsLen, + len(committee), + ) } attestingIndices := []uint64{} @@ -310,13 +340,19 @@ func (b *CachingBeaconState) GetAttestingIndicies(attestation solid.AttestationD // See: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#get_validator_churn_limit func (b *CachingBeaconState) GetValidatorChurnLimit() uint64 { activeIndsCount := uint64(len(b.GetActiveValidatorsIndices(Epoch(b)))) - return utils.Max64(activeIndsCount/b.BeaconConfig().ChurnLimitQuotient, b.BeaconConfig().MinPerEpochChurnLimit) + return utils.Max64( + activeIndsCount/b.BeaconConfig().ChurnLimitQuotient, + b.BeaconConfig().MinPerEpochChurnLimit, + ) } // https://github.com/ethereum/consensus-specs/blob/dev/specs/deneb/beacon-chain.md#new-get_validator_activation_churn_limit func (b *CachingBeaconState) GetValidatorActivationChurnLimit() uint64 { if b.Version() >= clparams.DenebVersion { - return utils.Min64(b.BeaconConfig().MaxPerEpochActivationChurnLimit, b.GetValidatorChurnLimit()) + return utils.Min64( + b.BeaconConfig().MaxPerEpochActivationChurnLimit, + b.GetValidatorChurnLimit(), + ) } return b.GetValidatorChurnLimit() } diff --git a/cl/transition/impl/eth2/operations.go b/cl/transition/impl/eth2/operations.go index 9e56e991e44..84e2b09e915 100644 --- a/cl/transition/impl/eth2/operations.go +++ b/cl/transition/impl/eth2/operations.go @@ -26,7 +26,10 @@ import ( "github.com/ledgerwatch/erigon/cl/utils" ) -func (I *impl) ProcessProposerSlashing(s abstract.BeaconState, propSlashing *cltypes.ProposerSlashing) error { +func (I *impl) ProcessProposerSlashing( + s abstract.BeaconState, + propSlashing *cltypes.ProposerSlashing, +) error { h1 := propSlashing.Header1.Header h2 := propSlashing.Header2.Header @@ -35,7 +38,11 @@ func (I *impl) ProcessProposerSlashing(s abstract.BeaconState, propSlashing *clt } if h1.ProposerIndex != h2.ProposerIndex { - return fmt.Errorf("non-matching proposer indices proposer slashing: %d != %d", h1.ProposerIndex, h2.ProposerIndex) + return fmt.Errorf( + "non-matching proposer indices proposer slashing: %d != %d", + h1.ProposerIndex, + h2.ProposerIndex, + ) } if *h1 == *h2 { @@ -51,7 +58,10 @@ func (I *impl) ProcessProposerSlashing(s abstract.BeaconState, propSlashing *clt } for _, signedHeader := range []*cltypes.SignedBeaconBlockHeader{propSlashing.Header1, propSlashing.Header2} { - domain, err := s.GetDomain(s.BeaconConfig().DomainBeaconProposer, state.GetEpochAtSlot(s.BeaconConfig(), signedHeader.Header.Slot)) + domain, err := s.GetDomain( + s.BeaconConfig().DomainBeaconProposer, + state.GetEpochAtSlot(s.BeaconConfig(), signedHeader.Header.Slot), + ) if err != nil { return fmt.Errorf("unable to get domain: %v", err) } @@ -65,7 +75,12 @@ func (I *impl) ProcessProposerSlashing(s abstract.BeaconState, propSlashing *clt return fmt.Errorf("unable to verify signature: %v", err) } if !valid { - return fmt.Errorf("invalid signature: signature %v, root %v, pubkey %v", signedHeader.Signature[:], signingRoot[:], pk) + return fmt.Errorf( + "invalid signature: signature %v, root %v, pubkey %v", + signedHeader.Signature[:], + signingRoot[:], + pk, + ) } } @@ -77,7 +92,10 @@ func (I *impl) ProcessProposerSlashing(s abstract.BeaconState, propSlashing *clt return err } -func (I *impl) ProcessAttesterSlashing(s abstract.BeaconState, attSlashing *cltypes.AttesterSlashing) error { +func (I *impl) ProcessAttesterSlashing( + s abstract.BeaconState, + attSlashing *cltypes.AttesterSlashing, +) error { att1 := attSlashing.Attestation_1 att2 := attSlashing.Attestation_2 @@ -162,7 +180,11 @@ func (I *impl) ProcessDeposit(s abstract.BeaconState, deposit *cltypes.Deposit) validatorIndex, has := s.ValidatorIndexByPubkey(publicKey) if !has { // Agnostic domain. - domain, err := fork.ComputeDomain(s.BeaconConfig().DomainDeposit[:], utils.Uint32ToBytes4(uint32(s.BeaconConfig().GenesisForkVersion)), [32]byte{}) + domain, err := fork.ComputeDomain( + s.BeaconConfig().DomainDeposit[:], + utils.Uint32ToBytes4(uint32(s.BeaconConfig().GenesisForkVersion)), + [32]byte{}, + ) if err != nil { return err } @@ -192,10 +214,7 @@ func (I *impl) ProcessDeposit(s abstract.BeaconState, deposit *cltypes.Deposit) return state.IncreaseBalance(s, validatorIndex, amount) } -// ProcessVoluntaryExit takes a voluntary exit and applies state transition. -func (I *impl) ProcessVoluntaryExit(s abstract.BeaconState, signedVoluntaryExit *cltypes.SignedVoluntaryExit) error { - // Sanity checks so that we know it is good. - voluntaryExit := signedVoluntaryExit.VoluntaryExit +func IsVoluntaryExitApplicable(s abstract.BeaconState, voluntaryExit *cltypes.VoluntaryExit) error { currentEpoch := state.Epoch(s) validator, err := s.ValidatorForValidatorIndex(int(voluntaryExit.ValidatorIndex)) if err != nil { @@ -205,7 +224,9 @@ func (I *impl) ProcessVoluntaryExit(s abstract.BeaconState, signedVoluntaryExit return errors.New("ProcessVoluntaryExit: validator is not active") } if validator.ExitEpoch() != s.BeaconConfig().FarFutureEpoch { - return errors.New("ProcessVoluntaryExit: another exit for the same validator is already getting processed") + return errors.New( + "ProcessVoluntaryExit: another exit for the same validator is already getting processed", + ) } if currentEpoch < voluntaryExit.Epoch { return errors.New("ProcessVoluntaryExit: exit is happening in the future") @@ -213,6 +234,24 @@ func (I *impl) ProcessVoluntaryExit(s abstract.BeaconState, signedVoluntaryExit if currentEpoch < validator.ActivationEpoch()+s.BeaconConfig().ShardCommitteePeriod { return errors.New("ProcessVoluntaryExit: exit is happening too fast") } + return nil +} + +// ProcessVoluntaryExit takes a voluntary exit and applies state transition. +func (I *impl) ProcessVoluntaryExit( + s abstract.BeaconState, + signedVoluntaryExit *cltypes.SignedVoluntaryExit, +) error { + // Sanity checks so that we know it is good. + voluntaryExit := signedVoluntaryExit.VoluntaryExit + err := IsVoluntaryExitApplicable(s, voluntaryExit) + if err != nil { + return err + } + validator, err := s.ValidatorForValidatorIndex(int(voluntaryExit.ValidatorIndex)) + if err != nil { + return err + } // We can skip it in some instances if we want to optimistically sync up. if I.FullValidation { @@ -244,7 +283,10 @@ func (I *impl) ProcessVoluntaryExit(s abstract.BeaconState, signedVoluntaryExit // ProcessWithdrawals processes withdrawals by decreasing the balance of each validator // and updating the next withdrawal index and validator index. -func (I *impl) ProcessWithdrawals(s abstract.BeaconState, withdrawals *solid.ListSSZ[*cltypes.Withdrawal]) error { +func (I *impl) ProcessWithdrawals( + s abstract.BeaconState, + withdrawals *solid.ListSSZ[*cltypes.Withdrawal], +) error { // Get the list of withdrawals, the expected withdrawals (if performing full validation), // and the beacon configuration. beaconConfig := s.BeaconConfig() @@ -254,7 +296,11 @@ func (I *impl) ProcessWithdrawals(s abstract.BeaconState, withdrawals *solid.Lis if I.FullValidation { expectedWithdrawals := state.ExpectedWithdrawals(s, state.Epoch(s)) if len(expectedWithdrawals) != withdrawals.Len() { - return fmt.Errorf("ProcessWithdrawals: expected %d withdrawals, but got %d", len(expectedWithdrawals), withdrawals.Len()) + return fmt.Errorf( + "ProcessWithdrawals: expected %d withdrawals, but got %d", + len(expectedWithdrawals), + withdrawals.Len(), + ) } if err := solid.RangeErr[*cltypes.Withdrawal](withdrawals, func(i int, w *cltypes.Withdrawal, _ int) error { if *expectedWithdrawals[i] != *w { @@ -301,7 +347,11 @@ func (I *impl) ProcessExecutionPayload(s abstract.BeaconState, payload *cltypes. } } if payload.PrevRandao != s.GetRandaoMixes(state.Epoch(s)) { - return fmt.Errorf("ProcessExecutionPayload: randao mix mismatches with mix digest, expected %x, got %x", s.GetRandaoMixes(state.Epoch(s)), payload.PrevRandao) + return fmt.Errorf( + "ProcessExecutionPayload: randao mix mismatches with mix digest, expected %x, got %x", + s.GetRandaoMixes(state.Epoch(s)), + payload.PrevRandao, + ) } if payload.Time != state.ComputeTimestampAtSlot(s, s.Slot()) { return fmt.Errorf("ProcessExecutionPayload: invalid Eth1 timestamp") @@ -322,7 +372,12 @@ func (I *impl) ProcessSyncAggregate(s abstract.BeaconState, sync *cltypes.SyncAg if I.FullValidation { previousSlot := s.PreviousSlot() - domain, err := fork.Domain(s.Fork(), state.GetEpochAtSlot(s.BeaconConfig(), previousSlot), s.BeaconConfig().DomainSyncCommittee, s.GenesisValidatorsRoot()) + domain, err := fork.Domain( + s.Fork(), + state.GetEpochAtSlot(s.BeaconConfig(), previousSlot), + s.BeaconConfig().DomainSyncCommittee, + s.GenesisValidatorsRoot(), + ) if err != nil { return nil } @@ -345,7 +400,10 @@ func (I *impl) ProcessSyncAggregate(s abstract.BeaconState, sync *cltypes.SyncAg // processSyncAggregate applies all the logic in the spec function `process_sync_aggregate` except // verifying the BLS signatures. It returns the modified beacons state and the list of validators' // public keys that voted, for future signature verification. -func (I *impl) processSyncAggregate(s abstract.BeaconState, sync *cltypes.SyncAggregate) ([][]byte, error) { +func (I *impl) processSyncAggregate( + s abstract.BeaconState, + sync *cltypes.SyncAggregate, +) ([][]byte, error) { currentSyncCommittee := s.CurrentSyncCommittee() if currentSyncCommittee == nil { @@ -375,7 +433,10 @@ func (I *impl) processSyncAggregate(s abstract.BeaconState, sync *cltypes.SyncAg vIdx, exists := s.ValidatorIndexByPubkey(committeeKeys[currPubKeyIndex]) // Impossible scenario. if !exists { - return nil, fmt.Errorf("validator public key does not exist in state: %x", committeeKeys[currPubKeyIndex]) + return nil, fmt.Errorf( + "validator public key does not exist in state: %x", + committeeKeys[currPubKeyIndex], + ) } if syncAggregateBits[i]&byte(bit) > 0 { votedKeys = append(votedKeys, committeeKeys[currPubKeyIndex][:]) @@ -399,7 +460,10 @@ func (I *impl) processSyncAggregate(s abstract.BeaconState, sync *cltypes.SyncAg } // ProcessBlsToExecutionChange processes a BLSToExecutionChange message by updating a validator's withdrawal credentials. -func (I *impl) ProcessBlsToExecutionChange(s abstract.BeaconState, signedChange *cltypes.SignedBLSToExecutionChange) error { +func (I *impl) ProcessBlsToExecutionChange( + s abstract.BeaconState, + signedChange *cltypes.SignedBLSToExecutionChange, +) error { change := signedChange.Message beaconConfig := s.BeaconConfig() @@ -423,7 +487,11 @@ func (I *impl) ProcessBlsToExecutionChange(s abstract.BeaconState, signedChange } // Compute the signing domain and verify the message signature. - domain, err := fork.ComputeDomain(beaconConfig.DomainBLSToExecutionChange[:], utils.Uint32ToBytes4(uint32(beaconConfig.GenesisForkVersion)), s.GenesisValidatorsRoot()) + domain, err := fork.ComputeDomain( + beaconConfig.DomainBLSToExecutionChange[:], + utils.Uint32ToBytes4(uint32(beaconConfig.GenesisForkVersion)), + s.GenesisValidatorsRoot(), + ) if err != nil { return err } @@ -450,7 +518,10 @@ func (I *impl) ProcessBlsToExecutionChange(s abstract.BeaconState, signedChange return nil } -func (I *impl) ProcessAttestations(s abstract.BeaconState, attestations *solid.ListSSZ[*solid.Attestation]) error { +func (I *impl) ProcessAttestations( + s abstract.BeaconState, + attestations *solid.ListSSZ[*solid.Attestation], +) error { attestingIndiciesSet := make([][]uint64, attestations.Len()) h := metrics.NewHistTimer("beacon_process_attestations") baseRewardPerIncrement := s.BaseRewardPerIncrement() @@ -485,7 +556,11 @@ func (I *impl) ProcessAttestations(s abstract.BeaconState, attestations *solid.L return nil } -func (I *impl) processAttestationPostAltair(s abstract.BeaconState, attestation *solid.Attestation, baseRewardPerIncrement uint64) ([]uint64, error) { +func (I *impl) processAttestationPostAltair( + s abstract.BeaconState, + attestation *solid.Attestation, + baseRewardPerIncrement uint64, +) ([]uint64, error) { data := attestation.AttestantionData() currentEpoch := state.Epoch(s) stateSlot := s.Slot() @@ -494,7 +569,11 @@ func (I *impl) processAttestationPostAltair(s abstract.BeaconState, attestation h := metrics.NewHistTimer("beacon_process_attestation_post_altair") c := h.Tag("step", "get_participation_flag") - participationFlagsIndicies, err := s.GetAttestationParticipationFlagIndicies(data, stateSlot-data.Slot(), false) + participationFlagsIndicies, err := s.GetAttestationParticipationFlagIndicies( + data, + stateSlot-data.Slot(), + false, + ) if err != nil { return nil, err } @@ -522,11 +601,19 @@ func (I *impl) processAttestationPostAltair(s abstract.BeaconState, attestation baseReward := (val / beaconConfig.EffectiveBalanceIncrement) * baseRewardPerIncrement for flagIndex, weight := range beaconConfig.ParticipationWeights() { - flagParticipation := s.EpochParticipationForValidatorIndex(isCurrentEpoch, int(attesterIndex)) - if !slices.Contains(participationFlagsIndicies, uint8(flagIndex)) || flagParticipation.HasFlag(flagIndex) { + flagParticipation := s.EpochParticipationForValidatorIndex( + isCurrentEpoch, + int(attesterIndex), + ) + if !slices.Contains(participationFlagsIndicies, uint8(flagIndex)) || + flagParticipation.HasFlag(flagIndex) { continue } - s.SetEpochParticipationForValidatorIndex(isCurrentEpoch, int(attesterIndex), flagParticipation.Add(flagIndex)) + s.SetEpochParticipationForValidatorIndex( + isCurrentEpoch, + int(attesterIndex), + flagParticipation.Add(flagIndex), + ) proposerRewardNumerator += baseReward * weight } } @@ -547,7 +634,10 @@ func (I *impl) processAttestationPostAltair(s abstract.BeaconState, attestation } // processAttestationsPhase0 implements the rules for phase0 processing. -func (I *impl) processAttestationPhase0(s abstract.BeaconState, attestation *solid.Attestation) ([]uint64, error) { +func (I *impl) processAttestationPhase0( + s abstract.BeaconState, + attestation *solid.Attestation, +) ([]uint64, error) { data := attestation.AttestantionData() committee, err := s.GetBeaconCommitee(data.Slot(), data.CommitteeIndex()) if err != nil { @@ -584,7 +674,11 @@ func (I *impl) processAttestationPhase0(s abstract.BeaconState, attestation *sol s.AddPreviousEpochAttestation(pendingAttestation) } // Not required by specs but needed if we want performant epoch transition. - indicies, err := s.GetAttestingIndicies(attestation.AttestantionData(), attestation.AggregationBits(), true) + indicies, err := s.GetAttestingIndicies( + attestation.AttestantionData(), + attestation.AggregationBits(), + true, + ) if err != nil { return nil, err } @@ -598,12 +692,16 @@ func (I *impl) processAttestationPhase0(s abstract.BeaconState, attestation *sol } // Basically we flag all validators we are currently attesting. will be important for rewards/finalization processing. for _, index := range indicies { - minCurrentInclusionDelayAttestation, err := s.ValidatorMinCurrentInclusionDelayAttestation(int(index)) + minCurrentInclusionDelayAttestation, err := s.ValidatorMinCurrentInclusionDelayAttestation( + int(index), + ) if err != nil { return nil, err } - minPreviousInclusionDelayAttestation, err := s.ValidatorMinPreviousInclusionDelayAttestation(int(index)) + minPreviousInclusionDelayAttestation, err := s.ValidatorMinPreviousInclusionDelayAttestation( + int(index), + ) if err != nil { return nil, err } @@ -657,25 +755,40 @@ func (I *impl) processAttestationPhase0(s abstract.BeaconState, attestation *sol return indicies, nil } -// ProcessAttestation takes an attestation and process it. -func (I *impl) processAttestation(s abstract.BeaconState, attestation *solid.Attestation, baseRewardPerIncrement uint64) ([]uint64, error) { +func IsAttestationApplicable(s abstract.BeaconState, attestation *solid.Attestation) error { data := attestation.AttestantionData() currentEpoch := state.Epoch(s) previousEpoch := state.PreviousEpoch(s) stateSlot := s.Slot() beaconConfig := s.BeaconConfig() // Prelimary checks. - if (data.Target().Epoch() != currentEpoch && data.Target().Epoch() != previousEpoch) || data.Target().Epoch() != state.GetEpochAtSlot(s.BeaconConfig(), data.Slot()) { - return nil, errors.New("ProcessAttestation: attestation with invalid epoch") + if (data.Target().Epoch() != currentEpoch && data.Target().Epoch() != previousEpoch) || + data.Target().Epoch() != state.GetEpochAtSlot(s.BeaconConfig(), data.Slot()) { + return errors.New("ProcessAttestation: attestation with invalid epoch") } - if s.Version() < clparams.DenebVersion && ((data.Slot()+beaconConfig.MinAttestationInclusionDelay > stateSlot) || (stateSlot > data.Slot()+beaconConfig.SlotsPerEpoch)) { - return nil, errors.New("ProcessAttestation: attestation slot not in range") + if s.Version() < clparams.DenebVersion && + ((data.Slot()+beaconConfig.MinAttestationInclusionDelay > stateSlot) || (stateSlot > data.Slot()+beaconConfig.SlotsPerEpoch)) { + return errors.New("ProcessAttestation: attestation slot not in range") } - if s.Version() >= clparams.DenebVersion && data.Slot()+beaconConfig.MinAttestationInclusionDelay > stateSlot { - return nil, errors.New("ProcessAttestation: attestation slot not in range") + if s.Version() >= clparams.DenebVersion && + data.Slot()+beaconConfig.MinAttestationInclusionDelay > stateSlot { + return errors.New("ProcessAttestation: attestation slot not in range") } if data.CommitteeIndex() >= s.CommitteeCount(data.Target().Epoch()) { - return nil, errors.New("ProcessAttestation: attester index out of range") + return errors.New("ProcessAttestation: attester index out of range") + } + return nil +} + +// ProcessAttestation takes an attestation and process it. +func (I *impl) processAttestation( + s abstract.BeaconState, + attestation *solid.Attestation, + baseRewardPerIncrement uint64, +) ([]uint64, error) { + // Prelimary checks. + if err := IsAttestationApplicable(s, attestation); err != nil { + return nil, err } // check if we need to use rules for phase0 or post-altair. if s.Version() == clparams.Phase0Version { @@ -684,7 +797,11 @@ func (I *impl) processAttestation(s abstract.BeaconState, attestation *solid.Att return I.processAttestationPostAltair(s, attestation, baseRewardPerIncrement) } -func verifyAttestations(s abstract.BeaconState, attestations *solid.ListSSZ[*solid.Attestation], attestingIndicies [][]uint64) (bool, error) { +func verifyAttestations( + s abstract.BeaconState, + attestations *solid.ListSSZ[*solid.Attestation], + attestingIndicies [][]uint64, +) (bool, error) { indexedAttestations := make([]*cltypes.IndexedAttestation, 0, attestations.Len()) commonBuffer := make([]byte, 8*2048) attestations.Range(func(idx int, a *solid.Attestation, _ int) bool { @@ -704,7 +821,10 @@ type indexedAttestationVerificationResult struct { } // Concurrent verification of BLS. -func batchVerifyAttestations(s abstract.BeaconState, indexedAttestations []*cltypes.IndexedAttestation) (valid bool, err error) { +func batchVerifyAttestations( + s abstract.BeaconState, + indexedAttestations []*cltypes.IndexedAttestation, +) (valid bool, err error) { c := make(chan indexedAttestationVerificationResult, 1) for idx := range indexedAttestations { @@ -733,14 +853,22 @@ func (I *impl) ProcessBlockHeader(s abstract.BeaconState, block *cltypes.BeaconB return fmt.Errorf("state slot: %d, not equal to block slot: %d", s.Slot(), block.Slot) } if block.Slot <= s.LatestBlockHeader().Slot { - return fmt.Errorf("slock slot: %d, not greater than latest block slot: %d", block.Slot, s.LatestBlockHeader().Slot) + return fmt.Errorf( + "slock slot: %d, not greater than latest block slot: %d", + block.Slot, + s.LatestBlockHeader().Slot, + ) } propInd, err := s.GetBeaconProposerIndex() if err != nil { return fmt.Errorf("error in GetBeaconProposerIndex: %v", err) } if block.ProposerIndex != propInd { - return fmt.Errorf("block proposer index: %d, does not match beacon proposer index: %d", block.ProposerIndex, propInd) + return fmt.Errorf( + "block proposer index: %d, does not match beacon proposer index: %d", + block.ProposerIndex, + propInd, + ) } blockHeader := s.LatestBlockHeader() latestRoot, err := (&blockHeader).HashSSZ() @@ -748,7 +876,11 @@ func (I *impl) ProcessBlockHeader(s abstract.BeaconState, block *cltypes.BeaconB return fmt.Errorf("unable to hash tree root of latest block header: %v", err) } if block.ParentRoot != latestRoot { - return fmt.Errorf("block parent root: %x, does not match latest block root: %x", block.ParentRoot, latestRoot) + return fmt.Errorf( + "block parent root: %x, does not match latest block root: %x", + block.ParentRoot, + latestRoot, + ) } bodyRoot, err := block.Body.HashSSZ() @@ -790,10 +922,21 @@ func (I *impl) ProcessRandao(s abstract.BeaconState, randao [96]byte, proposerIn pk := proposer.PublicKey() valid, err := bls.Verify(randao[:], signingRoot[:], pk[:]) if err != nil { - return fmt.Errorf("ProcessRandao: unable to verify public key: %x, with signing root: %x, and signature: %x, %v", pk[:], signingRoot[:], randao[:], err) + return fmt.Errorf( + "ProcessRandao: unable to verify public key: %x, with signing root: %x, and signature: %x, %v", + pk[:], + signingRoot[:], + randao[:], + err, + ) } if !valid { - return fmt.Errorf("ProcessRandao: invalid signature: public key: %x, signing root: %x, signature: %x", pk[:], signingRoot[:], randao[:]) + return fmt.Errorf( + "ProcessRandao: invalid signature: public key: %x, signing root: %x, signature: %x", + pk[:], + signingRoot[:], + randao[:], + ) } } @@ -844,7 +987,13 @@ func (I *impl) ProcessSlots(s abstract.BeaconState, slot uint64) error { if err := statechange.ProcessEpoch(s); err != nil { return err } - log.Trace("Processed new epoch successfully", "epoch", state.Epoch(s), "process_epoch_elpsed", time.Since(start)) + log.Trace( + "Processed new epoch successfully", + "epoch", + state.Epoch(s), + "process_epoch_elpsed", + time.Since(start), + ) } sSlot += 1