github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/core/blocks/proposer_slashing_test.go (about) 1 package blocks_test 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 8 types "github.com/prysmaticlabs/eth2-types" 9 "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" 10 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 11 v "github.com/prysmaticlabs/prysm/beacon-chain/core/validators" 12 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 13 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1" 14 pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 15 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 16 "github.com/prysmaticlabs/prysm/shared/bls" 17 "github.com/prysmaticlabs/prysm/shared/bytesutil" 18 "github.com/prysmaticlabs/prysm/shared/params" 19 "github.com/prysmaticlabs/prysm/shared/testutil" 20 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 21 "github.com/prysmaticlabs/prysm/shared/testutil/require" 22 ) 23 24 func TestProcessProposerSlashings_UnmatchedHeaderSlots(t *testing.T) { 25 26 beaconState, _ := testutil.DeterministicGenesisState(t, 20) 27 currentSlot := types.Slot(0) 28 slashings := []*ethpb.ProposerSlashing{ 29 { 30 Header_1: ðpb.SignedBeaconBlockHeader{ 31 Header: ðpb.BeaconBlockHeader{ 32 ProposerIndex: 1, 33 Slot: params.BeaconConfig().SlotsPerEpoch + 1, 34 }, 35 }, 36 Header_2: ðpb.SignedBeaconBlockHeader{ 37 Header: ðpb.BeaconBlockHeader{ 38 ProposerIndex: 1, 39 Slot: 0, 40 }, 41 }, 42 }, 43 } 44 require.NoError(t, beaconState.SetSlot(currentSlot)) 45 46 b := testutil.NewBeaconBlock() 47 b.Block = ðpb.BeaconBlock{ 48 Body: ðpb.BeaconBlockBody{ 49 ProposerSlashings: slashings, 50 }, 51 } 52 want := "mismatched header slots" 53 _, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, b.Block.Body.ProposerSlashings, v.SlashValidator) 54 assert.ErrorContains(t, want, err) 55 } 56 57 func TestProcessProposerSlashings_SameHeaders(t *testing.T) { 58 59 beaconState, _ := testutil.DeterministicGenesisState(t, 2) 60 currentSlot := types.Slot(0) 61 slashings := []*ethpb.ProposerSlashing{ 62 { 63 Header_1: ðpb.SignedBeaconBlockHeader{ 64 Header: ðpb.BeaconBlockHeader{ 65 ProposerIndex: 1, 66 Slot: 0, 67 }, 68 }, 69 Header_2: ðpb.SignedBeaconBlockHeader{ 70 Header: ðpb.BeaconBlockHeader{ 71 ProposerIndex: 1, 72 Slot: 0, 73 }, 74 }, 75 }, 76 } 77 78 require.NoError(t, beaconState.SetSlot(currentSlot)) 79 b := testutil.NewBeaconBlock() 80 b.Block = ðpb.BeaconBlock{ 81 Body: ðpb.BeaconBlockBody{ 82 ProposerSlashings: slashings, 83 }, 84 } 85 want := "expected slashing headers to differ" 86 _, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, b.Block.Body.ProposerSlashings, v.SlashValidator) 87 assert.ErrorContains(t, want, err) 88 } 89 90 func TestProcessProposerSlashings_ValidatorNotSlashable(t *testing.T) { 91 registry := []*ethpb.Validator{ 92 { 93 PublicKey: []byte("key"), 94 Slashed: true, 95 ActivationEpoch: 0, 96 WithdrawableEpoch: 0, 97 }, 98 } 99 currentSlot := types.Slot(0) 100 slashings := []*ethpb.ProposerSlashing{ 101 { 102 Header_1: ðpb.SignedBeaconBlockHeader{ 103 Header: ðpb.BeaconBlockHeader{ 104 ProposerIndex: 0, 105 Slot: 0, 106 BodyRoot: []byte("foo"), 107 }, 108 Signature: bytesutil.PadTo([]byte("A"), 96), 109 }, 110 Header_2: ðpb.SignedBeaconBlockHeader{ 111 Header: ðpb.BeaconBlockHeader{ 112 ProposerIndex: 0, 113 Slot: 0, 114 BodyRoot: []byte("bar"), 115 }, 116 Signature: bytesutil.PadTo([]byte("B"), 96), 117 }, 118 }, 119 } 120 121 beaconState, err := v1.InitializeFromProto(&pb.BeaconState{ 122 Validators: registry, 123 Slot: currentSlot, 124 }) 125 require.NoError(t, err) 126 b := testutil.NewBeaconBlock() 127 b.Block = ðpb.BeaconBlock{ 128 Body: ðpb.BeaconBlockBody{ 129 ProposerSlashings: slashings, 130 }, 131 } 132 want := fmt.Sprintf( 133 "validator with key %#x is not slashable", 134 bytesutil.ToBytes48(beaconState.Validators()[0].PublicKey), 135 ) 136 _, err = blocks.ProcessProposerSlashings(context.Background(), beaconState, b.Block.Body.ProposerSlashings, v.SlashValidator) 137 assert.ErrorContains(t, want, err) 138 } 139 140 func TestProcessProposerSlashings_AppliesCorrectStatus(t *testing.T) { 141 // We test the case when data is correct and verify the validator 142 // registry has been updated. 143 beaconState, privKeys := testutil.DeterministicGenesisState(t, 100) 144 proposerIdx := types.ValidatorIndex(1) 145 146 header1 := ðpb.SignedBeaconBlockHeader{ 147 Header: testutil.HydrateBeaconHeader(ðpb.BeaconBlockHeader{ 148 ProposerIndex: proposerIdx, 149 StateRoot: bytesutil.PadTo([]byte("A"), 32), 150 }), 151 } 152 var err error 153 header1.Signature, err = helpers.ComputeDomainAndSign(beaconState, 0, header1.Header, params.BeaconConfig().DomainBeaconProposer, privKeys[proposerIdx]) 154 require.NoError(t, err) 155 156 header2 := testutil.HydrateSignedBeaconHeader(ðpb.SignedBeaconBlockHeader{ 157 Header: ðpb.BeaconBlockHeader{ 158 ProposerIndex: proposerIdx, 159 StateRoot: bytesutil.PadTo([]byte("B"), 32), 160 }, 161 }) 162 header2.Signature, err = helpers.ComputeDomainAndSign(beaconState, 0, header2.Header, params.BeaconConfig().DomainBeaconProposer, privKeys[proposerIdx]) 163 require.NoError(t, err) 164 165 slashings := []*ethpb.ProposerSlashing{ 166 { 167 Header_1: header1, 168 Header_2: header2, 169 }, 170 } 171 172 block := testutil.NewBeaconBlock() 173 block.Block.Body.ProposerSlashings = slashings 174 175 newState, err := blocks.ProcessProposerSlashings(context.Background(), beaconState, block.Block.Body.ProposerSlashings, v.SlashValidator) 176 require.NoError(t, err) 177 178 newStateVals := newState.Validators() 179 if newStateVals[1].ExitEpoch != beaconState.Validators()[1].ExitEpoch { 180 t.Errorf("Proposer with index 1 did not correctly exit,"+"wanted slot:%d, got:%d", 181 newStateVals[1].ExitEpoch, beaconState.Validators()[1].ExitEpoch) 182 } 183 } 184 185 func TestVerifyProposerSlashing(t *testing.T) { 186 type args struct { 187 beaconState iface.BeaconState 188 slashing *ethpb.ProposerSlashing 189 } 190 191 beaconState, sks := testutil.DeterministicGenesisState(t, 2) 192 currentSlot := types.Slot(0) 193 require.NoError(t, beaconState.SetSlot(currentSlot)) 194 rand1, err := bls.RandKey() 195 require.NoError(t, err) 196 sig1 := rand1.Sign([]byte("foo")).Marshal() 197 198 rand2, err := bls.RandKey() 199 require.NoError(t, err) 200 sig2 := rand2.Sign([]byte("bar")).Marshal() 201 202 tests := []struct { 203 name string 204 args args 205 wantErr string 206 }{ 207 { 208 name: "same header, same slot as state", 209 args: args{ 210 slashing: ðpb.ProposerSlashing{ 211 Header_1: testutil.HydrateSignedBeaconHeader(ðpb.SignedBeaconBlockHeader{ 212 Header: ðpb.BeaconBlockHeader{ 213 ProposerIndex: 1, 214 Slot: currentSlot, 215 }, 216 }), 217 Header_2: testutil.HydrateSignedBeaconHeader(ðpb.SignedBeaconBlockHeader{ 218 Header: ðpb.BeaconBlockHeader{ 219 ProposerIndex: 1, 220 Slot: currentSlot, 221 }, 222 }), 223 }, 224 beaconState: beaconState, 225 }, 226 wantErr: "expected slashing headers to differ", 227 }, 228 { // Regression test for https://github.com/sigp/beacon-fuzz/issues/74 229 name: "same header, different signatures", 230 args: args{ 231 slashing: ðpb.ProposerSlashing{ 232 Header_1: testutil.HydrateSignedBeaconHeader(ðpb.SignedBeaconBlockHeader{ 233 Header: ðpb.BeaconBlockHeader{ 234 ProposerIndex: 1, 235 }, 236 Signature: sig1, 237 }), 238 Header_2: testutil.HydrateSignedBeaconHeader(ðpb.SignedBeaconBlockHeader{ 239 Header: ðpb.BeaconBlockHeader{ 240 ProposerIndex: 1, 241 }, 242 Signature: sig2, 243 }), 244 }, 245 beaconState: beaconState, 246 }, 247 wantErr: "expected slashing headers to differ", 248 }, 249 { 250 name: "slashing in future epoch", 251 args: args{ 252 slashing: ðpb.ProposerSlashing{ 253 Header_1: ðpb.SignedBeaconBlockHeader{ 254 Header: ðpb.BeaconBlockHeader{ 255 ProposerIndex: 1, 256 Slot: 65, 257 StateRoot: bytesutil.PadTo([]byte{}, 32), 258 BodyRoot: bytesutil.PadTo([]byte{}, 32), 259 ParentRoot: bytesutil.PadTo([]byte("foo"), 32), 260 }, 261 }, 262 Header_2: ðpb.SignedBeaconBlockHeader{ 263 Header: ðpb.BeaconBlockHeader{ 264 ProposerIndex: 1, 265 Slot: 65, 266 StateRoot: bytesutil.PadTo([]byte{}, 32), 267 BodyRoot: bytesutil.PadTo([]byte{}, 32), 268 ParentRoot: bytesutil.PadTo([]byte("bar"), 32), 269 }, 270 }, 271 }, 272 beaconState: beaconState, 273 }, 274 wantErr: "", 275 }, 276 } 277 for _, tt := range tests { 278 t.Run(tt.name, func(t *testing.T) { 279 280 sk := sks[tt.args.slashing.Header_1.Header.ProposerIndex] 281 d, err := helpers.Domain(tt.args.beaconState.Fork(), helpers.SlotToEpoch(tt.args.slashing.Header_1.Header.Slot), params.BeaconConfig().DomainBeaconProposer, tt.args.beaconState.GenesisValidatorRoot()) 282 require.NoError(t, err) 283 if tt.args.slashing.Header_1.Signature == nil { 284 sr, err := helpers.ComputeSigningRoot(tt.args.slashing.Header_1.Header, d) 285 require.NoError(t, err) 286 tt.args.slashing.Header_1.Signature = sk.Sign(sr[:]).Marshal() 287 } 288 if tt.args.slashing.Header_2.Signature == nil { 289 sr, err := helpers.ComputeSigningRoot(tt.args.slashing.Header_2.Header, d) 290 require.NoError(t, err) 291 tt.args.slashing.Header_2.Signature = sk.Sign(sr[:]).Marshal() 292 } 293 if err := blocks.VerifyProposerSlashing(tt.args.beaconState, tt.args.slashing); (err != nil || tt.wantErr != "") && err.Error() != tt.wantErr { 294 t.Errorf("VerifyProposerSlashing() error = %v, wantErr %v", err, tt.wantErr) 295 } 296 }) 297 } 298 }