package monitor import ( "context" "fmt" "testing" types "github.com/prysmaticlabs/eth2-types" "github.com/prysmaticlabs/prysm/config/params" "github.com/prysmaticlabs/prysm/encoding/bytesutil" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper" "github.com/prysmaticlabs/prysm/testing/require" "github.com/prysmaticlabs/prysm/testing/util" logTest "github.com/sirupsen/logrus/hooks/test" ) func TestProcessSlashings(t *testing.T) { tests := []struct { name string block *ethpb.BeaconBlock wantedErr string }{ { name: "Proposer slashing a tracked index", block: ðpb.BeaconBlock{ Body: ðpb.BeaconBlockBody{ ProposerSlashings: []*ethpb.ProposerSlashing{ { Header_1: ðpb.SignedBeaconBlockHeader{ Header: ðpb.BeaconBlockHeader{ ProposerIndex: 2, Slot: params.BeaconConfig().SlotsPerEpoch + 1, }, }, Header_2: ðpb.SignedBeaconBlockHeader{ Header: ðpb.BeaconBlockHeader{ ProposerIndex: 2, Slot: 0, }, }, }, }, }, }, wantedErr: "\"Proposer slashing was included\" ProposerIndex=2", }, { name: "Proposer slashing an untracked index", block: ðpb.BeaconBlock{ Body: ðpb.BeaconBlockBody{ ProposerSlashings: []*ethpb.ProposerSlashing{ { Header_1: ðpb.SignedBeaconBlockHeader{ Header: ðpb.BeaconBlockHeader{ ProposerIndex: 3, Slot: params.BeaconConfig().SlotsPerEpoch + 4, }, }, Header_2: ðpb.SignedBeaconBlockHeader{ Header: ðpb.BeaconBlockHeader{ ProposerIndex: 3, Slot: 0, }, }, }, }, }, }, wantedErr: "", }, { name: "Attester slashing a tracked index", block: ðpb.BeaconBlock{ Body: ðpb.BeaconBlockBody{ AttesterSlashings: []*ethpb.AttesterSlashing{ { Attestation_1: util.HydrateIndexedAttestation(ðpb.IndexedAttestation{ Data: ðpb.AttestationData{ Source: ðpb.Checkpoint{Epoch: 1}, }, AttestingIndices: []uint64{1, 3, 4}, }), Attestation_2: util.HydrateIndexedAttestation(ðpb.IndexedAttestation{ AttestingIndices: []uint64{1, 5, 6}, }), }, }, }, }, wantedErr: "\"Attester slashing was included\" AttesterIndex=1", }, { name: "Attester slashing untracked index", block: ðpb.BeaconBlock{ Body: ðpb.BeaconBlockBody{ AttesterSlashings: []*ethpb.AttesterSlashing{ { Attestation_1: util.HydrateIndexedAttestation(ðpb.IndexedAttestation{ Data: ðpb.AttestationData{ Source: ðpb.Checkpoint{Epoch: 1}, }, AttestingIndices: []uint64{1, 3, 4}, }), Attestation_2: util.HydrateIndexedAttestation(ðpb.IndexedAttestation{ AttestingIndices: []uint64{3, 5, 6}, }), }, }, }, }, wantedErr: "", }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hook := logTest.NewGlobal() s := &Service{ config: &ValidatorMonitorConfig{ TrackedValidators: map[types.ValidatorIndex]interface{}{ 1: nil, 2: nil, }, }, } s.processSlashings(wrapper.WrappedPhase0BeaconBlock(tt.block)) if tt.wantedErr != "" { require.LogsContain(t, hook, tt.wantedErr) } else { require.LogsDoNotContain(t, hook, "slashing") } }) } } func TestProcessProposedBlock(t *testing.T) { tests := []struct { name string block *ethpb.BeaconBlock wantedErr string }{ { name: "Block proposed by tracked validator", block: ðpb.BeaconBlock{ Slot: 6, ProposerIndex: 12, ParentRoot: bytesutil.PadTo([]byte("hello-world"), 32), StateRoot: bytesutil.PadTo([]byte("state-world"), 32), }, wantedErr: "\"Proposed block was included\" BalanceChange=100000000 BlockRoot=0x68656c6c6f2d NewBalance=32000000000 ParentRoot=0x68656c6c6f2d ProposerIndex=12 Slot=6 Version=0 prefix=monitor", }, { name: "Block proposed by untracked validator", block: ðpb.BeaconBlock{ Slot: 6, ProposerIndex: 13, ParentRoot: bytesutil.PadTo([]byte("hello-world"), 32), StateRoot: bytesutil.PadTo([]byte("state-world"), 32), }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hook := logTest.NewGlobal() s := setupService(t) beaconState, _ := util.DeterministicGenesisState(t, 256) root := [32]byte{} copy(root[:], "hello-world") s.processProposedBlock(beaconState, root, wrapper.WrappedPhase0BeaconBlock(tt.block)) if tt.wantedErr != "" { require.LogsContain(t, hook, tt.wantedErr) } else { require.LogsDoNotContain(t, hook, "included") } }) } } func TestProcessBlock_ProposerAndSlashedTrackedVals(t *testing.T) { hook := logTest.NewGlobal() ctx := context.Background() s := setupService(t) genesis, keys := util.DeterministicGenesisState(t, 64) genConfig := util.DefaultBlockGenConfig() genConfig.NumProposerSlashings = 1 b, err := util.GenerateFullBlock(genesis, keys, genConfig, 1) idx := b.Block.Body.ProposerSlashings[0].Header_1.Header.ProposerIndex if !s.TrackedIndex(idx) { s.config.TrackedValidators[idx] = nil s.latestPerformance[idx] = ValidatorLatestPerformance{ balance: 31900000000, } s.aggregatedPerformance[idx] = ValidatorAggregatedPerformance{} } require.NoError(t, err) root, err := b.GetBlock().HashTreeRoot() require.NoError(t, err) require.NoError(t, s.config.StateGen.SaveState(ctx, root, genesis)) wanted1 := fmt.Sprintf("\"Proposed block was included\" BalanceChange=100000000 BlockRoot=%#x NewBalance=32000000000 ParentRoot=0x67a9fe4d0d8d ProposerIndex=15 Slot=1 Version=0 prefix=monitor", bytesutil.Trunc(root[:])) wanted2 := fmt.Sprintf("\"Proposer slashing was included\" ProposerIndex=%d Root1=0x000100000000 Root2=0x000200000000 SlashingSlot=0 Slot:=1 prefix=monitor", idx) wrapped := wrapper.WrappedPhase0SignedBeaconBlock(b) s.processBlock(ctx, wrapped) require.LogsContain(t, hook, wanted1) require.LogsContain(t, hook, wanted2) }