github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/operations/voluntaryexits/service_test.go (about) 1 package voluntaryexits 2 3 import ( 4 "context" 5 "reflect" 6 "testing" 7 8 types "github.com/prysmaticlabs/eth2-types" 9 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1" 10 p2ppb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 11 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 12 "github.com/prysmaticlabs/prysm/shared/params" 13 "github.com/prysmaticlabs/prysm/shared/testutil/require" 14 "google.golang.org/protobuf/proto" 15 ) 16 17 func TestPool_InsertVoluntaryExit(t *testing.T) { 18 type fields struct { 19 pending []*ethpb.SignedVoluntaryExit 20 } 21 type args struct { 22 exit *ethpb.SignedVoluntaryExit 23 } 24 tests := []struct { 25 name string 26 fields fields 27 args args 28 want []*ethpb.SignedVoluntaryExit 29 }{ 30 { 31 name: "Prevent inserting nil exit", 32 fields: fields{ 33 pending: make([]*ethpb.SignedVoluntaryExit, 0), 34 }, 35 args: args{ 36 exit: nil, 37 }, 38 want: []*ethpb.SignedVoluntaryExit{}, 39 }, 40 { 41 name: "Prevent inserting malformed exit", 42 fields: fields{ 43 pending: make([]*ethpb.SignedVoluntaryExit, 0), 44 }, 45 args: args{ 46 exit: ðpb.SignedVoluntaryExit{ 47 Exit: nil, 48 }, 49 }, 50 want: []*ethpb.SignedVoluntaryExit{}, 51 }, 52 { 53 name: "Empty list", 54 fields: fields{ 55 pending: make([]*ethpb.SignedVoluntaryExit, 0), 56 }, 57 args: args{ 58 exit: ðpb.SignedVoluntaryExit{ 59 Exit: ðpb.VoluntaryExit{ 60 Epoch: 12, 61 ValidatorIndex: 1, 62 }, 63 }, 64 }, 65 want: []*ethpb.SignedVoluntaryExit{ 66 { 67 Exit: ðpb.VoluntaryExit{ 68 Epoch: 12, 69 ValidatorIndex: 1, 70 }, 71 }, 72 }, 73 }, 74 { 75 name: "Duplicate identical exit", 76 fields: fields{ 77 pending: []*ethpb.SignedVoluntaryExit{ 78 { 79 Exit: ðpb.VoluntaryExit{ 80 Epoch: 12, 81 ValidatorIndex: 1, 82 }, 83 }, 84 }, 85 }, 86 args: args{ 87 exit: ðpb.SignedVoluntaryExit{ 88 Exit: ðpb.VoluntaryExit{ 89 Epoch: 12, 90 ValidatorIndex: 1, 91 }, 92 }, 93 }, 94 want: []*ethpb.SignedVoluntaryExit{ 95 { 96 Exit: ðpb.VoluntaryExit{ 97 Epoch: 12, 98 ValidatorIndex: 1, 99 }, 100 }, 101 }, 102 }, 103 { 104 name: "Duplicate exit in pending list", 105 fields: fields{ 106 pending: []*ethpb.SignedVoluntaryExit{ 107 { 108 Exit: ðpb.VoluntaryExit{ 109 Epoch: 12, 110 ValidatorIndex: 1, 111 }, 112 }, 113 }, 114 }, 115 args: args{ 116 exit: ðpb.SignedVoluntaryExit{ 117 Exit: ðpb.VoluntaryExit{ 118 Epoch: 12, 119 ValidatorIndex: 1, 120 }, 121 }, 122 }, 123 want: []*ethpb.SignedVoluntaryExit{ 124 { 125 Exit: ðpb.VoluntaryExit{ 126 Epoch: 12, 127 ValidatorIndex: 1, 128 }, 129 }, 130 }, 131 }, 132 { 133 name: "Duplicate validator index", 134 fields: fields{ 135 pending: []*ethpb.SignedVoluntaryExit{ 136 { 137 Exit: ðpb.VoluntaryExit{ 138 Epoch: 12, 139 ValidatorIndex: 1, 140 }, 141 }, 142 }, 143 }, 144 args: args{ 145 exit: ðpb.SignedVoluntaryExit{ 146 Exit: ðpb.VoluntaryExit{ 147 Epoch: 20, 148 ValidatorIndex: 1, 149 }, 150 }, 151 }, 152 want: []*ethpb.SignedVoluntaryExit{ 153 { 154 Exit: ðpb.VoluntaryExit{ 155 Epoch: 12, 156 ValidatorIndex: 1, 157 }, 158 }, 159 }, 160 }, 161 { 162 name: "Duplicate received with more favorable exit epoch", 163 fields: fields{ 164 pending: []*ethpb.SignedVoluntaryExit{ 165 { 166 Exit: ðpb.VoluntaryExit{ 167 Epoch: 12, 168 ValidatorIndex: 1, 169 }, 170 }, 171 }, 172 }, 173 args: args{ 174 exit: ðpb.SignedVoluntaryExit{ 175 Exit: ðpb.VoluntaryExit{ 176 Epoch: 4, 177 ValidatorIndex: 1, 178 }, 179 }, 180 }, 181 want: []*ethpb.SignedVoluntaryExit{ 182 { 183 Exit: ðpb.VoluntaryExit{ 184 Epoch: 4, 185 ValidatorIndex: 1, 186 }, 187 }, 188 }, 189 }, 190 { 191 name: "Exit for already exited validator", 192 fields: fields{ 193 pending: []*ethpb.SignedVoluntaryExit{}, 194 }, 195 args: args{ 196 exit: ðpb.SignedVoluntaryExit{ 197 Exit: ðpb.VoluntaryExit{ 198 Epoch: 12, 199 ValidatorIndex: 2, 200 }, 201 }, 202 }, 203 want: []*ethpb.SignedVoluntaryExit{}, 204 }, 205 { 206 name: "Maintains sorted order", 207 fields: fields{ 208 pending: []*ethpb.SignedVoluntaryExit{ 209 { 210 Exit: ðpb.VoluntaryExit{ 211 Epoch: 12, 212 ValidatorIndex: 0, 213 }, 214 }, 215 { 216 Exit: ðpb.VoluntaryExit{ 217 Epoch: 12, 218 ValidatorIndex: 2, 219 }, 220 }, 221 }, 222 }, 223 args: args{ 224 exit: ðpb.SignedVoluntaryExit{ 225 Exit: ðpb.VoluntaryExit{ 226 Epoch: 10, 227 ValidatorIndex: 1, 228 }, 229 }, 230 }, 231 want: []*ethpb.SignedVoluntaryExit{ 232 { 233 Exit: ðpb.VoluntaryExit{ 234 Epoch: 12, 235 ValidatorIndex: 0, 236 }, 237 }, 238 { 239 Exit: ðpb.VoluntaryExit{ 240 Epoch: 10, 241 ValidatorIndex: 1, 242 }, 243 }, 244 { 245 Exit: ðpb.VoluntaryExit{ 246 Epoch: 12, 247 ValidatorIndex: 2, 248 }, 249 }, 250 }, 251 }, 252 } 253 ctx := context.Background() 254 validators := []*ethpb.Validator{ 255 { // 0 256 ExitEpoch: params.BeaconConfig().FarFutureEpoch, 257 }, 258 { // 1 259 ExitEpoch: params.BeaconConfig().FarFutureEpoch, 260 }, 261 { // 2 - Already exited. 262 ExitEpoch: 15, 263 }, 264 { // 3 265 ExitEpoch: params.BeaconConfig().FarFutureEpoch, 266 }, 267 } 268 for _, tt := range tests { 269 t.Run(tt.name, func(t *testing.T) { 270 p := &Pool{ 271 pending: tt.fields.pending, 272 } 273 s, err := v1.InitializeFromProtoUnsafe(&p2ppb.BeaconState{Validators: validators}) 274 require.NoError(t, err) 275 p.InsertVoluntaryExit(ctx, s, tt.args.exit) 276 if len(p.pending) != len(tt.want) { 277 t.Fatalf("Mismatched lengths of pending list. Got %d, wanted %d.", len(p.pending), len(tt.want)) 278 } 279 for i := range p.pending { 280 if !proto.Equal(p.pending[i], tt.want[i]) { 281 t.Errorf("Pending exit at index %d does not match expected. Got=%v wanted=%v", i, p.pending[i], tt.want[i]) 282 } 283 } 284 }) 285 } 286 } 287 288 func TestPool_MarkIncluded(t *testing.T) { 289 type fields struct { 290 pending []*ethpb.SignedVoluntaryExit 291 } 292 type args struct { 293 exit *ethpb.SignedVoluntaryExit 294 } 295 tests := []struct { 296 name string 297 fields fields 298 args args 299 want fields 300 }{ 301 { 302 name: "Removes from pending list", 303 fields: fields{ 304 pending: []*ethpb.SignedVoluntaryExit{ 305 { 306 Exit: ðpb.VoluntaryExit{ValidatorIndex: 1}, 307 }, 308 { 309 Exit: ðpb.VoluntaryExit{ValidatorIndex: 2}, 310 }, 311 { 312 Exit: ðpb.VoluntaryExit{ValidatorIndex: 3}, 313 }, 314 }, 315 }, 316 args: args{ 317 exit: ðpb.SignedVoluntaryExit{ 318 Exit: ðpb.VoluntaryExit{ValidatorIndex: 2}, 319 }, 320 }, 321 want: fields{ 322 pending: []*ethpb.SignedVoluntaryExit{ 323 { 324 Exit: ðpb.VoluntaryExit{ValidatorIndex: 1}, 325 }, 326 { 327 Exit: ðpb.VoluntaryExit{ValidatorIndex: 3}, 328 }, 329 }, 330 }, 331 }, 332 } 333 for _, tt := range tests { 334 t.Run(tt.name, func(t *testing.T) { 335 p := &Pool{ 336 pending: tt.fields.pending, 337 } 338 p.MarkIncluded(tt.args.exit) 339 if len(p.pending) != len(tt.want.pending) { 340 t.Fatalf("Mismatched lengths of pending list. Got %d, wanted %d.", len(p.pending), len(tt.want.pending)) 341 } 342 for i := range p.pending { 343 if !proto.Equal(p.pending[i], tt.want.pending[i]) { 344 t.Errorf("Pending exit at index %d does not match expected. Got=%v wanted=%v", i, p.pending[i], tt.want.pending[i]) 345 } 346 } 347 }) 348 } 349 } 350 351 func TestPool_PendingExits(t *testing.T) { 352 type fields struct { 353 pending []*ethpb.SignedVoluntaryExit 354 noLimit bool 355 } 356 type args struct { 357 slot types.Slot 358 } 359 tests := []struct { 360 name string 361 fields fields 362 args args 363 want []*ethpb.SignedVoluntaryExit 364 }{ 365 { 366 name: "Empty list", 367 fields: fields{ 368 pending: []*ethpb.SignedVoluntaryExit{}, 369 }, 370 args: args{ 371 slot: 100000, 372 }, 373 want: []*ethpb.SignedVoluntaryExit{}, 374 }, 375 { 376 name: "All eligible", 377 fields: fields{ 378 pending: []*ethpb.SignedVoluntaryExit{ 379 {Exit: ðpb.VoluntaryExit{Epoch: 0}}, 380 {Exit: ðpb.VoluntaryExit{Epoch: 1}}, 381 {Exit: ðpb.VoluntaryExit{Epoch: 2}}, 382 {Exit: ðpb.VoluntaryExit{Epoch: 3}}, 383 {Exit: ðpb.VoluntaryExit{Epoch: 4}}, 384 }, 385 }, 386 args: args{ 387 slot: 1000000, 388 }, 389 want: []*ethpb.SignedVoluntaryExit{ 390 {Exit: ðpb.VoluntaryExit{Epoch: 0}}, 391 {Exit: ðpb.VoluntaryExit{Epoch: 1}}, 392 {Exit: ðpb.VoluntaryExit{Epoch: 2}}, 393 {Exit: ðpb.VoluntaryExit{Epoch: 3}}, 394 {Exit: ðpb.VoluntaryExit{Epoch: 4}}, 395 }, 396 }, 397 { 398 name: "All eligible, above max", 399 fields: fields{ 400 noLimit: true, 401 pending: []*ethpb.SignedVoluntaryExit{ 402 {Exit: ðpb.VoluntaryExit{Epoch: 0}}, 403 {Exit: ðpb.VoluntaryExit{Epoch: 1}}, 404 {Exit: ðpb.VoluntaryExit{Epoch: 2}}, 405 {Exit: ðpb.VoluntaryExit{Epoch: 3}}, 406 {Exit: ðpb.VoluntaryExit{Epoch: 4}}, 407 {Exit: ðpb.VoluntaryExit{Epoch: 5}}, 408 {Exit: ðpb.VoluntaryExit{Epoch: 6}}, 409 {Exit: ðpb.VoluntaryExit{Epoch: 7}}, 410 {Exit: ðpb.VoluntaryExit{Epoch: 8}}, 411 {Exit: ðpb.VoluntaryExit{Epoch: 9}}, 412 {Exit: ðpb.VoluntaryExit{Epoch: 10}}, 413 {Exit: ðpb.VoluntaryExit{Epoch: 11}}, 414 {Exit: ðpb.VoluntaryExit{Epoch: 12}}, 415 {Exit: ðpb.VoluntaryExit{Epoch: 13}}, 416 {Exit: ðpb.VoluntaryExit{Epoch: 14}}, 417 {Exit: ðpb.VoluntaryExit{Epoch: 15}}, 418 {Exit: ðpb.VoluntaryExit{Epoch: 16}}, 419 {Exit: ðpb.VoluntaryExit{Epoch: 17}}, 420 {Exit: ðpb.VoluntaryExit{Epoch: 18}}, 421 {Exit: ðpb.VoluntaryExit{Epoch: 19}}, 422 }, 423 }, 424 args: args{ 425 slot: 1000000, 426 }, 427 want: []*ethpb.SignedVoluntaryExit{ 428 {Exit: ðpb.VoluntaryExit{Epoch: 0}}, 429 {Exit: ðpb.VoluntaryExit{Epoch: 1}}, 430 {Exit: ðpb.VoluntaryExit{Epoch: 2}}, 431 {Exit: ðpb.VoluntaryExit{Epoch: 3}}, 432 {Exit: ðpb.VoluntaryExit{Epoch: 4}}, 433 {Exit: ðpb.VoluntaryExit{Epoch: 5}}, 434 {Exit: ðpb.VoluntaryExit{Epoch: 6}}, 435 {Exit: ðpb.VoluntaryExit{Epoch: 7}}, 436 {Exit: ðpb.VoluntaryExit{Epoch: 8}}, 437 {Exit: ðpb.VoluntaryExit{Epoch: 9}}, 438 {Exit: ðpb.VoluntaryExit{Epoch: 10}}, 439 {Exit: ðpb.VoluntaryExit{Epoch: 11}}, 440 {Exit: ðpb.VoluntaryExit{Epoch: 12}}, 441 {Exit: ðpb.VoluntaryExit{Epoch: 13}}, 442 {Exit: ðpb.VoluntaryExit{Epoch: 14}}, 443 {Exit: ðpb.VoluntaryExit{Epoch: 15}}, 444 {Exit: ðpb.VoluntaryExit{Epoch: 16}}, 445 {Exit: ðpb.VoluntaryExit{Epoch: 17}}, 446 {Exit: ðpb.VoluntaryExit{Epoch: 18}}, 447 {Exit: ðpb.VoluntaryExit{Epoch: 19}}, 448 }, 449 }, 450 { 451 name: "All eligible, block max", 452 fields: fields{ 453 pending: []*ethpb.SignedVoluntaryExit{ 454 {Exit: ðpb.VoluntaryExit{Epoch: 0}}, 455 {Exit: ðpb.VoluntaryExit{Epoch: 1}}, 456 {Exit: ðpb.VoluntaryExit{Epoch: 2}}, 457 {Exit: ðpb.VoluntaryExit{Epoch: 3}}, 458 {Exit: ðpb.VoluntaryExit{Epoch: 4}}, 459 {Exit: ðpb.VoluntaryExit{Epoch: 5}}, 460 {Exit: ðpb.VoluntaryExit{Epoch: 6}}, 461 {Exit: ðpb.VoluntaryExit{Epoch: 7}}, 462 {Exit: ðpb.VoluntaryExit{Epoch: 8}}, 463 {Exit: ðpb.VoluntaryExit{Epoch: 9}}, 464 {Exit: ðpb.VoluntaryExit{Epoch: 10}}, 465 {Exit: ðpb.VoluntaryExit{Epoch: 11}}, 466 {Exit: ðpb.VoluntaryExit{Epoch: 12}}, 467 {Exit: ðpb.VoluntaryExit{Epoch: 13}}, 468 {Exit: ðpb.VoluntaryExit{Epoch: 14}}, 469 {Exit: ðpb.VoluntaryExit{Epoch: 15}}, 470 {Exit: ðpb.VoluntaryExit{Epoch: 16}}, 471 {Exit: ðpb.VoluntaryExit{Epoch: 17}}, 472 {Exit: ðpb.VoluntaryExit{Epoch: 18}}, 473 {Exit: ðpb.VoluntaryExit{Epoch: 19}}, 474 }, 475 }, 476 args: args{ 477 slot: 1000000, 478 }, 479 want: []*ethpb.SignedVoluntaryExit{ 480 {Exit: ðpb.VoluntaryExit{Epoch: 0}}, 481 {Exit: ðpb.VoluntaryExit{Epoch: 1}}, 482 {Exit: ðpb.VoluntaryExit{Epoch: 2}}, 483 {Exit: ðpb.VoluntaryExit{Epoch: 3}}, 484 {Exit: ðpb.VoluntaryExit{Epoch: 4}}, 485 {Exit: ðpb.VoluntaryExit{Epoch: 5}}, 486 {Exit: ðpb.VoluntaryExit{Epoch: 6}}, 487 {Exit: ðpb.VoluntaryExit{Epoch: 7}}, 488 {Exit: ðpb.VoluntaryExit{Epoch: 8}}, 489 {Exit: ðpb.VoluntaryExit{Epoch: 9}}, 490 {Exit: ðpb.VoluntaryExit{Epoch: 10}}, 491 {Exit: ðpb.VoluntaryExit{Epoch: 11}}, 492 {Exit: ðpb.VoluntaryExit{Epoch: 12}}, 493 {Exit: ðpb.VoluntaryExit{Epoch: 13}}, 494 {Exit: ðpb.VoluntaryExit{Epoch: 14}}, 495 {Exit: ðpb.VoluntaryExit{Epoch: 15}}, 496 }, 497 }, 498 { 499 name: "Some eligible", 500 fields: fields{ 501 pending: []*ethpb.SignedVoluntaryExit{ 502 {Exit: ðpb.VoluntaryExit{Epoch: 0}}, 503 {Exit: ðpb.VoluntaryExit{Epoch: 3}}, 504 {Exit: ðpb.VoluntaryExit{Epoch: 4}}, 505 {Exit: ðpb.VoluntaryExit{Epoch: 2}}, 506 {Exit: ðpb.VoluntaryExit{Epoch: 1}}, 507 }, 508 }, 509 args: args{ 510 slot: 2 * params.BeaconConfig().SlotsPerEpoch, 511 }, 512 want: []*ethpb.SignedVoluntaryExit{ 513 {Exit: ðpb.VoluntaryExit{Epoch: 0}}, 514 {Exit: ðpb.VoluntaryExit{Epoch: 2}}, 515 {Exit: ðpb.VoluntaryExit{Epoch: 1}}, 516 }, 517 }, 518 } 519 for _, tt := range tests { 520 t.Run(tt.name, func(t *testing.T) { 521 p := &Pool{ 522 pending: tt.fields.pending, 523 } 524 s, err := v1.InitializeFromProtoUnsafe(&p2ppb.BeaconState{Validators: []*ethpb.Validator{{ExitEpoch: params.BeaconConfig().FarFutureEpoch}}}) 525 require.NoError(t, err) 526 if got := p.PendingExits(s, tt.args.slot, tt.fields.noLimit); !reflect.DeepEqual(got, tt.want) { 527 t.Errorf("PendingExits() = %v, want %v", got, tt.want) 528 } 529 }) 530 } 531 }