github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/operations/slashings/service_proposer_test.go (about) 1 package slashings 2 3 import ( 4 "context" 5 "testing" 6 7 types "github.com/prysmaticlabs/eth2-types" 8 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 9 "github.com/prysmaticlabs/prysm/shared/params" 10 "github.com/prysmaticlabs/prysm/shared/testutil" 11 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 12 "github.com/prysmaticlabs/prysm/shared/testutil/require" 13 ) 14 15 func proposerSlashingForValIdx(valIdx types.ValidatorIndex) *ethpb.ProposerSlashing { 16 return ðpb.ProposerSlashing{ 17 Header_1: ðpb.SignedBeaconBlockHeader{ 18 Header: ðpb.BeaconBlockHeader{ProposerIndex: valIdx}, 19 }, 20 Header_2: ðpb.SignedBeaconBlockHeader{ 21 Header: ðpb.BeaconBlockHeader{ProposerIndex: valIdx}, 22 }, 23 } 24 } 25 26 func TestPool_InsertProposerSlashing(t *testing.T) { 27 type fields struct { 28 wantedErr string 29 pending []*ethpb.ProposerSlashing 30 included map[types.ValidatorIndex]bool 31 } 32 type args struct { 33 slashings []*ethpb.ProposerSlashing 34 } 35 36 beaconState, privKeys := testutil.DeterministicGenesisState(t, 64) 37 slashings := make([]*ethpb.ProposerSlashing, 20) 38 for i := 0; i < len(slashings); i++ { 39 sl, err := testutil.GenerateProposerSlashingForValidator(beaconState, privKeys[i], types.ValidatorIndex(i)) 40 require.NoError(t, err) 41 slashings[i] = sl 42 } 43 44 require.NoError(t, beaconState.SetSlot(params.BeaconConfig().SlotsPerEpoch)) 45 46 // We mark the following validators with some preconditions. 47 exitedVal, err := beaconState.ValidatorAtIndex(types.ValidatorIndex(2)) 48 require.NoError(t, err) 49 exitedVal.WithdrawableEpoch = 0 50 futureExitedVal, err := beaconState.ValidatorAtIndex(types.ValidatorIndex(4)) 51 require.NoError(t, err) 52 futureExitedVal.WithdrawableEpoch = 17 53 slashedVal, err := beaconState.ValidatorAtIndex(types.ValidatorIndex(5)) 54 require.NoError(t, err) 55 slashedVal.Slashed = true 56 require.NoError(t, beaconState.UpdateValidatorAtIndex(types.ValidatorIndex(2), exitedVal)) 57 require.NoError(t, beaconState.UpdateValidatorAtIndex(types.ValidatorIndex(4), futureExitedVal)) 58 require.NoError(t, beaconState.UpdateValidatorAtIndex(types.ValidatorIndex(5), slashedVal)) 59 60 tests := []struct { 61 name string 62 fields fields 63 args args 64 want []*ethpb.ProposerSlashing 65 }{ 66 { 67 name: "Empty list", 68 fields: fields{ 69 pending: make([]*ethpb.ProposerSlashing, 0), 70 included: make(map[types.ValidatorIndex]bool), 71 }, 72 args: args{ 73 slashings: slashings[0:1], 74 }, 75 want: slashings[0:1], 76 }, 77 { 78 name: "Duplicate identical slashing", 79 fields: fields{ 80 pending: slashings[0:1], 81 included: make(map[types.ValidatorIndex]bool), 82 wantedErr: "slashing object already exists in pending proposer slashings", 83 }, 84 args: args{ 85 slashings: slashings[0:1], 86 }, 87 want: slashings[0:1], 88 }, 89 { 90 name: "Slashing for exited validator", 91 fields: fields{ 92 pending: []*ethpb.ProposerSlashing{}, 93 included: make(map[types.ValidatorIndex]bool), 94 wantedErr: "is not slashable", 95 }, 96 args: args{ 97 slashings: slashings[2:3], 98 }, 99 want: []*ethpb.ProposerSlashing{}, 100 }, 101 { 102 name: "Slashing for exiting validator", 103 fields: fields{ 104 pending: []*ethpb.ProposerSlashing{}, 105 included: make(map[types.ValidatorIndex]bool), 106 }, 107 args: args{ 108 slashings: slashings[4:5], 109 }, 110 want: slashings[4:5], 111 }, 112 { 113 name: "Slashing for slashed validator", 114 fields: fields{ 115 pending: []*ethpb.ProposerSlashing{}, 116 included: make(map[types.ValidatorIndex]bool), 117 wantedErr: "not slashable", 118 }, 119 args: args{ 120 slashings: slashings[5:6], 121 }, 122 want: []*ethpb.ProposerSlashing{}, 123 }, 124 { 125 name: "Already included", 126 fields: fields{ 127 pending: []*ethpb.ProposerSlashing{}, 128 included: map[types.ValidatorIndex]bool{ 129 1: true, 130 }, 131 wantedErr: "cannot be slashed", 132 }, 133 args: args{ 134 slashings: slashings[1:2], 135 }, 136 want: []*ethpb.ProposerSlashing{}, 137 }, 138 { 139 name: "Maintains sorted order", 140 fields: fields{ 141 pending: []*ethpb.ProposerSlashing{ 142 slashings[0], 143 slashings[2], 144 }, 145 included: make(map[types.ValidatorIndex]bool), 146 }, 147 args: args{ 148 slashings: slashings[1:2], 149 }, 150 want: []*ethpb.ProposerSlashing{ 151 slashings[0], 152 slashings[1], 153 slashings[2], 154 }, 155 }, 156 } 157 for _, tt := range tests { 158 t.Run(tt.name, func(t *testing.T) { 159 p := &Pool{ 160 pendingProposerSlashing: tt.fields.pending, 161 included: tt.fields.included, 162 } 163 var err error 164 for i := 0; i < len(tt.args.slashings); i++ { 165 err = p.InsertProposerSlashing(context.Background(), beaconState, tt.args.slashings[i]) 166 } 167 if tt.fields.wantedErr != "" { 168 require.ErrorContains(t, tt.fields.wantedErr, err) 169 } else { 170 require.NoError(t, err) 171 } 172 assert.Equal(t, len(tt.want), len(p.pendingProposerSlashing)) 173 for i := range p.pendingAttesterSlashing { 174 assert.Equal(t, p.pendingProposerSlashing[i].Header_1.Header.ProposerIndex, tt.want[i].Header_1.Header.ProposerIndex) 175 assert.DeepEqual(t, tt.want[i], p.pendingProposerSlashing[i], "Proposer slashing at index %d does not match expected", i) 176 } 177 }) 178 } 179 } 180 181 func TestPool_InsertProposerSlashing_SigFailsVerify_ClearPool(t *testing.T) { 182 params.SetupTestConfigCleanup(t) 183 conf := params.BeaconConfig() 184 conf.MaxAttesterSlashings = 2 185 params.OverrideBeaconConfig(conf) 186 beaconState, privKeys := testutil.DeterministicGenesisState(t, 64) 187 slashings := make([]*ethpb.ProposerSlashing, 2) 188 for i := 0; i < 2; i++ { 189 sl, err := testutil.GenerateProposerSlashingForValidator(beaconState, privKeys[i], types.ValidatorIndex(i)) 190 require.NoError(t, err) 191 slashings[i] = sl 192 } 193 // We mess up the signature of the second slashing. 194 badSig := make([]byte, 96) 195 copy(badSig, "muahaha") 196 slashings[1].Header_1.Signature = badSig 197 p := &Pool{ 198 pendingProposerSlashing: make([]*ethpb.ProposerSlashing, 0), 199 } 200 // We only want a single slashing to remain. 201 require.NoError(t, p.InsertProposerSlashing(context.Background(), beaconState, slashings[0])) 202 err := p.InsertProposerSlashing(context.Background(), beaconState, slashings[1]) 203 require.ErrorContains(t, "could not verify proposer slashing", err, "Expected slashing with bad signature to fail") 204 assert.Equal(t, 1, len(p.pendingProposerSlashing)) 205 } 206 207 func TestPool_MarkIncludedProposerSlashing(t *testing.T) { 208 type fields struct { 209 pending []*ethpb.ProposerSlashing 210 included map[types.ValidatorIndex]bool 211 } 212 type args struct { 213 slashing *ethpb.ProposerSlashing 214 } 215 tests := []struct { 216 name string 217 fields fields 218 args args 219 want fields 220 }{ 221 { 222 name: "Included, does not exist in pending", 223 fields: fields{ 224 pending: []*ethpb.ProposerSlashing{ 225 proposerSlashingForValIdx(1), 226 }, 227 included: make(map[types.ValidatorIndex]bool), 228 }, 229 args: args{ 230 slashing: proposerSlashingForValIdx(3), 231 }, 232 want: fields{ 233 pending: []*ethpb.ProposerSlashing{ 234 proposerSlashingForValIdx(1), 235 }, 236 included: map[types.ValidatorIndex]bool{ 237 3: true, 238 }, 239 }, 240 }, 241 { 242 name: "Removes from pending list", 243 fields: fields{ 244 pending: []*ethpb.ProposerSlashing{ 245 proposerSlashingForValIdx(1), 246 proposerSlashingForValIdx(2), 247 proposerSlashingForValIdx(3), 248 }, 249 included: map[types.ValidatorIndex]bool{ 250 0: true, 251 }, 252 }, 253 args: args{ 254 slashing: proposerSlashingForValIdx(2), 255 }, 256 want: fields{ 257 pending: []*ethpb.ProposerSlashing{ 258 proposerSlashingForValIdx(1), 259 proposerSlashingForValIdx(3), 260 }, 261 included: map[types.ValidatorIndex]bool{ 262 0: true, 263 2: true, 264 }, 265 }, 266 }, 267 { 268 name: "Removes from pending long list", 269 fields: fields{ 270 pending: []*ethpb.ProposerSlashing{ 271 proposerSlashingForValIdx(1), 272 proposerSlashingForValIdx(2), 273 proposerSlashingForValIdx(3), 274 proposerSlashingForValIdx(4), 275 proposerSlashingForValIdx(5), 276 proposerSlashingForValIdx(6), 277 proposerSlashingForValIdx(7), 278 proposerSlashingForValIdx(8), 279 proposerSlashingForValIdx(9), 280 proposerSlashingForValIdx(10), 281 }, 282 included: map[types.ValidatorIndex]bool{ 283 0: true, 284 }, 285 }, 286 args: args{ 287 slashing: proposerSlashingForValIdx(7), 288 }, 289 want: fields{ 290 pending: []*ethpb.ProposerSlashing{ 291 proposerSlashingForValIdx(1), 292 proposerSlashingForValIdx(2), 293 proposerSlashingForValIdx(3), 294 proposerSlashingForValIdx(4), 295 proposerSlashingForValIdx(5), 296 proposerSlashingForValIdx(6), 297 proposerSlashingForValIdx(8), 298 proposerSlashingForValIdx(9), 299 proposerSlashingForValIdx(10), 300 }, 301 included: map[types.ValidatorIndex]bool{ 302 0: true, 303 7: true, 304 }, 305 }, 306 }, 307 } 308 for _, tt := range tests { 309 t.Run(tt.name, func(t *testing.T) { 310 p := &Pool{ 311 pendingProposerSlashing: tt.fields.pending, 312 included: tt.fields.included, 313 } 314 p.MarkIncludedProposerSlashing(tt.args.slashing) 315 assert.Equal(t, len(tt.want.pending), len(p.pendingProposerSlashing)) 316 for i := range p.pendingProposerSlashing { 317 assert.DeepSSZEqual(t, tt.want.pending[i], p.pendingProposerSlashing[i], "Unexpected pending proposer slashing at index %d", i) 318 } 319 assert.DeepEqual(t, tt.want.included, p.included) 320 }) 321 } 322 } 323 324 func TestPool_PendingProposerSlashings(t *testing.T) { 325 type fields struct { 326 pending []*ethpb.ProposerSlashing 327 noLimit bool 328 } 329 beaconState, privKeys := testutil.DeterministicGenesisState(t, 64) 330 slashings := make([]*ethpb.ProposerSlashing, 20) 331 for i := 0; i < len(slashings); i++ { 332 sl, err := testutil.GenerateProposerSlashingForValidator(beaconState, privKeys[i], types.ValidatorIndex(i)) 333 require.NoError(t, err) 334 slashings[i] = sl 335 } 336 tests := []struct { 337 name string 338 fields fields 339 want []*ethpb.ProposerSlashing 340 }{ 341 { 342 name: "Empty list", 343 fields: fields{ 344 pending: []*ethpb.ProposerSlashing{}, 345 }, 346 want: []*ethpb.ProposerSlashing{}, 347 }, 348 { 349 name: "All", 350 fields: fields{ 351 pending: slashings, 352 noLimit: true, 353 }, 354 want: slashings, 355 }, 356 { 357 name: "All block eligible", 358 fields: fields{ 359 pending: slashings[:params.BeaconConfig().MaxProposerSlashings], 360 }, 361 want: slashings[:params.BeaconConfig().MaxProposerSlashings], 362 }, 363 { 364 name: "Multiple indices", 365 fields: fields{ 366 pending: slashings[3:6], 367 }, 368 want: slashings[3:6], 369 }, 370 } 371 for _, tt := range tests { 372 t.Run(tt.name, func(t *testing.T) { 373 p := &Pool{ 374 pendingProposerSlashing: tt.fields.pending, 375 } 376 assert.DeepEqual(t, tt.want, p.PendingProposerSlashings(context.Background(), beaconState, tt.fields.noLimit)) 377 }) 378 } 379 } 380 381 func TestPool_PendingProposerSlashings_Slashed(t *testing.T) { 382 type fields struct { 383 all bool 384 pending []*ethpb.ProposerSlashing 385 } 386 beaconState, privKeys := testutil.DeterministicGenesisState(t, 64) 387 val, err := beaconState.ValidatorAtIndex(0) 388 require.NoError(t, err) 389 val.Slashed = true 390 require.NoError(t, beaconState.UpdateValidatorAtIndex(0, val)) 391 val, err = beaconState.ValidatorAtIndex(5) 392 require.NoError(t, err) 393 val.Slashed = true 394 require.NoError(t, beaconState.UpdateValidatorAtIndex(5, val)) 395 slashings := make([]*ethpb.ProposerSlashing, 32) 396 slashings2 := make([]*ethpb.ProposerSlashing, 32) 397 result := make([]*ethpb.ProposerSlashing, 32) 398 for i := 0; i < len(slashings); i++ { 399 sl, err := testutil.GenerateProposerSlashingForValidator(beaconState, privKeys[i], types.ValidatorIndex(i)) 400 require.NoError(t, err) 401 slashings[i] = sl 402 slashings2[i] = sl 403 result[i] = sl 404 } 405 result = append(result[1:5], result[6:]...) 406 tests := []struct { 407 name string 408 fields fields 409 want []*ethpb.ProposerSlashing 410 }{ 411 { 412 name: "removes slashed", 413 fields: fields{ 414 pending: slashings, 415 }, 416 want: result[:16], 417 }, 418 { 419 name: "gets noLimit and no slashed", 420 fields: fields{ 421 all: true, 422 pending: slashings2, 423 }, 424 want: result, 425 }, 426 } 427 for _, tt := range tests { 428 t.Run(tt.name, func(t *testing.T) { 429 p := &Pool{ 430 pendingProposerSlashing: tt.fields.pending, 431 } 432 result := p.PendingProposerSlashings(context.Background(), beaconState, tt.fields.all /*noLimit*/) 433 t.Log(tt.want[0].Header_1.Header.ProposerIndex) 434 t.Log(result[0].Header_1.Header.ProposerIndex) 435 assert.DeepEqual(t, tt.want, result) 436 }) 437 } 438 }