diff --git a/beacon-chain/blockchain/testing/BUILD.bazel b/beacon-chain/blockchain/testing/BUILD.bazel index 3e35577475ec..303ead3557d0 100644 --- a/beacon-chain/blockchain/testing/BUILD.bazel +++ b/beacon-chain/blockchain/testing/BUILD.bazel @@ -7,6 +7,7 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing", visibility = ["//beacon-chain:__subpackages__"], deps = [ + "//beacon-chain/core/feed/operation:go_default_library", "//beacon-chain/core/feed/state:go_default_library", "//beacon-chain/db:go_default_library", "//proto/beacon/p2p/v1:go_default_library", diff --git a/beacon-chain/blockchain/testing/mock.go b/beacon-chain/blockchain/testing/mock.go index 95aa80f34e5d..abfb0dbe4140 100644 --- a/beacon-chain/blockchain/testing/mock.go +++ b/beacon-chain/blockchain/testing/mock.go @@ -8,6 +8,7 @@ import ( "github.com/pkg/errors" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" + opfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation" statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" "github.com/prysmaticlabs/prysm/beacon-chain/db" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" @@ -26,6 +27,7 @@ type ChainService struct { Fork *pb.Fork DB db.Database stateNotifier statefeed.Notifier + opNotifier opfeed.Notifier } // StateNotifier mocks the same method in the chain service. @@ -49,6 +51,27 @@ func (msn *MockStateNotifier) StateFeed() *event.Feed { return msn.feed } +// OperationNotifier mocks the same method in the chain service. +func (ms *ChainService) OperationNotifier() opfeed.Notifier { + if ms.opNotifier == nil { + ms.opNotifier = &MockOperationNotifier{} + } + return ms.opNotifier +} + +// MockOperationNotifier mocks the operation notifier. +type MockOperationNotifier struct { + feed *event.Feed +} + +// OperationFeed returns an operation feed. +func (mon *MockOperationNotifier) OperationFeed() *event.Feed { + if mon.feed == nil { + mon.feed = new(event.Feed) + } + return mon.feed +} + // ReceiveBlock mocks ReceiveBlock method in chain service. func (ms *ChainService) ReceiveBlock(ctx context.Context, block *ethpb.BeaconBlock) error { return nil diff --git a/beacon-chain/core/exit/BUILD.bazel b/beacon-chain/core/exit/BUILD.bazel new file mode 100644 index 000000000000..03829be0e946 --- /dev/null +++ b/beacon-chain/core/exit/BUILD.bazel @@ -0,0 +1,38 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["validation.go"], + importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/exit", + visibility = [ + "//beacon-chain:__subpackages__", + ], + deps = [ + "//beacon-chain/core/helpers:go_default_library", + "//proto/beacon/p2p/v1:go_default_library", + "//shared/bls:go_default_library", + "//shared/mathutil:go_default_library", + "//shared/params:go_default_library", + "//shared/roughtime:go_default_library", + "@com_github_pkg_errors//:go_default_library", + "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", + "@com_github_prysmaticlabs_go_ssz//:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["validation_test.go"], + embed = [":go_default_library"], + deps = [ + "//beacon-chain/blockchain/testing:go_default_library", + "//beacon-chain/core/blocks:go_default_library", + "//beacon-chain/core/helpers:go_default_library", + "//beacon-chain/core/state:go_default_library", + "//beacon-chain/db/testing:go_default_library", + "//shared/params:go_default_library", + "//shared/testutil:go_default_library", + "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", + "@com_github_prysmaticlabs_go_ssz//:go_default_library", + ], +) diff --git a/beacon-chain/core/exit/validation.go b/beacon-chain/core/exit/validation.go new file mode 100644 index 000000000000..8294bb9f7464 --- /dev/null +++ b/beacon-chain/core/exit/validation.go @@ -0,0 +1,62 @@ +package exit + +import ( + "fmt" + "time" + + "github.com/pkg/errors" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-ssz" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" + "github.com/prysmaticlabs/prysm/shared/bls" + "github.com/prysmaticlabs/prysm/shared/mathutil" + "github.com/prysmaticlabs/prysm/shared/params" + "github.com/prysmaticlabs/prysm/shared/roughtime" +) + +// ValidateVoluntaryExit validates the voluntary exit. +// If it is invalid for some reason an error, if valid it will return no error. +func ValidateVoluntaryExit(state *pb.BeaconState, genesisTime time.Time, ve *ethpb.VoluntaryExit) error { + if ve.ValidatorIndex >= uint64(len(state.Validators)) { + return fmt.Errorf("unknown validator index %d", ve.ValidatorIndex) + } + validator := state.Validators[ve.ValidatorIndex] + + if !helpers.IsActiveValidator(validator, ve.Epoch) { + return fmt.Errorf("validator %d not active at epoch %d", ve.ValidatorIndex, ve.Epoch) + } + if validator.ExitEpoch != params.BeaconConfig().FarFutureEpoch { + return fmt.Errorf("validator %d already exiting or exited", ve.ValidatorIndex) + } + + secondsPerEpoch := params.BeaconConfig().SecondsPerSlot * params.BeaconConfig().SlotsPerEpoch + currentEpoch := uint64(roughtime.Now().Unix()-genesisTime.Unix()) / secondsPerEpoch + earliestRequestedExitEpoch := mathutil.Max(ve.Epoch, currentEpoch) + earliestExitEpoch := validator.ActivationEpoch + params.BeaconConfig().PersistentCommitteePeriod + if earliestRequestedExitEpoch < earliestExitEpoch { + return fmt.Errorf("validator %d cannot exit before epoch %d", ve.ValidatorIndex, earliestExitEpoch) + } + + // Confirm signature is valid + root, err := ssz.SigningRoot(ve) + if err != nil { + return errors.Wrap(err, "cannot confirm signature") + } + sig, err := bls.SignatureFromBytes(ve.Signature) + if err != nil { + return errors.Wrap(err, "malformed signature") + } + validatorPubKey, err := bls.PublicKeyFromBytes(validator.PublicKey) + if err != nil { + return errors.Wrap(err, "invalid validator public key") + } + domain := bls.ComputeDomain(params.BeaconConfig().DomainVoluntaryExit) + verified := sig.Verify(root[:], validatorPubKey, domain) + if !verified { + return errors.New("incorrect signature") + } + + // Parameters are valid. + return nil +} diff --git a/beacon-chain/core/exit/validation_test.go b/beacon-chain/core/exit/validation_test.go new file mode 100644 index 000000000000..3571699d45f9 --- /dev/null +++ b/beacon-chain/core/exit/validation_test.go @@ -0,0 +1,118 @@ +package exit_test + +import ( + "context" + "errors" + "testing" + "time" + + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-ssz" + mockChain "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" + blk "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" + "github.com/prysmaticlabs/prysm/beacon-chain/core/exit" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/beacon-chain/core/state" + dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + "github.com/prysmaticlabs/prysm/shared/params" + "github.com/prysmaticlabs/prysm/shared/testutil" +) + +func TestValidation(t *testing.T) { + tests := []struct { + name string + epoch uint64 + validatorIndex uint64 + signature []byte + err error + }{ + { + name: "MissingValidator", + epoch: 2048, + validatorIndex: 16, + err: errors.New("unknown validator index 16"), + }, + { + name: "EarlyExit", + epoch: 2047, + validatorIndex: 0, + err: errors.New("validator 0 cannot exit before epoch 2048"), + }, + { + name: "NoSignature", + epoch: 2048, + validatorIndex: 0, + err: errors.New("malformed signature: signature must be 96 bytes"), + }, + { + name: "InvalidSignature", + epoch: 2048, + validatorIndex: 0, + signature: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + err: errors.New("malformed signature: could not unmarshal bytes into signature: err blsSignatureDeserialize 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + }, + { + name: "IncorrectSignature", + epoch: 2048, + validatorIndex: 0, + signature: []byte{0xab, 0xb0, 0x12, 0x4c, 0x75, 0x74, 0xf2, 0x81, 0xa2, 0x93, 0xf4, 0x18, 0x5c, 0xad, 0x3c, 0xb2, 0x26, 0x81, 0xd5, 0x20, 0x91, 0x7c, 0xe4, 0x66, 0x65, 0x24, 0x3e, 0xac, 0xb0, 0x51, 0x00, 0x0d, 0x8b, 0xac, 0xf7, 0x5e, 0x14, 0x51, 0x87, 0x0c, 0xa6, 0xb3, 0xb9, 0xe6, 0xc9, 0xd4, 0x1a, 0x7b, 0x02, 0xea, 0xd2, 0x68, 0x5a, 0x84, 0x18, 0x8a, 0x4f, 0xaf, 0xd3, 0x82, 0x5d, 0xaf, 0x6a, 0x98, 0x96, 0x25, 0xd7, 0x19, 0xcc, 0xd2, 0xd8, 0x3a, 0x40, 0x10, 0x1f, 0x4a, 0x45, 0x3f, 0xca, 0x62, 0x87, 0x8c, 0x89, 0x0e, 0xca, 0x62, 0x23, 0x63, 0xf9, 0xdd, 0xb8, 0xf3, 0x67, 0xa9, 0x1e, 0x84}, + err: errors.New("incorrect signature"), + }, + { + name: "Good", + epoch: 2048, + validatorIndex: 0, + signature: []byte{0xb3, 0xe1, 0x9d, 0xc6, 0x7c, 0x78, 0x6c, 0xcf, 0x33, 0x1d, 0xb9, 0x6f, 0x59, 0x64, 0x44, 0xe1, 0x29, 0xd0, 0x87, 0x03, 0x26, 0x6e, 0x49, 0x1c, 0x05, 0xae, 0x16, 0x7b, 0x04, 0x0f, 0x3f, 0xf8, 0x82, 0x77, 0x60, 0xfc, 0xcf, 0x2f, 0x59, 0xc7, 0x40, 0x0b, 0x2c, 0xa9, 0x23, 0x8a, 0x6c, 0x8d, 0x01, 0x21, 0x5e, 0xa8, 0xac, 0x36, 0x70, 0x31, 0xb0, 0xe1, 0xa8, 0xb8, 0x8f, 0x93, 0x8c, 0x1c, 0xa2, 0x86, 0xe7, 0x22, 0x00, 0x6a, 0x7d, 0x36, 0xc0, 0x2b, 0x86, 0x2c, 0xf5, 0xf9, 0x10, 0xb9, 0xf2, 0xbd, 0x5e, 0xa6, 0x5f, 0x12, 0x86, 0x43, 0x20, 0x4d, 0xa2, 0x9d, 0x8b, 0xe6, 0x6f, 0x09}, + }, + } + + db := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, db) + ctx := context.Background() + helpers.ClearAllCaches() + deposits, _, _ := testutil.DeterministicDepositsAndKeys(8) + beaconState, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{BlockHash: make([]byte, 32)}) + if err != nil { + t.Fatal(err) + } + block := blk.NewGenesisBlock([]byte{}) + if err := db.SaveBlock(ctx, block); err != nil { + t.Fatalf("Could not save genesis block: %v", err) + } + genesisRoot, err := ssz.SigningRoot(block) + if err != nil { + t.Fatalf("Could not get signing root %v", err) + } + + // Set genesis time to be 100 epochs ago + genesisTime := time.Now().Add(time.Duration(-100*int64(params.BeaconConfig().SecondsPerSlot*params.BeaconConfig().SlotsPerEpoch)) * time.Second) + mockChainService := &mockChain.ChainService{State: beaconState, Root: genesisRoot[:], Genesis: genesisTime} + headState, err := mockChainService.HeadState(context.Background()) + if err != nil { + t.Fatal("Failed to obtain head state") + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + req := ðpb.VoluntaryExit{ + Epoch: test.epoch, + ValidatorIndex: test.validatorIndex, + Signature: test.signature, + } + + err := exit.ValidateVoluntaryExit(headState, genesisTime, req) + if test.err == nil { + if err != nil { + t.Errorf("Unexpected error: received %v", err) + } + } else { + if err == nil { + t.Error("Failed to receive expected error") + } + if err.Error() != test.err.Error() { + t.Errorf("Unexpected error: expected %s, received %s", test.err.Error(), err.Error()) + } + } + }) + } +} diff --git a/beacon-chain/core/feed/operation/events.go b/beacon-chain/core/feed/operation/events.go index 97c748bc41de..a1b1549fadaa 100644 --- a/beacon-chain/core/feed/operation/events.go +++ b/beacon-chain/core/feed/operation/events.go @@ -30,8 +30,8 @@ type AggregatedAttReceivedData struct { Attestation *pb.AggregateAndProof } -// ExitRecievedData is the data sent with ExitReceived events. -type ExitRecievedData struct { +// ExitReceivedData is the data sent with ExitReceived events. +type ExitReceivedData struct { // Exit is the voluntary exit object. Exit *ethpb.VoluntaryExit } diff --git a/beacon-chain/node/node.go b/beacon-chain/node/node.go index e977ed421558..e731031c9661 100644 --- a/beacon-chain/node/node.go +++ b/beacon-chain/node/node.go @@ -61,6 +61,7 @@ type BeaconNode struct { attestationPool attestations.Pool depositCache *depositcache.DepositCache stateFeed *event.Feed + opFeed *event.Feed } // NewBeaconNode creates a new node instance, sets up configuration options, and registers @@ -84,6 +85,7 @@ func NewBeaconNode(ctx *cli.Context) (*BeaconNode, error) { services: registry, stop: make(chan struct{}), stateFeed: new(event.Feed), + opFeed: new(event.Feed), attestationPool: attestations.NewPool(), } @@ -160,6 +162,11 @@ func (b *BeaconNode) StateFeed() *event.Feed { return b.stateFeed } +// OperationFeed implements opfeed.Notifier. +func (b *BeaconNode) OperationFeed() *event.Feed { + return b.opFeed +} + // Start the BeaconNode and kicks off every registered service. func (b *BeaconNode) Start() { b.lock.Lock() @@ -464,6 +471,7 @@ func (b *BeaconNode) registerRPCService(ctx *cli.Context) error { DepositFetcher: depositFetcher, PendingDepositFetcher: b.depositCache, StateNotifier: b, + OperationNotifier: b, }) return b.services.RegisterService(rpcService) diff --git a/beacon-chain/rpc/BUILD.bazel b/beacon-chain/rpc/BUILD.bazel index 1de77f36530b..c13670428651 100644 --- a/beacon-chain/rpc/BUILD.bazel +++ b/beacon-chain/rpc/BUILD.bazel @@ -9,6 +9,7 @@ go_library( "//beacon-chain/blockchain:go_default_library", "//beacon-chain/cache:go_default_library", "//beacon-chain/cache/depositcache:go_default_library", + "//beacon-chain/core/feed/operation:go_default_library", "//beacon-chain/core/feed/state:go_default_library", "//beacon-chain/db:go_default_library", "//beacon-chain/operations/attestations:go_default_library", diff --git a/beacon-chain/rpc/service.go b/beacon-chain/rpc/service.go index 9da792dedbfc..90eef5f8f115 100644 --- a/beacon-chain/rpc/service.go +++ b/beacon-chain/rpc/service.go @@ -16,6 +16,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" "github.com/prysmaticlabs/prysm/beacon-chain/cache" "github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache" + opfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation" statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" "github.com/prysmaticlabs/prysm/beacon-chain/db" "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" @@ -73,6 +74,7 @@ type Service struct { depositFetcher depositcache.DepositFetcher pendingDepositFetcher depositcache.PendingDepositsFetcher stateNotifier statefeed.Notifier + operationNotifier opfeed.Notifier } // Config options for the beacon node RPC server. @@ -97,6 +99,7 @@ type Config struct { DepositFetcher depositcache.DepositFetcher PendingDepositFetcher depositcache.PendingDepositsFetcher StateNotifier statefeed.Notifier + OperationNotifier opfeed.Notifier } // NewService instantiates a new RPC service instance that will @@ -128,6 +131,7 @@ func NewService(ctx context.Context, cfg *Config) *Service { canonicalStateChan: make(chan *pbp2p.BeaconState, params.BeaconConfig().DefaultBufferSize), incomingAttestation: make(chan *ethpb.Attestation, params.BeaconConfig().DefaultBufferSize), stateNotifier: cfg.StateNotifier, + operationNotifier: cfg.OperationNotifier, } } @@ -184,11 +188,13 @@ func (s *Service) Start() { Eth1InfoFetcher: s.powChainService, SyncChecker: s.syncService, StateNotifier: s.stateNotifier, + OperationNotifier: s.operationNotifier, P2P: s.p2p, BlockReceiver: s.blockReceiver, MockEth1Votes: s.mockEth1Votes, Eth1BlockFetcher: s.powChainService, PendingDepositsFetcher: s.pendingDepositFetcher, + GenesisTimeFetcher: s.genesisTimeFetcher, } nodeServer := &node.Server{ BeaconDB: s.beaconDB, diff --git a/beacon-chain/rpc/validator/BUILD.bazel b/beacon-chain/rpc/validator/BUILD.bazel index 1ed11f05dee5..ab4f46248eab 100644 --- a/beacon-chain/rpc/validator/BUILD.bazel +++ b/beacon-chain/rpc/validator/BUILD.bazel @@ -5,6 +5,7 @@ go_library( srcs = [ "assignments.go", "attester.go", + "exit.go", "proposer.go", "server.go", "status.go", @@ -16,7 +17,9 @@ go_library( "//beacon-chain/cache:go_default_library", "//beacon-chain/cache/depositcache:go_default_library", "//beacon-chain/core/blocks:go_default_library", + "//beacon-chain/core/exit:go_default_library", "//beacon-chain/core/feed:go_default_library", + "//beacon-chain/core/feed/operation:go_default_library", "//beacon-chain/core/feed/state:go_default_library", "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/core/state:go_default_library", @@ -51,6 +54,7 @@ go_test( srcs = [ "assignments_test.go", "attester_test.go", + "exit_test.go", "proposer_test.go", "server_test.go", "status_test.go", @@ -63,6 +67,7 @@ go_test( "//beacon-chain/cache/depositcache:go_default_library", "//beacon-chain/core/blocks:go_default_library", "//beacon-chain/core/feed:go_default_library", + "//beacon-chain/core/feed/operation:go_default_library", "//beacon-chain/core/feed/state:go_default_library", "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/core/state:go_default_library", diff --git a/beacon-chain/rpc/validator/exit.go b/beacon-chain/rpc/validator/exit.go new file mode 100644 index 000000000000..97ec9be01e8a --- /dev/null +++ b/beacon-chain/rpc/validator/exit.go @@ -0,0 +1,37 @@ +package validator + +import ( + "context" + + ptypes "github.com/gogo/protobuf/types" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/prysm/beacon-chain/core/exit" + "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" + opfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// ProposeExit proposes an exit for a validator. +func (vs *Server) ProposeExit(ctx context.Context, req *ethpb.VoluntaryExit) (*ptypes.Empty, error) { + s, err := vs.HeadFetcher.HeadState(ctx) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not get head state: %v", err) + } + + // Confirm the validator is eligible to exit with the parameters provided + err = exit.ValidateVoluntaryExit(s, vs.GenesisTimeFetcher.GenesisTime(), req) + if err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + // Send the voluntary exit to the operation feed. + vs.OperationNotifier.OperationFeed().Send(&feed.Event{ + Type: opfeed.ExitReceived, + Data: &opfeed.ExitReceivedData{ + Exit: req, + }, + }) + + return nil, nil +} diff --git a/beacon-chain/rpc/validator/exit_test.go b/beacon-chain/rpc/validator/exit_test.go new file mode 100644 index 000000000000..16001cbf1f04 --- /dev/null +++ b/beacon-chain/rpc/validator/exit_test.go @@ -0,0 +1,93 @@ +package validator + +import ( + "context" + "testing" + "time" + + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-ssz" + mockChain "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" + blk "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" + "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" + opfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/beacon-chain/core/state" + dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing" + "github.com/prysmaticlabs/prysm/shared/params" + "github.com/prysmaticlabs/prysm/shared/testutil" +) + +func TestSub(t *testing.T) { + + db := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, db) + ctx := context.Background() + helpers.ClearAllCaches() + deposits, _, _ := testutil.DeterministicDepositsAndKeys(8) + beaconState, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{BlockHash: make([]byte, 32)}) + if err != nil { + t.Fatal(err) + } + block := blk.NewGenesisBlock([]byte{}) + if err := db.SaveBlock(ctx, block); err != nil { + t.Fatalf("Could not save genesis block: %v", err) + } + genesisRoot, err := ssz.SigningRoot(block) + if err != nil { + t.Fatalf("Could not get signing root %v", err) + } + + // Set genesis time to be 100 epochs ago + genesisTime := time.Now().Add(time.Duration(-100*int64(params.BeaconConfig().SecondsPerSlot*params.BeaconConfig().SlotsPerEpoch)) * time.Second) + mockChainService := &mockChain.ChainService{State: beaconState, Root: genesisRoot[:], Genesis: genesisTime} + server := &Server{ + BeaconDB: db, + HeadFetcher: mockChainService, + SyncChecker: &mockSync.Sync{IsSyncing: false}, + GenesisTimeFetcher: mockChainService, + StateNotifier: mockChainService.StateNotifier(), + OperationNotifier: mockChainService.OperationNotifier(), + } + + // Subscribe to operation notifications + opChannel := make(chan *feed.Event, 1024) + opSub := server.OperationNotifier.OperationFeed().Subscribe(opChannel) + defer opSub.Unsubscribe() + + // Send the request, expect a result on the state feed + epoch := uint64(2048) + validatorIndex := uint64(0) + req := ðpb.VoluntaryExit{ + Epoch: epoch, + ValidatorIndex: validatorIndex, + Signature: []byte{0xb3, 0xe1, 0x9d, 0xc6, 0x7c, 0x78, 0x6c, 0xcf, 0x33, 0x1d, 0xb9, 0x6f, 0x59, 0x64, 0x44, 0xe1, 0x29, 0xd0, 0x87, 0x03, 0x26, 0x6e, 0x49, 0x1c, 0x05, 0xae, 0x16, 0x7b, 0x04, 0x0f, 0x3f, 0xf8, 0x82, 0x77, 0x60, 0xfc, 0xcf, 0x2f, 0x59, 0xc7, 0x40, 0x0b, 0x2c, 0xa9, 0x23, 0x8a, 0x6c, 0x8d, 0x01, 0x21, 0x5e, 0xa8, 0xac, 0x36, 0x70, 0x31, 0xb0, 0xe1, 0xa8, 0xb8, 0x8f, 0x93, 0x8c, 0x1c, 0xa2, 0x86, 0xe7, 0x22, 0x00, 0x6a, 0x7d, 0x36, 0xc0, 0x2b, 0x86, 0x2c, 0xf5, 0xf9, 0x10, 0xb9, 0xf2, 0xbd, 0x5e, 0xa6, 0x5f, 0x12, 0x86, 0x43, 0x20, 0x4d, 0xa2, 0x9d, 0x8b, 0xe6, 0x6f, 0x09}, + } + + _, err = server.ProposeExit(context.Background(), req) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // Ensure the state notification was broadcast + notificationFound := false + for !notificationFound { + select { + case event := <-opChannel: + if event.Type == opfeed.ExitReceived { + notificationFound = true + data := event.Data.(*opfeed.ExitReceivedData) + if epoch != data.Exit.Epoch { + t.Errorf("Unexpected state feed epoch: expected %v, found %v", epoch, data.Exit.Epoch) + } + if validatorIndex != data.Exit.ValidatorIndex { + t.Errorf("Unexpected state feed validator index: expected %v, found %v", validatorIndex, data.Exit.ValidatorIndex) + } + } + case <-opSub.Err(): + t.Error("Subscription to state notifier failed") + return + } + } +} diff --git a/beacon-chain/rpc/validator/proposer.go b/beacon-chain/rpc/validator/proposer.go index 03a75b36d9e6..fa8fcf9490dc 100644 --- a/beacon-chain/rpc/validator/proposer.go +++ b/beacon-chain/rpc/validator/proposer.go @@ -6,7 +6,6 @@ import ( "math/big" "math/rand" - ptypes "github.com/gogo/protobuf/types" "github.com/pkg/errors" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" @@ -116,11 +115,6 @@ func (vs *Server) ProposeBlock(ctx context.Context, blk *ethpb.BeaconBlock) (*et }, nil } -// ProposeExit -- -func (vs *Server) ProposeExit(context.Context, *ethpb.VoluntaryExit) (*ptypes.Empty, error) { - return &ptypes.Empty{}, status.Error(codes.Unimplemented, "Not yet implemented") -} - // eth1Data determines the appropriate eth1data for a block proposal. The algorithm for this method // is as follows: // - Determine the timestamp for the start slot for the eth1 voting period. diff --git a/beacon-chain/rpc/validator/server.go b/beacon-chain/rpc/validator/server.go index 194ce52e8d33..aaa8d15b8954 100644 --- a/beacon-chain/rpc/validator/server.go +++ b/beacon-chain/rpc/validator/server.go @@ -10,6 +10,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/cache" "github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache" "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" + opfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation" statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/db" @@ -57,6 +58,8 @@ type Server struct { MockEth1Votes bool Eth1BlockFetcher powchain.POWBlockFetcher PendingDepositsFetcher depositcache.PendingDepositsFetcher + OperationNotifier opfeed.Notifier + GenesisTimeFetcher blockchain.GenesisTimeFetcher } // WaitForActivation checks if a validator public key exists in the active validator registry of the current diff --git a/proto/beacon/rpc/v1/services.proto b/proto/beacon/rpc/v1/services.proto index 35fb0442ccc2..b9c5c4825be9 100644 --- a/proto/beacon/rpc/v1/services.proto +++ b/proto/beacon/rpc/v1/services.proto @@ -30,6 +30,7 @@ service ValidatorService { rpc ExitedValidators(ExitedValidatorsRequest) returns (ExitedValidatorsResponse); rpc WaitForChainStart(google.protobuf.Empty) returns (stream ChainStartResponse); rpc CanonicalHead(google.protobuf.Empty) returns (ethereum.eth.v1alpha1.BeaconBlock); + rpc ProposeExit(ethereum.eth.v1alpha1.VoluntaryExit) returns (google.protobuf.Empty); } message BlockRequest { diff --git a/validator/internal/BUILD.bazel b/validator/internal/BUILD.bazel index 01e3a101844a..ee3c77faa760 100644 --- a/validator/internal/BUILD.bazel +++ b/validator/internal/BUILD.bazel @@ -8,6 +8,7 @@ go_library( "beacon_chain_service_mock.go", "beacon_node_validator_service_mock.go", "node_mock.go", + "validator_service_mock.go", ], importpath = "github.com/prysmaticlabs/prysm/validator/internal", visibility = ["//validator:__subpackages__"], diff --git a/validator/internal/validator_service_mock.go b/validator/internal/validator_service_mock.go new file mode 100644 index 000000000000..bbce828b9d4d --- /dev/null +++ b/validator/internal/validator_service_mock.go @@ -0,0 +1,486 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1 (interfaces: ValidatorServiceClient,ValidatorService_WaitForActivationClient,ValidatorService_WaitForChainStartClient) + +// Package internal is a generated GoMock package. +package internal + +import ( + context "context" + reflect "reflect" + + types "github.com/gogo/protobuf/types" + gomock "github.com/golang/mock/gomock" + v1alpha1 "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + v1 "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" + grpc "google.golang.org/grpc" + metadata "google.golang.org/grpc/metadata" +) + +// MockValidatorServiceClient is a mock of ValidatorServiceClient interface +type MockValidatorServiceClient struct { + ctrl *gomock.Controller + recorder *MockValidatorServiceClientMockRecorder +} + +// MockValidatorServiceClientMockRecorder is the mock recorder for MockValidatorServiceClient +type MockValidatorServiceClientMockRecorder struct { + mock *MockValidatorServiceClient +} + +// NewMockValidatorServiceClient creates a new mock instance +func NewMockValidatorServiceClient(ctrl *gomock.Controller) *MockValidatorServiceClient { + mock := &MockValidatorServiceClient{ctrl: ctrl} + mock.recorder = &MockValidatorServiceClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockValidatorServiceClient) EXPECT() *MockValidatorServiceClientMockRecorder { + return m.recorder +} + +// CanonicalHead mocks base method +func (m *MockValidatorServiceClient) CanonicalHead(arg0 context.Context, arg1 *types.Empty, arg2 ...grpc.CallOption) (*v1alpha1.BeaconBlock, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CanonicalHead", varargs...) + ret0, _ := ret[0].(*v1alpha1.BeaconBlock) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CanonicalHead indicates an expected call of CanonicalHead +func (mr *MockValidatorServiceClientMockRecorder) CanonicalHead(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CanonicalHead", reflect.TypeOf((*MockValidatorServiceClient)(nil).CanonicalHead), varargs...) +} + +// CommitteeAssignment mocks base method +func (m *MockValidatorServiceClient) CommitteeAssignment(arg0 context.Context, arg1 *v1.AssignmentRequest, arg2 ...grpc.CallOption) (*v1.AssignmentResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CommitteeAssignment", varargs...) + ret0, _ := ret[0].(*v1.AssignmentResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CommitteeAssignment indicates an expected call of CommitteeAssignment +func (mr *MockValidatorServiceClientMockRecorder) CommitteeAssignment(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitteeAssignment", reflect.TypeOf((*MockValidatorServiceClient)(nil).CommitteeAssignment), varargs...) +} + +// DomainData mocks base method +func (m *MockValidatorServiceClient) DomainData(arg0 context.Context, arg1 *v1.DomainRequest, arg2 ...grpc.CallOption) (*v1.DomainResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DomainData", varargs...) + ret0, _ := ret[0].(*v1.DomainResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DomainData indicates an expected call of DomainData +func (mr *MockValidatorServiceClientMockRecorder) DomainData(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DomainData", reflect.TypeOf((*MockValidatorServiceClient)(nil).DomainData), varargs...) +} + +// ExitedValidators mocks base method +func (m *MockValidatorServiceClient) ExitedValidators(arg0 context.Context, arg1 *v1.ExitedValidatorsRequest, arg2 ...grpc.CallOption) (*v1.ExitedValidatorsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ExitedValidators", varargs...) + ret0, _ := ret[0].(*v1.ExitedValidatorsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ExitedValidators indicates an expected call of ExitedValidators +func (mr *MockValidatorServiceClientMockRecorder) ExitedValidators(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExitedValidators", reflect.TypeOf((*MockValidatorServiceClient)(nil).ExitedValidators), varargs...) +} + +// ValidatorIndex mocks base method +func (m *MockValidatorServiceClient) ValidatorIndex(arg0 context.Context, arg1 *v1.ValidatorIndexRequest, arg2 ...grpc.CallOption) (*v1.ValidatorIndexResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ValidatorIndex", varargs...) + ret0, _ := ret[0].(*v1.ValidatorIndexResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ValidatorIndex indicates an expected call of ValidatorIndex +func (mr *MockValidatorServiceClientMockRecorder) ValidatorIndex(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatorIndex", reflect.TypeOf((*MockValidatorServiceClient)(nil).ValidatorIndex), varargs...) +} + +// ValidatorPerformance mocks base method +func (m *MockValidatorServiceClient) ValidatorPerformance(arg0 context.Context, arg1 *v1.ValidatorPerformanceRequest, arg2 ...grpc.CallOption) (*v1.ValidatorPerformanceResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ValidatorPerformance", varargs...) + ret0, _ := ret[0].(*v1.ValidatorPerformanceResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ValidatorPerformance indicates an expected call of ValidatorPerformance +func (mr *MockValidatorServiceClientMockRecorder) ValidatorPerformance(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatorPerformance", reflect.TypeOf((*MockValidatorServiceClient)(nil).ValidatorPerformance), varargs...) +} + +// ValidatorStatus mocks base method +func (m *MockValidatorServiceClient) ValidatorStatus(arg0 context.Context, arg1 *v1.ValidatorIndexRequest, arg2 ...grpc.CallOption) (*v1.ValidatorStatusResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ValidatorStatus", varargs...) + ret0, _ := ret[0].(*v1.ValidatorStatusResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ValidatorStatus indicates an expected call of ValidatorStatus +func (mr *MockValidatorServiceClientMockRecorder) ValidatorStatus(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatorStatus", reflect.TypeOf((*MockValidatorServiceClient)(nil).ValidatorStatus), varargs...) +} + +// ProposeExit mocks base method +func (m *MockValidatorServiceClient) ProposeExit(arg0 context.Context, arg1 *v1alpha1.VoluntaryExit, arg2 ...grpc.CallOption) (*types.Empty, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ProposeExit", varargs) + ret0, _ := ret[0].(*types.Empty) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ProposeExit indicates an expected call of ProposeExit +func (mr *MockValidatorServiceClientMockRecorder) ProposeExit(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProposeExit", reflect.TypeOf((*MockValidatorServiceClient)(nil).ProposeExit), varargs...) +} + +// WaitForActivation mocks base method +func (m *MockValidatorServiceClient) WaitForActivation(arg0 context.Context, arg1 *v1.ValidatorActivationRequest, arg2 ...grpc.CallOption) (v1.ValidatorService_WaitForActivationClient, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "WaitForActivation", varargs...) + ret0, _ := ret[0].(v1.ValidatorService_WaitForActivationClient) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// WaitForActivation indicates an expected call of WaitForActivation +func (mr *MockValidatorServiceClientMockRecorder) WaitForActivation(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForActivation", reflect.TypeOf((*MockValidatorServiceClient)(nil).WaitForActivation), varargs...) +} + +// WaitForChainStart mocks base method +func (m *MockValidatorServiceClient) WaitForChainStart(arg0 context.Context, arg1 *types.Empty, arg2 ...grpc.CallOption) (v1.ValidatorService_WaitForChainStartClient, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "WaitForChainStart", varargs...) + ret0, _ := ret[0].(v1.ValidatorService_WaitForChainStartClient) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// WaitForChainStart indicates an expected call of WaitForChainStart +func (mr *MockValidatorServiceClientMockRecorder) WaitForChainStart(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForChainStart", reflect.TypeOf((*MockValidatorServiceClient)(nil).WaitForChainStart), varargs...) +} + +// MockValidatorService_WaitForActivationClient is a mock of ValidatorService_WaitForActivationClient interface +type MockValidatorService_WaitForActivationClient struct { + ctrl *gomock.Controller + recorder *MockValidatorService_WaitForActivationClientMockRecorder +} + +// MockValidatorService_WaitForActivationClientMockRecorder is the mock recorder for MockValidatorService_WaitForActivationClient +type MockValidatorService_WaitForActivationClientMockRecorder struct { + mock *MockValidatorService_WaitForActivationClient +} + +// NewMockValidatorService_WaitForActivationClient creates a new mock instance +func NewMockValidatorService_WaitForActivationClient(ctrl *gomock.Controller) *MockValidatorService_WaitForActivationClient { + mock := &MockValidatorService_WaitForActivationClient{ctrl: ctrl} + mock.recorder = &MockValidatorService_WaitForActivationClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockValidatorService_WaitForActivationClient) EXPECT() *MockValidatorService_WaitForActivationClientMockRecorder { + return m.recorder +} + +// CloseSend mocks base method +func (m *MockValidatorService_WaitForActivationClient) CloseSend() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CloseSend") + ret0, _ := ret[0].(error) + return ret0 +} + +// CloseSend indicates an expected call of CloseSend +func (mr *MockValidatorService_WaitForActivationClientMockRecorder) CloseSend() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseSend", reflect.TypeOf((*MockValidatorService_WaitForActivationClient)(nil).CloseSend)) +} + +// Context mocks base method +func (m *MockValidatorService_WaitForActivationClient) Context() context.Context { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Context") + ret0, _ := ret[0].(context.Context) + return ret0 +} + +// Context indicates an expected call of Context +func (mr *MockValidatorService_WaitForActivationClientMockRecorder) Context() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockValidatorService_WaitForActivationClient)(nil).Context)) +} + +// Header mocks base method +func (m *MockValidatorService_WaitForActivationClient) Header() (metadata.MD, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Header") + ret0, _ := ret[0].(metadata.MD) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Header indicates an expected call of Header +func (mr *MockValidatorService_WaitForActivationClientMockRecorder) Header() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Header", reflect.TypeOf((*MockValidatorService_WaitForActivationClient)(nil).Header)) +} + +// Recv mocks base method +func (m *MockValidatorService_WaitForActivationClient) Recv() (*v1.ValidatorActivationResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Recv") + ret0, _ := ret[0].(*v1.ValidatorActivationResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Recv indicates an expected call of Recv +func (mr *MockValidatorService_WaitForActivationClientMockRecorder) Recv() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Recv", reflect.TypeOf((*MockValidatorService_WaitForActivationClient)(nil).Recv)) +} + +// RecvMsg mocks base method +func (m *MockValidatorService_WaitForActivationClient) RecvMsg(arg0 interface{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RecvMsg", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// RecvMsg indicates an expected call of RecvMsg +func (mr *MockValidatorService_WaitForActivationClientMockRecorder) RecvMsg(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecvMsg", reflect.TypeOf((*MockValidatorService_WaitForActivationClient)(nil).RecvMsg), arg0) +} + +// SendMsg mocks base method +func (m *MockValidatorService_WaitForActivationClient) SendMsg(arg0 interface{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendMsg", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendMsg indicates an expected call of SendMsg +func (mr *MockValidatorService_WaitForActivationClientMockRecorder) SendMsg(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMsg", reflect.TypeOf((*MockValidatorService_WaitForActivationClient)(nil).SendMsg), arg0) +} + +// Trailer mocks base method +func (m *MockValidatorService_WaitForActivationClient) Trailer() metadata.MD { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Trailer") + ret0, _ := ret[0].(metadata.MD) + return ret0 +} + +// Trailer indicates an expected call of Trailer +func (mr *MockValidatorService_WaitForActivationClientMockRecorder) Trailer() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Trailer", reflect.TypeOf((*MockValidatorService_WaitForActivationClient)(nil).Trailer)) +} + +// MockValidatorService_WaitForChainStartClient is a mock of ValidatorService_WaitForChainStartClient interface +type MockValidatorService_WaitForChainStartClient struct { + ctrl *gomock.Controller + recorder *MockValidatorService_WaitForChainStartClientMockRecorder +} + +// MockValidatorService_WaitForChainStartClientMockRecorder is the mock recorder for MockValidatorService_WaitForChainStartClient +type MockValidatorService_WaitForChainStartClientMockRecorder struct { + mock *MockValidatorService_WaitForChainStartClient +} + +// NewMockValidatorService_WaitForChainStartClient creates a new mock instance +func NewMockValidatorService_WaitForChainStartClient(ctrl *gomock.Controller) *MockValidatorService_WaitForChainStartClient { + mock := &MockValidatorService_WaitForChainStartClient{ctrl: ctrl} + mock.recorder = &MockValidatorService_WaitForChainStartClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockValidatorService_WaitForChainStartClient) EXPECT() *MockValidatorService_WaitForChainStartClientMockRecorder { + return m.recorder +} + +// CloseSend mocks base method +func (m *MockValidatorService_WaitForChainStartClient) CloseSend() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CloseSend") + ret0, _ := ret[0].(error) + return ret0 +} + +// CloseSend indicates an expected call of CloseSend +func (mr *MockValidatorService_WaitForChainStartClientMockRecorder) CloseSend() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseSend", reflect.TypeOf((*MockValidatorService_WaitForChainStartClient)(nil).CloseSend)) +} + +// Context mocks base method +func (m *MockValidatorService_WaitForChainStartClient) Context() context.Context { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Context") + ret0, _ := ret[0].(context.Context) + return ret0 +} + +// Context indicates an expected call of Context +func (mr *MockValidatorService_WaitForChainStartClientMockRecorder) Context() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockValidatorService_WaitForChainStartClient)(nil).Context)) +} + +// Header mocks base method +func (m *MockValidatorService_WaitForChainStartClient) Header() (metadata.MD, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Header") + ret0, _ := ret[0].(metadata.MD) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Header indicates an expected call of Header +func (mr *MockValidatorService_WaitForChainStartClientMockRecorder) Header() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Header", reflect.TypeOf((*MockValidatorService_WaitForChainStartClient)(nil).Header)) +} + +// Recv mocks base method +func (m *MockValidatorService_WaitForChainStartClient) Recv() (*v1.ChainStartResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Recv") + ret0, _ := ret[0].(*v1.ChainStartResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Recv indicates an expected call of Recv +func (mr *MockValidatorService_WaitForChainStartClientMockRecorder) Recv() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Recv", reflect.TypeOf((*MockValidatorService_WaitForChainStartClient)(nil).Recv)) +} + +// RecvMsg mocks base method +func (m *MockValidatorService_WaitForChainStartClient) RecvMsg(arg0 interface{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RecvMsg", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// RecvMsg indicates an expected call of RecvMsg +func (mr *MockValidatorService_WaitForChainStartClientMockRecorder) RecvMsg(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecvMsg", reflect.TypeOf((*MockValidatorService_WaitForChainStartClient)(nil).RecvMsg), arg0) +} + +// SendMsg mocks base method +func (m *MockValidatorService_WaitForChainStartClient) SendMsg(arg0 interface{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendMsg", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendMsg indicates an expected call of SendMsg +func (mr *MockValidatorService_WaitForChainStartClientMockRecorder) SendMsg(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMsg", reflect.TypeOf((*MockValidatorService_WaitForChainStartClient)(nil).SendMsg), arg0) +} + +// Trailer mocks base method +func (m *MockValidatorService_WaitForChainStartClient) Trailer() metadata.MD { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Trailer") + ret0, _ := ret[0].(metadata.MD) + return ret0 +} + +// Trailer indicates an expected call of Trailer +func (mr *MockValidatorService_WaitForChainStartClientMockRecorder) Trailer() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Trailer", reflect.TypeOf((*MockValidatorService_WaitForChainStartClient)(nil).Trailer)) +}