github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/operations/slashings/service_attester_test.go (about) 1 package slashings 2 3 import ( 4 "context" 5 "testing" 6 7 types "github.com/prysmaticlabs/eth2-types" 8 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 9 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 10 "github.com/prysmaticlabs/prysm/shared/bls" 11 "github.com/prysmaticlabs/prysm/shared/params" 12 "github.com/prysmaticlabs/prysm/shared/testutil" 13 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 14 "github.com/prysmaticlabs/prysm/shared/testutil/require" 15 ) 16 17 func validAttesterSlashingForValIdx(t *testing.T, beaconState iface.BeaconState, privs []bls.SecretKey, valIdx ...uint64) *ethpb.AttesterSlashing { 18 var slashings []*ethpb.AttesterSlashing 19 for _, idx := range valIdx { 20 slashing, err := testutil.GenerateAttesterSlashingForValidator(beaconState, privs[idx], types.ValidatorIndex(idx)) 21 require.NoError(t, err) 22 slashings = append(slashings, slashing) 23 } 24 var allSig1 []bls.Signature 25 var allSig2 []bls.Signature 26 for _, slashing := range slashings { 27 sig1 := slashing.Attestation_1.Signature 28 sig2 := slashing.Attestation_2.Signature 29 sigFromBytes1, err := bls.SignatureFromBytes(sig1) 30 require.NoError(t, err) 31 sigFromBytes2, err := bls.SignatureFromBytes(sig2) 32 require.NoError(t, err) 33 allSig1 = append(allSig1, sigFromBytes1) 34 allSig2 = append(allSig2, sigFromBytes2) 35 } 36 aggSig1 := bls.AggregateSignatures(allSig1) 37 aggSig2 := bls.AggregateSignatures(allSig2) 38 aggSlashing := ðpb.AttesterSlashing{ 39 Attestation_1: ðpb.IndexedAttestation{ 40 AttestingIndices: valIdx, 41 Data: slashings[0].Attestation_1.Data, 42 Signature: aggSig1.Marshal(), 43 }, 44 Attestation_2: ðpb.IndexedAttestation{ 45 AttestingIndices: valIdx, 46 Data: slashings[0].Attestation_2.Data, 47 Signature: aggSig2.Marshal(), 48 }, 49 } 50 return aggSlashing 51 } 52 53 func attesterSlashingForValIdx(valIdx ...uint64) *ethpb.AttesterSlashing { 54 return ðpb.AttesterSlashing{ 55 Attestation_1: ðpb.IndexedAttestation{AttestingIndices: valIdx}, 56 Attestation_2: ðpb.IndexedAttestation{AttestingIndices: valIdx}, 57 } 58 } 59 60 func pendingSlashingForValIdx(valIdx ...uint64) *PendingAttesterSlashing { 61 return &PendingAttesterSlashing{ 62 attesterSlashing: attesterSlashingForValIdx(valIdx...), 63 validatorToSlash: types.ValidatorIndex(valIdx[0]), 64 } 65 } 66 67 func TestPool_InsertAttesterSlashing(t *testing.T) { 68 type fields struct { 69 pending []*PendingAttesterSlashing 70 included map[types.ValidatorIndex]bool 71 wantErr []bool 72 } 73 type args struct { 74 slashings []*ethpb.AttesterSlashing 75 } 76 77 beaconState, privKeys := testutil.DeterministicGenesisState(t, 64) 78 pendingSlashings := make([]*PendingAttesterSlashing, 20) 79 slashings := make([]*ethpb.AttesterSlashing, 20) 80 for i := 0; i < len(pendingSlashings); i++ { 81 sl, err := testutil.GenerateAttesterSlashingForValidator(beaconState, privKeys[i], types.ValidatorIndex(i)) 82 require.NoError(t, err) 83 pendingSlashings[i] = &PendingAttesterSlashing{ 84 attesterSlashing: sl, 85 validatorToSlash: types.ValidatorIndex(i), 86 } 87 slashings[i] = sl 88 } 89 require.NoError(t, beaconState.SetSlot(params.BeaconConfig().SlotsPerEpoch)) 90 91 // We mark the following validators with some preconditions. 92 exitedVal, err := beaconState.ValidatorAtIndex(types.ValidatorIndex(2)) 93 require.NoError(t, err) 94 exitedVal.WithdrawableEpoch = 0 95 require.NoError(t, beaconState.UpdateValidatorAtIndex(types.ValidatorIndex(2), exitedVal)) 96 futureWithdrawVal, err := beaconState.ValidatorAtIndex(types.ValidatorIndex(4)) 97 require.NoError(t, err) 98 futureWithdrawVal.WithdrawableEpoch = 17 99 require.NoError(t, beaconState.UpdateValidatorAtIndex(types.ValidatorIndex(4), futureWithdrawVal)) 100 slashedVal, err := beaconState.ValidatorAtIndex(types.ValidatorIndex(5)) 101 require.NoError(t, err) 102 slashedVal.Slashed = true 103 require.NoError(t, beaconState.UpdateValidatorAtIndex(types.ValidatorIndex(5), slashedVal)) 104 slashedVal2, err := beaconState.ValidatorAtIndex(types.ValidatorIndex(21)) 105 require.NoError(t, err) 106 slashedVal2.Slashed = true 107 require.NoError(t, beaconState.UpdateValidatorAtIndex(types.ValidatorIndex(21), slashedVal2)) 108 109 aggSlashing1 := validAttesterSlashingForValIdx(t, beaconState, privKeys, 0, 1, 2) 110 aggSlashing2 := validAttesterSlashingForValIdx(t, beaconState, privKeys, 5, 9, 13) 111 aggSlashing3 := validAttesterSlashingForValIdx(t, beaconState, privKeys, 15, 20, 21) 112 aggSlashing4 := validAttesterSlashingForValIdx(t, beaconState, privKeys, 2, 5, 21) 113 114 tests := []struct { 115 name string 116 fields fields 117 args args 118 want []*PendingAttesterSlashing 119 err string 120 }{ 121 { 122 name: "Empty list", 123 fields: fields{ 124 pending: make([]*PendingAttesterSlashing, 0), 125 included: make(map[types.ValidatorIndex]bool), 126 wantErr: []bool{false}, 127 }, 128 args: args{ 129 slashings: slashings[0:1], 130 }, 131 want: []*PendingAttesterSlashing{ 132 { 133 attesterSlashing: slashings[0], 134 validatorToSlash: 0, 135 }, 136 }, 137 }, 138 { 139 name: "Empty list two validators slashed", 140 fields: fields{ 141 pending: make([]*PendingAttesterSlashing, 0), 142 included: make(map[types.ValidatorIndex]bool), 143 wantErr: []bool{false, false}, 144 }, 145 args: args{ 146 slashings: slashings[0:2], 147 }, 148 want: pendingSlashings[0:2], 149 }, 150 { 151 name: "Duplicate identical slashing", 152 fields: fields{ 153 pending: []*PendingAttesterSlashing{ 154 pendingSlashings[1], 155 }, 156 included: make(map[types.ValidatorIndex]bool), 157 wantErr: []bool{true}, 158 }, 159 args: args{ 160 slashings: slashings[1:2], 161 }, 162 want: pendingSlashings[1:2], 163 }, 164 { 165 name: "Slashing for already exit validator", 166 fields: fields{ 167 pending: []*PendingAttesterSlashing{}, 168 included: make(map[types.ValidatorIndex]bool), 169 wantErr: []bool{true}, 170 }, 171 args: args{ 172 slashings: slashings[5:6], 173 }, 174 want: []*PendingAttesterSlashing{}, 175 }, 176 { 177 name: "Slashing for withdrawable validator", 178 fields: fields{ 179 pending: []*PendingAttesterSlashing{}, 180 included: make(map[types.ValidatorIndex]bool), 181 wantErr: []bool{true}, 182 }, 183 args: args{ 184 slashings: slashings[2:3], 185 }, 186 want: []*PendingAttesterSlashing{}, 187 }, 188 { 189 name: "Slashing for slashed validator", 190 fields: fields{ 191 pending: []*PendingAttesterSlashing{}, 192 included: make(map[types.ValidatorIndex]bool), 193 wantErr: []bool{false}, 194 }, 195 args: args{ 196 slashings: slashings[4:5], 197 }, 198 want: pendingSlashings[4:5], 199 }, 200 { 201 name: "Already included", 202 fields: fields{ 203 pending: []*PendingAttesterSlashing{}, 204 included: map[types.ValidatorIndex]bool{ 205 1: true, 206 }, 207 wantErr: []bool{true}, 208 }, 209 args: args{ 210 slashings: slashings[1:2], 211 }, 212 want: []*PendingAttesterSlashing{}, 213 }, 214 { 215 name: "Maintains sorted order", 216 fields: fields{ 217 pending: []*PendingAttesterSlashing{ 218 pendingSlashings[0], 219 pendingSlashings[2], 220 }, 221 included: make(map[types.ValidatorIndex]bool), 222 wantErr: []bool{false}, 223 }, 224 args: args{ 225 slashings: slashings[1:2], 226 }, 227 want: pendingSlashings[0:3], 228 }, 229 { 230 name: "Doesn't reject partially slashed slashings", 231 fields: fields{ 232 pending: []*PendingAttesterSlashing{}, 233 included: make(map[types.ValidatorIndex]bool), 234 wantErr: []bool{false, false, false, true}, 235 }, 236 args: args{ 237 slashings: []*ethpb.AttesterSlashing{ 238 aggSlashing1, 239 aggSlashing2, 240 aggSlashing3, 241 aggSlashing4, 242 }, 243 }, 244 want: []*PendingAttesterSlashing{ 245 { 246 attesterSlashing: aggSlashing1, 247 validatorToSlash: 0, 248 }, 249 { 250 attesterSlashing: aggSlashing1, 251 validatorToSlash: 1, 252 }, 253 { 254 attesterSlashing: aggSlashing2, 255 validatorToSlash: 9, 256 }, 257 { 258 attesterSlashing: aggSlashing2, 259 validatorToSlash: 13, 260 }, 261 { 262 attesterSlashing: aggSlashing3, 263 validatorToSlash: 15, 264 }, 265 { 266 attesterSlashing: aggSlashing3, 267 validatorToSlash: 20, 268 }, 269 }, 270 }, 271 } 272 for _, tt := range tests { 273 t.Run(tt.name, func(t *testing.T) { 274 p := &Pool{ 275 pendingAttesterSlashing: tt.fields.pending, 276 included: tt.fields.included, 277 } 278 var err error 279 for i := 0; i < len(tt.args.slashings); i++ { 280 err = p.InsertAttesterSlashing(context.Background(), beaconState, tt.args.slashings[i]) 281 if tt.fields.wantErr[i] { 282 assert.NotNil(t, err) 283 } else { 284 assert.NoError(t, err) 285 } 286 } 287 assert.Equal(t, len(tt.want), len(p.pendingAttesterSlashing)) 288 289 for i := range p.pendingAttesterSlashing { 290 assert.Equal(t, tt.want[i].validatorToSlash, p.pendingAttesterSlashing[i].validatorToSlash) 291 assert.DeepEqual(t, tt.want[i].attesterSlashing, p.pendingAttesterSlashing[i].attesterSlashing, "At index %d", i) 292 } 293 }) 294 } 295 } 296 297 func TestPool_InsertAttesterSlashing_SigFailsVerify_ClearPool(t *testing.T) { 298 params.SetupTestConfigCleanup(t) 299 conf := params.BeaconConfig() 300 conf.MaxAttesterSlashings = 2 301 params.OverrideBeaconConfig(conf) 302 beaconState, privKeys := testutil.DeterministicGenesisState(t, 64) 303 pendingSlashings := make([]*PendingAttesterSlashing, 2) 304 slashings := make([]*ethpb.AttesterSlashing, 2) 305 for i := 0; i < 2; i++ { 306 sl, err := testutil.GenerateAttesterSlashingForValidator(beaconState, privKeys[i], types.ValidatorIndex(i)) 307 require.NoError(t, err) 308 pendingSlashings[i] = &PendingAttesterSlashing{ 309 attesterSlashing: sl, 310 validatorToSlash: types.ValidatorIndex(i), 311 } 312 slashings[i] = sl 313 } 314 // We mess up the signature of the second slashing. 315 badSig := make([]byte, 96) 316 copy(badSig, "muahaha") 317 pendingSlashings[1].attesterSlashing.Attestation_1.Signature = badSig 318 slashings[1].Attestation_1.Signature = badSig 319 p := &Pool{ 320 pendingAttesterSlashing: make([]*PendingAttesterSlashing, 0), 321 } 322 require.NoError(t, p.InsertAttesterSlashing(context.Background(), beaconState, slashings[0])) 323 err := p.InsertAttesterSlashing(context.Background(), beaconState, slashings[1]) 324 require.ErrorContains(t, "could not verify attester slashing", err, "Expected error when inserting slashing with bad sig") 325 assert.Equal(t, 1, len(p.pendingAttesterSlashing)) 326 } 327 328 func TestPool_MarkIncludedAttesterSlashing(t *testing.T) { 329 type fields struct { 330 pending []*PendingAttesterSlashing 331 included map[types.ValidatorIndex]bool 332 } 333 type args struct { 334 slashing *ethpb.AttesterSlashing 335 } 336 tests := []struct { 337 name string 338 fields fields 339 args args 340 want fields 341 }{ 342 { 343 name: "Included, does not exist in pending", 344 fields: fields{ 345 pending: []*PendingAttesterSlashing{ 346 { 347 attesterSlashing: attesterSlashingForValIdx(1), 348 validatorToSlash: 1, 349 }, 350 }, 351 included: make(map[types.ValidatorIndex]bool), 352 }, 353 args: args{ 354 slashing: attesterSlashingForValIdx(3), 355 }, 356 want: fields{ 357 pending: []*PendingAttesterSlashing{ 358 pendingSlashingForValIdx(1), 359 }, 360 included: map[types.ValidatorIndex]bool{ 361 3: true, 362 }, 363 }, 364 }, 365 { 366 name: "Removes from pending list", 367 fields: fields{ 368 pending: []*PendingAttesterSlashing{ 369 pendingSlashingForValIdx(1), 370 pendingSlashingForValIdx(2), 371 pendingSlashingForValIdx(3), 372 }, 373 included: map[types.ValidatorIndex]bool{ 374 0: true, 375 }, 376 }, 377 args: args{ 378 slashing: attesterSlashingForValIdx(2), 379 }, 380 want: fields{ 381 pending: []*PendingAttesterSlashing{ 382 pendingSlashingForValIdx(1), 383 pendingSlashingForValIdx(3), 384 }, 385 included: map[types.ValidatorIndex]bool{ 386 0: true, 387 2: true, 388 }, 389 }, 390 }, 391 { 392 name: "Removes from long pending list", 393 fields: fields{ 394 pending: []*PendingAttesterSlashing{ 395 pendingSlashingForValIdx(1), 396 pendingSlashingForValIdx(2), 397 pendingSlashingForValIdx(3), 398 pendingSlashingForValIdx(4), 399 pendingSlashingForValIdx(5), 400 pendingSlashingForValIdx(6), 401 pendingSlashingForValIdx(7), 402 pendingSlashingForValIdx(8), 403 pendingSlashingForValIdx(9), 404 pendingSlashingForValIdx(10), 405 pendingSlashingForValIdx(11), 406 }, 407 included: map[types.ValidatorIndex]bool{ 408 0: true, 409 }, 410 }, 411 args: args{ 412 slashing: attesterSlashingForValIdx(6), 413 }, 414 want: fields{ 415 pending: []*PendingAttesterSlashing{ 416 pendingSlashingForValIdx(1), 417 pendingSlashingForValIdx(2), 418 pendingSlashingForValIdx(3), 419 pendingSlashingForValIdx(4), 420 pendingSlashingForValIdx(5), 421 pendingSlashingForValIdx(7), 422 pendingSlashingForValIdx(8), 423 pendingSlashingForValIdx(9), 424 pendingSlashingForValIdx(10), 425 pendingSlashingForValIdx(11), 426 }, 427 included: map[types.ValidatorIndex]bool{ 428 0: true, 429 6: true, 430 }, 431 }, 432 }, 433 } 434 for _, tt := range tests { 435 t.Run(tt.name, func(t *testing.T) { 436 p := &Pool{ 437 pendingAttesterSlashing: tt.fields.pending, 438 included: tt.fields.included, 439 } 440 p.MarkIncludedAttesterSlashing(tt.args.slashing) 441 assert.Equal(t, len(tt.want.pending), len(p.pendingAttesterSlashing)) 442 for i := range p.pendingAttesterSlashing { 443 assert.DeepEqual(t, tt.want.pending[i], p.pendingAttesterSlashing[i]) 444 } 445 assert.DeepEqual(t, tt.want.included, p.included) 446 }) 447 } 448 } 449 450 func TestPool_PendingAttesterSlashings(t *testing.T) { 451 type fields struct { 452 pending []*PendingAttesterSlashing 453 all bool 454 } 455 params.SetupTestConfigCleanup(t) 456 beaconState, privKeys := testutil.DeterministicGenesisState(t, 64) 457 pendingSlashings := make([]*PendingAttesterSlashing, 20) 458 slashings := make([]*ethpb.AttesterSlashing, 20) 459 for i := 0; i < len(pendingSlashings); i++ { 460 sl, err := testutil.GenerateAttesterSlashingForValidator(beaconState, privKeys[i], types.ValidatorIndex(i)) 461 require.NoError(t, err) 462 pendingSlashings[i] = &PendingAttesterSlashing{ 463 attesterSlashing: sl, 464 validatorToSlash: types.ValidatorIndex(i), 465 } 466 slashings[i] = sl 467 } 468 tests := []struct { 469 name string 470 fields fields 471 want []*ethpb.AttesterSlashing 472 }{ 473 { 474 name: "Empty list", 475 fields: fields{ 476 pending: []*PendingAttesterSlashing{}, 477 }, 478 want: []*ethpb.AttesterSlashing{}, 479 }, 480 { 481 name: "All pending", 482 fields: fields{ 483 pending: pendingSlashings, 484 all: true, 485 }, 486 want: slashings, 487 }, 488 { 489 name: "All eligible", 490 fields: fields{ 491 pending: pendingSlashings, 492 }, 493 want: slashings[0:2], 494 }, 495 { 496 name: "Multiple indices", 497 fields: fields{ 498 pending: pendingSlashings[3:6], 499 }, 500 want: slashings[3:5], 501 }, 502 } 503 for _, tt := range tests { 504 t.Run(tt.name, func(t *testing.T) { 505 p := &Pool{ 506 pendingAttesterSlashing: tt.fields.pending, 507 } 508 assert.DeepEqual(t, tt.want, p.PendingAttesterSlashings(context.Background(), beaconState, tt.fields.all)) 509 }) 510 } 511 } 512 513 func TestPool_PendingAttesterSlashings_Slashed(t *testing.T) { 514 type fields struct { 515 pending []*PendingAttesterSlashing 516 all bool 517 } 518 params.SetupTestConfigCleanup(t) 519 conf := params.BeaconConfig() 520 conf.MaxAttesterSlashings = 2 521 params.OverrideBeaconConfig(conf) 522 beaconState, privKeys := testutil.DeterministicGenesisState(t, 64) 523 val, err := beaconState.ValidatorAtIndex(0) 524 require.NoError(t, err) 525 val.Slashed = true 526 require.NoError(t, beaconState.UpdateValidatorAtIndex(0, val)) 527 val, err = beaconState.ValidatorAtIndex(5) 528 require.NoError(t, err) 529 val.Slashed = true 530 require.NoError(t, beaconState.UpdateValidatorAtIndex(5, val)) 531 pendingSlashings := make([]*PendingAttesterSlashing, 20) 532 pendingSlashings2 := make([]*PendingAttesterSlashing, 20) 533 slashings := make([]*ethpb.AttesterSlashing, 20) 534 for i := 0; i < len(pendingSlashings); i++ { 535 sl, err := testutil.GenerateAttesterSlashingForValidator(beaconState, privKeys[i], types.ValidatorIndex(i)) 536 require.NoError(t, err) 537 pendingSlashings[i] = &PendingAttesterSlashing{ 538 attesterSlashing: sl, 539 validatorToSlash: types.ValidatorIndex(i), 540 } 541 pendingSlashings2[i] = &PendingAttesterSlashing{ 542 attesterSlashing: sl, 543 validatorToSlash: types.ValidatorIndex(i), 544 } 545 slashings[i] = sl 546 } 547 result := append(slashings[1:5], slashings[6:]...) 548 tests := []struct { 549 name string 550 fields fields 551 want []*ethpb.AttesterSlashing 552 }{ 553 { 554 name: "One item", 555 fields: fields{ 556 pending: pendingSlashings[:2], 557 }, 558 want: slashings[1:2], 559 }, 560 { 561 name: "Skips gapped slashed", 562 fields: fields{ 563 pending: pendingSlashings[4:7], 564 }, 565 want: result[3:5], 566 }, 567 { 568 name: "All and skips gapped slashed validators", 569 fields: fields{ 570 pending: pendingSlashings2, 571 all: true, 572 }, 573 want: result, 574 }, 575 } 576 for _, tt := range tests { 577 t.Run(tt.name, func(t *testing.T) { 578 p := &Pool{pendingAttesterSlashing: tt.fields.pending} 579 assert.DeepEqual(t, tt.want, p.PendingAttesterSlashings(context.Background(), beaconState, tt.fields.all /*noLimit*/)) 580 }) 581 } 582 } 583 584 func TestPool_PendingAttesterSlashings_NoDuplicates(t *testing.T) { 585 params.SetupTestConfigCleanup(t) 586 conf := params.BeaconConfig() 587 conf.MaxAttesterSlashings = 2 588 params.OverrideBeaconConfig(conf) 589 beaconState, privKeys := testutil.DeterministicGenesisState(t, 64) 590 pendingSlashings := make([]*PendingAttesterSlashing, 3) 591 slashings := make([]*ethpb.AttesterSlashing, 3) 592 for i := 0; i < 2; i++ { 593 sl, err := testutil.GenerateAttesterSlashingForValidator(beaconState, privKeys[i], types.ValidatorIndex(i)) 594 require.NoError(t, err) 595 pendingSlashings[i] = &PendingAttesterSlashing{ 596 attesterSlashing: sl, 597 validatorToSlash: types.ValidatorIndex(i), 598 } 599 slashings[i] = sl 600 } 601 // We duplicate the last slashing. 602 pendingSlashings[2] = pendingSlashings[1] 603 slashings[2] = slashings[1] 604 p := &Pool{ 605 pendingAttesterSlashing: pendingSlashings, 606 } 607 assert.DeepEqual(t, slashings[0:2], p.PendingAttesterSlashings(context.Background(), beaconState, false /*noLimit*/)) 608 }