github.com/prysmaticlabs/prysm@v1.4.4/validator/client/propose_test.go (about) 1 package client 2 3 import ( 4 "context" 5 "encoding/hex" 6 "errors" 7 "strings" 8 "testing" 9 "time" 10 11 "github.com/golang/mock/gomock" 12 lru "github.com/hashicorp/golang-lru" 13 types "github.com/prysmaticlabs/eth2-types" 14 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 15 validatorpb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2" 16 "github.com/prysmaticlabs/prysm/shared/bls" 17 "github.com/prysmaticlabs/prysm/shared/bytesutil" 18 "github.com/prysmaticlabs/prysm/shared/mock" 19 "github.com/prysmaticlabs/prysm/shared/params" 20 "github.com/prysmaticlabs/prysm/shared/testutil" 21 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 22 "github.com/prysmaticlabs/prysm/shared/testutil/require" 23 testing2 "github.com/prysmaticlabs/prysm/validator/db/testing" 24 "github.com/prysmaticlabs/prysm/validator/graffiti" 25 logTest "github.com/sirupsen/logrus/hooks/test" 26 grpc "google.golang.org/grpc" 27 "google.golang.org/protobuf/types/known/timestamppb" 28 ) 29 30 type mocks struct { 31 validatorClient *mock.MockBeaconNodeValidatorClient 32 nodeClient *mock.MockNodeClient 33 signExitFunc func(context.Context, *validatorpb.SignRequest) (bls.Signature, error) 34 } 35 36 type mockSignature struct{} 37 38 func (mockSignature) Verify(bls.PublicKey, []byte) bool { 39 return true 40 } 41 func (mockSignature) AggregateVerify([]bls.PublicKey, [][32]byte) bool { 42 return true 43 } 44 func (mockSignature) FastAggregateVerify([]bls.PublicKey, [32]byte) bool { 45 return true 46 } 47 func (mockSignature) Eth2FastAggregateVerify([]bls.PublicKey, [32]byte) bool { 48 return true 49 } 50 func (mockSignature) Marshal() []byte { 51 return make([]byte, 32) 52 } 53 func (m mockSignature) Copy() bls.Signature { 54 return m 55 } 56 57 func setup(t *testing.T) (*validator, *mocks, bls.SecretKey, func()) { 58 validatorKey, err := bls.RandKey() 59 require.NoError(t, err) 60 pubKey := [48]byte{} 61 copy(pubKey[:], validatorKey.PublicKey().Marshal()) 62 valDB := testing2.SetupDB(t, [][48]byte{pubKey}) 63 ctrl := gomock.NewController(t) 64 m := &mocks{ 65 validatorClient: mock.NewMockBeaconNodeValidatorClient(ctrl), 66 nodeClient: mock.NewMockNodeClient(ctrl), 67 signExitFunc: func(ctx context.Context, req *validatorpb.SignRequest) (bls.Signature, error) { 68 return mockSignature{}, nil 69 }, 70 } 71 72 aggregatedSlotCommitteeIDCache, err := lru.New(int(params.BeaconConfig().MaxCommitteesPerSlot)) 73 require.NoError(t, err) 74 copy(pubKey[:], validatorKey.PublicKey().Marshal()) 75 km := &mockKeymanager{ 76 keysMap: map[[48]byte]bls.SecretKey{ 77 pubKey: validatorKey, 78 }, 79 } 80 validator := &validator{ 81 db: valDB, 82 keyManager: km, 83 validatorClient: m.validatorClient, 84 graffiti: []byte{}, 85 attLogs: make(map[[32]byte]*attSubmitted), 86 aggregatedSlotCommitteeIDCache: aggregatedSlotCommitteeIDCache, 87 } 88 89 return validator, m, validatorKey, ctrl.Finish 90 } 91 92 func TestProposeBlock_DoesNotProposeGenesisBlock(t *testing.T) { 93 hook := logTest.NewGlobal() 94 validator, _, validatorKey, finish := setup(t) 95 defer finish() 96 pubKey := [48]byte{} 97 copy(pubKey[:], validatorKey.PublicKey().Marshal()) 98 validator.ProposeBlock(context.Background(), 0, pubKey) 99 100 require.LogsContain(t, hook, "Assigned to genesis slot, skipping proposal") 101 } 102 103 func TestProposeBlock_DomainDataFailed(t *testing.T) { 104 hook := logTest.NewGlobal() 105 validator, m, validatorKey, finish := setup(t) 106 defer finish() 107 pubKey := [48]byte{} 108 copy(pubKey[:], validatorKey.PublicKey().Marshal()) 109 110 m.validatorClient.EXPECT().DomainData( 111 gomock.Any(), // ctx 112 gomock.Any(), // epoch 113 ).Return(nil /*response*/, errors.New("uh oh")) 114 115 validator.ProposeBlock(context.Background(), 1, pubKey) 116 require.LogsContain(t, hook, "Failed to sign randao reveal") 117 } 118 119 func TestProposeBlock_DomainDataIsNil(t *testing.T) { 120 hook := logTest.NewGlobal() 121 validator, m, validatorKey, finish := setup(t) 122 defer finish() 123 pubKey := [48]byte{} 124 copy(pubKey[:], validatorKey.PublicKey().Marshal()) 125 126 m.validatorClient.EXPECT().DomainData( 127 gomock.Any(), // ctx 128 gomock.Any(), // epoch 129 ).Return(nil /*response*/, nil) 130 131 validator.ProposeBlock(context.Background(), 1, pubKey) 132 require.LogsContain(t, hook, domainDataErr) 133 } 134 135 func TestProposeBlock_RequestBlockFailed(t *testing.T) { 136 hook := logTest.NewGlobal() 137 validator, m, validatorKey, finish := setup(t) 138 defer finish() 139 pubKey := [48]byte{} 140 copy(pubKey[:], validatorKey.PublicKey().Marshal()) 141 142 m.validatorClient.EXPECT().DomainData( 143 gomock.Any(), // ctx 144 gomock.Any(), // epoch 145 ).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) 146 147 m.validatorClient.EXPECT().GetBlock( 148 gomock.Any(), // ctx 149 gomock.Any(), // block request 150 ).Return(nil /*response*/, errors.New("uh oh")) 151 152 validator.ProposeBlock(context.Background(), 1, pubKey) 153 require.LogsContain(t, hook, "Failed to request block from beacon node") 154 } 155 156 func TestProposeBlock_ProposeBlockFailed(t *testing.T) { 157 hook := logTest.NewGlobal() 158 validator, m, validatorKey, finish := setup(t) 159 defer finish() 160 pubKey := [48]byte{} 161 copy(pubKey[:], validatorKey.PublicKey().Marshal()) 162 163 m.validatorClient.EXPECT().DomainData( 164 gomock.Any(), // ctx 165 gomock.Any(), // epoch 166 ).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) 167 168 m.validatorClient.EXPECT().GetBlock( 169 gomock.Any(), // ctx 170 gomock.Any(), 171 ).Return(testutil.NewBeaconBlock().Block, nil /*err*/) 172 173 m.validatorClient.EXPECT().DomainData( 174 gomock.Any(), // ctx 175 gomock.Any(), // epoch 176 ).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) 177 178 m.validatorClient.EXPECT().ProposeBlock( 179 gomock.Any(), // ctx 180 gomock.AssignableToTypeOf(ðpb.SignedBeaconBlock{}), 181 ).Return(nil /*response*/, errors.New("uh oh")) 182 183 validator.ProposeBlock(context.Background(), 1, pubKey) 184 require.LogsContain(t, hook, "Failed to propose block") 185 } 186 187 func TestProposeBlock_BlocksDoubleProposal(t *testing.T) { 188 hook := logTest.NewGlobal() 189 validator, m, validatorKey, finish := setup(t) 190 defer finish() 191 pubKey := [48]byte{} 192 copy(pubKey[:], validatorKey.PublicKey().Marshal()) 193 194 dummyRoot := [32]byte{} 195 // Save a dummy proposal history at slot 0. 196 err := validator.db.SaveProposalHistoryForSlot(context.Background(), pubKey, 0, dummyRoot[:]) 197 require.NoError(t, err) 198 199 m.validatorClient.EXPECT().DomainData( 200 gomock.Any(), // ctx 201 gomock.Any(), // epoch 202 ).Times(1).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) 203 204 testBlock := testutil.NewBeaconBlock() 205 slot := params.BeaconConfig().SlotsPerEpoch*5 + 2 206 testBlock.Block.Slot = slot 207 m.validatorClient.EXPECT().GetBlock( 208 gomock.Any(), // ctx 209 gomock.Any(), 210 ).Return(testBlock.Block, nil /*err*/) 211 212 secondTestBlock := testutil.NewBeaconBlock() 213 secondTestBlock.Block.Slot = slot 214 graffiti := [32]byte{} 215 copy(graffiti[:], "someothergraffiti") 216 secondTestBlock.Block.Body.Graffiti = graffiti[:] 217 m.validatorClient.EXPECT().GetBlock( 218 gomock.Any(), // ctx 219 gomock.Any(), 220 ).Return(secondTestBlock.Block, nil /*err*/) 221 222 m.validatorClient.EXPECT().DomainData( 223 gomock.Any(), // ctx 224 gomock.Any(), // epoch 225 ).Times(3).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) 226 227 m.validatorClient.EXPECT().ProposeBlock( 228 gomock.Any(), // ctx 229 gomock.AssignableToTypeOf(ðpb.SignedBeaconBlock{}), 230 ).Return(ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil /*error*/) 231 232 validator.ProposeBlock(context.Background(), slot, pubKey) 233 require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr) 234 235 validator.ProposeBlock(context.Background(), slot, pubKey) 236 require.LogsContain(t, hook, failedPreBlockSignLocalErr) 237 } 238 239 func TestProposeBlock_BlocksDoubleProposal_After54KEpochs(t *testing.T) { 240 hook := logTest.NewGlobal() 241 validator, m, validatorKey, finish := setup(t) 242 defer finish() 243 pubKey := [48]byte{} 244 copy(pubKey[:], validatorKey.PublicKey().Marshal()) 245 246 dummyRoot := [32]byte{} 247 // Save a dummy proposal history at slot 0. 248 err := validator.db.SaveProposalHistoryForSlot(context.Background(), pubKey, 0, dummyRoot[:]) 249 require.NoError(t, err) 250 251 m.validatorClient.EXPECT().DomainData( 252 gomock.Any(), // ctx 253 gomock.Any(), // epoch 254 ).Times(1).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) 255 256 testBlock := testutil.NewBeaconBlock() 257 farFuture := params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().WeakSubjectivityPeriod + 9)) 258 testBlock.Block.Slot = farFuture 259 m.validatorClient.EXPECT().GetBlock( 260 gomock.Any(), // ctx 261 gomock.Any(), 262 ).Return(testBlock.Block, nil /*err*/) 263 264 secondTestBlock := testutil.NewBeaconBlock() 265 secondTestBlock.Block.Slot = farFuture 266 graffiti := [32]byte{} 267 copy(graffiti[:], "someothergraffiti") 268 secondTestBlock.Block.Body.Graffiti = graffiti[:] 269 m.validatorClient.EXPECT().GetBlock( 270 gomock.Any(), // ctx 271 gomock.Any(), 272 ).Return(secondTestBlock.Block, nil /*err*/) 273 274 m.validatorClient.EXPECT().DomainData( 275 gomock.Any(), // ctx 276 gomock.Any(), // epoch 277 ).Times(3).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) 278 279 m.validatorClient.EXPECT().ProposeBlock( 280 gomock.Any(), // ctx 281 gomock.AssignableToTypeOf(ðpb.SignedBeaconBlock{}), 282 ).Return(ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil /*error*/) 283 284 validator.ProposeBlock(context.Background(), farFuture, pubKey) 285 require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr) 286 287 validator.ProposeBlock(context.Background(), farFuture, pubKey) 288 require.LogsContain(t, hook, failedPreBlockSignLocalErr) 289 } 290 291 func TestProposeBlock_AllowsPastProposals(t *testing.T) { 292 hook := logTest.NewGlobal() 293 validator, m, validatorKey, finish := setup(t) 294 defer finish() 295 pubKey := [48]byte{} 296 copy(pubKey[:], validatorKey.PublicKey().Marshal()) 297 298 // Save a dummy proposal history at slot 0. 299 err := validator.db.SaveProposalHistoryForSlot(context.Background(), pubKey, 0, []byte{}) 300 require.NoError(t, err) 301 302 m.validatorClient.EXPECT().DomainData( 303 gomock.Any(), // ctx 304 gomock.Any(), // epoch 305 ).Times(2).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) 306 307 farAhead := params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().WeakSubjectivityPeriod + 9)) 308 blk := testutil.NewBeaconBlock() 309 blk.Block.Slot = farAhead 310 m.validatorClient.EXPECT().GetBlock( 311 gomock.Any(), // ctx 312 gomock.Any(), 313 ).Return(blk.Block, nil /*err*/) 314 315 m.validatorClient.EXPECT().DomainData( 316 gomock.Any(), // ctx 317 gomock.Any(), // epoch 318 ).Times(2).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) 319 320 m.validatorClient.EXPECT().ProposeBlock( 321 gomock.Any(), // ctx 322 gomock.AssignableToTypeOf(ðpb.SignedBeaconBlock{}), 323 ).Times(2).Return(ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil /*error*/) 324 325 validator.ProposeBlock(context.Background(), farAhead, pubKey) 326 require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr) 327 328 past := params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().WeakSubjectivityPeriod - 400)) 329 blk2 := testutil.NewBeaconBlock() 330 blk2.Block.Slot = past 331 m.validatorClient.EXPECT().GetBlock( 332 gomock.Any(), // ctx 333 gomock.Any(), 334 ).Return(blk2.Block, nil /*err*/) 335 validator.ProposeBlock(context.Background(), past, pubKey) 336 require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr) 337 } 338 339 func TestProposeBlock_AllowsSameEpoch(t *testing.T) { 340 hook := logTest.NewGlobal() 341 validator, m, validatorKey, finish := setup(t) 342 defer finish() 343 pubKey := [48]byte{} 344 copy(pubKey[:], validatorKey.PublicKey().Marshal()) 345 346 // Save a dummy proposal history at slot 0. 347 err := validator.db.SaveProposalHistoryForSlot(context.Background(), pubKey, 0, []byte{}) 348 require.NoError(t, err) 349 350 m.validatorClient.EXPECT().DomainData( 351 gomock.Any(), // ctx 352 gomock.Any(), // epoch 353 ).Times(2).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) 354 355 farAhead := params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().WeakSubjectivityPeriod + 9)) 356 blk := testutil.NewBeaconBlock() 357 blk.Block.Slot = farAhead 358 m.validatorClient.EXPECT().GetBlock( 359 gomock.Any(), // ctx 360 gomock.Any(), 361 ).Return(blk.Block, nil /*err*/) 362 363 m.validatorClient.EXPECT().DomainData( 364 gomock.Any(), // ctx 365 gomock.Any(), // epoch 366 ).Times(2).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) 367 368 m.validatorClient.EXPECT().ProposeBlock( 369 gomock.Any(), // ctx 370 gomock.AssignableToTypeOf(ðpb.SignedBeaconBlock{}), 371 ).Times(2).Return(ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil /*error*/) 372 373 validator.ProposeBlock(context.Background(), farAhead, pubKey) 374 require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr) 375 376 blk2 := testutil.NewBeaconBlock() 377 blk2.Block.Slot = farAhead - 4 378 m.validatorClient.EXPECT().GetBlock( 379 gomock.Any(), // ctx 380 gomock.Any(), 381 ).Return(blk2.Block, nil /*err*/) 382 383 validator.ProposeBlock(context.Background(), farAhead-4, pubKey) 384 require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr) 385 } 386 387 func TestProposeBlock_BroadcastsBlock(t *testing.T) { 388 validator, m, validatorKey, finish := setup(t) 389 defer finish() 390 pubKey := [48]byte{} 391 copy(pubKey[:], validatorKey.PublicKey().Marshal()) 392 393 m.validatorClient.EXPECT().DomainData( 394 gomock.Any(), // ctx 395 gomock.Any(), // epoch 396 ).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) 397 398 m.validatorClient.EXPECT().GetBlock( 399 gomock.Any(), // ctx 400 gomock.Any(), 401 ).Return(testutil.NewBeaconBlock().Block, nil /*err*/) 402 403 m.validatorClient.EXPECT().DomainData( 404 gomock.Any(), // ctx 405 gomock.Any(), // epoch 406 ).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) 407 408 m.validatorClient.EXPECT().ProposeBlock( 409 gomock.Any(), // ctx 410 gomock.AssignableToTypeOf(ðpb.SignedBeaconBlock{}), 411 ).Return(ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil /*error*/) 412 413 validator.ProposeBlock(context.Background(), 1, pubKey) 414 } 415 416 func TestProposeBlock_BroadcastsBlock_WithGraffiti(t *testing.T) { 417 validator, m, validatorKey, finish := setup(t) 418 defer finish() 419 pubKey := [48]byte{} 420 copy(pubKey[:], validatorKey.PublicKey().Marshal()) 421 422 validator.graffiti = []byte("12345678901234567890123456789012") 423 424 m.validatorClient.EXPECT().DomainData( 425 gomock.Any(), // ctx 426 gomock.Any(), // epoch 427 ).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) 428 429 blk := testutil.NewBeaconBlock() 430 blk.Block.Body.Graffiti = validator.graffiti 431 m.validatorClient.EXPECT().GetBlock( 432 gomock.Any(), // ctx 433 gomock.Any(), 434 ).Return(blk.Block, nil /*err*/) 435 436 m.validatorClient.EXPECT().DomainData( 437 gomock.Any(), // ctx 438 gomock.Any(), // epoch 439 ).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) 440 441 var sentBlock *ethpb.SignedBeaconBlock 442 443 m.validatorClient.EXPECT().ProposeBlock( 444 gomock.Any(), // ctx 445 gomock.AssignableToTypeOf(ðpb.SignedBeaconBlock{}), 446 ).DoAndReturn(func(ctx context.Context, block *ethpb.SignedBeaconBlock, arg2 ...grpc.CallOption) (*ethpb.ProposeResponse, error) { 447 sentBlock = block 448 return ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil 449 }) 450 451 validator.ProposeBlock(context.Background(), 1, pubKey) 452 assert.Equal(t, string(validator.graffiti), string(sentBlock.Block.Body.Graffiti)) 453 } 454 455 func TestProposeExit_ValidatorIndexFailed(t *testing.T) { 456 _, m, validatorKey, finish := setup(t) 457 defer finish() 458 459 m.validatorClient.EXPECT().ValidatorIndex( 460 gomock.Any(), 461 gomock.Any(), 462 ).Return(nil, errors.New("uh oh")) 463 464 err := ProposeExit( 465 context.Background(), 466 m.validatorClient, 467 m.nodeClient, 468 m.signExitFunc, 469 validatorKey.PublicKey().Marshal(), 470 ) 471 assert.NotNil(t, err) 472 assert.ErrorContains(t, "uh oh", err) 473 assert.ErrorContains(t, "gRPC call to get validator index failed", err) 474 } 475 476 func TestProposeExit_GetGenesisFailed(t *testing.T) { 477 _, m, validatorKey, finish := setup(t) 478 defer finish() 479 480 m.validatorClient.EXPECT(). 481 ValidatorIndex(gomock.Any(), gomock.Any()). 482 Return(nil, nil) 483 484 m.nodeClient.EXPECT(). 485 GetGenesis(gomock.Any(), gomock.Any()). 486 Return(nil, errors.New("uh oh")) 487 488 err := ProposeExit( 489 context.Background(), 490 m.validatorClient, 491 m.nodeClient, 492 m.signExitFunc, 493 validatorKey.PublicKey().Marshal(), 494 ) 495 assert.NotNil(t, err) 496 assert.ErrorContains(t, "uh oh", err) 497 assert.ErrorContains(t, "gRPC call to get genesis time failed", err) 498 } 499 500 func TestProposeExit_DomainDataFailed(t *testing.T) { 501 _, m, validatorKey, finish := setup(t) 502 defer finish() 503 504 m.validatorClient.EXPECT(). 505 ValidatorIndex(gomock.Any(), gomock.Any()). 506 Return(ðpb.ValidatorIndexResponse{Index: 1}, nil) 507 508 // Any time in the past will suffice 509 genesisTime := ×tamppb.Timestamp{ 510 Seconds: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC).Unix(), 511 } 512 513 m.nodeClient.EXPECT(). 514 GetGenesis(gomock.Any(), gomock.Any()). 515 Return(ðpb.Genesis{GenesisTime: genesisTime}, nil) 516 517 m.validatorClient.EXPECT(). 518 DomainData(gomock.Any(), gomock.Any()). 519 Return(nil, errors.New("uh oh")) 520 521 err := ProposeExit( 522 context.Background(), 523 m.validatorClient, 524 m.nodeClient, 525 m.signExitFunc, 526 validatorKey.PublicKey().Marshal(), 527 ) 528 assert.NotNil(t, err) 529 assert.ErrorContains(t, domainDataErr, err) 530 assert.ErrorContains(t, "uh oh", err) 531 assert.ErrorContains(t, "failed to sign voluntary exit", err) 532 } 533 534 func TestProposeExit_DomainDataIsNil(t *testing.T) { 535 _, m, validatorKey, finish := setup(t) 536 defer finish() 537 538 m.validatorClient.EXPECT(). 539 ValidatorIndex(gomock.Any(), gomock.Any()). 540 Return(ðpb.ValidatorIndexResponse{Index: 1}, nil) 541 542 // Any time in the past will suffice 543 genesisTime := ×tamppb.Timestamp{ 544 Seconds: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC).Unix(), 545 } 546 547 m.nodeClient.EXPECT(). 548 GetGenesis(gomock.Any(), gomock.Any()). 549 Return(ðpb.Genesis{GenesisTime: genesisTime}, nil) 550 551 m.validatorClient.EXPECT(). 552 DomainData(gomock.Any(), gomock.Any()). 553 Return(nil, nil) 554 555 err := ProposeExit( 556 context.Background(), 557 m.validatorClient, 558 m.nodeClient, 559 m.signExitFunc, 560 validatorKey.PublicKey().Marshal(), 561 ) 562 assert.NotNil(t, err) 563 assert.ErrorContains(t, domainDataErr, err) 564 assert.ErrorContains(t, "failed to sign voluntary exit", err) 565 } 566 567 func TestProposeBlock_ProposeExitFailed(t *testing.T) { 568 _, m, validatorKey, finish := setup(t) 569 defer finish() 570 571 m.validatorClient.EXPECT(). 572 ValidatorIndex(gomock.Any(), gomock.Any()). 573 Return(ðpb.ValidatorIndexResponse{Index: 1}, nil) 574 575 // Any time in the past will suffice 576 genesisTime := ×tamppb.Timestamp{ 577 Seconds: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC).Unix(), 578 } 579 580 m.nodeClient.EXPECT(). 581 GetGenesis(gomock.Any(), gomock.Any()). 582 Return(ðpb.Genesis{GenesisTime: genesisTime}, nil) 583 584 m.validatorClient.EXPECT(). 585 DomainData(gomock.Any(), gomock.Any()). 586 Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil) 587 588 m.validatorClient.EXPECT(). 589 ProposeExit(gomock.Any(), gomock.AssignableToTypeOf(ðpb.SignedVoluntaryExit{})). 590 Return(nil, errors.New("uh oh")) 591 592 err := ProposeExit( 593 context.Background(), 594 m.validatorClient, 595 m.nodeClient, 596 m.signExitFunc, 597 validatorKey.PublicKey().Marshal(), 598 ) 599 assert.NotNil(t, err) 600 assert.ErrorContains(t, "uh oh", err) 601 assert.ErrorContains(t, "failed to propose voluntary exit", err) 602 } 603 604 func TestProposeExit_BroadcastsBlock(t *testing.T) { 605 _, m, validatorKey, finish := setup(t) 606 defer finish() 607 608 m.validatorClient.EXPECT(). 609 ValidatorIndex(gomock.Any(), gomock.Any()). 610 Return(ðpb.ValidatorIndexResponse{Index: 1}, nil) 611 612 // Any time in the past will suffice 613 genesisTime := ×tamppb.Timestamp{ 614 Seconds: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC).Unix(), 615 } 616 617 m.nodeClient.EXPECT(). 618 GetGenesis(gomock.Any(), gomock.Any()). 619 Return(ðpb.Genesis{GenesisTime: genesisTime}, nil) 620 621 m.validatorClient.EXPECT(). 622 DomainData(gomock.Any(), gomock.Any()). 623 Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil) 624 625 m.validatorClient.EXPECT(). 626 ProposeExit(gomock.Any(), gomock.AssignableToTypeOf(ðpb.SignedVoluntaryExit{})). 627 Return(ðpb.ProposeExitResponse{}, nil) 628 629 assert.NoError(t, ProposeExit( 630 context.Background(), 631 m.validatorClient, 632 m.nodeClient, 633 m.signExitFunc, 634 validatorKey.PublicKey().Marshal(), 635 )) 636 } 637 638 func TestSignBlock(t *testing.T) { 639 validator, m, _, finish := setup(t) 640 defer finish() 641 642 secretKey, err := bls.SecretKeyFromBytes(bytesutil.PadTo([]byte{1}, 32)) 643 require.NoError(t, err, "Failed to generate key from bytes") 644 publicKey := secretKey.PublicKey() 645 proposerDomain := make([]byte, 32) 646 m.validatorClient.EXPECT(). 647 DomainData(gomock.Any(), gomock.Any()). 648 Return(ðpb.DomainResponse{SignatureDomain: proposerDomain}, nil) 649 ctx := context.Background() 650 blk := testutil.NewBeaconBlock() 651 blk.Block.Slot = 1 652 blk.Block.ProposerIndex = 100 653 var pubKey [48]byte 654 copy(pubKey[:], publicKey.Marshal()) 655 km := &mockKeymanager{ 656 keysMap: map[[48]byte]bls.SecretKey{ 657 pubKey: secretKey, 658 }, 659 } 660 validator.keyManager = km 661 sig, domain, err := validator.signBlock(ctx, pubKey, 0, blk.Block) 662 require.NoError(t, err, "%x,%x,%v", sig, domain.SignatureDomain, err) 663 require.Equal(t, "a049e1dc723e5a8b5bd14f292973572dffd53785ddb337"+ 664 "82f20bf762cbe10ee7b9b4f5ae1ad6ff2089d352403750bed402b94b58469c072536"+ 665 "faa9a09a88beaff697404ca028b1c7052b0de37dbcff985dfa500459783370312bdd"+ 666 "36d6e0f224", hex.EncodeToString(sig)) 667 // proposer domain 668 require.DeepEqual(t, proposerDomain, domain.SignatureDomain) 669 } 670 671 func TestGetGraffiti_Ok(t *testing.T) { 672 ctrl := gomock.NewController(t) 673 m := &mocks{ 674 validatorClient: mock.NewMockBeaconNodeValidatorClient(ctrl), 675 } 676 pubKey := [48]byte{'a'} 677 tests := []struct { 678 name string 679 v *validator 680 want []byte 681 }{ 682 {name: "use default cli graffiti", 683 v: &validator{ 684 graffiti: []byte{'b'}, 685 graffitiStruct: &graffiti.Graffiti{ 686 Default: "c", 687 Random: []string{"d", "e"}, 688 Specific: map[types.ValidatorIndex]string{ 689 1: "f", 690 2: "g", 691 }, 692 }, 693 }, 694 want: []byte{'b'}, 695 }, 696 {name: "use default file graffiti", 697 v: &validator{ 698 validatorClient: m.validatorClient, 699 graffitiStruct: &graffiti.Graffiti{ 700 Default: "c", 701 }, 702 }, 703 want: []byte{'c'}, 704 }, 705 {name: "use random file graffiti", 706 v: &validator{ 707 validatorClient: m.validatorClient, 708 graffitiStruct: &graffiti.Graffiti{ 709 Random: []string{"d"}, 710 Default: "c", 711 }, 712 }, 713 want: []byte{'d'}, 714 }, 715 {name: "use validator file graffiti, has validator", 716 v: &validator{ 717 validatorClient: m.validatorClient, 718 graffitiStruct: &graffiti.Graffiti{ 719 Random: []string{"d"}, 720 Default: "c", 721 Specific: map[types.ValidatorIndex]string{ 722 1: "f", 723 2: "g", 724 }, 725 }, 726 }, 727 want: []byte{'g'}, 728 }, 729 {name: "use validator file graffiti, none specified", 730 v: &validator{ 731 validatorClient: m.validatorClient, 732 graffitiStruct: &graffiti.Graffiti{}, 733 }, 734 want: []byte{}, 735 }, 736 } 737 738 for _, tt := range tests { 739 t.Run(tt.name, func(t *testing.T) { 740 if !strings.Contains(tt.name, "use default cli graffiti") { 741 m.validatorClient.EXPECT(). 742 ValidatorIndex(gomock.Any(), ðpb.ValidatorIndexRequest{PublicKey: pubKey[:]}). 743 Return(ðpb.ValidatorIndexResponse{Index: 2}, nil) 744 } 745 got, err := tt.v.getGraffiti(context.Background(), pubKey) 746 require.NoError(t, err) 747 require.DeepEqual(t, tt.want, got) 748 }) 749 } 750 } 751 752 func TestGetGraffitiOrdered_Ok(t *testing.T) { 753 pubKey := [48]byte{'a'} 754 valDB := testing2.SetupDB(t, [][48]byte{pubKey}) 755 ctrl := gomock.NewController(t) 756 m := &mocks{ 757 validatorClient: mock.NewMockBeaconNodeValidatorClient(ctrl), 758 } 759 m.validatorClient.EXPECT(). 760 ValidatorIndex(gomock.Any(), ðpb.ValidatorIndexRequest{PublicKey: pubKey[:]}). 761 Times(5). 762 Return(ðpb.ValidatorIndexResponse{Index: 2}, nil) 763 764 v := &validator{ 765 db: valDB, 766 validatorClient: m.validatorClient, 767 graffitiStruct: &graffiti.Graffiti{ 768 Ordered: []string{"a", "b", "c"}, 769 Default: "d", 770 }, 771 } 772 for _, want := range [][]byte{{'a'}, {'b'}, {'c'}, {'d'}, {'d'}} { 773 got, err := v.getGraffiti(context.Background(), pubKey) 774 require.NoError(t, err) 775 require.DeepEqual(t, want, got) 776 } 777 }