From 07d0a00f88acec809c720e49cfd1fa3594c696f3 Mon Sep 17 00:00:00 2001 From: terencechain Date: Thu, 17 Nov 2022 12:03:53 -0500 Subject: [PATCH] Add capella slashing parameters (#11660) * Add capella slashing parameters * Add capella to epoch precompute * Revert "Add capella to epoch precompute" This reverts commit 61a5f3d2bda8c2d73b113a4252c70027a727af73. Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> --- beacon-chain/core/blocks/attester_slashing.go | 50 +++++++------- .../core/blocks/attester_slashing_test.go | 69 +++++++++++++++++++ beacon-chain/core/blocks/proposer_slashing.go | 39 ++++++----- .../core/blocks/proposer_slashing_test.go | 48 +++++++++++++ 4 files changed, 163 insertions(+), 43 deletions(-) diff --git a/beacon-chain/core/blocks/attester_slashing.go b/beacon-chain/core/blocks/attester_slashing.go index 8e7712a726..b0a9f7f517 100644 --- a/beacon-chain/core/blocks/attester_slashing.go +++ b/beacon-chain/core/blocks/attester_slashing.go @@ -22,20 +22,21 @@ import ( // Casper FFG slashing conditions if any slashable events occurred. // // Spec pseudocode definition: -// def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None: -// attestation_1 = attester_slashing.attestation_1 -// attestation_2 = attester_slashing.attestation_2 -// assert is_slashable_attestation_data(attestation_1.data, attestation_2.data) -// assert is_valid_indexed_attestation(state, attestation_1) -// assert is_valid_indexed_attestation(state, attestation_2) // -// slashed_any = False -// indices = set(attestation_1.attesting_indices).intersection(attestation_2.attesting_indices) -// for index in sorted(indices): -// if is_slashable_validator(state.validators[index], get_current_epoch(state)): -// slash_validator(state, index) -// slashed_any = True -// assert slashed_any +// def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None: +// attestation_1 = attester_slashing.attestation_1 +// attestation_2 = attester_slashing.attestation_2 +// assert is_slashable_attestation_data(attestation_1.data, attestation_2.data) +// assert is_valid_indexed_attestation(state, attestation_1) +// assert is_valid_indexed_attestation(state, attestation_2) +// +// slashed_any = False +// indices = set(attestation_1.attesting_indices).intersection(attestation_2.attesting_indices) +// for index in sorted(indices): +// if is_slashable_validator(state.validators[index], get_current_epoch(state)): +// slash_validator(state, index) +// slashed_any = True +// assert slashed_any func ProcessAttesterSlashings( ctx context.Context, beaconState state.BeaconState, @@ -83,7 +84,7 @@ func ProcessAttesterSlashing( slashingQuotient = cfg.MinSlashingPenaltyQuotient case beaconState.Version() == version.Altair: slashingQuotient = cfg.MinSlashingPenaltyQuotientAltair - case beaconState.Version() == version.Bellatrix: + case beaconState.Version() == version.Bellatrix, beaconState.Version() == version.Capella: slashingQuotient = cfg.MinSlashingPenaltyQuotientBellatrix default: return nil, errors.New("unknown state version") @@ -132,16 +133,17 @@ func VerifyAttesterSlashing(ctx context.Context, beaconState state.ReadOnlyBeaco // IsSlashableAttestationData verifies a slashing against the Casper Proof of Stake FFG rules. // // Spec pseudocode definition: -// def is_slashable_attestation_data(data_1: AttestationData, data_2: AttestationData) -> bool: -// """ -// Check if ``data_1`` and ``data_2`` are slashable according to Casper FFG rules. -// """ -// return ( -// # Double vote -// (data_1 != data_2 and data_1.target.epoch == data_2.target.epoch) or -// # Surround vote -// (data_1.source.epoch < data_2.source.epoch and data_2.target.epoch < data_1.target.epoch) -// ) +// +// def is_slashable_attestation_data(data_1: AttestationData, data_2: AttestationData) -> bool: +// """ +// Check if ``data_1`` and ``data_2`` are slashable according to Casper FFG rules. +// """ +// return ( +// # Double vote +// (data_1 != data_2 and data_1.target.epoch == data_2.target.epoch) or +// # Surround vote +// (data_1.source.epoch < data_2.source.epoch and data_2.target.epoch < data_1.target.epoch) +// ) func IsSlashableAttestationData(data1, data2 *ethpb.AttestationData) bool { if data1 == nil || data2 == nil || data1.Target == nil || data2.Target == nil || data1.Source == nil || data2.Source == nil { return false diff --git a/beacon-chain/core/blocks/attester_slashing_test.go b/beacon-chain/core/blocks/attester_slashing_test.go index cc39977f36..ccfd46755e 100644 --- a/beacon-chain/core/blocks/attester_slashing_test.go +++ b/beacon-chain/core/blocks/attester_slashing_test.go @@ -302,3 +302,72 @@ func TestProcessAttesterSlashings_AppliesCorrectStatusBellatrix(t *testing.T) { require.Equal(t, uint64(31000000000), newState.Balances()[1]) require.Equal(t, uint64(32000000000), newState.Balances()[2]) } + +func TestProcessAttesterSlashings_AppliesCorrectStatusCapella(t *testing.T) { + beaconState, privKeys := util.DeterministicGenesisStateCapella(t, 100) + for _, vv := range beaconState.Validators() { + vv.WithdrawableEpoch = types.Epoch(params.BeaconConfig().SlotsPerEpoch) + } + + att1 := util.HydrateIndexedAttestation(ðpb.IndexedAttestation{ + Data: ðpb.AttestationData{ + Source: ðpb.Checkpoint{Epoch: 1}, + }, + AttestingIndices: []uint64{0, 1}, + }) + domain, err := signing.Domain(beaconState.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, beaconState.GenesisValidatorsRoot()) + require.NoError(t, err) + signingRoot, err := signing.ComputeSigningRoot(att1.Data, domain) + assert.NoError(t, err, "Could not get signing root of beacon block header") + sig0 := privKeys[0].Sign(signingRoot[:]) + sig1 := privKeys[1].Sign(signingRoot[:]) + aggregateSig := bls.AggregateSignatures([]bls.Signature{sig0, sig1}) + att1.Signature = aggregateSig.Marshal() + + att2 := util.HydrateIndexedAttestation(ðpb.IndexedAttestation{ + AttestingIndices: []uint64{0, 1}, + }) + signingRoot, err = signing.ComputeSigningRoot(att2.Data, domain) + assert.NoError(t, err, "Could not get signing root of beacon block header") + sig0 = privKeys[0].Sign(signingRoot[:]) + sig1 = privKeys[1].Sign(signingRoot[:]) + aggregateSig = bls.AggregateSignatures([]bls.Signature{sig0, sig1}) + att2.Signature = aggregateSig.Marshal() + + slashings := []*ethpb.AttesterSlashing{ + { + Attestation_1: att1, + Attestation_2: att2, + }, + } + + currentSlot := 2 * params.BeaconConfig().SlotsPerEpoch + require.NoError(t, beaconState.SetSlot(currentSlot)) + + b := util.NewBeaconBlock() + b.Block = ðpb.BeaconBlock{ + Body: ðpb.BeaconBlockBody{ + AttesterSlashings: slashings, + }, + } + + newState, err := blocks.ProcessAttesterSlashings(context.Background(), beaconState, b.Block.Body.AttesterSlashings, v.SlashValidator) + require.NoError(t, err) + newRegistry := newState.Validators() + + // Given the intersection of slashable indices is [1], only validator + // at index 1 should be slashed and exited. We confirm this below. + if newRegistry[1].ExitEpoch != beaconState.Validators()[1].ExitEpoch { + t.Errorf( + ` + Expected validator at index 1's exit epoch to match + %d, received %d instead + `, + beaconState.Validators()[1].ExitEpoch, + newRegistry[1].ExitEpoch, + ) + } + + require.Equal(t, uint64(31000000000), newState.Balances()[1]) + require.Equal(t, uint64(32000000000), newState.Balances()[2]) +} diff --git a/beacon-chain/core/blocks/proposer_slashing.go b/beacon-chain/core/blocks/proposer_slashing.go index 4f59d5c4a1..33b2dc5eee 100644 --- a/beacon-chain/core/blocks/proposer_slashing.go +++ b/beacon-chain/core/blocks/proposer_slashing.go @@ -24,26 +24,27 @@ type slashValidatorFunc func(ctx context.Context, st state.BeaconState, vid type // slashing conditions if any slashable events occurred. // // Spec pseudocode definition: -// def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None: -// header_1 = proposer_slashing.signed_header_1.message -// header_2 = proposer_slashing.signed_header_2.message // -// # Verify header slots match -// assert header_1.slot == header_2.slot -// # Verify header proposer indices match -// assert header_1.proposer_index == header_2.proposer_index -// # Verify the headers are different -// assert header_1 != header_2 -// # Verify the proposer is slashable -// proposer = state.validators[header_1.proposer_index] -// assert is_slashable_validator(proposer, get_current_epoch(state)) -// # Verify signatures -// for signed_header in (proposer_slashing.signed_header_1, proposer_slashing.signed_header_2): -// domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(signed_header.message.slot)) -// signing_root = compute_signing_root(signed_header.message, domain) -// assert bls.Verify(proposer.pubkey, signing_root, signed_header.signature) +// def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None: +// header_1 = proposer_slashing.signed_header_1.message +// header_2 = proposer_slashing.signed_header_2.message // -// slash_validator(state, header_1.proposer_index) +// # Verify header slots match +// assert header_1.slot == header_2.slot +// # Verify header proposer indices match +// assert header_1.proposer_index == header_2.proposer_index +// # Verify the headers are different +// assert header_1 != header_2 +// # Verify the proposer is slashable +// proposer = state.validators[header_1.proposer_index] +// assert is_slashable_validator(proposer, get_current_epoch(state)) +// # Verify signatures +// for signed_header in (proposer_slashing.signed_header_1, proposer_slashing.signed_header_2): +// domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(signed_header.message.slot)) +// signing_root = compute_signing_root(signed_header.message, domain) +// assert bls.Verify(proposer.pubkey, signing_root, signed_header.signature) +// +// slash_validator(state, header_1.proposer_index) func ProcessProposerSlashings( ctx context.Context, beaconState state.BeaconState, @@ -81,7 +82,7 @@ func ProcessProposerSlashing( slashingQuotient = cfg.MinSlashingPenaltyQuotient case beaconState.Version() == version.Altair: slashingQuotient = cfg.MinSlashingPenaltyQuotientAltair - case beaconState.Version() == version.Bellatrix: + case beaconState.Version() == version.Bellatrix, beaconState.Version() == version.Capella: slashingQuotient = cfg.MinSlashingPenaltyQuotientBellatrix default: return nil, errors.New("unknown state version") diff --git a/beacon-chain/core/blocks/proposer_slashing_test.go b/beacon-chain/core/blocks/proposer_slashing_test.go index 3162481b18..a5aa3abfe4 100644 --- a/beacon-chain/core/blocks/proposer_slashing_test.go +++ b/beacon-chain/core/blocks/proposer_slashing_test.go @@ -282,6 +282,54 @@ func TestProcessProposerSlashings_AppliesCorrectStatusBellatrix(t *testing.T) { require.Equal(t, uint64(32000000000), newState.Balances()[2]) } +func TestProcessProposerSlashings_AppliesCorrectStatusCapella(t *testing.T) { + // We test the case when data is correct and verify the validator + // registry has been updated. + beaconState, privKeys := util.DeterministicGenesisStateCapella(t, 100) + proposerIdx := types.ValidatorIndex(1) + + header1 := ðpb.SignedBeaconBlockHeader{ + Header: util.HydrateBeaconHeader(ðpb.BeaconBlockHeader{ + ProposerIndex: proposerIdx, + StateRoot: bytesutil.PadTo([]byte("A"), 32), + }), + } + var err error + header1.Signature, err = signing.ComputeDomainAndSign(beaconState, 0, header1.Header, params.BeaconConfig().DomainBeaconProposer, privKeys[proposerIdx]) + require.NoError(t, err) + + header2 := util.HydrateSignedBeaconHeader(ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + ProposerIndex: proposerIdx, + StateRoot: bytesutil.PadTo([]byte("B"), 32), + }, + }) + header2.Signature, err = signing.ComputeDomainAndSign(beaconState, 0, header2.Header, params.BeaconConfig().DomainBeaconProposer, privKeys[proposerIdx]) + require.NoError(t, err) + + slashings := []*ethpb.ProposerSlashing{ + { + Header_1: header1, + Header_2: header2, + }, + } + + block := util.NewBeaconBlock() + block.Block.Body.ProposerSlashings = slashings + + newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator) + require.NoError(t, err) + + newStateVals := newState.Validators() + if newStateVals[1].ExitEpoch != beaconState.Validators()[1].ExitEpoch { + t.Errorf("Proposer with index 1 did not correctly exit,"+"wanted slot:%d, got:%d", + newStateVals[1].ExitEpoch, beaconState.Validators()[1].ExitEpoch) + } + + require.Equal(t, uint64(31000000000), newState.Balances()[1]) + require.Equal(t, uint64(32000000000), newState.Balances()[2]) +} + func TestVerifyProposerSlashing(t *testing.T) { type args struct { beaconState state.BeaconState