github.com/prysmaticlabs/prysm@v1.4.4/slasher/detection/detect_test.go (about) 1 package detection 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 8 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 9 slashpb "github.com/prysmaticlabs/prysm/proto/slashing" 10 "github.com/prysmaticlabs/prysm/shared/bytesutil" 11 "github.com/prysmaticlabs/prysm/shared/slashutil" 12 "github.com/prysmaticlabs/prysm/shared/sszutil" 13 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 14 "github.com/prysmaticlabs/prysm/shared/testutil/require" 15 testDB "github.com/prysmaticlabs/prysm/slasher/db/testing" 16 status "github.com/prysmaticlabs/prysm/slasher/db/types" 17 "github.com/prysmaticlabs/prysm/slasher/detection/attestations" 18 "github.com/prysmaticlabs/prysm/slasher/detection/attestations/types" 19 "github.com/prysmaticlabs/prysm/slasher/detection/proposals" 20 testDetect "github.com/prysmaticlabs/prysm/slasher/detection/testing" 21 ) 22 23 func TestDetect_detectAttesterSlashings_Surround(t *testing.T) { 24 type testStruct struct { 25 name string 26 savedAtts []*ethpb.IndexedAttestation 27 incomingAtt *ethpb.IndexedAttestation 28 slashingsFound int 29 } 30 tests := []testStruct{ 31 { 32 name: "surrounding vote detected should report a slashing", 33 savedAtts: []*ethpb.IndexedAttestation{ 34 { 35 AttestingIndices: []uint64{3}, 36 Data: ðpb.AttestationData{ 37 Source: ðpb.Checkpoint{Epoch: 9, Root: make([]byte, 32)}, 38 Target: ðpb.Checkpoint{Epoch: 13, Root: make([]byte, 32)}, 39 BeaconBlockRoot: make([]byte, 32), 40 }, 41 Signature: bytesutil.PadTo([]byte{1, 2}, 96), 42 }, 43 }, 44 incomingAtt: ðpb.IndexedAttestation{ 45 AttestingIndices: []uint64{1, 3, 7}, 46 Data: ðpb.AttestationData{ 47 Source: ðpb.Checkpoint{Epoch: 7, Root: make([]byte, 32)}, 48 Target: ðpb.Checkpoint{Epoch: 14, Root: make([]byte, 32)}, 49 BeaconBlockRoot: make([]byte, 32), 50 }, 51 Signature: make([]byte, 96), 52 }, 53 slashingsFound: 1, 54 }, 55 { 56 name: "surrounded vote detected should report a slashing", 57 savedAtts: []*ethpb.IndexedAttestation{ 58 { 59 AttestingIndices: []uint64{0, 2, 4, 8}, 60 Data: ðpb.AttestationData{ 61 Source: ðpb.Checkpoint{Epoch: 6, Root: make([]byte, 32)}, 62 Target: ðpb.Checkpoint{Epoch: 10, Root: make([]byte, 32)}, 63 BeaconBlockRoot: make([]byte, 32), 64 }, 65 Signature: bytesutil.PadTo([]byte{1, 2}, 96), 66 }, 67 }, 68 incomingAtt: ðpb.IndexedAttestation{ 69 AttestingIndices: []uint64{0, 4}, 70 Data: ðpb.AttestationData{ 71 Source: ðpb.Checkpoint{Epoch: 7, Root: make([]byte, 32)}, 72 Target: ðpb.Checkpoint{Epoch: 9, Root: make([]byte, 32)}, 73 BeaconBlockRoot: make([]byte, 32), 74 }, 75 Signature: make([]byte, 96), 76 }, 77 slashingsFound: 1, 78 }, 79 { 80 name: "2 different surrounded votes detected should report 2 slashings", 81 savedAtts: []*ethpb.IndexedAttestation{ 82 { 83 AttestingIndices: []uint64{0, 2}, 84 Data: ðpb.AttestationData{ 85 Source: ðpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)}, 86 Target: ðpb.Checkpoint{Epoch: 5, Root: make([]byte, 32)}, 87 BeaconBlockRoot: make([]byte, 32), 88 }, 89 Signature: bytesutil.PadTo([]byte{1, 2}, 96), 90 }, 91 { 92 AttestingIndices: []uint64{4, 8}, 93 Data: ðpb.AttestationData{ 94 Source: ðpb.Checkpoint{Epoch: 3, Root: make([]byte, 32)}, 95 Target: ðpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)}, 96 BeaconBlockRoot: make([]byte, 32), 97 }, 98 Signature: bytesutil.PadTo([]byte{1, 3}, 96), 99 }, 100 }, 101 incomingAtt: ðpb.IndexedAttestation{ 102 AttestingIndices: []uint64{0, 4}, 103 Data: ðpb.AttestationData{ 104 Source: ðpb.Checkpoint{Epoch: 2, Root: make([]byte, 32)}, 105 Target: ðpb.Checkpoint{Epoch: 7, Root: make([]byte, 32)}, 106 BeaconBlockRoot: make([]byte, 32), 107 }, 108 Signature: make([]byte, 96), 109 }, 110 slashingsFound: 2, 111 }, 112 { 113 name: "2 different surrounding votes detected should report 2 slashings", 114 savedAtts: []*ethpb.IndexedAttestation{ 115 { 116 AttestingIndices: []uint64{0, 2}, 117 Data: ðpb.AttestationData{ 118 Source: ðpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)}, 119 Target: ðpb.Checkpoint{Epoch: 10, Root: make([]byte, 32)}, 120 BeaconBlockRoot: make([]byte, 32), 121 }, 122 Signature: bytesutil.PadTo([]byte{1, 2}, 96), 123 }, 124 { 125 AttestingIndices: []uint64{4, 8}, 126 Data: ðpb.AttestationData{ 127 Source: ðpb.Checkpoint{Epoch: 5, Root: make([]byte, 32)}, 128 Target: ðpb.Checkpoint{Epoch: 9, Root: make([]byte, 32)}, 129 BeaconBlockRoot: make([]byte, 32), 130 }, 131 Signature: bytesutil.PadTo([]byte{1, 3}, 96), 132 }, 133 }, 134 incomingAtt: ðpb.IndexedAttestation{ 135 AttestingIndices: []uint64{0, 4}, 136 Data: ðpb.AttestationData{ 137 Source: ðpb.Checkpoint{Epoch: 7, Root: make([]byte, 32)}, 138 Target: ðpb.Checkpoint{Epoch: 8, Root: make([]byte, 32)}, 139 BeaconBlockRoot: make([]byte, 32), 140 }, 141 Signature: make([]byte, 96), 142 }, 143 slashingsFound: 2, 144 }, 145 { 146 name: "no slashable detected should not report a slashing", 147 savedAtts: []*ethpb.IndexedAttestation{ 148 { 149 AttestingIndices: []uint64{0}, 150 Data: ðpb.AttestationData{ 151 Source: ðpb.Checkpoint{Epoch: 1, Root: make([]byte, 32)}, 152 Target: ðpb.Checkpoint{Epoch: 2, Root: make([]byte, 32)}, 153 BeaconBlockRoot: make([]byte, 32), 154 }, 155 Signature: bytesutil.PadTo([]byte{1, 2}, 96), 156 }, 157 }, 158 incomingAtt: ðpb.IndexedAttestation{ 159 AttestingIndices: []uint64{0}, 160 Data: ðpb.AttestationData{ 161 Source: ðpb.Checkpoint{Epoch: 0, Root: make([]byte, 32)}, 162 Target: ðpb.Checkpoint{Epoch: 1, Root: make([]byte, 32)}, 163 BeaconBlockRoot: make([]byte, 32), 164 }, 165 Signature: make([]byte, 96), 166 }, 167 slashingsFound: 0, 168 }, 169 } 170 for _, tt := range tests { 171 t.Run(tt.name, func(t *testing.T) { 172 db := testDB.SetupSlasherDB(t, false) 173 ctx := context.Background() 174 ds := Service{ 175 ctx: ctx, 176 cfg: &Config{SlasherDB: db}, 177 minMaxSpanDetector: attestations.NewSpanDetector(db), 178 } 179 require.NoError(t, db.SaveIndexedAttestations(ctx, tt.savedAtts)) 180 for _, att := range tt.savedAtts { 181 require.NoError(t, ds.minMaxSpanDetector.UpdateSpans(ctx, att)) 182 } 183 184 slashings, err := ds.DetectAttesterSlashings(ctx, tt.incomingAtt) 185 require.NoError(t, err) 186 require.Equal(t, tt.slashingsFound, len(slashings), "Unexpected amount of slashings found") 187 attsl, err := db.AttesterSlashings(ctx, status.Active) 188 require.NoError(t, err) 189 require.Equal(t, tt.slashingsFound, len(attsl), "Didnt save slashing to db") 190 for _, ss := range slashings { 191 slashingAtt1 := ss.Attestation_1 192 slashingAtt2 := ss.Attestation_2 193 if !slashutil.IsSurround(slashingAtt1, slashingAtt2) { 194 t.Fatalf( 195 "Expected slashing to be valid, received atts %d->%d and %d->%d", 196 slashingAtt2.Data.Source.Epoch, 197 slashingAtt2.Data.Target.Epoch, 198 slashingAtt1.Data.Source.Epoch, 199 slashingAtt1.Data.Target.Epoch, 200 ) 201 } 202 } 203 }) 204 } 205 } 206 207 func TestDetect_detectAttesterSlashings_Double(t *testing.T) { 208 type testStruct struct { 209 name string 210 savedAtts []*ethpb.IndexedAttestation 211 incomingAtt *ethpb.IndexedAttestation 212 slashingsFound int 213 } 214 tests := []testStruct{ 215 { 216 name: "different source, same target, should report a slashing", 217 savedAtts: []*ethpb.IndexedAttestation{ 218 { 219 AttestingIndices: []uint64{3}, 220 Data: ðpb.AttestationData{ 221 Source: ðpb.Checkpoint{Epoch: 3, Root: make([]byte, 32)}, 222 Target: ðpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)}, 223 BeaconBlockRoot: make([]byte, 32), 224 }, 225 Signature: bytesutil.PadTo([]byte{1, 2}, 96), 226 }, 227 }, 228 incomingAtt: ðpb.IndexedAttestation{ 229 AttestingIndices: []uint64{1, 3, 7}, 230 Data: ðpb.AttestationData{ 231 Source: ðpb.Checkpoint{Epoch: 2, Root: make([]byte, 32)}, 232 Target: ðpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)}, 233 BeaconBlockRoot: make([]byte, 32), 234 }, 235 Signature: bytesutil.PadTo([]byte{1, 2}, 96), 236 }, 237 slashingsFound: 1, 238 }, 239 { 240 name: "different histories, same target, should report 2 slashings", 241 savedAtts: []*ethpb.IndexedAttestation{ 242 { 243 AttestingIndices: []uint64{1}, 244 Data: ðpb.AttestationData{ 245 Source: ðpb.Checkpoint{Epoch: 3, Root: make([]byte, 32)}, 246 Target: ðpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)}, 247 BeaconBlockRoot: make([]byte, 32), 248 }, 249 Signature: bytesutil.PadTo([]byte{1, 2}, 96), 250 }, 251 { 252 AttestingIndices: []uint64{3}, 253 Data: ðpb.AttestationData{ 254 Source: ðpb.Checkpoint{Epoch: 1, Root: make([]byte, 32)}, 255 Target: ðpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)}, 256 BeaconBlockRoot: make([]byte, 32), 257 }, 258 Signature: bytesutil.PadTo([]byte{1, 3}, 96), 259 }, 260 }, 261 incomingAtt: ðpb.IndexedAttestation{ 262 AttestingIndices: []uint64{1, 3, 7}, 263 Data: ðpb.AttestationData{ 264 Source: ðpb.Checkpoint{Epoch: 2, Root: make([]byte, 32)}, 265 Target: ðpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)}, 266 BeaconBlockRoot: make([]byte, 32), 267 }, 268 Signature: bytesutil.PadTo([]byte{1, 4}, 96), 269 }, 270 slashingsFound: 2, 271 }, 272 { 273 name: "same source and target, different block root, should report a slashing ", 274 savedAtts: []*ethpb.IndexedAttestation{ 275 { 276 AttestingIndices: []uint64{0, 2, 4, 8}, 277 Data: ðpb.AttestationData{ 278 Source: ðpb.Checkpoint{Epoch: 2, Root: make([]byte, 32)}, 279 Target: ðpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)}, 280 BeaconBlockRoot: bytesutil.PadTo([]byte("good block root"), 32), 281 }, 282 Signature: bytesutil.PadTo([]byte{1, 2}, 96), 283 }, 284 }, 285 incomingAtt: ðpb.IndexedAttestation{ 286 AttestingIndices: []uint64{0, 4}, 287 Data: ðpb.AttestationData{ 288 Source: ðpb.Checkpoint{Epoch: 2, Root: make([]byte, 32)}, 289 Target: ðpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)}, 290 BeaconBlockRoot: bytesutil.PadTo([]byte("bad block root"), 32), 291 }, 292 Signature: make([]byte, 96), 293 }, 294 slashingsFound: 1, 295 }, 296 { 297 name: "same attestation should not report double", 298 savedAtts: []*ethpb.IndexedAttestation{ 299 { 300 AttestingIndices: []uint64{0}, 301 Data: ðpb.AttestationData{ 302 Source: ðpb.Checkpoint{Epoch: 0, Root: make([]byte, 32)}, 303 Target: ðpb.Checkpoint{Epoch: 2, Root: make([]byte, 32)}, 304 BeaconBlockRoot: bytesutil.PadTo([]byte("good block root"), 32), 305 }, 306 Signature: bytesutil.PadTo([]byte{1, 2}, 96), 307 }, 308 }, 309 incomingAtt: ðpb.IndexedAttestation{ 310 AttestingIndices: []uint64{0}, 311 Data: ðpb.AttestationData{ 312 Source: ðpb.Checkpoint{Epoch: 0, Root: make([]byte, 32)}, 313 Target: ðpb.Checkpoint{Epoch: 2, Root: make([]byte, 32)}, 314 BeaconBlockRoot: bytesutil.PadTo([]byte("good block root"), 32), 315 }, 316 Signature: make([]byte, 96), 317 }, 318 slashingsFound: 0, 319 }, 320 } 321 for _, tt := range tests { 322 t.Run(tt.name, func(t *testing.T) { 323 db := testDB.SetupSlasherDB(t, false) 324 ctx := context.Background() 325 ds := Service{ 326 ctx: ctx, 327 cfg: &Config{SlasherDB: db}, 328 minMaxSpanDetector: attestations.NewSpanDetector(db), 329 } 330 require.NoError(t, db.SaveIndexedAttestations(ctx, tt.savedAtts)) 331 for _, att := range tt.savedAtts { 332 require.NoError(t, ds.minMaxSpanDetector.UpdateSpans(ctx, att)) 333 } 334 335 slashings, err := ds.DetectAttesterSlashings(ctx, tt.incomingAtt) 336 require.NoError(t, err) 337 require.Equal(t, tt.slashingsFound, len(slashings), "Unexpected amount of slashings found") 338 savedSlashings, err := db.AttesterSlashings(ctx, status.Active) 339 require.NoError(t, err) 340 require.Equal(t, tt.slashingsFound, len(savedSlashings), "Did not save slashing to db") 341 342 for _, ss := range slashings { 343 slashingAtt1 := ss.Attestation_1 344 slashingAtt2 := ss.Attestation_2 345 if !isDoubleVote(slashingAtt1, slashingAtt2) { 346 t.Fatalf( 347 "Expected slashing to be valid, received atts with target epoch %d and %d but not valid", 348 slashingAtt2.Data.Target.Epoch, 349 slashingAtt1.Data.Target.Epoch, 350 ) 351 } 352 } 353 }) 354 } 355 } 356 357 func TestDetect_updateHighestAttestation(t *testing.T) { 358 tests := []struct { 359 name string 360 savedHighest *slashpb.HighestAttestation 361 incomingAtt *ethpb.IndexedAttestation 362 expected *slashpb.HighestAttestation 363 }{ 364 { 365 name: "update only target to higher", 366 savedHighest: &slashpb.HighestAttestation{ 367 ValidatorId: 1, 368 HighestSourceEpoch: 1, 369 HighestTargetEpoch: 2, 370 }, 371 incomingAtt: ðpb.IndexedAttestation{ 372 AttestingIndices: []uint64{1, 3, 7}, 373 Data: ðpb.AttestationData{ 374 Source: ðpb.Checkpoint{Epoch: 1, Root: make([]byte, 32)}, 375 Target: ðpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)}, 376 BeaconBlockRoot: make([]byte, 32), 377 }, 378 Signature: bytesutil.PadTo([]byte{1, 2}, 96), 379 }, 380 expected: &slashpb.HighestAttestation{ 381 ValidatorId: 1, 382 HighestSourceEpoch: 1, 383 HighestTargetEpoch: 4, 384 }, 385 }, 386 { 387 name: "update target and source to higher", 388 savedHighest: &slashpb.HighestAttestation{ 389 ValidatorId: 1, 390 HighestSourceEpoch: 1, 391 HighestTargetEpoch: 2, 392 }, 393 incomingAtt: ðpb.IndexedAttestation{ 394 AttestingIndices: []uint64{1, 3, 7}, 395 Data: ðpb.AttestationData{ 396 Source: ðpb.Checkpoint{Epoch: 3, Root: make([]byte, 32)}, 397 Target: ðpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)}, 398 BeaconBlockRoot: make([]byte, 32), 399 }, 400 Signature: bytesutil.PadTo([]byte{1, 2}, 96), 401 }, 402 expected: &slashpb.HighestAttestation{ 403 ValidatorId: 1, 404 HighestSourceEpoch: 3, 405 HighestTargetEpoch: 4, 406 }, 407 }, 408 { 409 name: "no update", 410 savedHighest: &slashpb.HighestAttestation{ 411 ValidatorId: 1, 412 HighestSourceEpoch: 1, 413 HighestTargetEpoch: 2, 414 }, 415 incomingAtt: ðpb.IndexedAttestation{ 416 AttestingIndices: []uint64{1, 3, 7}, 417 Data: ðpb.AttestationData{ 418 Source: ðpb.Checkpoint{Epoch: 1, Root: make([]byte, 32)}, 419 Target: ðpb.Checkpoint{Epoch: 2, Root: make([]byte, 32)}, 420 BeaconBlockRoot: make([]byte, 32), 421 }, 422 Signature: bytesutil.PadTo([]byte{1, 2}, 96), 423 }, 424 expected: &slashpb.HighestAttestation{ 425 ValidatorId: 1, 426 HighestSourceEpoch: 1, 427 HighestTargetEpoch: 2, 428 }, 429 }, 430 { 431 name: "update target to higher when source is lower(should be a slashable attestation)", 432 savedHighest: &slashpb.HighestAttestation{ 433 ValidatorId: 1, 434 HighestSourceEpoch: 5, 435 HighestTargetEpoch: 6, 436 }, 437 incomingAtt: ðpb.IndexedAttestation{ 438 AttestingIndices: []uint64{1, 3, 7}, 439 Data: ðpb.AttestationData{ 440 Source: ðpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)}, 441 Target: ðpb.Checkpoint{Epoch: 8, Root: make([]byte, 32)}, 442 BeaconBlockRoot: make([]byte, 32), 443 }, 444 Signature: bytesutil.PadTo([]byte{1, 2}, 96), 445 }, 446 expected: &slashpb.HighestAttestation{ 447 ValidatorId: 1, 448 HighestSourceEpoch: 5, 449 HighestTargetEpoch: 8, 450 }, 451 }, 452 { 453 name: "update source to higher when target is same", 454 savedHighest: &slashpb.HighestAttestation{ 455 ValidatorId: 1, 456 HighestSourceEpoch: 3, 457 HighestTargetEpoch: 6, 458 }, 459 incomingAtt: ðpb.IndexedAttestation{ 460 AttestingIndices: []uint64{1, 3, 7}, 461 Data: ðpb.AttestationData{ 462 Source: ðpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)}, 463 Target: ðpb.Checkpoint{Epoch: 6, Root: make([]byte, 32)}, 464 BeaconBlockRoot: make([]byte, 32), 465 }, 466 Signature: bytesutil.PadTo([]byte{1, 2}, 96), 467 }, 468 expected: &slashpb.HighestAttestation{ 469 ValidatorId: 1, 470 HighestSourceEpoch: 4, 471 HighestTargetEpoch: 6, 472 }, 473 }, 474 } 475 476 for _, tt := range tests { 477 t.Run(tt.name, func(t *testing.T) { 478 db := testDB.SetupSlasherDB(t, false) 479 ctx := context.Background() 480 ds := Service{ 481 ctx: ctx, 482 cfg: &Config{SlasherDB: db}, 483 proposalsDetector: proposals.NewProposeDetector(db), 484 } 485 require.NoError(t, db.SaveHighestAttestation(ctx, tt.savedHighest)) 486 487 // Update and assert. 488 require.NoError(t, ds.UpdateHighestAttestation(ctx, tt.incomingAtt)) 489 h, err := db.HighestAttestation(ctx, tt.savedHighest.ValidatorId) 490 require.NoError(t, err) 491 assert.Equal(t, tt.expected.HighestSourceEpoch, h.HighestSourceEpoch) 492 assert.Equal(t, tt.expected.HighestTargetEpoch, h.HighestTargetEpoch) 493 }) 494 } 495 } 496 497 func TestDetect_detectProposerSlashing(t *testing.T) { 498 type testStruct struct { 499 name string 500 blk *ethpb.SignedBeaconBlockHeader 501 incomingBlk *ethpb.SignedBeaconBlockHeader 502 slashing *ethpb.ProposerSlashing 503 } 504 s0, err := helpers.StartSlot(0) 505 require.NoError(t, err) 506 sigBlk1slot0, err := testDetect.SignedBlockHeader(s0, 0) 507 require.NoError(t, err) 508 sigBlk2slot0, err := testDetect.SignedBlockHeader(s0, 0) 509 require.NoError(t, err) 510 s1, err := helpers.StartSlot(1) 511 require.NoError(t, err) 512 sigBlk1epoch1, err := testDetect.SignedBlockHeader(s1, 0) 513 require.NoError(t, err) 514 tests := []testStruct{ 515 { 516 name: "same block sig dont slash", 517 blk: sigBlk1slot0, 518 incomingBlk: sigBlk1slot0, 519 slashing: nil, 520 }, 521 { 522 name: "block from different epoch dont slash", 523 blk: sigBlk1slot0, 524 incomingBlk: sigBlk1epoch1, 525 slashing: nil, 526 }, 527 { 528 name: "different sig from same slot slash", 529 blk: sigBlk1slot0, 530 incomingBlk: sigBlk2slot0, 531 slashing: ðpb.ProposerSlashing{Header_1: sigBlk2slot0, Header_2: sigBlk1slot0}, 532 }, 533 } 534 for _, tt := range tests { 535 t.Run(tt.name, func(t *testing.T) { 536 db := testDB.SetupSlasherDB(t, false) 537 ctx := context.Background() 538 ds := Service{ 539 ctx: ctx, 540 cfg: &Config{SlasherDB: db}, 541 proposalsDetector: proposals.NewProposeDetector(db), 542 } 543 require.NoError(t, db.SaveBlockHeader(ctx, tt.blk)) 544 545 slashing, err := ds.proposalsDetector.DetectDoublePropose(ctx, tt.incomingBlk) 546 require.NoError(t, err) 547 assert.DeepEqual(t, tt.slashing, slashing) 548 savedSlashings, err := db.ProposalSlashingsByStatus(ctx, status.Active) 549 require.NoError(t, err) 550 if tt.slashing != nil { 551 require.Equal(t, 1, len(savedSlashings), "Did not save slashing to db") 552 } 553 554 if slashing != nil && !isDoublePropose(slashing.Header_1, slashing.Header_2) { 555 t.Fatalf( 556 "Expected slashing to be valid, received atts with target epoch %v and %v but not valid", 557 slashing.Header_1, 558 slashing.Header_2, 559 ) 560 } 561 }) 562 } 563 } 564 func TestDetect_detectProposerSlashingNoUpdate(t *testing.T) { 565 type testStruct struct { 566 name string 567 blk *ethpb.SignedBeaconBlockHeader 568 noUpdtaeBlk *ethpb.BeaconBlockHeader 569 slashable bool 570 } 571 s0, err := helpers.StartSlot(0) 572 require.NoError(t, err) 573 sigBlk1slot0, err := testDetect.SignedBlockHeader(s0, 0) 574 require.NoError(t, err) 575 blk1slot0, err := testDetect.BlockHeader(s0, 0) 576 require.NoError(t, err) 577 blk2slot0, err := testDetect.BlockHeader(s0, 0) 578 require.NoError(t, err) 579 diffRoot := [32]byte{1, 1, 1} 580 blk2slot0.ParentRoot = diffRoot[:] 581 blk3slot0, err := testDetect.BlockHeader(s0, 0) 582 require.NoError(t, err) 583 blk3slot0.StateRoot = diffRoot[:] 584 blk4slot0, err := testDetect.BlockHeader(s0, 0) 585 require.NoError(t, err) 586 blk4slot0.BodyRoot = diffRoot[:] 587 tests := []testStruct{ 588 { 589 name: "same block don't slash", 590 blk: sigBlk1slot0, 591 noUpdtaeBlk: blk1slot0, 592 slashable: false, 593 }, 594 { 595 name: "diff parent root slash", 596 blk: sigBlk1slot0, 597 noUpdtaeBlk: blk2slot0, 598 slashable: true, 599 }, 600 { 601 name: "diff state root slash", 602 blk: sigBlk1slot0, 603 noUpdtaeBlk: blk3slot0, 604 slashable: true, 605 }, 606 { 607 name: "diff body root slash", 608 blk: sigBlk1slot0, 609 noUpdtaeBlk: blk4slot0, 610 slashable: true, 611 }, 612 } 613 for _, tt := range tests { 614 t.Run(tt.name, func(t *testing.T) { 615 db := testDB.SetupSlasherDB(t, false) 616 ctx := context.Background() 617 ds := Service{ 618 ctx: ctx, 619 cfg: &Config{SlasherDB: db}, 620 proposalsDetector: proposals.NewProposeDetector(db), 621 } 622 require.NoError(t, db.SaveBlockHeader(ctx, tt.blk)) 623 624 slashble, err := ds.proposalsDetector.DetectDoubleProposeNoUpdate(ctx, tt.noUpdtaeBlk) 625 require.NoError(t, err) 626 assert.Equal(t, tt.slashable, slashble) 627 }) 628 } 629 } 630 631 func TestServer_MapResultsToAtts(t *testing.T) { 632 db := testDB.SetupSlasherDB(t, false) 633 ctx := context.Background() 634 ds := Service{ 635 ctx: ctx, 636 cfg: &Config{SlasherDB: db}, 637 } 638 // 3 unique results, but 7 validators in total. 639 results := []*types.DetectionResult{ 640 // 3 For the same slashable epoch and same sigs. 641 { 642 ValidatorIndex: 1, 643 SlashableEpoch: 5, 644 Kind: types.DoubleVote, 645 SigBytes: [2]byte{5, 5}, 646 }, 647 { 648 ValidatorIndex: 2, 649 SlashableEpoch: 5, 650 Kind: types.DoubleVote, 651 SigBytes: [2]byte{5, 5}, 652 }, 653 { 654 ValidatorIndex: 3, 655 SlashableEpoch: 5, 656 Kind: types.DoubleVote, 657 SigBytes: [2]byte{5, 5}, 658 }, 659 // Different signature. 660 { 661 ValidatorIndex: 5, 662 SlashableEpoch: 5, 663 Kind: types.DoubleVote, 664 SigBytes: [2]byte{3, 5}, 665 }, 666 // Different slashable epoch. 667 { 668 ValidatorIndex: 5, 669 SlashableEpoch: 4, 670 Kind: types.DoubleVote, 671 SigBytes: [2]byte{5, 5}, 672 }, 673 // Different both. 674 { 675 ValidatorIndex: 8, 676 SlashableEpoch: 6, 677 Kind: types.DoubleVote, 678 SigBytes: [2]byte{2, 1}, 679 }, 680 { 681 ValidatorIndex: 7, 682 SlashableEpoch: 6, 683 Kind: types.DoubleVote, 684 SigBytes: [2]byte{2, 1}, 685 }, 686 } 687 expectedResultsToAtts := map[[32]byte][]*ethpb.IndexedAttestation{ 688 resultHash(results[0]): { 689 createIndexedAttForResult(results[0]), 690 createIndexedAttForResult(results[1]), 691 createIndexedAttForResult(results[2]), 692 }, 693 resultHash(results[3]): { 694 createIndexedAttForResult(results[3]), 695 }, 696 resultHash(results[4]): { 697 createIndexedAttForResult(results[4]), 698 }, 699 resultHash(results[5]): { 700 createIndexedAttForResult(results[6]), 701 createIndexedAttForResult(results[5]), 702 }, 703 } 704 for _, atts := range expectedResultsToAtts { 705 require.NoError(t, ds.cfg.SlasherDB.SaveIndexedAttestations(ctx, atts)) 706 } 707 708 resultsToAtts, err := ds.mapResultsToAtts(ctx, results) 709 require.NoError(t, err) 710 for k := range expectedResultsToAtts { 711 exp := expectedResultsToAtts[k] 712 recv := resultsToAtts[k] 713 if !sszutil.DeepEqual(exp, recv) { 714 t.Error("Expected map:") 715 for key, value := range resultsToAtts { 716 t.Errorf("Key %#x: %d atts", key, len(value)) 717 t.Errorf("%+v", value) 718 } 719 t.Error("To equal:") 720 for key, value := range expectedResultsToAtts { 721 t.Errorf("Key %#x: %d atts", key, len(value)) 722 t.Errorf("%+v", value) 723 } 724 } 725 } 726 } 727 728 func createIndexedAttForResult(result *types.DetectionResult) *ethpb.IndexedAttestation { 729 return ðpb.IndexedAttestation{ 730 AttestingIndices: []uint64{result.ValidatorIndex}, 731 Data: ðpb.AttestationData{ 732 BeaconBlockRoot: []byte("text block root"), 733 Target: ðpb.Checkpoint{ 734 Epoch: result.SlashableEpoch, 735 }, 736 }, 737 Signature: append(result.SigBytes[:], []byte{uint8(result.ValidatorIndex), 4, 5, 6, 7, 8}...), 738 } 739 }