From 26a3193b62c1d0d159dc40d43ca6be76132de324 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Wed, 26 Feb 2025 08:13:20 -0700 Subject: [PATCH 1/4] Calculate max epoch and churn for slashing once --- beacon-chain/core/blocks/attester_slashing.go | 8 ++++++-- beacon-chain/core/blocks/proposer_slashing.go | 4 +++- beacon-chain/core/validators/validator.go | 1 - 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/beacon-chain/core/blocks/attester_slashing.go b/beacon-chain/core/blocks/attester_slashing.go index f1b96e8a157f..bcd4ff6809a0 100644 --- a/beacon-chain/core/blocks/attester_slashing.go +++ b/beacon-chain/core/blocks/attester_slashing.go @@ -6,6 +6,7 @@ import ( "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/container/slice" @@ -42,8 +43,9 @@ func ProcessAttesterSlashings( slashFunc slashValidatorFunc, ) (state.BeaconState, error) { var err error + maxExitEpoch, churn := validators.MaxExitEpochAndChurn(s) for _, slashing := range slashings { - beaconState, err = ProcessAttesterSlashing(ctx, beaconState, slashing, slashFunc) + beaconState, err = ProcessAttesterSlashing(ctx, beaconState, slashing, slashFunc, maxExitEpoch, churn) if err != nil { return nil, err } @@ -57,6 +59,8 @@ func ProcessAttesterSlashing( beaconState state.BeaconState, slashing ethpb.AttSlashing, slashFunc slashValidatorFunc, + maxEpoch primitives.Epoch, + churn uint64, ) (state.BeaconState, error) { if err := VerifyAttesterSlashing(ctx, beaconState, slashing); err != nil { return nil, errors.Wrap(err, "could not verify attester slashing") @@ -75,7 +79,7 @@ func ProcessAttesterSlashing( return nil, err } if helpers.IsSlashableValidator(val.ActivationEpoch(), val.WithdrawableEpoch(), val.Slashed(), currentEpoch) { - beaconState, err = slashFunc(ctx, beaconState, primitives.ValidatorIndex(validatorIndex)) + beaconState, err = slashFunc(ctx, beaconState, primitives.ValidatorIndex(validatorIndex), maxEpoch, churn) if err != nil { return nil, errors.Wrapf(err, "could not slash validator index %d", validatorIndex) diff --git a/beacon-chain/core/blocks/proposer_slashing.go b/beacon-chain/core/blocks/proposer_slashing.go index 85b3b99010bc..9da947492f8c 100644 --- a/beacon-chain/core/blocks/proposer_slashing.go +++ b/beacon-chain/core/blocks/proposer_slashing.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/pion/rtcp" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing" @@ -19,7 +20,8 @@ import ( type slashValidatorFunc func( ctx context.Context, st state.BeaconState, - vid primitives.ValidatorIndex) (state.BeaconState, error) + vid primitives.ValidatorIndex, + maxEpoch primitives.Epoch, churn uint64) (state.BeaconState, error) // ProcessProposerSlashings is one of the operations performed // on each processed beacon block to slash proposers based on diff --git a/beacon-chain/core/validators/validator.go b/beacon-chain/core/validators/validator.go index a68264d9c962..01d2d0934eef 100644 --- a/beacon-chain/core/validators/validator.go +++ b/beacon-chain/core/validators/validator.go @@ -153,7 +153,6 @@ func SlashValidator( ctx context.Context, s state.BeaconState, slashedIdx primitives.ValidatorIndex) (state.BeaconState, error) { - maxExitEpoch, churn := MaxExitEpochAndChurn(s) s, _, err := InitiateValidatorExit(ctx, s, slashedIdx, maxExitEpoch, churn) if err != nil && !errors.Is(err, ErrValidatorAlreadyExited) { return nil, errors.Wrapf(err, "could not initiate validator %d exit", slashedIdx) From 9a90cdb9c69b6392c3bbc26a4f529e0f315b5e9e Mon Sep 17 00:00:00 2001 From: rkapka Date: Wed, 26 Feb 2025 16:58:00 +0100 Subject: [PATCH 2/4] calculate once for proposer and attester slashings --- beacon-chain/core/blocks/attester_slashing.go | 4 ++-- .../core/blocks/attester_slashing_test.go | 9 +++++--- .../core/blocks/block_operations_fuzz_test.go | 6 ++++-- .../core/blocks/block_regression_test.go | 3 ++- beacon-chain/core/blocks/proposer_slashing.go | 13 ++++++++---- .../core/blocks/proposer_slashing_test.go | 21 ++++++++++++------- .../core/electra/transition_no_verify_sig.go | 5 +++-- .../transition/transition_no_verify_sig.go | 10 +++++---- beacon-chain/core/validators/validator.go | 4 +++- .../core/validators/validator_test.go | 6 ++++-- beacon-chain/rpc/eth/rewards/service.go | 5 +++-- .../v1alpha1/validator/proposer_slashings.go | 14 ++++++++++--- .../common/operations/attester_slashing.go | 3 ++- .../common/operations/proposer_slashing.go | 3 ++- 14 files changed, 71 insertions(+), 35 deletions(-) diff --git a/beacon-chain/core/blocks/attester_slashing.go b/beacon-chain/core/blocks/attester_slashing.go index bcd4ff6809a0..2e10b9daafd8 100644 --- a/beacon-chain/core/blocks/attester_slashing.go +++ b/beacon-chain/core/blocks/attester_slashing.go @@ -6,7 +6,6 @@ import ( "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" - "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/container/slice" @@ -41,9 +40,10 @@ func ProcessAttesterSlashings( beaconState state.BeaconState, slashings []ethpb.AttSlashing, slashFunc slashValidatorFunc, + maxExitEpoch primitives.Epoch, + churn uint64, ) (state.BeaconState, error) { var err error - maxExitEpoch, churn := validators.MaxExitEpochAndChurn(s) for _, slashing := range slashings { beaconState, err = ProcessAttesterSlashing(ctx, beaconState, slashing, slashFunc, maxExitEpoch, churn) if err != nil { diff --git a/beacon-chain/core/blocks/attester_slashing_test.go b/beacon-chain/core/blocks/attester_slashing_test.go index e6be851117f3..6acdee235c45 100644 --- a/beacon-chain/core/blocks/attester_slashing_test.go +++ b/beacon-chain/core/blocks/attester_slashing_test.go @@ -63,7 +63,8 @@ func TestProcessAttesterSlashings_DataNotSlashable(t *testing.T) { for i, s := range b.Block.Body.AttesterSlashings { ss[i] = s } - _, err = blocks.ProcessAttesterSlashings(context.Background(), beaconState, ss, v.SlashValidator) + maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) + _, err = blocks.ProcessAttesterSlashings(context.Background(), beaconState, ss, v.SlashValidator, maxExitEpoch, churn) assert.ErrorContains(t, "attestations are not slashable", err) } @@ -102,7 +103,8 @@ func TestProcessAttesterSlashings_IndexedAttestationFailedToVerify(t *testing.T) for i, s := range b.Block.Body.AttesterSlashings { ss[i] = s } - _, err = blocks.ProcessAttesterSlashings(context.Background(), beaconState, ss, v.SlashValidator) + maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) + _, err = blocks.ProcessAttesterSlashings(context.Background(), beaconState, ss, v.SlashValidator, maxExitEpoch, churn) assert.ErrorContains(t, "validator indices count exceeds MAX_VALIDATORS_PER_COMMITTEE", err) } @@ -244,7 +246,8 @@ func TestProcessAttesterSlashings_AppliesCorrectStatus(t *testing.T) { currentSlot := 2 * params.BeaconConfig().SlotsPerEpoch require.NoError(t, tc.st.SetSlot(currentSlot)) - newState, err := blocks.ProcessAttesterSlashings(context.Background(), tc.st, []ethpb.AttSlashing{tc.slashing}, v.SlashValidator) + maxExitEpoch, churn := v.MaxExitEpochAndChurn(tc.st) + newState, err := blocks.ProcessAttesterSlashings(context.Background(), tc.st, []ethpb.AttSlashing{tc.slashing}, v.SlashValidator, maxExitEpoch, churn) require.NoError(t, err) newRegistry := newState.Validators() diff --git a/beacon-chain/core/blocks/block_operations_fuzz_test.go b/beacon-chain/core/blocks/block_operations_fuzz_test.go index 3e02b87f1112..25728015819c 100644 --- a/beacon-chain/core/blocks/block_operations_fuzz_test.go +++ b/beacon-chain/core/blocks/block_operations_fuzz_test.go @@ -185,7 +185,8 @@ func TestFuzzProcessProposerSlashings_10000(t *testing.T) { fuzzer.Fuzz(p) s, err := state_native.InitializeFromProtoUnsafePhase0(state) require.NoError(t, err) - r, err := ProcessProposerSlashings(ctx, s, []*ethpb.ProposerSlashing{p}, v.SlashValidator) + maxExitEpoch, churn := v.MaxExitEpochAndChurn(s) + r, err := ProcessProposerSlashings(ctx, s, []*ethpb.ProposerSlashing{p}, v.SlashValidator, maxExitEpoch, churn) if err != nil && r != nil { t.Fatalf("return value should be nil on err. found: %v on error: %v for state: %v and slashing: %v", r, err, state, p) } @@ -216,7 +217,8 @@ func TestFuzzProcessAttesterSlashings_10000(t *testing.T) { fuzzer.Fuzz(a) s, err := state_native.InitializeFromProtoUnsafePhase0(state) require.NoError(t, err) - r, err := ProcessAttesterSlashings(ctx, s, []ethpb.AttSlashing{a}, v.SlashValidator) + maxExitEpoch, churn := v.MaxExitEpochAndChurn(s) + r, err := ProcessAttesterSlashings(ctx, s, []ethpb.AttSlashing{a}, v.SlashValidator, maxExitEpoch, churn) if err != nil && r != nil { t.Fatalf("return value should be nil on err. found: %v on error: %v for state: %v and slashing: %v", r, err, state, a) } diff --git a/beacon-chain/core/blocks/block_regression_test.go b/beacon-chain/core/blocks/block_regression_test.go index 96339adba904..6e9dc14756b4 100644 --- a/beacon-chain/core/blocks/block_regression_test.go +++ b/beacon-chain/core/blocks/block_regression_test.go @@ -95,7 +95,8 @@ func TestProcessAttesterSlashings_RegressionSlashableIndices(t *testing.T) { for i, s := range b.Block.Body.AttesterSlashings { ss[i] = s } - newState, err := blocks.ProcessAttesterSlashings(context.Background(), beaconState, ss, v.SlashValidator) + maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) + newState, err := blocks.ProcessAttesterSlashings(context.Background(), beaconState, ss, v.SlashValidator, maxExitEpoch, churn) require.NoError(t, err) newRegistry := newState.Validators() if !newRegistry[expectedSlashedVal].Slashed { diff --git a/beacon-chain/core/blocks/proposer_slashing.go b/beacon-chain/core/blocks/proposer_slashing.go index 9da947492f8c..47faf083b95e 100644 --- a/beacon-chain/core/blocks/proposer_slashing.go +++ b/beacon-chain/core/blocks/proposer_slashing.go @@ -4,7 +4,6 @@ import ( "context" "fmt" - "github.com/pion/rtcp" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing" @@ -21,7 +20,9 @@ type slashValidatorFunc func( ctx context.Context, st state.BeaconState, vid primitives.ValidatorIndex, - maxEpoch primitives.Epoch, churn uint64) (state.BeaconState, error) + maxEpoch primitives.Epoch, + churn uint64, +) (state.BeaconState, error) // ProcessProposerSlashings is one of the operations performed // on each processed beacon block to slash proposers based on @@ -54,10 +55,12 @@ func ProcessProposerSlashings( beaconState state.BeaconState, slashings []*ethpb.ProposerSlashing, slashFunc slashValidatorFunc, + maxExitEpoch primitives.Epoch, + churn uint64, ) (state.BeaconState, error) { var err error for _, slashing := range slashings { - beaconState, err = ProcessProposerSlashing(ctx, beaconState, slashing, slashFunc) + beaconState, err = ProcessProposerSlashing(ctx, beaconState, slashing, slashFunc, maxExitEpoch, churn) if err != nil { return nil, err } @@ -71,6 +74,8 @@ func ProcessProposerSlashing( beaconState state.BeaconState, slashing *ethpb.ProposerSlashing, slashFunc slashValidatorFunc, + maxExitEpoch primitives.Epoch, + churn uint64, ) (state.BeaconState, error) { var err error if slashing == nil { @@ -79,7 +84,7 @@ func ProcessProposerSlashing( if err = VerifyProposerSlashing(beaconState, slashing); err != nil { return nil, errors.Wrap(err, "could not verify proposer slashing") } - beaconState, err = slashFunc(ctx, beaconState, slashing.Header_1.Header.ProposerIndex) + beaconState, err = slashFunc(ctx, beaconState, slashing.Header_1.Header.ProposerIndex, maxExitEpoch, churn) if err != nil { return nil, errors.Wrapf(err, "could not slash proposer index %d", slashing.Header_1.Header.ProposerIndex) } diff --git a/beacon-chain/core/blocks/proposer_slashing_test.go b/beacon-chain/core/blocks/proposer_slashing_test.go index 2ed48d3f2801..de6b812e49a8 100644 --- a/beacon-chain/core/blocks/proposer_slashing_test.go +++ b/beacon-chain/core/blocks/proposer_slashing_test.go @@ -51,7 +51,8 @@ func TestProcessProposerSlashings_UnmatchedHeaderSlots(t *testing.T) { }, } want := "mismatched header slots" - _, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, b.Block.Body.ProposerSlashings, v.SlashValidator) + maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) + _, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, b.Block.Body.ProposerSlashings, v.SlashValidator, maxExitEpoch, churn) assert.ErrorContains(t, want, err) } @@ -84,7 +85,8 @@ func TestProcessProposerSlashings_SameHeaders(t *testing.T) { }, } want := "expected slashing headers to differ" - _, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, b.Block.Body.ProposerSlashings, v.SlashValidator) + maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) + _, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, b.Block.Body.ProposerSlashings, v.SlashValidator, maxExitEpoch, churn) assert.ErrorContains(t, want, err) } @@ -134,7 +136,8 @@ func TestProcessProposerSlashings_ValidatorNotSlashable(t *testing.T) { "validator with key %#x is not slashable", bytesutil.ToBytes48(beaconState.Validators()[0].PublicKey), ) - _, err = blocks.ProcessProposerSlashings(context.Background(), beaconState, b.Block.Body.ProposerSlashings, v.SlashValidator) + maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) + _, err = blocks.ProcessProposerSlashings(context.Background(), beaconState, b.Block.Body.ProposerSlashings, v.SlashValidator, maxExitEpoch, churn) assert.ErrorContains(t, want, err) } @@ -173,7 +176,8 @@ func TestProcessProposerSlashings_AppliesCorrectStatus(t *testing.T) { block := util.NewBeaconBlock() block.Block.Body.ProposerSlashings = slashings - newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator) + maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) + newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator, maxExitEpoch, churn) require.NoError(t, err) newStateVals := newState.Validators() @@ -221,7 +225,8 @@ func TestProcessProposerSlashings_AppliesCorrectStatusAltair(t *testing.T) { block := util.NewBeaconBlock() block.Block.Body.ProposerSlashings = slashings - newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator) + maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) + newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator, maxExitEpoch, churn) require.NoError(t, err) newStateVals := newState.Validators() @@ -269,7 +274,8 @@ func TestProcessProposerSlashings_AppliesCorrectStatusBellatrix(t *testing.T) { block := util.NewBeaconBlock() block.Block.Body.ProposerSlashings = slashings - newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator) + maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) + newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator, maxExitEpoch, churn) require.NoError(t, err) newStateVals := newState.Validators() @@ -317,7 +323,8 @@ func TestProcessProposerSlashings_AppliesCorrectStatusCapella(t *testing.T) { block := util.NewBeaconBlock() block.Block.Body.ProposerSlashings = slashings - newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator) + maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) + newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator, maxExitEpoch, churn) require.NoError(t, err) newStateVals := newState.Validators() diff --git a/beacon-chain/core/electra/transition_no_verify_sig.go b/beacon-chain/core/electra/transition_no_verify_sig.go index 018e519abc2a..3bd71885baa0 100644 --- a/beacon-chain/core/electra/transition_no_verify_sig.go +++ b/beacon-chain/core/electra/transition_no_verify_sig.go @@ -53,11 +53,12 @@ func ProcessOperations( // 6110 validations are in VerifyOperationLengths bb := block.Body() // Electra extends the altair operations. - st, err := ProcessProposerSlashings(ctx, st, bb.ProposerSlashings(), v.SlashValidator) + maxExitEpoch, churn := v.MaxExitEpochAndChurn(st) + st, err := ProcessProposerSlashings(ctx, st, bb.ProposerSlashings(), v.SlashValidator, maxExitEpoch, churn) if err != nil { return nil, errors.Wrap(err, "could not process altair proposer slashing") } - st, err = ProcessAttesterSlashings(ctx, st, bb.AttesterSlashings(), v.SlashValidator) + st, err = ProcessAttesterSlashings(ctx, st, bb.AttesterSlashings(), v.SlashValidator, maxExitEpoch, churn) if err != nil { return nil, errors.Wrap(err, "could not process altair attester slashing") } diff --git a/beacon-chain/core/transition/transition_no_verify_sig.go b/beacon-chain/core/transition/transition_no_verify_sig.go index e633c844d35b..b9a31cb977bc 100644 --- a/beacon-chain/core/transition/transition_no_verify_sig.go +++ b/beacon-chain/core/transition/transition_no_verify_sig.go @@ -378,11 +378,12 @@ func altairOperations( ctx context.Context, st state.BeaconState, beaconBlock interfaces.ReadOnlyBeaconBlock) (state.BeaconState, error) { - st, err := b.ProcessProposerSlashings(ctx, st, beaconBlock.Body().ProposerSlashings(), v.SlashValidator) + maxExitEpoch, churn := v.MaxExitEpochAndChurn(st) + st, err := b.ProcessProposerSlashings(ctx, st, beaconBlock.Body().ProposerSlashings(), v.SlashValidator, maxExitEpoch, churn) if err != nil { return nil, errors.Wrap(err, "could not process altair proposer slashing") } - st, err = b.ProcessAttesterSlashings(ctx, st, beaconBlock.Body().AttesterSlashings(), v.SlashValidator) + st, err = b.ProcessAttesterSlashings(ctx, st, beaconBlock.Body().AttesterSlashings(), v.SlashValidator, maxExitEpoch, churn) if err != nil { return nil, errors.Wrap(err, "could not process altair attester slashing") } @@ -405,11 +406,12 @@ func phase0Operations( ctx context.Context, st state.BeaconState, beaconBlock interfaces.ReadOnlyBeaconBlock) (state.BeaconState, error) { - st, err := b.ProcessProposerSlashings(ctx, st, beaconBlock.Body().ProposerSlashings(), v.SlashValidator) + maxExitEpoch, churn := v.MaxExitEpochAndChurn(st) + st, err := b.ProcessProposerSlashings(ctx, st, beaconBlock.Body().ProposerSlashings(), v.SlashValidator, maxExitEpoch, churn) if err != nil { return nil, errors.Wrap(err, "could not process block proposer slashings") } - st, err = b.ProcessAttesterSlashings(ctx, st, beaconBlock.Body().AttesterSlashings(), v.SlashValidator) + st, err = b.ProcessAttesterSlashings(ctx, st, beaconBlock.Body().AttesterSlashings(), v.SlashValidator, maxExitEpoch, churn) if err != nil { return nil, errors.Wrap(err, "could not process block attester slashings") } diff --git a/beacon-chain/core/validators/validator.go b/beacon-chain/core/validators/validator.go index 01d2d0934eef..b1fc0941b8d7 100644 --- a/beacon-chain/core/validators/validator.go +++ b/beacon-chain/core/validators/validator.go @@ -152,7 +152,9 @@ func InitiateValidatorExit(ctx context.Context, s state.BeaconState, idx primiti func SlashValidator( ctx context.Context, s state.BeaconState, - slashedIdx primitives.ValidatorIndex) (state.BeaconState, error) { + slashedIdx primitives.ValidatorIndex, + maxExitEpoch primitives.Epoch, + churn uint64) (state.BeaconState, error) { s, _, err := InitiateValidatorExit(ctx, s, slashedIdx, maxExitEpoch, churn) if err != nil && !errors.Is(err, ErrValidatorAlreadyExited) { return nil, errors.Wrapf(err, "could not initiate validator %d exit", slashedIdx) diff --git a/beacon-chain/core/validators/validator_test.go b/beacon-chain/core/validators/validator_test.go index 3c05b0599e1a..c99ac7973fdc 100644 --- a/beacon-chain/core/validators/validator_test.go +++ b/beacon-chain/core/validators/validator_test.go @@ -191,7 +191,8 @@ func TestSlashValidator_OK(t *testing.T) { require.NoError(t, err, "Could not get proposer") proposerBal, err := state.BalanceAtIndex(proposer) require.NoError(t, err) - slashedState, err := validators.SlashValidator(context.Background(), state, slashedIdx) + maxExitEpoch, churn := validators.MaxExitEpochAndChurn(state) + slashedState, err := validators.SlashValidator(context.Background(), state, slashedIdx, maxExitEpoch, churn) require.NoError(t, err, "Could not slash validator") require.Equal(t, true, slashedState.Version() == version.Phase0) @@ -245,7 +246,8 @@ func TestSlashValidator_Electra(t *testing.T) { require.NoError(t, err, "Could not get proposer") proposerBal, err := state.BalanceAtIndex(proposer) require.NoError(t, err) - slashedState, err := validators.SlashValidator(context.Background(), state, slashedIdx) + maxExitEpoch, churn := validators.MaxExitEpochAndChurn(state) + slashedState, err := validators.SlashValidator(context.Background(), state, slashedIdx, maxExitEpoch, churn) require.NoError(t, err, "Could not slash validator") require.Equal(t, true, slashedState.Version() == version.Electra) diff --git a/beacon-chain/rpc/eth/rewards/service.go b/beacon-chain/rpc/eth/rewards/service.go index 1e6c12a3f317..0a877c4c4d7f 100644 --- a/beacon-chain/rpc/eth/rewards/service.go +++ b/beacon-chain/rpc/eth/rewards/service.go @@ -68,7 +68,8 @@ func (rs *BlockRewardService) GetBlockRewardsData(ctx context.Context, blk inter Code: http.StatusInternalServerError, } } - st, err = coreblocks.ProcessAttesterSlashings(ctx, st, blk.Body().AttesterSlashings(), validators.SlashValidator) + maxExitEpoch, churn := validators.MaxExitEpochAndChurn(st) + st, err = coreblocks.ProcessAttesterSlashings(ctx, st, blk.Body().AttesterSlashings(), validators.SlashValidator, maxExitEpoch, churn) if err != nil { return nil, &httputil.DefaultJsonError{ Message: "Could not get attester slashing rewards: " + err.Error(), @@ -82,7 +83,7 @@ func (rs *BlockRewardService) GetBlockRewardsData(ctx context.Context, blk inter Code: http.StatusInternalServerError, } } - st, err = coreblocks.ProcessProposerSlashings(ctx, st, blk.Body().ProposerSlashings(), validators.SlashValidator) + st, err = coreblocks.ProcessProposerSlashings(ctx, st, blk.Body().ProposerSlashings(), validators.SlashValidator, maxExitEpoch, churn) if err != nil { return nil, &httputil.DefaultJsonError{ Message: "Could not get proposer slashing rewards: " + err.Error(), diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_slashings.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_slashings.go index f8f8494a6a75..d6466d3057f5 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_slashings.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_slashings.go @@ -6,24 +6,32 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks" v "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" + types "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" ) func (vs *Server) getSlashings(ctx context.Context, head state.BeaconState) ([]*ethpb.ProposerSlashing, []ethpb.AttSlashing) { proposerSlashings := vs.SlashingsPool.PendingProposerSlashings(ctx, head, false /*noLimit*/) + attSlashings := vs.SlashingsPool.PendingAttesterSlashings(ctx, head, false /*noLimit*/) + var maxExitEpoch types.Epoch + var churn uint64 + + if len(proposerSlashings) >= 0 || len(attSlashings) >= 0 { + maxExitEpoch, churn = v.MaxExitEpochAndChurn(head) + } + validProposerSlashings := make([]*ethpb.ProposerSlashing, 0, len(proposerSlashings)) for _, slashing := range proposerSlashings { - _, err := blocks.ProcessProposerSlashing(ctx, head, slashing, v.SlashValidator) + _, err := blocks.ProcessProposerSlashing(ctx, head, slashing, v.SlashValidator, maxExitEpoch, churn) if err != nil { log.WithError(err).Warn("Could not validate proposer slashing for block inclusion") continue } validProposerSlashings = append(validProposerSlashings, slashing) } - attSlashings := vs.SlashingsPool.PendingAttesterSlashings(ctx, head, false /*noLimit*/) validAttSlashings := make([]ethpb.AttSlashing, 0, len(attSlashings)) for _, slashing := range attSlashings { - _, err := blocks.ProcessAttesterSlashing(ctx, head, slashing, v.SlashValidator) + _, err := blocks.ProcessAttesterSlashing(ctx, head, slashing, v.SlashValidator, maxExitEpoch, churn) if err != nil { log.WithError(err).Warn("Could not validate attester slashing for block inclusion") continue diff --git a/testing/spectest/shared/common/operations/attester_slashing.go b/testing/spectest/shared/common/operations/attester_slashing.go index 85f2aabc4e35..66a4ba9ad14b 100644 --- a/testing/spectest/shared/common/operations/attester_slashing.go +++ b/testing/spectest/shared/common/operations/attester_slashing.go @@ -12,6 +12,7 @@ import ( func RunAttesterSlashingTest(t *testing.T, config string, fork string, block blockWithSSZObject, sszToState SSZToState) { runSlashingTest(t, config, fork, "attester_slashing", block, sszToState, func(ctx context.Context, s state.BeaconState, b interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) { - return blocks.ProcessAttesterSlashings(ctx, s, b.Block().Body().AttesterSlashings(), v.SlashValidator) + maxExitEpoch, churn := v.MaxExitEpochAndChurn(s) + return blocks.ProcessAttesterSlashings(ctx, s, b.Block().Body().AttesterSlashings(), v.SlashValidator, maxExitEpoch, churn) }) } diff --git a/testing/spectest/shared/common/operations/proposer_slashing.go b/testing/spectest/shared/common/operations/proposer_slashing.go index dda287e6a16e..8baaedec1111 100644 --- a/testing/spectest/shared/common/operations/proposer_slashing.go +++ b/testing/spectest/shared/common/operations/proposer_slashing.go @@ -12,6 +12,7 @@ import ( func RunProposerSlashingTest(t *testing.T, config string, fork string, block blockWithSSZObject, sszToState SSZToState) { runSlashingTest(t, config, fork, "proposer_slashing", block, sszToState, func(ctx context.Context, s state.BeaconState, b interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) { - return blocks.ProcessProposerSlashings(ctx, s, b.Block().Body().ProposerSlashings(), v.SlashValidator) + maxExitEpoch, churn := v.MaxExitEpochAndChurn(s) + return blocks.ProcessProposerSlashings(ctx, s, b.Block().Body().ProposerSlashings(), v.SlashValidator, maxExitEpoch, churn) }) } From 0ee0b10c2cf02bc9ddea7aac2cc272ced076f36c Mon Sep 17 00:00:00 2001 From: rkapka Date: Wed, 26 Feb 2025 17:24:40 +0100 Subject: [PATCH 3/4] changelog <3 --- changelog/radek_fix-max-epoch-calculation-once.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog/radek_fix-max-epoch-calculation-once.md diff --git a/changelog/radek_fix-max-epoch-calculation-once.md b/changelog/radek_fix-max-epoch-calculation-once.md new file mode 100644 index 000000000000..bcb20c015543 --- /dev/null +++ b/changelog/radek_fix-max-epoch-calculation-once.md @@ -0,0 +1,3 @@ +### Changed + +- Call `MaxExitEpochAndChurn` just once before processing any slashings to reduce CPU load. \ No newline at end of file From b63205aa333d5f0585e9225a0179d177e859f39d Mon Sep 17 00:00:00 2001 From: rkapka Date: Fri, 28 Feb 2025 14:10:18 +0100 Subject: [PATCH 4/4] introduce struct --- beacon-chain/core/blocks/attester_slashing.go | 11 ++-- .../core/blocks/attester_slashing_test.go | 9 +-- .../core/blocks/block_operations_fuzz_test.go | 10 ++-- .../core/blocks/block_regression_test.go | 3 +- beacon-chain/core/blocks/exit.go | 15 +---- beacon-chain/core/blocks/exit_test.go | 7 ++- beacon-chain/core/blocks/proposer_slashing.go | 14 ++--- .../core/blocks/proposer_slashing_test.go | 21 +++---- beacon-chain/core/electra/registry_updates.go | 3 +- .../core/electra/transition_no_verify_sig.go | 8 +-- beacon-chain/core/electra/withdrawals.go | 3 +- beacon-chain/core/epoch/epoch_processing.go | 3 +- .../transition/transition_no_verify_sig.go | 17 +++--- beacon-chain/core/validators/validator.go | 55 ++++++++++++------- .../core/validators/validator_test.go | 22 ++++---- beacon-chain/rpc/eth/rewards/service.go | 6 +- .../v1alpha1/validator/proposer_slashings.go | 12 ++-- .../common/operations/attester_slashing.go | 3 +- .../common/operations/proposer_slashing.go | 3 +- .../common/operations/voluntary_exit.go | 3 +- 20 files changed, 106 insertions(+), 122 deletions(-) diff --git a/beacon-chain/core/blocks/attester_slashing.go b/beacon-chain/core/blocks/attester_slashing.go index 2e10b9daafd8..247d5ec5b63d 100644 --- a/beacon-chain/core/blocks/attester_slashing.go +++ b/beacon-chain/core/blocks/attester_slashing.go @@ -6,6 +6,7 @@ import ( "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/container/slice" @@ -40,12 +41,11 @@ func ProcessAttesterSlashings( beaconState state.BeaconState, slashings []ethpb.AttSlashing, slashFunc slashValidatorFunc, - maxExitEpoch primitives.Epoch, - churn uint64, + exitData *validators.ExitData, ) (state.BeaconState, error) { var err error for _, slashing := range slashings { - beaconState, err = ProcessAttesterSlashing(ctx, beaconState, slashing, slashFunc, maxExitEpoch, churn) + beaconState, err = ProcessAttesterSlashing(ctx, beaconState, slashing, slashFunc, exitData) if err != nil { return nil, err } @@ -59,8 +59,7 @@ func ProcessAttesterSlashing( beaconState state.BeaconState, slashing ethpb.AttSlashing, slashFunc slashValidatorFunc, - maxEpoch primitives.Epoch, - churn uint64, + exitData *validators.ExitData, ) (state.BeaconState, error) { if err := VerifyAttesterSlashing(ctx, beaconState, slashing); err != nil { return nil, errors.Wrap(err, "could not verify attester slashing") @@ -79,7 +78,7 @@ func ProcessAttesterSlashing( return nil, err } if helpers.IsSlashableValidator(val.ActivationEpoch(), val.WithdrawableEpoch(), val.Slashed(), currentEpoch) { - beaconState, err = slashFunc(ctx, beaconState, primitives.ValidatorIndex(validatorIndex), maxEpoch, churn) + beaconState, err = slashFunc(ctx, beaconState, primitives.ValidatorIndex(validatorIndex), exitData) if err != nil { return nil, errors.Wrapf(err, "could not slash validator index %d", validatorIndex) diff --git a/beacon-chain/core/blocks/attester_slashing_test.go b/beacon-chain/core/blocks/attester_slashing_test.go index 6acdee235c45..85025b68c3bb 100644 --- a/beacon-chain/core/blocks/attester_slashing_test.go +++ b/beacon-chain/core/blocks/attester_slashing_test.go @@ -63,8 +63,7 @@ func TestProcessAttesterSlashings_DataNotSlashable(t *testing.T) { for i, s := range b.Block.Body.AttesterSlashings { ss[i] = s } - maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) - _, err = blocks.ProcessAttesterSlashings(context.Background(), beaconState, ss, v.SlashValidator, maxExitEpoch, churn) + _, err = blocks.ProcessAttesterSlashings(context.Background(), beaconState, ss, v.SlashValidator, v.MaxExitEpochAndChurn(beaconState)) assert.ErrorContains(t, "attestations are not slashable", err) } @@ -103,8 +102,7 @@ func TestProcessAttesterSlashings_IndexedAttestationFailedToVerify(t *testing.T) for i, s := range b.Block.Body.AttesterSlashings { ss[i] = s } - maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) - _, err = blocks.ProcessAttesterSlashings(context.Background(), beaconState, ss, v.SlashValidator, maxExitEpoch, churn) + _, err = blocks.ProcessAttesterSlashings(context.Background(), beaconState, ss, v.SlashValidator, v.MaxExitEpochAndChurn(beaconState)) assert.ErrorContains(t, "validator indices count exceeds MAX_VALIDATORS_PER_COMMITTEE", err) } @@ -246,8 +244,7 @@ func TestProcessAttesterSlashings_AppliesCorrectStatus(t *testing.T) { currentSlot := 2 * params.BeaconConfig().SlotsPerEpoch require.NoError(t, tc.st.SetSlot(currentSlot)) - maxExitEpoch, churn := v.MaxExitEpochAndChurn(tc.st) - newState, err := blocks.ProcessAttesterSlashings(context.Background(), tc.st, []ethpb.AttSlashing{tc.slashing}, v.SlashValidator, maxExitEpoch, churn) + newState, err := blocks.ProcessAttesterSlashings(context.Background(), tc.st, []ethpb.AttSlashing{tc.slashing}, v.SlashValidator, v.MaxExitEpochAndChurn(tc.st)) require.NoError(t, err) newRegistry := newState.Validators() diff --git a/beacon-chain/core/blocks/block_operations_fuzz_test.go b/beacon-chain/core/blocks/block_operations_fuzz_test.go index 25728015819c..fc025152a79e 100644 --- a/beacon-chain/core/blocks/block_operations_fuzz_test.go +++ b/beacon-chain/core/blocks/block_operations_fuzz_test.go @@ -185,8 +185,7 @@ func TestFuzzProcessProposerSlashings_10000(t *testing.T) { fuzzer.Fuzz(p) s, err := state_native.InitializeFromProtoUnsafePhase0(state) require.NoError(t, err) - maxExitEpoch, churn := v.MaxExitEpochAndChurn(s) - r, err := ProcessProposerSlashings(ctx, s, []*ethpb.ProposerSlashing{p}, v.SlashValidator, maxExitEpoch, churn) + r, err := ProcessProposerSlashings(ctx, s, []*ethpb.ProposerSlashing{p}, v.SlashValidator, v.MaxExitEpochAndChurn(s)) if err != nil && r != nil { t.Fatalf("return value should be nil on err. found: %v on error: %v for state: %v and slashing: %v", r, err, state, p) } @@ -217,8 +216,7 @@ func TestFuzzProcessAttesterSlashings_10000(t *testing.T) { fuzzer.Fuzz(a) s, err := state_native.InitializeFromProtoUnsafePhase0(state) require.NoError(t, err) - maxExitEpoch, churn := v.MaxExitEpochAndChurn(s) - r, err := ProcessAttesterSlashings(ctx, s, []ethpb.AttSlashing{a}, v.SlashValidator, maxExitEpoch, churn) + r, err := ProcessAttesterSlashings(ctx, s, []ethpb.AttSlashing{a}, v.SlashValidator, v.MaxExitEpochAndChurn(s)) if err != nil && r != nil { t.Fatalf("return value should be nil on err. found: %v on error: %v for state: %v and slashing: %v", r, err, state, a) } @@ -323,7 +321,7 @@ func TestFuzzProcessVoluntaryExits_10000(t *testing.T) { fuzzer.Fuzz(e) s, err := state_native.InitializeFromProtoUnsafePhase0(state) require.NoError(t, err) - r, err := ProcessVoluntaryExits(ctx, s, []*ethpb.SignedVoluntaryExit{e}) + r, err := ProcessVoluntaryExits(ctx, s, []*ethpb.SignedVoluntaryExit{e}, v.MaxExitEpochAndChurn(s)) if err != nil && r != nil { t.Fatalf("return value should be nil on err. found: %v on error: %v for state: %v and exit: %v", r, err, state, e) } @@ -339,7 +337,7 @@ func TestFuzzProcessVoluntaryExitsNoVerify_10000(t *testing.T) { fuzzer.Fuzz(e) s, err := state_native.InitializeFromProtoUnsafePhase0(state) require.NoError(t, err) - r, err := ProcessVoluntaryExits(context.Background(), s, []*ethpb.SignedVoluntaryExit{e}) + r, err := ProcessVoluntaryExits(context.Background(), s, []*ethpb.SignedVoluntaryExit{e}, v.MaxExitEpochAndChurn(s)) if err != nil && r != nil { t.Fatalf("return value should be nil on err. found: %v on error: %v for state: %v and block: %v", r, err, state, e) } diff --git a/beacon-chain/core/blocks/block_regression_test.go b/beacon-chain/core/blocks/block_regression_test.go index 6e9dc14756b4..2a30d7588066 100644 --- a/beacon-chain/core/blocks/block_regression_test.go +++ b/beacon-chain/core/blocks/block_regression_test.go @@ -95,8 +95,7 @@ func TestProcessAttesterSlashings_RegressionSlashableIndices(t *testing.T) { for i, s := range b.Block.Body.AttesterSlashings { ss[i] = s } - maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) - newState, err := blocks.ProcessAttesterSlashings(context.Background(), beaconState, ss, v.SlashValidator, maxExitEpoch, churn) + newState, err := blocks.ProcessAttesterSlashings(context.Background(), beaconState, ss, v.SlashValidator, v.MaxExitEpochAndChurn(beaconState)) require.NoError(t, err) newRegistry := newState.Validators() if !newRegistry[expectedSlashedVal].Slashed { diff --git a/beacon-chain/core/blocks/exit.go b/beacon-chain/core/blocks/exit.go index ca0e8f33e127..5154ad6648eb 100644 --- a/beacon-chain/core/blocks/exit.go +++ b/beacon-chain/core/blocks/exit.go @@ -10,7 +10,6 @@ import ( v "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" "github.com/prysmaticlabs/prysm/v5/config/params" - "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/time/slots" @@ -50,13 +49,12 @@ func ProcessVoluntaryExits( ctx context.Context, beaconState state.BeaconState, exits []*ethpb.SignedVoluntaryExit, + exitData *v.ExitData, ) (state.BeaconState, error) { // Avoid calculating the epoch churn if no exits exist. if len(exits) == 0 { return beaconState, nil } - maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) - var exitEpoch primitives.Epoch for idx, exit := range exits { if exit == nil || exit.Exit == nil { return nil, errors.New("nil voluntary exit in block body") @@ -68,15 +66,8 @@ func ProcessVoluntaryExits( if err := VerifyExitAndSignature(val, beaconState, exit); err != nil { return nil, errors.Wrapf(err, "could not verify exit %d", idx) } - beaconState, exitEpoch, err = v.InitiateValidatorExit(ctx, beaconState, exit.Exit.ValidatorIndex, maxExitEpoch, churn) - if err == nil { - if exitEpoch > maxExitEpoch { - maxExitEpoch = exitEpoch - churn = 1 - } else if exitEpoch == maxExitEpoch { - churn++ - } - } else if !errors.Is(err, v.ErrValidatorAlreadyExited) { + beaconState, _, err = v.InitiateValidatorExit(ctx, beaconState, exit.Exit.ValidatorIndex, exitData) + if !errors.Is(err, v.ErrValidatorAlreadyExited) { return nil, err } } diff --git a/beacon-chain/core/blocks/exit_test.go b/beacon-chain/core/blocks/exit_test.go index bda5f7ef81b4..b2924703bd02 100644 --- a/beacon-chain/core/blocks/exit_test.go +++ b/beacon-chain/core/blocks/exit_test.go @@ -8,6 +8,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators" "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/config/params" @@ -47,7 +48,7 @@ func TestProcessVoluntaryExits_NotActiveLongEnoughToExit(t *testing.T) { } want := "validator has not been active long enough to exit" - _, err = blocks.ProcessVoluntaryExits(context.Background(), state, b.Block.Body.VoluntaryExits) + _, err = blocks.ProcessVoluntaryExits(context.Background(), state, b.Block.Body.VoluntaryExits, validators.MaxExitEpochAndChurn(state)) assert.ErrorContains(t, want, err) } @@ -77,7 +78,7 @@ func TestProcessVoluntaryExits_ExitAlreadySubmitted(t *testing.T) { } want := "validator with index 0 has already submitted an exit, which will take place at epoch: 10" - _, err = blocks.ProcessVoluntaryExits(context.Background(), state, b.Block.Body.VoluntaryExits) + _, err = blocks.ProcessVoluntaryExits(context.Background(), state, b.Block.Body.VoluntaryExits, validators.MaxExitEpochAndChurn(state)) assert.ErrorContains(t, want, err) } @@ -125,7 +126,7 @@ func TestProcessVoluntaryExits_AppliesCorrectStatus(t *testing.T) { }, } - newState, err := blocks.ProcessVoluntaryExits(context.Background(), state, b.Block.Body.VoluntaryExits) + newState, err := blocks.ProcessVoluntaryExits(context.Background(), state, b.Block.Body.VoluntaryExits, validators.MaxExitEpochAndChurn(state)) require.NoError(t, err, "Could not process exits") newRegistry := newState.Validators() if newRegistry[0].ExitEpoch != helpers.ActivationExitEpoch(primitives.Epoch(state.Slot()/params.BeaconConfig().SlotsPerEpoch)) { diff --git a/beacon-chain/core/blocks/proposer_slashing.go b/beacon-chain/core/blocks/proposer_slashing.go index 47faf083b95e..8925aed615f9 100644 --- a/beacon-chain/core/blocks/proposer_slashing.go +++ b/beacon-chain/core/blocks/proposer_slashing.go @@ -8,6 +8,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" @@ -20,8 +21,7 @@ type slashValidatorFunc func( ctx context.Context, st state.BeaconState, vid primitives.ValidatorIndex, - maxEpoch primitives.Epoch, - churn uint64, + exitData *validators.ExitData, ) (state.BeaconState, error) // ProcessProposerSlashings is one of the operations performed @@ -55,12 +55,11 @@ func ProcessProposerSlashings( beaconState state.BeaconState, slashings []*ethpb.ProposerSlashing, slashFunc slashValidatorFunc, - maxExitEpoch primitives.Epoch, - churn uint64, + exitData *validators.ExitData, ) (state.BeaconState, error) { var err error for _, slashing := range slashings { - beaconState, err = ProcessProposerSlashing(ctx, beaconState, slashing, slashFunc, maxExitEpoch, churn) + beaconState, err = ProcessProposerSlashing(ctx, beaconState, slashing, slashFunc, exitData) if err != nil { return nil, err } @@ -74,8 +73,7 @@ func ProcessProposerSlashing( beaconState state.BeaconState, slashing *ethpb.ProposerSlashing, slashFunc slashValidatorFunc, - maxExitEpoch primitives.Epoch, - churn uint64, + exitData *validators.ExitData, ) (state.BeaconState, error) { var err error if slashing == nil { @@ -84,7 +82,7 @@ func ProcessProposerSlashing( if err = VerifyProposerSlashing(beaconState, slashing); err != nil { return nil, errors.Wrap(err, "could not verify proposer slashing") } - beaconState, err = slashFunc(ctx, beaconState, slashing.Header_1.Header.ProposerIndex, maxExitEpoch, churn) + beaconState, err = slashFunc(ctx, beaconState, slashing.Header_1.Header.ProposerIndex, exitData) if err != nil { return nil, errors.Wrapf(err, "could not slash proposer index %d", slashing.Header_1.Header.ProposerIndex) } diff --git a/beacon-chain/core/blocks/proposer_slashing_test.go b/beacon-chain/core/blocks/proposer_slashing_test.go index de6b812e49a8..324b4ec31824 100644 --- a/beacon-chain/core/blocks/proposer_slashing_test.go +++ b/beacon-chain/core/blocks/proposer_slashing_test.go @@ -51,8 +51,7 @@ func TestProcessProposerSlashings_UnmatchedHeaderSlots(t *testing.T) { }, } want := "mismatched header slots" - maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) - _, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, b.Block.Body.ProposerSlashings, v.SlashValidator, maxExitEpoch, churn) + _, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, b.Block.Body.ProposerSlashings, v.SlashValidator, v.MaxExitEpochAndChurn(beaconState)) assert.ErrorContains(t, want, err) } @@ -85,8 +84,7 @@ func TestProcessProposerSlashings_SameHeaders(t *testing.T) { }, } want := "expected slashing headers to differ" - maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) - _, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, b.Block.Body.ProposerSlashings, v.SlashValidator, maxExitEpoch, churn) + _, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, b.Block.Body.ProposerSlashings, v.SlashValidator, v.MaxExitEpochAndChurn(beaconState)) assert.ErrorContains(t, want, err) } @@ -136,8 +134,7 @@ func TestProcessProposerSlashings_ValidatorNotSlashable(t *testing.T) { "validator with key %#x is not slashable", bytesutil.ToBytes48(beaconState.Validators()[0].PublicKey), ) - maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) - _, err = blocks.ProcessProposerSlashings(context.Background(), beaconState, b.Block.Body.ProposerSlashings, v.SlashValidator, maxExitEpoch, churn) + _, err = blocks.ProcessProposerSlashings(context.Background(), beaconState, b.Block.Body.ProposerSlashings, v.SlashValidator, v.MaxExitEpochAndChurn(beaconState)) assert.ErrorContains(t, want, err) } @@ -176,8 +173,7 @@ func TestProcessProposerSlashings_AppliesCorrectStatus(t *testing.T) { block := util.NewBeaconBlock() block.Block.Body.ProposerSlashings = slashings - maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) - newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator, maxExitEpoch, churn) + newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator, v.MaxExitEpochAndChurn(beaconState)) require.NoError(t, err) newStateVals := newState.Validators() @@ -225,8 +221,7 @@ func TestProcessProposerSlashings_AppliesCorrectStatusAltair(t *testing.T) { block := util.NewBeaconBlock() block.Block.Body.ProposerSlashings = slashings - maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) - newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator, maxExitEpoch, churn) + newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator, v.MaxExitEpochAndChurn(beaconState)) require.NoError(t, err) newStateVals := newState.Validators() @@ -274,8 +269,7 @@ func TestProcessProposerSlashings_AppliesCorrectStatusBellatrix(t *testing.T) { block := util.NewBeaconBlock() block.Block.Body.ProposerSlashings = slashings - maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) - newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator, maxExitEpoch, churn) + newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator, v.MaxExitEpochAndChurn(beaconState)) require.NoError(t, err) newStateVals := newState.Validators() @@ -323,8 +317,7 @@ func TestProcessProposerSlashings_AppliesCorrectStatusCapella(t *testing.T) { block := util.NewBeaconBlock() block.Block.Body.ProposerSlashings = slashings - maxExitEpoch, churn := v.MaxExitEpochAndChurn(beaconState) - newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator, maxExitEpoch, churn) + newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator, v.MaxExitEpochAndChurn(beaconState)) require.NoError(t, err) newStateVals := newState.Validators() diff --git a/beacon-chain/core/electra/registry_updates.go b/beacon-chain/core/electra/registry_updates.go index 07424568e1af..ffd398a490e9 100644 --- a/beacon-chain/core/electra/registry_updates.go +++ b/beacon-chain/core/electra/registry_updates.go @@ -84,8 +84,7 @@ func ProcessRegistryUpdates(ctx context.Context, st state.BeaconState) error { // Handle validator ejections. for _, idx := range eligibleForEjection { var err error - // exitQueueEpoch and churn arguments are not used in electra. - st, _, err = validators.InitiateValidatorExit(ctx, st, idx, 0 /*exitQueueEpoch*/, 0 /*churn*/) + st, _, err = validators.InitiateValidatorExit(ctx, st, idx, &validators.ExitData{}) if err != nil && !errors.Is(err, validators.ErrValidatorAlreadyExited) { return fmt.Errorf("failed to initiate validator exit at index %d: %w", idx, err) } diff --git a/beacon-chain/core/electra/transition_no_verify_sig.go b/beacon-chain/core/electra/transition_no_verify_sig.go index 3bd71885baa0..8607e1193c0a 100644 --- a/beacon-chain/core/electra/transition_no_verify_sig.go +++ b/beacon-chain/core/electra/transition_no_verify_sig.go @@ -53,12 +53,12 @@ func ProcessOperations( // 6110 validations are in VerifyOperationLengths bb := block.Body() // Electra extends the altair operations. - maxExitEpoch, churn := v.MaxExitEpochAndChurn(st) - st, err := ProcessProposerSlashings(ctx, st, bb.ProposerSlashings(), v.SlashValidator, maxExitEpoch, churn) + exitData := v.MaxExitEpochAndChurn(st) + st, err := ProcessProposerSlashings(ctx, st, bb.ProposerSlashings(), v.SlashValidator, exitData) if err != nil { return nil, errors.Wrap(err, "could not process altair proposer slashing") } - st, err = ProcessAttesterSlashings(ctx, st, bb.AttesterSlashings(), v.SlashValidator, maxExitEpoch, churn) + st, err = ProcessAttesterSlashings(ctx, st, bb.AttesterSlashings(), v.SlashValidator, exitData) if err != nil { return nil, errors.Wrap(err, "could not process altair attester slashing") } @@ -69,7 +69,7 @@ func ProcessOperations( if _, err := ProcessDeposits(ctx, st, bb.Deposits()); err != nil { // new in electra return nil, errors.Wrap(err, "could not process altair deposit") } - st, err = ProcessVoluntaryExits(ctx, st, bb.VoluntaryExits()) + st, err = ProcessVoluntaryExits(ctx, st, bb.VoluntaryExits(), exitData) if err != nil { return nil, errors.Wrap(err, "could not process voluntary exits") } diff --git a/beacon-chain/core/electra/withdrawals.go b/beacon-chain/core/electra/withdrawals.go index 62156188ccd8..4c2e3eb199c7 100644 --- a/beacon-chain/core/electra/withdrawals.go +++ b/beacon-chain/core/electra/withdrawals.go @@ -147,9 +147,8 @@ func ProcessWithdrawalRequests(ctx context.Context, st state.BeaconState, wrs [] if isFullExitRequest { // Only exit validator if it has no pending withdrawals in the queue if pendingBalanceToWithdraw == 0 { - maxExitEpoch, churn := validators.MaxExitEpochAndChurn(st) var err error - st, _, err = validators.InitiateValidatorExit(ctx, st, vIdx, maxExitEpoch, churn) + st, _, err = validators.InitiateValidatorExit(ctx, st, vIdx, validators.MaxExitEpochAndChurn(st)) if err != nil { return nil, err } diff --git a/beacon-chain/core/epoch/epoch_processing.go b/beacon-chain/core/epoch/epoch_processing.go index 720e1ac9d42a..a08f65a02324 100644 --- a/beacon-chain/core/epoch/epoch_processing.go +++ b/beacon-chain/core/epoch/epoch_processing.go @@ -99,8 +99,7 @@ func ProcessRegistryUpdates(ctx context.Context, st state.BeaconState) (state.Be for _, idx := range eligibleForEjection { // Here is fine to do a quadratic loop since this should // barely happen - maxExitEpoch, churn := validators.MaxExitEpochAndChurn(st) - st, _, err = validators.InitiateValidatorExit(ctx, st, idx, maxExitEpoch, churn) + st, _, err = validators.InitiateValidatorExit(ctx, st, idx, validators.MaxExitEpochAndChurn(st)) if err != nil && !errors.Is(err, validators.ErrValidatorAlreadyExited) { return nil, errors.Wrapf(err, "could not initiate exit for validator %d", idx) } diff --git a/beacon-chain/core/transition/transition_no_verify_sig.go b/beacon-chain/core/transition/transition_no_verify_sig.go index b9a31cb977bc..d0992d4c3301 100644 --- a/beacon-chain/core/transition/transition_no_verify_sig.go +++ b/beacon-chain/core/transition/transition_no_verify_sig.go @@ -378,12 +378,13 @@ func altairOperations( ctx context.Context, st state.BeaconState, beaconBlock interfaces.ReadOnlyBeaconBlock) (state.BeaconState, error) { - maxExitEpoch, churn := v.MaxExitEpochAndChurn(st) - st, err := b.ProcessProposerSlashings(ctx, st, beaconBlock.Body().ProposerSlashings(), v.SlashValidator, maxExitEpoch, churn) + // TODO It might be unclear that exitData is mutated + exitData := v.MaxExitEpochAndChurn(st) + st, err := b.ProcessProposerSlashings(ctx, st, beaconBlock.Body().ProposerSlashings(), v.SlashValidator, exitData) if err != nil { return nil, errors.Wrap(err, "could not process altair proposer slashing") } - st, err = b.ProcessAttesterSlashings(ctx, st, beaconBlock.Body().AttesterSlashings(), v.SlashValidator, maxExitEpoch, churn) + st, err = b.ProcessAttesterSlashings(ctx, st, beaconBlock.Body().AttesterSlashings(), v.SlashValidator, exitData) if err != nil { return nil, errors.Wrap(err, "could not process altair attester slashing") } @@ -394,7 +395,7 @@ func altairOperations( if _, err := altair.ProcessDeposits(ctx, st, beaconBlock.Body().Deposits()); err != nil { return nil, errors.Wrap(err, "could not process altair deposit") } - st, err = b.ProcessVoluntaryExits(ctx, st, beaconBlock.Body().VoluntaryExits()) + st, err = b.ProcessVoluntaryExits(ctx, st, beaconBlock.Body().VoluntaryExits(), exitData) if err != nil { return nil, errors.Wrap(err, "could not process voluntary exits") } @@ -406,12 +407,12 @@ func phase0Operations( ctx context.Context, st state.BeaconState, beaconBlock interfaces.ReadOnlyBeaconBlock) (state.BeaconState, error) { - maxExitEpoch, churn := v.MaxExitEpochAndChurn(st) - st, err := b.ProcessProposerSlashings(ctx, st, beaconBlock.Body().ProposerSlashings(), v.SlashValidator, maxExitEpoch, churn) + exitData := v.MaxExitEpochAndChurn(st) + st, err := b.ProcessProposerSlashings(ctx, st, beaconBlock.Body().ProposerSlashings(), v.SlashValidator, exitData) if err != nil { return nil, errors.Wrap(err, "could not process block proposer slashings") } - st, err = b.ProcessAttesterSlashings(ctx, st, beaconBlock.Body().AttesterSlashings(), v.SlashValidator, maxExitEpoch, churn) + st, err = b.ProcessAttesterSlashings(ctx, st, beaconBlock.Body().AttesterSlashings(), v.SlashValidator, exitData) if err != nil { return nil, errors.Wrap(err, "could not process block attester slashings") } @@ -422,5 +423,5 @@ func phase0Operations( if _, err := altair.ProcessDeposits(ctx, st, beaconBlock.Body().Deposits()); err != nil { return nil, errors.Wrap(err, "could not process deposits") } - return b.ProcessVoluntaryExits(ctx, st, beaconBlock.Body().VoluntaryExits()) + return b.ProcessVoluntaryExits(ctx, st, beaconBlock.Body().VoluntaryExits(), exitData) } diff --git a/beacon-chain/core/validators/validator.go b/beacon-chain/core/validators/validator.go index b1fc0941b8d7..37b6c59e06ec 100644 --- a/beacon-chain/core/validators/validator.go +++ b/beacon-chain/core/validators/validator.go @@ -19,28 +19,36 @@ import ( "github.com/prysmaticlabs/prysm/v5/time/slots" ) +type ExitData struct { + MaxExitEpoch primitives.Epoch + Churn uint64 +} + // ErrValidatorAlreadyExited is an error raised when trying to process an exit of // an already exited validator var ErrValidatorAlreadyExited = errors.New("validator already exited") +// TODO: rename // MaxExitEpochAndChurn returns the maximum non-FAR_FUTURE_EPOCH exit // epoch and the number of them -func MaxExitEpochAndChurn(s state.BeaconState) (maxExitEpoch primitives.Epoch, churn uint64) { +func MaxExitEpochAndChurn(s state.BeaconState) *ExitData { + exitData := &ExitData{} + farFutureEpoch := params.BeaconConfig().FarFutureEpoch err := s.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error { e := val.ExitEpoch() if e != farFutureEpoch { - if e > maxExitEpoch { - maxExitEpoch = e - churn = 1 - } else if e == maxExitEpoch { - churn++ + if e > exitData.MaxExitEpoch { + exitData.MaxExitEpoch = e + exitData.Churn = 1 + } else if e == exitData.MaxExitEpoch { + exitData.Churn++ } } return nil }) _ = err - return + return exitData } // InitiateValidatorExit takes in validator index and updates @@ -64,7 +72,12 @@ func MaxExitEpochAndChurn(s state.BeaconState) (maxExitEpoch primitives.Epoch, c // # Set validator exit epoch and withdrawable epoch // validator.exit_epoch = exit_queue_epoch // validator.withdrawable_epoch = Epoch(validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY) -func InitiateValidatorExit(ctx context.Context, s state.BeaconState, idx primitives.ValidatorIndex, exitQueueEpoch primitives.Epoch, churn uint64) (state.BeaconState, primitives.Epoch, error) { +func InitiateValidatorExit( + ctx context.Context, + s state.BeaconState, + idx primitives.ValidatorIndex, + exitData *ExitData, +) (state.BeaconState, primitives.Epoch, error) { validator, err := s.ValidatorAtIndex(idx) if err != nil { return nil, 0, err @@ -83,9 +96,9 @@ func InitiateValidatorExit(ctx context.Context, s state.BeaconState, idx primiti // if exit_queue_churn >= get_validator_churn_limit(state): // exit_queue_epoch += Epoch(1) exitableEpoch := helpers.ActivationExitEpoch(time.CurrentEpoch(s)) - if exitableEpoch > exitQueueEpoch { - exitQueueEpoch = exitableEpoch - churn = 0 + if exitableEpoch > exitData.MaxExitEpoch { + exitData.MaxExitEpoch = exitableEpoch + exitData.Churn = 0 } activeValidatorCount, err := helpers.ActiveValidatorCount(ctx, s, time.CurrentEpoch(s)) if err != nil { @@ -93,30 +106,33 @@ func InitiateValidatorExit(ctx context.Context, s state.BeaconState, idx primiti } currentChurn := helpers.ValidatorExitChurnLimit(activeValidatorCount) - if churn >= currentChurn { - exitQueueEpoch, err = exitQueueEpoch.SafeAdd(1) + if exitData.Churn >= currentChurn { + exitData.MaxExitEpoch, err = exitData.MaxExitEpoch.SafeAdd(1) if err != nil { return nil, 0, err } + exitData.Churn = 1 + } else { + exitData.Churn = exitData.Churn + 1 } } else { // [Modified in Electra:EIP7251] // exit_queue_epoch = compute_exit_epoch_and_update_churn(state, validator.effective_balance) var err error - exitQueueEpoch, err = s.ExitEpochAndUpdateChurn(primitives.Gwei(validator.EffectiveBalance)) + exitData.MaxExitEpoch, err = s.ExitEpochAndUpdateChurn(primitives.Gwei(validator.EffectiveBalance)) if err != nil { return nil, 0, err } } - validator.ExitEpoch = exitQueueEpoch - validator.WithdrawableEpoch, err = exitQueueEpoch.SafeAddEpoch(params.BeaconConfig().MinValidatorWithdrawabilityDelay) + validator.ExitEpoch = exitData.MaxExitEpoch + validator.WithdrawableEpoch, err = exitData.MaxExitEpoch.SafeAddEpoch(params.BeaconConfig().MinValidatorWithdrawabilityDelay) if err != nil { return nil, 0, err } if err := s.UpdateValidatorAtIndex(idx, validator); err != nil { return nil, 0, err } - return s, exitQueueEpoch, nil + return s, exitData.MaxExitEpoch, nil } // SlashValidator slashes the malicious validator's balance and awards @@ -153,9 +169,8 @@ func SlashValidator( ctx context.Context, s state.BeaconState, slashedIdx primitives.ValidatorIndex, - maxExitEpoch primitives.Epoch, - churn uint64) (state.BeaconState, error) { - s, _, err := InitiateValidatorExit(ctx, s, slashedIdx, maxExitEpoch, churn) + exitData *ExitData) (state.BeaconState, error) { + s, _, err := InitiateValidatorExit(ctx, s, slashedIdx, exitData) if err != nil && !errors.Is(err, ErrValidatorAlreadyExited) { return nil, errors.Wrapf(err, "could not initiate validator %d exit", slashedIdx) } diff --git a/beacon-chain/core/validators/validator_test.go b/beacon-chain/core/validators/validator_test.go index c99ac7973fdc..ab4e7dea1c45 100644 --- a/beacon-chain/core/validators/validator_test.go +++ b/beacon-chain/core/validators/validator_test.go @@ -50,7 +50,7 @@ func TestInitiateValidatorExit_AlreadyExited(t *testing.T) { }} state, err := state_native.InitializeFromProtoPhase0(base) require.NoError(t, err) - newState, epoch, err := validators.InitiateValidatorExit(context.Background(), state, 0, 199, 1) + newState, epoch, err := validators.InitiateValidatorExit(context.Background(), state, 0, &validators.ExitData{MaxExitEpoch: 199, Churn: 1}) require.ErrorIs(t, err, validators.ErrValidatorAlreadyExited) require.Equal(t, exitEpoch, epoch) v, err := newState.ValidatorAtIndex(0) @@ -69,7 +69,7 @@ func TestInitiateValidatorExit_ProperExit(t *testing.T) { }} state, err := state_native.InitializeFromProtoPhase0(base) require.NoError(t, err) - newState, epoch, err := validators.InitiateValidatorExit(context.Background(), state, idx, exitedEpoch+2, 1) + newState, epoch, err := validators.InitiateValidatorExit(context.Background(), state, idx, &validators.ExitData{MaxExitEpoch: exitedEpoch + 2, Churn: 1}) require.NoError(t, err) require.Equal(t, exitedEpoch+2, epoch) v, err := newState.ValidatorAtIndex(idx) @@ -89,7 +89,7 @@ func TestInitiateValidatorExit_ChurnOverflow(t *testing.T) { }} state, err := state_native.InitializeFromProtoPhase0(base) require.NoError(t, err) - newState, epoch, err := validators.InitiateValidatorExit(context.Background(), state, idx, exitedEpoch+2, 4) + newState, epoch, err := validators.InitiateValidatorExit(context.Background(), state, idx, &validators.ExitData{MaxExitEpoch: exitedEpoch + 2, Churn: 4}) require.NoError(t, err) require.Equal(t, exitedEpoch+3, epoch) @@ -111,7 +111,7 @@ func TestInitiateValidatorExit_WithdrawalOverflows(t *testing.T) { }} state, err := state_native.InitializeFromProtoPhase0(base) require.NoError(t, err) - _, _, err = validators.InitiateValidatorExit(context.Background(), state, 1, params.BeaconConfig().FarFutureEpoch-1, 1) + _, _, err = validators.InitiateValidatorExit(context.Background(), state, 1, &validators.ExitData{MaxExitEpoch: params.BeaconConfig().FarFutureEpoch - 1, Churn: 1}) require.ErrorContains(t, "addition overflows", err) } @@ -147,7 +147,7 @@ func TestInitiateValidatorExit_ProperExit_Electra(t *testing.T) { require.NoError(t, err) require.Equal(t, primitives.Gwei(0), ebtc) - newState, epoch, err := validators.InitiateValidatorExit(context.Background(), state, idx, 0, 0) // exitQueueEpoch and churn are not used in electra + newState, epoch, err := validators.InitiateValidatorExit(context.Background(), state, idx, &validators.ExitData{}) // exitQueueEpoch and churn are not used in electra require.NoError(t, err) // Expect that the exit epoch is the next available epoch with max seed lookahead. @@ -191,8 +191,7 @@ func TestSlashValidator_OK(t *testing.T) { require.NoError(t, err, "Could not get proposer") proposerBal, err := state.BalanceAtIndex(proposer) require.NoError(t, err) - maxExitEpoch, churn := validators.MaxExitEpochAndChurn(state) - slashedState, err := validators.SlashValidator(context.Background(), state, slashedIdx, maxExitEpoch, churn) + slashedState, err := validators.SlashValidator(context.Background(), state, slashedIdx, validators.MaxExitEpochAndChurn(state)) require.NoError(t, err, "Could not slash validator") require.Equal(t, true, slashedState.Version() == version.Phase0) @@ -246,8 +245,7 @@ func TestSlashValidator_Electra(t *testing.T) { require.NoError(t, err, "Could not get proposer") proposerBal, err := state.BalanceAtIndex(proposer) require.NoError(t, err) - maxExitEpoch, churn := validators.MaxExitEpochAndChurn(state) - slashedState, err := validators.SlashValidator(context.Background(), state, slashedIdx, maxExitEpoch, churn) + slashedState, err := validators.SlashValidator(context.Background(), state, slashedIdx, validators.MaxExitEpochAndChurn(state)) require.NoError(t, err, "Could not slash validator") require.Equal(t, true, slashedState.Version() == version.Electra) @@ -508,8 +506,8 @@ func TestValidatorMaxExitEpochAndChurn(t *testing.T) { for _, tt := range tests { s, err := state_native.InitializeFromProtoPhase0(tt.state) require.NoError(t, err) - epoch, churn := validators.MaxExitEpochAndChurn(s) - require.Equal(t, tt.wantedEpoch, epoch) - require.Equal(t, tt.wantedChurn, churn) + exitData := validators.MaxExitEpochAndChurn(s) + require.Equal(t, tt.wantedEpoch, exitData.MaxExitEpoch) + require.Equal(t, tt.wantedChurn, exitData.Churn) } } diff --git a/beacon-chain/rpc/eth/rewards/service.go b/beacon-chain/rpc/eth/rewards/service.go index 0a877c4c4d7f..e6b1b8405765 100644 --- a/beacon-chain/rpc/eth/rewards/service.go +++ b/beacon-chain/rpc/eth/rewards/service.go @@ -68,8 +68,8 @@ func (rs *BlockRewardService) GetBlockRewardsData(ctx context.Context, blk inter Code: http.StatusInternalServerError, } } - maxExitEpoch, churn := validators.MaxExitEpochAndChurn(st) - st, err = coreblocks.ProcessAttesterSlashings(ctx, st, blk.Body().AttesterSlashings(), validators.SlashValidator, maxExitEpoch, churn) + exitData := validators.MaxExitEpochAndChurn(st) + st, err = coreblocks.ProcessAttesterSlashings(ctx, st, blk.Body().AttesterSlashings(), validators.SlashValidator, exitData) if err != nil { return nil, &httputil.DefaultJsonError{ Message: "Could not get attester slashing rewards: " + err.Error(), @@ -83,7 +83,7 @@ func (rs *BlockRewardService) GetBlockRewardsData(ctx context.Context, blk inter Code: http.StatusInternalServerError, } } - st, err = coreblocks.ProcessProposerSlashings(ctx, st, blk.Body().ProposerSlashings(), validators.SlashValidator, maxExitEpoch, churn) + st, err = coreblocks.ProcessProposerSlashings(ctx, st, blk.Body().ProposerSlashings(), validators.SlashValidator, exitData) if err != nil { return nil, &httputil.DefaultJsonError{ Message: "Could not get proposer slashing rewards: " + err.Error(), diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_slashings.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_slashings.go index d6466d3057f5..745cd18a817b 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_slashings.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_slashings.go @@ -6,23 +6,21 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks" v "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" - types "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" ) func (vs *Server) getSlashings(ctx context.Context, head state.BeaconState) ([]*ethpb.ProposerSlashing, []ethpb.AttSlashing) { proposerSlashings := vs.SlashingsPool.PendingProposerSlashings(ctx, head, false /*noLimit*/) attSlashings := vs.SlashingsPool.PendingAttesterSlashings(ctx, head, false /*noLimit*/) - var maxExitEpoch types.Epoch - var churn uint64 - if len(proposerSlashings) >= 0 || len(attSlashings) >= 0 { - maxExitEpoch, churn = v.MaxExitEpochAndChurn(head) + if len(proposerSlashings) == 0 && len(attSlashings) == 0 { + return []*ethpb.ProposerSlashing{}, []ethpb.AttSlashing{} } + exitData := v.MaxExitEpochAndChurn(head) validProposerSlashings := make([]*ethpb.ProposerSlashing, 0, len(proposerSlashings)) for _, slashing := range proposerSlashings { - _, err := blocks.ProcessProposerSlashing(ctx, head, slashing, v.SlashValidator, maxExitEpoch, churn) + _, err := blocks.ProcessProposerSlashing(ctx, head, slashing, v.SlashValidator, exitData) if err != nil { log.WithError(err).Warn("Could not validate proposer slashing for block inclusion") continue @@ -31,7 +29,7 @@ func (vs *Server) getSlashings(ctx context.Context, head state.BeaconState) ([]* } validAttSlashings := make([]ethpb.AttSlashing, 0, len(attSlashings)) for _, slashing := range attSlashings { - _, err := blocks.ProcessAttesterSlashing(ctx, head, slashing, v.SlashValidator, maxExitEpoch, churn) + _, err := blocks.ProcessAttesterSlashing(ctx, head, slashing, v.SlashValidator, exitData) if err != nil { log.WithError(err).Warn("Could not validate attester slashing for block inclusion") continue diff --git a/testing/spectest/shared/common/operations/attester_slashing.go b/testing/spectest/shared/common/operations/attester_slashing.go index 66a4ba9ad14b..19b27c2edbd5 100644 --- a/testing/spectest/shared/common/operations/attester_slashing.go +++ b/testing/spectest/shared/common/operations/attester_slashing.go @@ -12,7 +12,6 @@ import ( func RunAttesterSlashingTest(t *testing.T, config string, fork string, block blockWithSSZObject, sszToState SSZToState) { runSlashingTest(t, config, fork, "attester_slashing", block, sszToState, func(ctx context.Context, s state.BeaconState, b interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) { - maxExitEpoch, churn := v.MaxExitEpochAndChurn(s) - return blocks.ProcessAttesterSlashings(ctx, s, b.Block().Body().AttesterSlashings(), v.SlashValidator, maxExitEpoch, churn) + return blocks.ProcessAttesterSlashings(ctx, s, b.Block().Body().AttesterSlashings(), v.SlashValidator, v.MaxExitEpochAndChurn(s)) }) } diff --git a/testing/spectest/shared/common/operations/proposer_slashing.go b/testing/spectest/shared/common/operations/proposer_slashing.go index 8baaedec1111..9a1290b62241 100644 --- a/testing/spectest/shared/common/operations/proposer_slashing.go +++ b/testing/spectest/shared/common/operations/proposer_slashing.go @@ -12,7 +12,6 @@ import ( func RunProposerSlashingTest(t *testing.T, config string, fork string, block blockWithSSZObject, sszToState SSZToState) { runSlashingTest(t, config, fork, "proposer_slashing", block, sszToState, func(ctx context.Context, s state.BeaconState, b interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) { - maxExitEpoch, churn := v.MaxExitEpochAndChurn(s) - return blocks.ProcessProposerSlashings(ctx, s, b.Block().Body().ProposerSlashings(), v.SlashValidator, maxExitEpoch, churn) + return blocks.ProcessProposerSlashings(ctx, s, b.Block().Body().ProposerSlashings(), v.SlashValidator, v.MaxExitEpochAndChurn(s)) }) } diff --git a/testing/spectest/shared/common/operations/voluntary_exit.go b/testing/spectest/shared/common/operations/voluntary_exit.go index ec2829d5bda4..a398fdd16f06 100644 --- a/testing/spectest/shared/common/operations/voluntary_exit.go +++ b/testing/spectest/shared/common/operations/voluntary_exit.go @@ -7,6 +7,7 @@ import ( "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/interfaces" "github.com/prysmaticlabs/prysm/v5/testing/require" @@ -30,7 +31,7 @@ func RunVoluntaryExitTest(t *testing.T, config string, fork string, block blockW blk, err := block(exitSSZ) require.NoError(t, err) RunBlockOperationTest(t, folderPath, blk, sszToState, func(ctx context.Context, s state.BeaconState, b interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, error) { - return blocks.ProcessVoluntaryExits(ctx, s, b.Block().Body().VoluntaryExits()) + return blocks.ProcessVoluntaryExits(ctx, s, b.Block().Body().VoluntaryExits(), validators.MaxExitEpochAndChurn(s)) }) }) }