diff --git a/.mockery.yaml b/.mockery.yaml index 779d1ded..751f53f0 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -4,6 +4,9 @@ mockname: "Mock{{.InterfaceName}}" outpkg: mocks filename: "mock_{{.InterfaceName}}.go" packages: + github.com/xmtp/xmtpd/pkg/proto/mls_validation/v1: + interfaces: + ValidationApiClient: github.com/xmtp/xmtpd/pkg/registry: interfaces: NodesContract: diff --git a/dev/docker/docker-compose.yml b/dev/docker/docker-compose.yml index 653cb67a..7d3447c4 100644 --- a/dev/docker/docker-compose.yml +++ b/dev/docker/docker-compose.yml @@ -20,3 +20,9 @@ services: - 9090:9090 volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml + + validation: + image: ghcr.io/xmtp/mls-validation-service:main + platform: linux/amd64 + ports: + - 60051:50051 diff --git a/dev/local.env b/dev/local.env index fe5fc0e8..741ccc7a 100755 --- a/dev/local.env +++ b/dev/local.env @@ -13,4 +13,5 @@ XMTPD_CONTRACTS_MESSAGES_ADDRESS="$(jq -r '.deployedTo' build/GroupMessages.json export XMTPD_CONTRACTS_MESSAGES_ADDRESS # Top Level Options -export XMTPD_SIGNER_PRIVATE_KEY=$PRIVATE_KEY # From contracts/.env \ No newline at end of file +export XMTPD_SIGNER_PRIVATE_KEY=$PRIVATE_KEY # From contracts/.env +export XMTPD_MLS_VALIDATION_GRPC_ADDRESS="localhost:60051" \ No newline at end of file diff --git a/pkg/config/options.go b/pkg/config/options.go index b0072c36..9ae7a33d 100644 --- a/pkg/config/options.go +++ b/pkg/config/options.go @@ -37,18 +37,8 @@ type PayerOptions struct { PrivateKey string `long:"private-key" env:"XMTPD_PAYER_PRIVATE_KEY" description:"Private key used to sign blockchain transactions"` } -type ServerOptions struct { - LogLevel string `short:"l" long:"log-level" env:"XMTPD_LOG_LEVEL" description:"Define the logging level, supported strings are: DEBUG, INFO, WARN, ERROR, DPANIC, PANIC, FATAL, and their lower-case forms." default:"INFO"` - LogEncoding string ` long:"log-encoding" env:"XMTPD_LOG_ENCODING" description:"Log encoding format. Either console or json" default:"console" choice:"console"` - SignerPrivateKey string ` long:"signer-private-key" env:"XMTPD_SIGNER_PRIVATE_KEY" description:"Private key used to sign messages" required:"true"` - - API ApiOptions `group:"API Options" namespace:"api"` - DB DbOptions `group:"Database Options" namespace:"db"` - Contracts ContractsOptions `group:"Contracts Options" namespace:"contracts"` - Metrics MetricsOptions `group:"Metrics Options" namespace:"metrics"` - Payer PayerOptions `group:"Payer Options" namespace:"payer"` - Reflection ReflectionOptions `group:"Reflection Options" namespace:"reflection"` - Tracing TracingOptions `group:"DD APM Tracing Options" namespace:"tracing"` +type MlsValidationOptions struct { + GrpcAddress string `long:"grpc-address" env:"XMTPD_MLS_VALIDATION_GRPC_ADDRESS" description:"Address of the MLS validation service"` } // TracingOptions are settings controlling collection of DD APM traces and error tracking. @@ -60,3 +50,18 @@ type TracingOptions struct { type ReflectionOptions struct { Enable bool `long:"enable" env:"XMTPD_REFLECTION_ENABLE" description:"Enable GRPC reflection"` } + +type ServerOptions struct { + LogLevel string `short:"l" long:"log-level" env:"XMTPD_LOG_LEVEL" description:"Define the logging level, supported strings are: DEBUG, INFO, WARN, ERROR, DPANIC, PANIC, FATAL, and their lower-case forms." default:"INFO"` + LogEncoding string ` long:"log-encoding" env:"XMTPD_LOG_ENCODING" description:"Log encoding format. Either console or json" default:"console" choice:"console"` + SignerPrivateKey string ` long:"signer-private-key" env:"XMTPD_SIGNER_PRIVATE_KEY" description:"Private key used to sign messages" required:"true"` + + API ApiOptions `group:"API Options" namespace:"api"` + DB DbOptions `group:"Database Options" namespace:"db"` + Contracts ContractsOptions `group:"Contracts Options" namespace:"contracts"` + Metrics MetricsOptions `group:"Metrics Options" namespace:"metrics"` + Payer PayerOptions `group:"Payer Options" namespace:"payer"` + Reflection ReflectionOptions `group:"Reflection Options" namespace:"reflection"` + Tracing TracingOptions `group:"DD APM Tracing Options" namespace:"tracing"` + MlsValidation MlsValidationOptions `group:"MLS Validation Options" namespace:"mls-validation"` +} diff --git a/pkg/mlsvalidate/interface.go b/pkg/mlsvalidate/interface.go new file mode 100644 index 00000000..30af1e06 --- /dev/null +++ b/pkg/mlsvalidate/interface.go @@ -0,0 +1,40 @@ +package mlsvalidate + +import ( + "context" + + identity_proto "github.com/xmtp/xmtpd/pkg/proto/identity" + associations "github.com/xmtp/xmtpd/pkg/proto/identity/associations" + mlsv1 "github.com/xmtp/xmtpd/pkg/proto/mls/api/v1" +) + +type KeyPackageValidationResult struct { + InstallationKey []byte + Credential *identity_proto.MlsCredential + Expiration uint64 +} + +type GroupMessageValidationResult struct { + GroupId string +} + +type AssociationStateResult struct { + AssociationState *associations.AssociationState `protobuf:"bytes,1,opt,name=association_state,json=associationState,proto3" json:"association_state,omitempty"` + StateDiff *associations.AssociationStateDiff `protobuf:"bytes,2,opt,name=state_diff,json=stateDiff,proto3" json:"state_diff,omitempty"` +} + +type MLSValidationService interface { + ValidateKeyPackages( + ctx context.Context, + keyPackages [][]byte, + ) ([]KeyPackageValidationResult, error) + ValidateGroupMessages( + ctx context.Context, + groupMessages []*mlsv1.GroupMessageInput, + ) ([]GroupMessageValidationResult, error) + GetAssociationState( + ctx context.Context, + oldUpdates []*associations.IdentityUpdate, + newUpdates []*associations.IdentityUpdate, + ) (*AssociationStateResult, error) +} diff --git a/pkg/mlsvalidate/service.go b/pkg/mlsvalidate/service.go new file mode 100644 index 00000000..60d4bcbe --- /dev/null +++ b/pkg/mlsvalidate/service.go @@ -0,0 +1,142 @@ +package mlsvalidate + +import ( + "context" + "fmt" + + "github.com/xmtp/xmtpd/pkg/config" + associations "github.com/xmtp/xmtpd/pkg/proto/identity/associations" + mlsv1 "github.com/xmtp/xmtpd/pkg/proto/mls/api/v1" + svc "github.com/xmtp/xmtpd/pkg/proto/mls_validation/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +type MLSValidationServiceImpl struct { + grpcClient svc.ValidationApiClient +} + +func NewMlsValidationService( + ctx context.Context, + cfg config.MlsValidationOptions, +) (*MLSValidationServiceImpl, error) { + conn, err := grpc.NewClient( + cfg.GrpcAddress, + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + return nil, err + } + + go func() { + <-ctx.Done() + conn.Close() + }() + + return &MLSValidationServiceImpl{ + grpcClient: svc.NewValidationApiClient(conn), + }, nil +} + +func (s *MLSValidationServiceImpl) GetAssociationState( + ctx context.Context, + oldUpdates []*associations.IdentityUpdate, + newUpdates []*associations.IdentityUpdate, +) (*AssociationStateResult, error) { + req := &svc.GetAssociationStateRequest{ + OldUpdates: oldUpdates, + NewUpdates: newUpdates, + } + response, err := s.grpcClient.GetAssociationState(ctx, req) + if err != nil { + return nil, err + } + return &AssociationStateResult{ + AssociationState: response.GetAssociationState(), + StateDiff: response.GetStateDiff(), + }, nil +} + +func (s *MLSValidationServiceImpl) ValidateKeyPackages( + ctx context.Context, + keyPackages [][]byte, +) ([]KeyPackageValidationResult, error) { + req := makeValidateKeyPackageRequest(keyPackages) + + response, err := s.grpcClient.ValidateInboxIdKeyPackages(ctx, req) + if err != nil { + return nil, err + } + + out := make([]KeyPackageValidationResult, len(response.Responses)) + for i, response := range response.Responses { + if !response.IsOk { + return nil, fmt.Errorf("validation failed with error %s", response.ErrorMessage) + } + out[i] = KeyPackageValidationResult{ + InstallationKey: response.InstallationPublicKey, + Credential: nil, + Expiration: response.Expiration, + } + } + return out, nil +} + +func makeValidateKeyPackageRequest( + keyPackageBytes [][]byte, +) *svc.ValidateInboxIdKeyPackagesRequest { + keyPackageRequests := make( + []*svc.ValidateInboxIdKeyPackagesRequest_KeyPackage, + len(keyPackageBytes), + ) + for i, keyPackage := range keyPackageBytes { + keyPackageRequests[i] = &svc.ValidateInboxIdKeyPackagesRequest_KeyPackage{ + KeyPackageBytesTlsSerialized: keyPackage, + IsInboxIdCredential: true, + } + } + return &svc.ValidateInboxIdKeyPackagesRequest{ + KeyPackages: keyPackageRequests, + } +} + +func (s *MLSValidationServiceImpl) ValidateGroupMessages( + ctx context.Context, + groupMessages []*mlsv1.GroupMessageInput, +) ([]GroupMessageValidationResult, error) { + req := makeValidateGroupMessagesRequest(groupMessages) + + response, err := s.grpcClient.ValidateGroupMessages(ctx, req) + if err != nil { + return nil, err + } + + out := make([]GroupMessageValidationResult, len(response.Responses)) + for i, response := range response.Responses { + if !response.IsOk { + return nil, fmt.Errorf("validation failed with error %s", response.ErrorMessage) + } + out[i] = GroupMessageValidationResult{ + GroupId: response.GroupId, + } + } + + return out, nil +} + +func makeValidateGroupMessagesRequest( + groupMessages []*mlsv1.GroupMessageInput, +) *svc.ValidateGroupMessagesRequest { + groupMessageRequests := make( + []*svc.ValidateGroupMessagesRequest_GroupMessage, + len(groupMessages), + ) + for i, groupMessage := range groupMessages { + groupMessageRequests[i] = &svc.ValidateGroupMessagesRequest_GroupMessage{ + GroupMessageBytesTlsSerialized: groupMessage.GetV1().Data, + } + } + return &svc.ValidateGroupMessagesRequest{ + GroupMessages: groupMessageRequests, + } +} diff --git a/pkg/mlsvalidate/service_test.go b/pkg/mlsvalidate/service_test.go new file mode 100644 index 00000000..50ca807c --- /dev/null +++ b/pkg/mlsvalidate/service_test.go @@ -0,0 +1,76 @@ +package mlsvalidate + +import ( + "context" + "testing" + + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/xmtp/xmtpd/pkg/mocks" + "github.com/xmtp/xmtpd/pkg/proto/identity/associations" + proto "github.com/xmtp/xmtpd/pkg/proto/mls_validation/v1" + "github.com/xmtp/xmtpd/pkg/testutils" +) + +func TestValidateKeyPackages(t *testing.T) { + apiClient := mocks.NewMockValidationApiClient(t) + svc := &MLSValidationServiceImpl{ + grpcClient: apiClient, + } + + mockResponse := proto.ValidateInboxIdKeyPackagesResponse_Response{ + IsOk: true, + InstallationPublicKey: testutils.RandomBytes(32), + Credential: nil, + Expiration: 1, + } + + apiClient.EXPECT(). + ValidateInboxIdKeyPackages(mock.Anything, mock.Anything). + Times(1). + Return(&proto.ValidateInboxIdKeyPackagesResponse{ + Responses: []*proto.ValidateInboxIdKeyPackagesResponse_Response{&mockResponse}}, + nil, + ) + + res, err := svc.ValidateKeyPackages(context.Background(), [][]byte{testutils.RandomBytes(32)}) + require.NoError(t, err) + require.Len(t, res, 1) + require.Equal(t, mockResponse.InstallationPublicKey, res[0].InstallationKey) + require.Nil(t, res[0].Credential) +} + +func TestGetAssociationState(t *testing.T) { + apiClient := mocks.NewMockValidationApiClient(t) + svc := &MLSValidationServiceImpl{ + grpcClient: apiClient, + } + + inboxId := testutils.RandomInboxId() + address := testutils.RandomAddress().String() + + mockResponse := proto.GetAssociationStateResponse{ + AssociationState: &associations.AssociationState{ + InboxId: inboxId, + }, + StateDiff: &associations.AssociationStateDiff{ + NewMembers: []*associations.MemberIdentifier{{ + Kind: &associations.MemberIdentifier_Address{Address: address}, + }}, + }, + } + + apiClient.EXPECT(). + GetAssociationState(mock.Anything, mock.Anything). + Times(1). + Return(&mockResponse, nil) + + res, err := svc.GetAssociationState( + context.Background(), + []*associations.IdentityUpdate{}, + []*associations.IdentityUpdate{}, + ) + require.NoError(t, err) + require.Equal(t, inboxId, res.AssociationState.InboxId) + require.Equal(t, address, res.StateDiff.NewMembers[0].GetAddress()) +} diff --git a/pkg/mocks/mock_ValidationApiClient.go b/pkg/mocks/mock_ValidationApiClient.go new file mode 100644 index 00000000..69c515a5 --- /dev/null +++ b/pkg/mocks/mock_ValidationApiClient.go @@ -0,0 +1,262 @@ +// Code generated by mockery v2.44.1. DO NOT EDIT. + +package mocks + +import ( + context "context" + + grpc "google.golang.org/grpc" + + mls_validationv1 "github.com/xmtp/xmtpd/pkg/proto/mls_validation/v1" + + mock "github.com/stretchr/testify/mock" +) + +// MockValidationApiClient is an autogenerated mock type for the ValidationApiClient type +type MockValidationApiClient struct { + mock.Mock +} + +type MockValidationApiClient_Expecter struct { + mock *mock.Mock +} + +func (_m *MockValidationApiClient) EXPECT() *MockValidationApiClient_Expecter { + return &MockValidationApiClient_Expecter{mock: &_m.Mock} +} + +// GetAssociationState provides a mock function with given fields: ctx, in, opts +func (_m *MockValidationApiClient) GetAssociationState(ctx context.Context, in *mls_validationv1.GetAssociationStateRequest, opts ...grpc.CallOption) (*mls_validationv1.GetAssociationStateResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for GetAssociationState") + } + + var r0 *mls_validationv1.GetAssociationStateResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *mls_validationv1.GetAssociationStateRequest, ...grpc.CallOption) (*mls_validationv1.GetAssociationStateResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *mls_validationv1.GetAssociationStateRequest, ...grpc.CallOption) *mls_validationv1.GetAssociationStateResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*mls_validationv1.GetAssociationStateResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *mls_validationv1.GetAssociationStateRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockValidationApiClient_GetAssociationState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAssociationState' +type MockValidationApiClient_GetAssociationState_Call struct { + *mock.Call +} + +// GetAssociationState is a helper method to define mock.On call +// - ctx context.Context +// - in *mls_validationv1.GetAssociationStateRequest +// - opts ...grpc.CallOption +func (_e *MockValidationApiClient_Expecter) GetAssociationState(ctx interface{}, in interface{}, opts ...interface{}) *MockValidationApiClient_GetAssociationState_Call { + return &MockValidationApiClient_GetAssociationState_Call{Call: _e.mock.On("GetAssociationState", + append([]interface{}{ctx, in}, opts...)...)} +} + +func (_c *MockValidationApiClient_GetAssociationState_Call) Run(run func(ctx context.Context, in *mls_validationv1.GetAssociationStateRequest, opts ...grpc.CallOption)) *MockValidationApiClient_GetAssociationState_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]grpc.CallOption, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(grpc.CallOption) + } + } + run(args[0].(context.Context), args[1].(*mls_validationv1.GetAssociationStateRequest), variadicArgs...) + }) + return _c +} + +func (_c *MockValidationApiClient_GetAssociationState_Call) Return(_a0 *mls_validationv1.GetAssociationStateResponse, _a1 error) *MockValidationApiClient_GetAssociationState_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockValidationApiClient_GetAssociationState_Call) RunAndReturn(run func(context.Context, *mls_validationv1.GetAssociationStateRequest, ...grpc.CallOption) (*mls_validationv1.GetAssociationStateResponse, error)) *MockValidationApiClient_GetAssociationState_Call { + _c.Call.Return(run) + return _c +} + +// ValidateGroupMessages provides a mock function with given fields: ctx, in, opts +func (_m *MockValidationApiClient) ValidateGroupMessages(ctx context.Context, in *mls_validationv1.ValidateGroupMessagesRequest, opts ...grpc.CallOption) (*mls_validationv1.ValidateGroupMessagesResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for ValidateGroupMessages") + } + + var r0 *mls_validationv1.ValidateGroupMessagesResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *mls_validationv1.ValidateGroupMessagesRequest, ...grpc.CallOption) (*mls_validationv1.ValidateGroupMessagesResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *mls_validationv1.ValidateGroupMessagesRequest, ...grpc.CallOption) *mls_validationv1.ValidateGroupMessagesResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*mls_validationv1.ValidateGroupMessagesResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *mls_validationv1.ValidateGroupMessagesRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockValidationApiClient_ValidateGroupMessages_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ValidateGroupMessages' +type MockValidationApiClient_ValidateGroupMessages_Call struct { + *mock.Call +} + +// ValidateGroupMessages is a helper method to define mock.On call +// - ctx context.Context +// - in *mls_validationv1.ValidateGroupMessagesRequest +// - opts ...grpc.CallOption +func (_e *MockValidationApiClient_Expecter) ValidateGroupMessages(ctx interface{}, in interface{}, opts ...interface{}) *MockValidationApiClient_ValidateGroupMessages_Call { + return &MockValidationApiClient_ValidateGroupMessages_Call{Call: _e.mock.On("ValidateGroupMessages", + append([]interface{}{ctx, in}, opts...)...)} +} + +func (_c *MockValidationApiClient_ValidateGroupMessages_Call) Run(run func(ctx context.Context, in *mls_validationv1.ValidateGroupMessagesRequest, opts ...grpc.CallOption)) *MockValidationApiClient_ValidateGroupMessages_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]grpc.CallOption, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(grpc.CallOption) + } + } + run(args[0].(context.Context), args[1].(*mls_validationv1.ValidateGroupMessagesRequest), variadicArgs...) + }) + return _c +} + +func (_c *MockValidationApiClient_ValidateGroupMessages_Call) Return(_a0 *mls_validationv1.ValidateGroupMessagesResponse, _a1 error) *MockValidationApiClient_ValidateGroupMessages_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockValidationApiClient_ValidateGroupMessages_Call) RunAndReturn(run func(context.Context, *mls_validationv1.ValidateGroupMessagesRequest, ...grpc.CallOption) (*mls_validationv1.ValidateGroupMessagesResponse, error)) *MockValidationApiClient_ValidateGroupMessages_Call { + _c.Call.Return(run) + return _c +} + +// ValidateInboxIdKeyPackages provides a mock function with given fields: ctx, in, opts +func (_m *MockValidationApiClient) ValidateInboxIdKeyPackages(ctx context.Context, in *mls_validationv1.ValidateInboxIdKeyPackagesRequest, opts ...grpc.CallOption) (*mls_validationv1.ValidateInboxIdKeyPackagesResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for ValidateInboxIdKeyPackages") + } + + var r0 *mls_validationv1.ValidateInboxIdKeyPackagesResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *mls_validationv1.ValidateInboxIdKeyPackagesRequest, ...grpc.CallOption) (*mls_validationv1.ValidateInboxIdKeyPackagesResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *mls_validationv1.ValidateInboxIdKeyPackagesRequest, ...grpc.CallOption) *mls_validationv1.ValidateInboxIdKeyPackagesResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*mls_validationv1.ValidateInboxIdKeyPackagesResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *mls_validationv1.ValidateInboxIdKeyPackagesRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockValidationApiClient_ValidateInboxIdKeyPackages_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ValidateInboxIdKeyPackages' +type MockValidationApiClient_ValidateInboxIdKeyPackages_Call struct { + *mock.Call +} + +// ValidateInboxIdKeyPackages is a helper method to define mock.On call +// - ctx context.Context +// - in *mls_validationv1.ValidateInboxIdKeyPackagesRequest +// - opts ...grpc.CallOption +func (_e *MockValidationApiClient_Expecter) ValidateInboxIdKeyPackages(ctx interface{}, in interface{}, opts ...interface{}) *MockValidationApiClient_ValidateInboxIdKeyPackages_Call { + return &MockValidationApiClient_ValidateInboxIdKeyPackages_Call{Call: _e.mock.On("ValidateInboxIdKeyPackages", + append([]interface{}{ctx, in}, opts...)...)} +} + +func (_c *MockValidationApiClient_ValidateInboxIdKeyPackages_Call) Run(run func(ctx context.Context, in *mls_validationv1.ValidateInboxIdKeyPackagesRequest, opts ...grpc.CallOption)) *MockValidationApiClient_ValidateInboxIdKeyPackages_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]grpc.CallOption, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(grpc.CallOption) + } + } + run(args[0].(context.Context), args[1].(*mls_validationv1.ValidateInboxIdKeyPackagesRequest), variadicArgs...) + }) + return _c +} + +func (_c *MockValidationApiClient_ValidateInboxIdKeyPackages_Call) Return(_a0 *mls_validationv1.ValidateInboxIdKeyPackagesResponse, _a1 error) *MockValidationApiClient_ValidateInboxIdKeyPackages_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockValidationApiClient_ValidateInboxIdKeyPackages_Call) RunAndReturn(run func(context.Context, *mls_validationv1.ValidateInboxIdKeyPackagesRequest, ...grpc.CallOption) (*mls_validationv1.ValidateInboxIdKeyPackagesResponse, error)) *MockValidationApiClient_ValidateInboxIdKeyPackages_Call { + _c.Call.Return(run) + return _c +} + +// NewMockValidationApiClient creates a new instance of MockValidationApiClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockValidationApiClient(t interface { + mock.TestingT + Cleanup(func()) +}) *MockValidationApiClient { + mock := &MockValidationApiClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +}