mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 21:08:10 -05:00
Handle AttesterSlashingElectra everywhere in the codebase (#14823)
* Handle `AttesterSlashingElectra` everywhere in the codebase * simplify `TestProcessAttesterSlashings_AppliesCorrectStatus`
This commit is contained in:
@@ -1076,6 +1076,48 @@ func TestService_insertSlashingsToForkChoiceStore(t *testing.T) {
|
||||
service.InsertSlashingsToForkChoiceStore(ctx, wb.Block().Body().AttesterSlashings())
|
||||
}
|
||||
|
||||
func TestService_insertSlashingsToForkChoiceStoreElectra(t *testing.T) {
|
||||
service, tr := minimalTestService(t)
|
||||
ctx := tr.ctx
|
||||
|
||||
beaconState, privKeys := util.DeterministicGenesisStateElectra(t, 100)
|
||||
att1 := util.HydrateIndexedAttestationElectra(ðpb.IndexedAttestationElectra{
|
||||
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.HydrateIndexedAttestationElectra(ðpb.IndexedAttestationElectra{
|
||||
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.AttesterSlashingElectra{
|
||||
{
|
||||
Attestation_1: att1,
|
||||
Attestation_2: att2,
|
||||
},
|
||||
}
|
||||
b := util.NewBeaconBlockElectra()
|
||||
b.Block.Body.AttesterSlashings = slashings
|
||||
wb, err := consensusblocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
service.InsertSlashingsToForkChoiceStore(ctx, wb.Block().Body().AttesterSlashings())
|
||||
}
|
||||
|
||||
func TestOnBlock_ProcessBlocksParallel(t *testing.T) {
|
||||
service, tr := minimalTestService(t)
|
||||
ctx := tr.ctx
|
||||
|
||||
@@ -7,12 +7,14 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing"
|
||||
v "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"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
|
||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/util"
|
||||
@@ -105,293 +107,162 @@ func TestProcessAttesterSlashings_IndexedAttestationFailedToVerify(t *testing.T)
|
||||
}
|
||||
|
||||
func TestProcessAttesterSlashings_AppliesCorrectStatus(t *testing.T) {
|
||||
beaconState, privKeys := util.DeterministicGenesisState(t, 100)
|
||||
for _, vv := range beaconState.Validators() {
|
||||
vv.WithdrawableEpoch = primitives.Epoch(params.BeaconConfig().SlotsPerEpoch)
|
||||
}
|
||||
statePhase0, keysPhase0 := util.DeterministicGenesisState(t, 100)
|
||||
stateAltair, keysAltair := util.DeterministicGenesisStateAltair(t, 100)
|
||||
stateBellatrix, keysBellatrix := util.DeterministicGenesisStateBellatrix(t, 100)
|
||||
stateCapella, keysCapella := util.DeterministicGenesisStateCapella(t, 100)
|
||||
stateDeneb, keysDeneb := util.DeterministicGenesisStateDeneb(t, 100)
|
||||
stateElectra, keysElectra := util.DeterministicGenesisStateElectra(t, 100)
|
||||
|
||||
att1 := util.HydrateIndexedAttestation(ðpb.IndexedAttestation{
|
||||
att1Phase0 := 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{
|
||||
att2Phase0 := 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,
|
||||
},
|
||||
}
|
||||
|
||||
ss := make([]ethpb.AttSlashing, len(b.Block.Body.AttesterSlashings))
|
||||
for i, s := range b.Block.Body.AttesterSlashings {
|
||||
ss[i] = s
|
||||
}
|
||||
newState, err := blocks.ProcessAttesterSlashings(context.Background(), beaconState, ss, 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(31750000000), newState.Balances()[1])
|
||||
require.Equal(t, uint64(32000000000), newState.Balances()[2])
|
||||
}
|
||||
|
||||
func TestProcessAttesterSlashings_AppliesCorrectStatusAltair(t *testing.T) {
|
||||
beaconState, privKeys := util.DeterministicGenesisStateAltair(t, 100)
|
||||
for _, vv := range beaconState.Validators() {
|
||||
vv.WithdrawableEpoch = primitives.Epoch(params.BeaconConfig().SlotsPerEpoch)
|
||||
}
|
||||
|
||||
att1 := util.HydrateIndexedAttestation(ðpb.IndexedAttestation{
|
||||
att1Electra := util.HydrateIndexedAttestationElectra(ðpb.IndexedAttestationElectra{
|
||||
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{
|
||||
att2Electra := util.HydrateIndexedAttestationElectra(ðpb.IndexedAttestationElectra{
|
||||
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{
|
||||
slashingPhase0 := ðpb.AttesterSlashing{
|
||||
Attestation_1: att1Phase0,
|
||||
Attestation_2: att2Phase0,
|
||||
}
|
||||
slashingElectra := ðpb.AttesterSlashingElectra{
|
||||
Attestation_1: att1Electra,
|
||||
Attestation_2: att2Electra,
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
name string
|
||||
st state.BeaconState
|
||||
keys []bls.SecretKey
|
||||
att1 ethpb.IndexedAtt
|
||||
att2 ethpb.IndexedAtt
|
||||
slashing ethpb.AttSlashing
|
||||
slashedBalance uint64
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
Attestation_1: att1,
|
||||
Attestation_2: att2,
|
||||
name: "phase0",
|
||||
st: statePhase0,
|
||||
keys: keysPhase0,
|
||||
att1: att1Phase0,
|
||||
att2: att2Phase0,
|
||||
slashing: slashingPhase0,
|
||||
slashedBalance: 31750000000,
|
||||
},
|
||||
{
|
||||
name: "altair",
|
||||
st: stateAltair,
|
||||
keys: keysAltair,
|
||||
att1: att1Phase0,
|
||||
att2: att2Phase0,
|
||||
slashing: slashingPhase0,
|
||||
slashedBalance: 31500000000,
|
||||
},
|
||||
{
|
||||
name: "bellatrix",
|
||||
st: stateBellatrix,
|
||||
keys: keysBellatrix,
|
||||
att1: att1Phase0,
|
||||
att2: att2Phase0,
|
||||
slashing: slashingPhase0,
|
||||
slashedBalance: 31000000000,
|
||||
},
|
||||
{
|
||||
name: "capella",
|
||||
st: stateCapella,
|
||||
keys: keysCapella,
|
||||
att1: att1Phase0,
|
||||
att2: att2Phase0,
|
||||
slashing: slashingPhase0,
|
||||
slashedBalance: 31000000000,
|
||||
},
|
||||
{
|
||||
name: "deneb",
|
||||
st: stateDeneb,
|
||||
keys: keysDeneb,
|
||||
att1: att1Phase0,
|
||||
att2: att2Phase0,
|
||||
slashing: slashingPhase0,
|
||||
slashedBalance: 31000000000,
|
||||
},
|
||||
{
|
||||
name: "electra",
|
||||
st: stateElectra,
|
||||
keys: keysElectra,
|
||||
att1: att1Electra,
|
||||
att2: att2Electra,
|
||||
slashing: slashingElectra,
|
||||
slashedBalance: 31992187500,
|
||||
},
|
||||
}
|
||||
|
||||
currentSlot := 2 * params.BeaconConfig().SlotsPerEpoch
|
||||
require.NoError(t, beaconState.SetSlot(currentSlot))
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, vv := range tc.st.Validators() {
|
||||
vv.WithdrawableEpoch = primitives.Epoch(params.BeaconConfig().SlotsPerEpoch)
|
||||
}
|
||||
|
||||
b := util.NewBeaconBlock()
|
||||
b.Block = ðpb.BeaconBlock{
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
AttesterSlashings: slashings,
|
||||
},
|
||||
}
|
||||
domain, err := signing.Domain(tc.st.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, tc.st.GenesisValidatorsRoot())
|
||||
require.NoError(t, err)
|
||||
signingRoot, err := signing.ComputeSigningRoot(tc.att1.GetData(), domain)
|
||||
assert.NoError(t, err, "Could not get signing root of beacon block header")
|
||||
sig0 := tc.keys[0].Sign(signingRoot[:])
|
||||
sig1 := tc.keys[1].Sign(signingRoot[:])
|
||||
aggregateSig := bls.AggregateSignatures([]bls.Signature{sig0, sig1})
|
||||
|
||||
ss := make([]ethpb.AttSlashing, len(b.Block.Body.AttesterSlashings))
|
||||
for i, s := range b.Block.Body.AttesterSlashings {
|
||||
ss[i] = s
|
||||
}
|
||||
newState, err := blocks.ProcessAttesterSlashings(context.Background(), beaconState, ss, v.SlashValidator)
|
||||
require.NoError(t, err)
|
||||
newRegistry := newState.Validators()
|
||||
if tc.att1.Version() >= version.Electra {
|
||||
tc.att1.(*ethpb.IndexedAttestationElectra).Signature = aggregateSig.Marshal()
|
||||
} else {
|
||||
tc.att1.(*ethpb.IndexedAttestation).Signature = aggregateSig.Marshal()
|
||||
}
|
||||
|
||||
// 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(
|
||||
`
|
||||
signingRoot, err = signing.ComputeSigningRoot(tc.att2.GetData(), domain)
|
||||
assert.NoError(t, err, "Could not get signing root of beacon block header")
|
||||
sig0 = tc.keys[0].Sign(signingRoot[:])
|
||||
sig1 = tc.keys[1].Sign(signingRoot[:])
|
||||
aggregateSig = bls.AggregateSignatures([]bls.Signature{sig0, sig1})
|
||||
|
||||
if tc.att2.Version() >= version.Electra {
|
||||
tc.att2.(*ethpb.IndexedAttestationElectra).Signature = aggregateSig.Marshal()
|
||||
} else {
|
||||
tc.att2.(*ethpb.IndexedAttestation).Signature = aggregateSig.Marshal()
|
||||
}
|
||||
|
||||
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)
|
||||
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 != tc.st.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,
|
||||
)
|
||||
}
|
||||
tc.st.Validators()[1].ExitEpoch,
|
||||
newRegistry[1].ExitEpoch,
|
||||
)
|
||||
}
|
||||
|
||||
require.Equal(t, uint64(31500000000), newState.Balances()[1])
|
||||
require.Equal(t, uint64(32000000000), newState.Balances()[2])
|
||||
}
|
||||
|
||||
func TestProcessAttesterSlashings_AppliesCorrectStatusBellatrix(t *testing.T) {
|
||||
beaconState, privKeys := util.DeterministicGenesisStateBellatrix(t, 100)
|
||||
for _, vv := range beaconState.Validators() {
|
||||
vv.WithdrawableEpoch = primitives.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,
|
||||
},
|
||||
}
|
||||
|
||||
ss := make([]ethpb.AttSlashing, len(b.Block.Body.AttesterSlashings))
|
||||
for i, s := range b.Block.Body.AttesterSlashings {
|
||||
ss[i] = s
|
||||
}
|
||||
newState, err := blocks.ProcessAttesterSlashings(context.Background(), beaconState, ss, 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])
|
||||
}
|
||||
|
||||
func TestProcessAttesterSlashings_AppliesCorrectStatusCapella(t *testing.T) {
|
||||
beaconState, privKeys := util.DeterministicGenesisStateCapella(t, 100)
|
||||
for _, vv := range beaconState.Validators() {
|
||||
vv.WithdrawableEpoch = primitives.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,
|
||||
},
|
||||
}
|
||||
|
||||
ss := make([]ethpb.AttSlashing, len(b.Block.Body.AttesterSlashings))
|
||||
for i, s := range b.Block.Body.AttesterSlashings {
|
||||
ss[i] = s
|
||||
}
|
||||
newState, err := blocks.ProcessAttesterSlashings(context.Background(), beaconState, ss, 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])
|
||||
require.Equal(t, tc.slashedBalance, newState.Balances()[1])
|
||||
require.Equal(t, uint64(32000000000), newState.Balances()[2])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,11 +403,15 @@ func VerifyOperationLengths(_ context.Context, state state.BeaconState, b interf
|
||||
)
|
||||
}
|
||||
|
||||
if uint64(len(body.AttesterSlashings())) > params.BeaconConfig().MaxAttesterSlashings {
|
||||
maxSlashings := params.BeaconConfig().MaxAttesterSlashings
|
||||
if body.Version() >= version.Electra {
|
||||
maxSlashings = params.BeaconConfig().MaxAttesterSlashingsElectra
|
||||
}
|
||||
if uint64(len(body.AttesterSlashings())) > maxSlashings {
|
||||
return nil, fmt.Errorf(
|
||||
"number of attester slashings (%d) in block body exceeds allowed threshold of %d",
|
||||
len(body.AttesterSlashings()),
|
||||
params.BeaconConfig().MaxAttesterSlashings,
|
||||
maxSlashings,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -437,6 +437,25 @@ func TestProcessBlock_OverMaxAttesterSlashings(t *testing.T) {
|
||||
assert.ErrorContains(t, want, err)
|
||||
}
|
||||
|
||||
func TestProcessBlock_OverMaxAttesterSlashingsElectra(t *testing.T) {
|
||||
maxSlashings := params.BeaconConfig().MaxAttesterSlashingsElectra
|
||||
b := ðpb.SignedBeaconBlockElectra{
|
||||
Block: ðpb.BeaconBlockElectra{
|
||||
Body: ðpb.BeaconBlockBodyElectra{
|
||||
AttesterSlashings: make([]*ethpb.AttesterSlashingElectra, maxSlashings+1),
|
||||
},
|
||||
},
|
||||
}
|
||||
want := fmt.Sprintf("number of attester slashings (%d) in block body exceeds allowed threshold of %d",
|
||||
len(b.Block.Body.AttesterSlashings), params.BeaconConfig().MaxAttesterSlashingsElectra)
|
||||
s, err := state_native.InitializeFromProtoUnsafeElectra(ðpb.BeaconStateElectra{})
|
||||
require.NoError(t, err)
|
||||
wsb, err := consensusblocks.NewSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
_, err = transition.VerifyOperationLengths(context.Background(), s, wsb.Block())
|
||||
assert.ErrorContains(t, want, err)
|
||||
}
|
||||
|
||||
func TestProcessBlock_OverMaxAttestations(t *testing.T) {
|
||||
b := ðpb.SignedBeaconBlock{
|
||||
Block: ðpb.BeaconBlock{
|
||||
|
||||
@@ -813,9 +813,9 @@ func unmarshalBlock(_ context.Context, enc []byte) (interfaces.ReadOnlySignedBea
|
||||
if err := rawBlock.UnmarshalSSZ(enc[len(denebBlindKey):]); err != nil {
|
||||
return nil, errors.Wrap(err, "could not unmarshal blinded Deneb block")
|
||||
}
|
||||
case hasElectraKey(enc):
|
||||
case HasElectraKey(enc):
|
||||
rawBlock = ðpb.SignedBeaconBlockElectra{}
|
||||
if err := rawBlock.UnmarshalSSZ(enc[len(electraKey):]); err != nil {
|
||||
if err := rawBlock.UnmarshalSSZ(enc[len(ElectraKey):]); err != nil {
|
||||
return nil, errors.Wrap(err, "could not unmarshal Electra block")
|
||||
}
|
||||
case hasElectraBlindKey(enc):
|
||||
@@ -874,7 +874,7 @@ func keyForBlock(blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) {
|
||||
if blk.IsBlinded() {
|
||||
return electraBlindKey, nil
|
||||
}
|
||||
return electraKey, nil
|
||||
return ElectraKey, nil
|
||||
}
|
||||
|
||||
if v >= version.Deneb {
|
||||
|
||||
@@ -52,11 +52,12 @@ func hasDenebBlindKey(enc []byte) bool {
|
||||
return bytes.Equal(enc[:len(denebBlindKey)], denebBlindKey)
|
||||
}
|
||||
|
||||
func hasElectraKey(enc []byte) bool {
|
||||
if len(electraKey) >= len(enc) {
|
||||
// HasElectraKey verifies if the encoding is Electra compatible.
|
||||
func HasElectraKey(enc []byte) bool {
|
||||
if len(ElectraKey) >= len(enc) {
|
||||
return false
|
||||
}
|
||||
return bytes.Equal(enc[:len(electraKey)], electraKey)
|
||||
return bytes.Equal(enc[:len(ElectraKey)], ElectraKey)
|
||||
}
|
||||
|
||||
func hasElectraBlindKey(enc []byte) bool {
|
||||
|
||||
@@ -167,13 +167,13 @@ func decodeLightClientBootstrap(enc []byte) (interfaces.LightClientBootstrap, []
|
||||
}
|
||||
m = bootstrap
|
||||
syncCommitteeHash = enc[len(denebKey) : len(denebKey)+32]
|
||||
case hasElectraKey(enc):
|
||||
case HasElectraKey(enc):
|
||||
bootstrap := ðpb.LightClientBootstrapElectra{}
|
||||
if err := bootstrap.UnmarshalSSZ(enc[len(electraKey)+32:]); err != nil {
|
||||
if err := bootstrap.UnmarshalSSZ(enc[len(ElectraKey)+32:]); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not unmarshal Electra light client bootstrap")
|
||||
}
|
||||
m = bootstrap
|
||||
syncCommitteeHash = enc[len(electraKey) : len(electraKey)+32]
|
||||
syncCommitteeHash = enc[len(ElectraKey) : len(ElectraKey)+32]
|
||||
default:
|
||||
return nil, nil, errors.New("decoding of saved light client bootstrap is unsupported")
|
||||
}
|
||||
@@ -277,9 +277,9 @@ func decodeLightClientUpdate(enc []byte) (interfaces.LightClientUpdate, error) {
|
||||
return nil, errors.Wrap(err, "could not unmarshal Deneb light client update")
|
||||
}
|
||||
m = update
|
||||
case hasElectraKey(enc):
|
||||
case HasElectraKey(enc):
|
||||
update := ðpb.LightClientUpdateElectra{}
|
||||
if err := update.UnmarshalSSZ(enc[len(electraKey):]); err != nil {
|
||||
if err := update.UnmarshalSSZ(enc[len(ElectraKey):]); err != nil {
|
||||
return nil, errors.Wrap(err, "could not unmarshal Electra light client update")
|
||||
}
|
||||
m = update
|
||||
@@ -292,7 +292,7 @@ func decodeLightClientUpdate(enc []byte) (interfaces.LightClientUpdate, error) {
|
||||
func keyForLightClientUpdate(v int) ([]byte, error) {
|
||||
switch v {
|
||||
case version.Electra:
|
||||
return electraKey, nil
|
||||
return ElectraKey, nil
|
||||
case version.Deneb:
|
||||
return denebKey, nil
|
||||
case version.Capella:
|
||||
|
||||
@@ -53,7 +53,7 @@ var (
|
||||
saveBlindedBeaconBlocksKey = []byte("save-blinded-beacon-blocks")
|
||||
denebKey = []byte("deneb")
|
||||
denebBlindKey = []byte("blind-deneb")
|
||||
electraKey = []byte("electra")
|
||||
ElectraKey = []byte("electra")
|
||||
electraBlindKey = []byte("blind-electra")
|
||||
fuluKey = []byte("fulu")
|
||||
fuluBlindKey = []byte("blind-fulu")
|
||||
|
||||
@@ -357,7 +357,7 @@ func (s *Store) processElectra(ctx context.Context, pbState *ethpb.BeaconStateEl
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encodedState := snappy.Encode(nil, append(electraKey, rawObj...))
|
||||
encodedState := snappy.Encode(nil, append(ElectraKey, rawObj...))
|
||||
if err := bucket.Put(rootHash, encodedState); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -530,9 +530,9 @@ func (s *Store) unmarshalState(_ context.Context, enc []byte, validatorEntries [
|
||||
protoState.Validators = validatorEntries
|
||||
}
|
||||
return statenative.InitializeFromProtoUnsafeFulu(protoState)
|
||||
case hasElectraKey(enc):
|
||||
case HasElectraKey(enc):
|
||||
protoState := ðpb.BeaconStateElectra{}
|
||||
if err := protoState.UnmarshalSSZ(enc[len(electraKey):]); err != nil {
|
||||
if err := protoState.UnmarshalSSZ(enc[len(ElectraKey):]); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal encoding for Electra")
|
||||
}
|
||||
ok, err := s.isStateValidatorMigrationOver()
|
||||
@@ -688,7 +688,7 @@ func marshalState(ctx context.Context, st state.ReadOnlyBeaconState) ([]byte, er
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return snappy.Encode(nil, append(electraKey, rawObj...)), nil
|
||||
return snappy.Encode(nil, append(ElectraKey, rawObj...)), nil
|
||||
case version.Fulu:
|
||||
rState, ok := st.ToProtoUnsafe().(*ethpb.BeaconStateFulu)
|
||||
if !ok {
|
||||
|
||||
@@ -15,6 +15,7 @@ go_library(
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/db/iface:go_default_library",
|
||||
"//beacon-chain/db/kv:go_default_library",
|
||||
"//beacon-chain/slasher/types:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
@@ -22,6 +23,7 @@ go_library(
|
||||
"//io/file:go_default_library",
|
||||
"//monitoring/tracing/trace:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_golang_snappy//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
@@ -50,6 +52,7 @@ go_test(
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_prysmaticlabs_fastssz//:go_default_library",
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
slashertypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/slasher/types"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
@@ -177,8 +178,8 @@ func TestStore_PruneAttestations_OK(t *testing.T) {
|
||||
if i > 0 {
|
||||
source = target - 1
|
||||
}
|
||||
att1 := createAttestationWrapper(source, target, []uint64{attester1}, []byte{0})
|
||||
att2 := createAttestationWrapper(source, target, []uint64{attester2}, []byte{1})
|
||||
att1 := createAttestationWrapper(version.Phase0, source, target, []uint64{attester1}, []byte{0})
|
||||
att2 := createAttestationWrapper(version.Phase0, source, target, []uint64{attester2}, []byte{1})
|
||||
attestations = append(attestations, att1, att2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,13 @@ import (
|
||||
"github.com/golang/snappy"
|
||||
"github.com/pkg/errors"
|
||||
ssz "github.com/prysmaticlabs/fastssz"
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/db/kv"
|
||||
slashertypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/slasher/types"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
@@ -692,6 +694,11 @@ func encodeAttestationRecord(att *slashertypes.IndexedAttestationWrapper) ([]byt
|
||||
return []byte{}, errors.New("nil proposal record")
|
||||
}
|
||||
|
||||
var versionKey []byte
|
||||
if att.IndexedAttestation.Version() >= version.Electra {
|
||||
versionKey = kv.ElectraKey
|
||||
}
|
||||
|
||||
// Encode attestation.
|
||||
encodedAtt, err := att.IndexedAttestation.MarshalSSZ()
|
||||
if err != nil {
|
||||
@@ -701,7 +708,14 @@ func encodeAttestationRecord(att *slashertypes.IndexedAttestationWrapper) ([]byt
|
||||
// Compress attestation.
|
||||
compressedAtt := snappy.Encode(nil, encodedAtt)
|
||||
|
||||
return append(att.DataRoot[:], compressedAtt...), nil
|
||||
enc := make([]byte, len(versionKey)+len(att.DataRoot)+len(compressedAtt))
|
||||
if len(versionKey) > 0 {
|
||||
copy(enc, versionKey)
|
||||
}
|
||||
copy(enc[len(versionKey):len(versionKey)+len(att.DataRoot)], att.DataRoot[:])
|
||||
copy(enc[len(versionKey)+len(att.DataRoot):], compressedAtt)
|
||||
|
||||
return enc, nil
|
||||
}
|
||||
|
||||
// Decode attestation record from bytes.
|
||||
@@ -711,6 +725,11 @@ func decodeAttestationRecord(encoded []byte) (*slashertypes.IndexedAttestationWr
|
||||
return nil, fmt.Errorf("wrong length for encoded attestation record, want minimum %d, got %d", rootSize, len(encoded))
|
||||
}
|
||||
|
||||
postElectra := kv.HasElectraKey(encoded)
|
||||
if postElectra {
|
||||
encoded = encoded[len(kv.ElectraKey):]
|
||||
}
|
||||
|
||||
// Decompress attestation.
|
||||
decodedAttBytes, err := snappy.Decode(nil, encoded[rootSize:])
|
||||
if err != nil {
|
||||
@@ -718,8 +737,14 @@ func decodeAttestationRecord(encoded []byte) (*slashertypes.IndexedAttestationWr
|
||||
}
|
||||
|
||||
// Decode attestation.
|
||||
decodedAtt := ðpb.IndexedAttestation{}
|
||||
if err := decodedAtt.UnmarshalSSZ(decodedAttBytes); err != nil {
|
||||
var decodedAtt ethpb.IndexedAtt
|
||||
if postElectra {
|
||||
decodedAtt = ðpb.IndexedAttestationElectra{}
|
||||
} else {
|
||||
decodedAtt = ðpb.IndexedAttestation{}
|
||||
}
|
||||
|
||||
if err = decodedAtt.UnmarshalSSZ(decodedAttBytes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -14,20 +14,16 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/runtime/version"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
)
|
||||
|
||||
func TestStore_AttestationRecordForValidator_SaveRetrieve(t *testing.T) {
|
||||
const attestationsCount = 11_000
|
||||
|
||||
// Create context.
|
||||
ctx := context.Background()
|
||||
|
||||
// Create database.
|
||||
beaconDB := setupDB(t)
|
||||
|
||||
// Define the validator index.
|
||||
validatorIndex := primitives.ValidatorIndex(1)
|
||||
phase0ValidatorIndex := primitives.ValidatorIndex(1)
|
||||
electraValidatorIndex := primitives.ValidatorIndex(2)
|
||||
|
||||
// Defines attestations to save and retrieve.
|
||||
attWrappers := make([]*slashertypes.IndexedAttestationWrapper, attestationsCount)
|
||||
@@ -36,33 +32,71 @@ func TestStore_AttestationRecordForValidator_SaveRetrieve(t *testing.T) {
|
||||
binary.LittleEndian.PutUint64(dataRoot[:], uint64(i))
|
||||
|
||||
attWrapper := createAttestationWrapper(
|
||||
version.Phase0,
|
||||
primitives.Epoch(i),
|
||||
primitives.Epoch(i+1),
|
||||
[]uint64{uint64(validatorIndex)},
|
||||
[]uint64{uint64(phase0ValidatorIndex)},
|
||||
dataRoot[:],
|
||||
)
|
||||
|
||||
attWrappers[i] = attWrapper
|
||||
}
|
||||
attWrappersElectra := make([]*slashertypes.IndexedAttestationWrapper, attestationsCount)
|
||||
for i := 0; i < attestationsCount; i++ {
|
||||
var dataRoot [32]byte
|
||||
binary.LittleEndian.PutUint64(dataRoot[:], uint64(i))
|
||||
|
||||
// Check on a sample of validators that no attestation records are available.
|
||||
for i := 0; i < attestationsCount; i += 100 {
|
||||
attRecord, err := beaconDB.AttestationRecordForValidator(ctx, validatorIndex, primitives.Epoch(i+1))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, attRecord == nil)
|
||||
attWrapper := createAttestationWrapper(
|
||||
version.Electra,
|
||||
primitives.Epoch(i),
|
||||
primitives.Epoch(i+1),
|
||||
[]uint64{uint64(electraValidatorIndex)},
|
||||
dataRoot[:],
|
||||
)
|
||||
|
||||
attWrappersElectra[i] = attWrapper
|
||||
}
|
||||
|
||||
// Save the attestation records to the database.
|
||||
err := beaconDB.SaveAttestationRecordsForValidators(ctx, attWrappers)
|
||||
require.NoError(t, err)
|
||||
type testCase struct {
|
||||
name string
|
||||
atts []*slashertypes.IndexedAttestationWrapper
|
||||
vi primitives.ValidatorIndex
|
||||
}
|
||||
testCases := []testCase{
|
||||
{
|
||||
name: "phase0",
|
||||
atts: attWrappers,
|
||||
vi: phase0ValidatorIndex,
|
||||
},
|
||||
{
|
||||
name: "electra",
|
||||
atts: attWrappersElectra,
|
||||
vi: electraValidatorIndex,
|
||||
},
|
||||
}
|
||||
|
||||
// Check on a sample of validators that attestation records are available.
|
||||
for i := 0; i < attestationsCount; i += 100 {
|
||||
expected := attWrappers[i]
|
||||
actual, err := beaconDB.AttestationRecordForValidator(ctx, validatorIndex, primitives.Epoch(i+1))
|
||||
require.NoError(t, err)
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Check on a sample of validators that no attestation records are available.
|
||||
for i := 0; i < attestationsCount; i += 100 {
|
||||
attRecord, err := beaconDB.AttestationRecordForValidator(ctx, tc.vi, primitives.Epoch(i+1))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, attRecord == nil)
|
||||
}
|
||||
|
||||
require.DeepEqual(t, expected.IndexedAttestation.GetData().Source.Epoch, actual.IndexedAttestation.GetData().Source.Epoch)
|
||||
// Save the attestation records to the database.
|
||||
err := beaconDB.SaveAttestationRecordsForValidators(ctx, tc.atts)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check on a sample of validators that attestation records are available.
|
||||
for i := 0; i < attestationsCount; i += 100 {
|
||||
expected := attWrappers[i]
|
||||
actual, err := beaconDB.AttestationRecordForValidator(ctx, tc.vi, primitives.Epoch(i+1))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.DeepEqual(t, expected.IndexedAttestation.GetData().Source.Epoch, actual.IndexedAttestation.GetData().Source.Epoch)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,55 +142,60 @@ func TestStore_LastEpochWrittenForValidators(t *testing.T) {
|
||||
|
||||
func TestStore_CheckAttesterDoubleVotes(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
beaconDB := setupDB(t)
|
||||
err := beaconDB.SaveAttestationRecordsForValidators(ctx, []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(2, 3, []uint64{0, 1}, []byte{1}),
|
||||
createAttestationWrapper(3, 4, []uint64{2, 3}, []byte{3}),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
slashableAtts := []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(2, 3, []uint64{0, 1}, []byte{2}), // Different signing root.
|
||||
createAttestationWrapper(3, 4, []uint64{2, 3}, []byte{4}), // Different signing root.
|
||||
}
|
||||
for _, ver := range []int{version.Phase0, version.Electra} {
|
||||
t.Run(version.String(ver), func(t *testing.T) {
|
||||
beaconDB := setupDB(t)
|
||||
err := beaconDB.SaveAttestationRecordsForValidators(ctx, []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(version.Phase0, 2, 3, []uint64{0, 1}, []byte{1}),
|
||||
createAttestationWrapper(version.Phase0, 3, 4, []uint64{2, 3}, []byte{3}),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
wanted := []*slashertypes.AttesterDoubleVote{
|
||||
{
|
||||
ValidatorIndex: 0,
|
||||
Target: 3,
|
||||
Wrapper_1: createAttestationWrapper(2, 3, []uint64{0, 1}, []byte{1}),
|
||||
Wrapper_2: createAttestationWrapper(2, 3, []uint64{0, 1}, []byte{2}),
|
||||
},
|
||||
{
|
||||
ValidatorIndex: 1,
|
||||
Target: 3,
|
||||
Wrapper_1: createAttestationWrapper(2, 3, []uint64{0, 1}, []byte{1}),
|
||||
Wrapper_2: createAttestationWrapper(2, 3, []uint64{0, 1}, []byte{2}),
|
||||
},
|
||||
{
|
||||
ValidatorIndex: 2,
|
||||
Target: 4,
|
||||
Wrapper_1: createAttestationWrapper(3, 4, []uint64{2, 3}, []byte{3}),
|
||||
Wrapper_2: createAttestationWrapper(3, 4, []uint64{2, 3}, []byte{4}),
|
||||
},
|
||||
{
|
||||
ValidatorIndex: 3,
|
||||
Target: 4,
|
||||
Wrapper_1: createAttestationWrapper(3, 4, []uint64{2, 3}, []byte{3}),
|
||||
Wrapper_2: createAttestationWrapper(3, 4, []uint64{2, 3}, []byte{4}),
|
||||
},
|
||||
}
|
||||
doubleVotes, err := beaconDB.CheckAttesterDoubleVotes(ctx, slashableAtts)
|
||||
require.NoError(t, err)
|
||||
sort.SliceStable(doubleVotes, func(i, j int) bool {
|
||||
return uint64(doubleVotes[i].ValidatorIndex) < uint64(doubleVotes[j].ValidatorIndex)
|
||||
})
|
||||
require.Equal(t, len(wanted), len(doubleVotes))
|
||||
for i, double := range doubleVotes {
|
||||
require.DeepEqual(t, wanted[i].ValidatorIndex, double.ValidatorIndex)
|
||||
require.DeepEqual(t, wanted[i].Target, double.Target)
|
||||
require.DeepEqual(t, wanted[i].Wrapper_1, double.Wrapper_1)
|
||||
require.DeepEqual(t, wanted[i].Wrapper_2, double.Wrapper_2)
|
||||
slashableAtts := []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(version.Phase0, 2, 3, []uint64{0, 1}, []byte{2}), // Different signing root.
|
||||
createAttestationWrapper(version.Phase0, 3, 4, []uint64{2, 3}, []byte{4}), // Different signing root.
|
||||
}
|
||||
|
||||
wanted := []*slashertypes.AttesterDoubleVote{
|
||||
{
|
||||
ValidatorIndex: 0,
|
||||
Target: 3,
|
||||
Wrapper_1: createAttestationWrapper(version.Phase0, 2, 3, []uint64{0, 1}, []byte{1}),
|
||||
Wrapper_2: createAttestationWrapper(version.Phase0, 2, 3, []uint64{0, 1}, []byte{2}),
|
||||
},
|
||||
{
|
||||
ValidatorIndex: 1,
|
||||
Target: 3,
|
||||
Wrapper_1: createAttestationWrapper(version.Phase0, 2, 3, []uint64{0, 1}, []byte{1}),
|
||||
Wrapper_2: createAttestationWrapper(version.Phase0, 2, 3, []uint64{0, 1}, []byte{2}),
|
||||
},
|
||||
{
|
||||
ValidatorIndex: 2,
|
||||
Target: 4,
|
||||
Wrapper_1: createAttestationWrapper(version.Phase0, 3, 4, []uint64{2, 3}, []byte{3}),
|
||||
Wrapper_2: createAttestationWrapper(version.Phase0, 3, 4, []uint64{2, 3}, []byte{4}),
|
||||
},
|
||||
{
|
||||
ValidatorIndex: 3,
|
||||
Target: 4,
|
||||
Wrapper_1: createAttestationWrapper(version.Phase0, 3, 4, []uint64{2, 3}, []byte{3}),
|
||||
Wrapper_2: createAttestationWrapper(version.Phase0, 3, 4, []uint64{2, 3}, []byte{4}),
|
||||
},
|
||||
}
|
||||
doubleVotes, err := beaconDB.CheckAttesterDoubleVotes(ctx, slashableAtts)
|
||||
require.NoError(t, err)
|
||||
sort.SliceStable(doubleVotes, func(i, j int) bool {
|
||||
return uint64(doubleVotes[i].ValidatorIndex) < uint64(doubleVotes[j].ValidatorIndex)
|
||||
})
|
||||
require.Equal(t, len(wanted), len(doubleVotes))
|
||||
for i, double := range doubleVotes {
|
||||
require.DeepEqual(t, wanted[i].ValidatorIndex, double.ValidatorIndex)
|
||||
require.DeepEqual(t, wanted[i].Target, double.Target)
|
||||
require.DeepEqual(t, wanted[i].Wrapper_1, double.Wrapper_1)
|
||||
require.DeepEqual(t, wanted[i].Wrapper_2, double.Wrapper_2)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,12 +415,20 @@ func Test_encodeDecodeAttestationRecord(t *testing.T) {
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty standard encode/decode",
|
||||
attWrapper: createAttestationWrapper(0, 0, nil /* indices */, nil /* signingRoot */),
|
||||
name: "phase0 empty standard encode/decode",
|
||||
attWrapper: createAttestationWrapper(version.Phase0, 0, 0, nil /* indices */, nil /* signingRoot */),
|
||||
},
|
||||
{
|
||||
name: "standard encode/decode",
|
||||
attWrapper: createAttestationWrapper(15, 6, []uint64{2, 4}, []byte("1") /* signingRoot */),
|
||||
name: "phase0 standard encode/decode",
|
||||
attWrapper: createAttestationWrapper(version.Phase0, 15, 6, []uint64{2, 4}, []byte("1") /* signingRoot */),
|
||||
},
|
||||
{
|
||||
name: "electra empty standard encode/decode",
|
||||
attWrapper: createAttestationWrapper(version.Electra, 0, 0, nil /* indices */, nil /* signingRoot */),
|
||||
},
|
||||
{
|
||||
name: "electra standard encode/decode",
|
||||
attWrapper: createAttestationWrapper(version.Electra, 15, 6, []uint64{2, 4}, []byte("1") /* signingRoot */),
|
||||
},
|
||||
{
|
||||
name: "failing encode/decode",
|
||||
@@ -433,7 +480,7 @@ func TestStore_HighestAttestations(t *testing.T) {
|
||||
{
|
||||
name: "should get highest att if single att in db",
|
||||
attestationsInDB: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(0, 3, []uint64{1}, []byte{1}),
|
||||
createAttestationWrapper(version.Phase0, 0, 3, []uint64{1}, []byte{1}),
|
||||
},
|
||||
indices: []primitives.ValidatorIndex{1},
|
||||
expected: []*ethpb.HighestAttestation{
|
||||
@@ -447,10 +494,10 @@ func TestStore_HighestAttestations(t *testing.T) {
|
||||
{
|
||||
name: "should get highest att for multiple with diff histories",
|
||||
attestationsInDB: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(0, 3, []uint64{2}, []byte{1}),
|
||||
createAttestationWrapper(1, 4, []uint64{3}, []byte{2}),
|
||||
createAttestationWrapper(2, 3, []uint64{4}, []byte{3}),
|
||||
createAttestationWrapper(5, 6, []uint64{5}, []byte{4}),
|
||||
createAttestationWrapper(version.Phase0, 0, 3, []uint64{2}, []byte{1}),
|
||||
createAttestationWrapper(version.Phase0, 1, 4, []uint64{3}, []byte{2}),
|
||||
createAttestationWrapper(version.Phase0, 2, 3, []uint64{4}, []byte{3}),
|
||||
createAttestationWrapper(version.Phase0, 5, 6, []uint64{5}, []byte{4}),
|
||||
},
|
||||
indices: []primitives.ValidatorIndex{2, 3, 4, 5},
|
||||
expected: []*ethpb.HighestAttestation{
|
||||
@@ -479,10 +526,10 @@ func TestStore_HighestAttestations(t *testing.T) {
|
||||
{
|
||||
name: "should get correct highest att for multiple shared atts with diff histories",
|
||||
attestationsInDB: []*slashertypes.IndexedAttestationWrapper{
|
||||
createAttestationWrapper(1, 4, []uint64{2, 3}, []byte{1}),
|
||||
createAttestationWrapper(2, 5, []uint64{3, 5}, []byte{2}),
|
||||
createAttestationWrapper(4, 5, []uint64{1, 2}, []byte{3}),
|
||||
createAttestationWrapper(6, 7, []uint64{5}, []byte{4}),
|
||||
createAttestationWrapper(version.Phase0, 1, 4, []uint64{2, 3}, []byte{1}),
|
||||
createAttestationWrapper(version.Phase0, 2, 5, []uint64{3, 5}, []byte{2}),
|
||||
createAttestationWrapper(version.Phase0, 4, 5, []uint64{1, 2}, []byte{3}),
|
||||
createAttestationWrapper(version.Phase0, 6, 7, []uint64{5}, []byte{4}),
|
||||
},
|
||||
indices: []primitives.ValidatorIndex{2, 3, 4, 5},
|
||||
expected: []*ethpb.HighestAttestation{
|
||||
@@ -533,7 +580,7 @@ func BenchmarkHighestAttestations(b *testing.B) {
|
||||
}
|
||||
atts := make([]*slashertypes.IndexedAttestationWrapper, count)
|
||||
for i := 0; i < count; i++ {
|
||||
atts[i] = createAttestationWrapper(primitives.Epoch(i), primitives.Epoch(i+2), indicesPerAtt[i], []byte{})
|
||||
atts[i] = createAttestationWrapper(version.Phase0, primitives.Epoch(i), primitives.Epoch(i+2), indicesPerAtt[i], []byte{})
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
@@ -570,7 +617,7 @@ func BenchmarkStore_CheckDoubleBlockProposals(b *testing.B) {
|
||||
}
|
||||
atts := make([]*slashertypes.IndexedAttestationWrapper, count)
|
||||
for i := 0; i < count; i++ {
|
||||
atts[i] = createAttestationWrapper(primitives.Epoch(i), primitives.Epoch(i+2), indicesPerAtt[i], []byte{})
|
||||
atts[i] = createAttestationWrapper(version.Phase0, primitives.Epoch(i), primitives.Epoch(i+2), indicesPerAtt[i], []byte{})
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
@@ -609,7 +656,7 @@ func createProposalWrapper(t *testing.T, slot primitives.Slot, proposerIndex pri
|
||||
}
|
||||
}
|
||||
|
||||
func createAttestationWrapper(source, target primitives.Epoch, indices []uint64, dataRootBytes []byte) *slashertypes.IndexedAttestationWrapper {
|
||||
func createAttestationWrapper(ver int, source, target primitives.Epoch, indices []uint64, dataRootBytes []byte) *slashertypes.IndexedAttestationWrapper {
|
||||
dataRoot := bytesutil.ToBytes32(dataRootBytes)
|
||||
if dataRootBytes == nil {
|
||||
dataRoot = params.BeaconConfig().ZeroHash
|
||||
@@ -627,6 +674,16 @@ func createAttestationWrapper(source, target primitives.Epoch, indices []uint64,
|
||||
},
|
||||
}
|
||||
|
||||
if ver >= version.Electra {
|
||||
return &slashertypes.IndexedAttestationWrapper{
|
||||
IndexedAttestation: ðpb.IndexedAttestationElectra{
|
||||
AttestingIndices: indices,
|
||||
Data: data,
|
||||
Signature: params.BeaconConfig().EmptySignature[:],
|
||||
},
|
||||
DataRoot: dataRoot,
|
||||
}
|
||||
}
|
||||
return &slashertypes.IndexedAttestationWrapper{
|
||||
IndexedAttestation: ðpb.IndexedAttestation{
|
||||
AttestingIndices: indices,
|
||||
|
||||
@@ -222,6 +222,39 @@ func operationEventsFixtures(t *testing.T) (*topicRequest, []*feed.Event) {
|
||||
},
|
||||
},
|
||||
},
|
||||
&feed.Event{
|
||||
Type: operation.AttesterSlashingReceived,
|
||||
Data: &operation.AttesterSlashingReceivedData{
|
||||
AttesterSlashing: ð.AttesterSlashingElectra{
|
||||
Attestation_1: ð.IndexedAttestationElectra{
|
||||
AttestingIndices: []uint64{0, 1},
|
||||
Data: ð.AttestationData{
|
||||
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
|
||||
Source: ð.Checkpoint{
|
||||
Root: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
Target: ð.Checkpoint{
|
||||
Root: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
},
|
||||
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
},
|
||||
Attestation_2: ð.IndexedAttestationElectra{
|
||||
AttestingIndices: []uint64{0, 1},
|
||||
Data: ð.AttestationData{
|
||||
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
|
||||
Source: ð.Checkpoint{
|
||||
Root: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
Target: ð.Checkpoint{
|
||||
Root: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
},
|
||||
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&feed.Event{
|
||||
Type: operation.ProposerSlashingReceived,
|
||||
Data: &operation.ProposerSlashingReceivedData{
|
||||
@@ -544,7 +577,7 @@ func TestStuckReaderScenarios(t *testing.T) {
|
||||
|
||||
func wedgedWriterTestCase(t *testing.T, queueDepth func([]*feed.Event) int) {
|
||||
topics, events := operationEventsFixtures(t)
|
||||
require.Equal(t, 8, len(events))
|
||||
require.Equal(t, 9, len(events))
|
||||
|
||||
// set eventFeedDepth to a number lower than the events we intend to send to force the server to drop the reader.
|
||||
stn := mockChain.NewEventFeedWrapper()
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
### Added
|
||||
|
||||
- Update slasher service to Electra
|
||||
- Update slasher service to Electra.
|
||||
3
changelog/radek_eip-7549-slasher-pt2.md
Normal file
3
changelog/radek_eip-7549-slasher-pt2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- Handle `AttesterSlashingElectra` everywhere in the codebase.
|
||||
@@ -132,6 +132,7 @@ func compareConfigs(t *testing.T, expected, actual *BeaconChainConfig) {
|
||||
require.DeepEqual(t, expected.ProportionalSlashingMultiplier, actual.ProportionalSlashingMultiplier)
|
||||
require.DeepEqual(t, expected.MaxProposerSlashings, actual.MaxProposerSlashings)
|
||||
require.DeepEqual(t, expected.MaxAttesterSlashings, actual.MaxAttesterSlashings)
|
||||
require.DeepEqual(t, expected.MaxAttesterSlashingsElectra, actual.MaxAttesterSlashingsElectra)
|
||||
require.DeepEqual(t, expected.MaxAttestations, actual.MaxAttestations)
|
||||
require.DeepEqual(t, expected.MaxDeposits, actual.MaxDeposits)
|
||||
require.DeepEqual(t, expected.MaxVoluntaryExits, actual.MaxVoluntaryExits)
|
||||
|
||||
@@ -135,6 +135,7 @@ func assertEqualConfigs(t *testing.T, name string, fields []string, expected, ac
|
||||
// Max operations per block.
|
||||
assert.Equal(t, expected.MaxProposerSlashings, actual.MaxProposerSlashings, "%s: MaxProposerSlashings", name)
|
||||
assert.Equal(t, expected.MaxAttesterSlashings, actual.MaxAttesterSlashings, "%s: MaxAttesterSlashings", name)
|
||||
assert.Equal(t, expected.MaxAttesterSlashingsElectra, actual.MaxAttesterSlashingsElectra, "%s: MaxAttesterSlashingsElectra", name)
|
||||
assert.Equal(t, expected.MaxAttestations, actual.MaxAttestations, "%s: MaxAttestations", name)
|
||||
assert.Equal(t, expected.MaxDeposits, actual.MaxDeposits, "%s: MaxDeposits", name)
|
||||
assert.Equal(t, expected.MaxVoluntaryExits, actual.MaxVoluntaryExits, "%s: MaxVoluntaryExits", name)
|
||||
|
||||
@@ -88,6 +88,7 @@ func compareConfigs(t *testing.T, expected, actual *params.BeaconChainConfig) {
|
||||
require.DeepEqual(t, expected.ProportionalSlashingMultiplier, actual.ProportionalSlashingMultiplier)
|
||||
require.DeepEqual(t, expected.MaxProposerSlashings, actual.MaxProposerSlashings)
|
||||
require.DeepEqual(t, expected.MaxAttesterSlashings, actual.MaxAttesterSlashings)
|
||||
require.DeepEqual(t, expected.MaxAttesterSlashingsElectra, actual.MaxAttesterSlashingsElectra)
|
||||
require.DeepEqual(t, expected.MaxAttestations, actual.MaxAttestations)
|
||||
require.DeepEqual(t, expected.MaxDeposits, actual.MaxDeposits)
|
||||
require.DeepEqual(t, expected.MaxVoluntaryExits, actual.MaxVoluntaryExits)
|
||||
|
||||
@@ -391,3 +391,16 @@ func HydrateIndexedAttestation(a *ethpb.IndexedAttestation) *ethpb.IndexedAttest
|
||||
a.Data = HydrateAttestationData(a.Data)
|
||||
return a
|
||||
}
|
||||
|
||||
// HydrateIndexedAttestationElectra hydrates an indexed attestation with correct field length sizes
|
||||
// to comply with fssz marshalling and unmarshalling rules.
|
||||
func HydrateIndexedAttestationElectra(a *ethpb.IndexedAttestationElectra) *ethpb.IndexedAttestationElectra {
|
||||
if a.Signature == nil {
|
||||
a.Signature = make([]byte, 96)
|
||||
}
|
||||
if a.Data == nil {
|
||||
a.Data = ðpb.AttestationData{}
|
||||
}
|
||||
a.Data = HydrateAttestationData(a.Data)
|
||||
return a
|
||||
}
|
||||
|
||||
@@ -122,18 +122,14 @@ func (v *validator) SubmitAttestation(ctx context.Context, slot primitives.Slot,
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Extend to Electra
|
||||
phase0Att, ok := indexedAtt.(*ethpb.IndexedAttestation)
|
||||
if ok {
|
||||
// Send the attestation to the beacon node.
|
||||
if err := v.db.SlashableAttestationCheck(ctx, phase0Att, pubKey, signingRoot, v.emitAccountMetrics, ValidatorAttestFailVec); err != nil {
|
||||
log.WithError(err).Error("Failed attestation slashing protection check")
|
||||
log.WithFields(
|
||||
attestationLogFields(pubKey, indexedAtt),
|
||||
).Debug("Attempted slashable attestation details")
|
||||
tracing.AnnotateError(span, err)
|
||||
return
|
||||
}
|
||||
// Send the attestation to the beacon node.
|
||||
if err := v.db.SlashableAttestationCheck(ctx, indexedAtt, pubKey, signingRoot, v.emitAccountMetrics, ValidatorAttestFailVec); err != nil {
|
||||
log.WithError(err).Error("Failed attestation slashing protection check")
|
||||
log.WithFields(
|
||||
attestationLogFields(pubKey, indexedAtt),
|
||||
).Debug("Attempted slashable attestation details")
|
||||
tracing.AnnotateError(span, err)
|
||||
return
|
||||
}
|
||||
|
||||
var aggregationBitfield bitfield.Bitlist
|
||||
|
||||
Reference in New Issue
Block a user