diff --git a/backend/mpc-system/.env.party.example b/backend/mpc-system/.env.party.example new file mode 100644 index 00000000..f8c6f2f5 --- /dev/null +++ b/backend/mpc-system/.env.party.example @@ -0,0 +1,59 @@ +# ============================================================================= +# MPC-System Production Party Configuration +# ============================================================================= +# Copy to .env.party and configure for your party's environment +# +# Usage: +# cp .env.party.example .env.party +# # Edit .env.party with your values +# ./deploy.sh party up +# +# Each party machine needs its own .env.party with unique PARTY_ID +# ============================================================================= + +# ============================================================================= +# Party Identity (REQUIRED - must be unique across all parties) +# ============================================================================= +PARTY_ID=server-party-1 +# Options: persistent (default), delegate, temporary +PARTY_ROLE=persistent + +# ============================================================================= +# Central Service (REQUIRED - public address) +# ============================================================================= +# Message Router gRPC endpoint (the ONLY connection parties need) +# Session operations are proxied through Message Router to Session Coordinator +MESSAGE_ROUTER_ADDR=grpc.mpc.example.com:50051 + +# ============================================================================= +# Local Database (for storing encrypted key shares) +# ============================================================================= +POSTGRES_USER=mpc_user +POSTGRES_PASSWORD=your_secure_local_postgres_password + +# ============================================================================= +# Security Keys (REQUIRED) +# ============================================================================= +# Master key for encrypting key shares (64 hex characters = 256-bit) +# IMPORTANT: Use the same key across all parties in the same MPC group +# Generate with: openssl rand -hex 32 +CRYPTO_MASTER_KEY=your_64_character_hex_master_key_here + +# ============================================================================= +# Optional: Notification Channels (for offline mode) +# ============================================================================= +# If any of these are set, party operates in offline mode (24h async) +# If none are set, party operates in real-time mode (Message Router push) +NOTIFICATION_EMAIL= +NOTIFICATION_PHONE= +NOTIFICATION_PUSH_TOKEN= + +# ============================================================================= +# Optional: Local HTTP Port (for health checks only) +# ============================================================================= +# This party doesn't need to expose any ports - it connects outbound +# HTTP port is optional for local health monitoring +PARTY_HTTP_PORT=8080 + +# Environment +ENVIRONMENT=production diff --git a/backend/mpc-system/.env.prod.example b/backend/mpc-system/.env.prod.example new file mode 100644 index 00000000..ccedcffa --- /dev/null +++ b/backend/mpc-system/.env.prod.example @@ -0,0 +1,57 @@ +# ============================================================================= +# MPC-System Production Central Configuration +# ============================================================================= +# Copy to .env.prod and configure for your environment +# +# Usage: +# cp .env.prod.example .env.prod +# # Edit .env.prod with your values +# ./deploy.sh prod up +# ============================================================================= + +# Environment +ENVIRONMENT=production + +# ============================================================================= +# Database Configuration +# ============================================================================= +POSTGRES_USER=mpc_user +POSTGRES_PASSWORD=your_secure_postgres_password_here + +# ============================================================================= +# Security Keys (IMPORTANT: Generate unique values!) +# ============================================================================= +# Generate with: openssl rand -base64 32 +JWT_SECRET_KEY=your_jwt_secret_key_here_min_32_chars + +# API Key for backend integration (shared with mpc-service) +# Generate with: openssl rand -base64 32 +MPC_API_KEY=your_api_key_here + +# Master key for encrypting key shares (64 hex characters = 256-bit) +# Generate with: openssl rand -hex 32 +CRYPTO_MASTER_KEY=your_64_character_hex_master_key_here + +# ============================================================================= +# Public Ports (must be accessible from server-parties) +# ============================================================================= +# Message Router gRPC - parties connect here +MESSAGE_ROUTER_GRPC_PORT=50051 +MESSAGE_ROUTER_HTTP_PORT=8082 + +# Session Coordinator gRPC - parties connect here +SESSION_COORDINATOR_GRPC_PORT=50052 +SESSION_COORDINATOR_HTTP_PORT=8081 + +# Account Service HTTP - backend API +ACCOUNT_SERVICE_PORT=4000 + +# Server Party API (optional) +SERVER_PARTY_API_PORT=8083 + +# ============================================================================= +# IP Whitelist (optional) +# ============================================================================= +# Comma-separated list of IPs allowed to access Account Service API +# Leave empty to allow all (protected by API_KEY) +ALLOWED_IPS= diff --git a/backend/mpc-system/api/grpc/coordinator/v1/session_coordinator.pb.go b/backend/mpc-system/api/grpc/coordinator/v1/session_coordinator.pb.go index 822a2249..dfba0e56 100644 --- a/backend/mpc-system/api/grpc/coordinator/v1/session_coordinator.pb.go +++ b/backend/mpc-system/api/grpc/coordinator/v1/session_coordinator.pb.go @@ -31,8 +31,10 @@ type CreateSessionRequest struct { MessageHash []byte `protobuf:"bytes,5,opt,name=message_hash,json=messageHash,proto3" json:"message_hash,omitempty"` // Required for sign sessions ExpiresInSeconds int64 `protobuf:"varint,6,opt,name=expires_in_seconds,json=expiresInSeconds,proto3" json:"expires_in_seconds,omitempty"` // Session expiration time PartyComposition *PartyComposition `protobuf:"bytes,7,opt,name=party_composition,json=partyComposition,proto3" json:"party_composition,omitempty"` // Optional: party composition requirements for auto-selection - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // For sign sessions with delegate party: user must provide their encrypted share + DelegateUserShare *DelegateUserShare `protobuf:"bytes,8,opt,name=delegate_user_share,json=delegateUserShare,proto3" json:"delegate_user_share,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *CreateSessionRequest) Reset() { @@ -114,6 +116,74 @@ func (x *CreateSessionRequest) GetPartyComposition() *PartyComposition { return nil } +func (x *CreateSessionRequest) GetDelegateUserShare() *DelegateUserShare { + if x != nil { + return x.DelegateUserShare + } + return nil +} + +// DelegateUserShare contains user's share for delegate party to use in signing +type DelegateUserShare struct { + state protoimpl.MessageState `protogen:"open.v1"` + DelegatePartyId string `protobuf:"bytes,1,opt,name=delegate_party_id,json=delegatePartyId,proto3" json:"delegate_party_id,omitempty"` // The delegate party that will use this share + EncryptedShare []byte `protobuf:"bytes,2,opt,name=encrypted_share,json=encryptedShare,proto3" json:"encrypted_share,omitempty"` // User's encrypted share (same as received from keygen) + PartyIndex int32 `protobuf:"varint,3,opt,name=party_index,json=partyIndex,proto3" json:"party_index,omitempty"` // Party index for this share + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DelegateUserShare) Reset() { + *x = DelegateUserShare{} + mi := &file_api_proto_session_coordinator_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DelegateUserShare) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DelegateUserShare) ProtoMessage() {} + +func (x *DelegateUserShare) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_session_coordinator_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DelegateUserShare.ProtoReflect.Descriptor instead. +func (*DelegateUserShare) Descriptor() ([]byte, []int) { + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{1} +} + +func (x *DelegateUserShare) GetDelegatePartyId() string { + if x != nil { + return x.DelegatePartyId + } + return "" +} + +func (x *DelegateUserShare) GetEncryptedShare() []byte { + if x != nil { + return x.EncryptedShare + } + return nil +} + +func (x *DelegateUserShare) GetPartyIndex() int32 { + if x != nil { + return x.PartyIndex + } + return 0 +} + // PartyComposition specifies requirements for automatic party selection type PartyComposition struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -126,7 +196,7 @@ type PartyComposition struct { func (x *PartyComposition) Reset() { *x = PartyComposition{} - mi := &file_api_proto_session_coordinator_proto_msgTypes[1] + mi := &file_api_proto_session_coordinator_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -138,7 +208,7 @@ func (x *PartyComposition) String() string { func (*PartyComposition) ProtoMessage() {} func (x *PartyComposition) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_session_coordinator_proto_msgTypes[1] + mi := &file_api_proto_session_coordinator_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -151,7 +221,7 @@ func (x *PartyComposition) ProtoReflect() protoreflect.Message { // Deprecated: Use PartyComposition.ProtoReflect.Descriptor instead. func (*PartyComposition) Descriptor() ([]byte, []int) { - return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{1} + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{2} } func (x *PartyComposition) GetPersistentCount() int32 { @@ -186,7 +256,7 @@ type ParticipantInfo struct { func (x *ParticipantInfo) Reset() { *x = ParticipantInfo{} - mi := &file_api_proto_session_coordinator_proto_msgTypes[2] + mi := &file_api_proto_session_coordinator_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -198,7 +268,7 @@ func (x *ParticipantInfo) String() string { func (*ParticipantInfo) ProtoMessage() {} func (x *ParticipantInfo) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_session_coordinator_proto_msgTypes[2] + mi := &file_api_proto_session_coordinator_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -211,7 +281,7 @@ func (x *ParticipantInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use ParticipantInfo.ProtoReflect.Descriptor instead. func (*ParticipantInfo) Descriptor() ([]byte, []int) { - return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{2} + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{3} } func (x *ParticipantInfo) GetPartyId() string { @@ -241,7 +311,7 @@ type DeviceInfo struct { func (x *DeviceInfo) Reset() { *x = DeviceInfo{} - mi := &file_api_proto_session_coordinator_proto_msgTypes[3] + mi := &file_api_proto_session_coordinator_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -253,7 +323,7 @@ func (x *DeviceInfo) String() string { func (*DeviceInfo) ProtoMessage() {} func (x *DeviceInfo) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_session_coordinator_proto_msgTypes[3] + mi := &file_api_proto_session_coordinator_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -266,7 +336,7 @@ func (x *DeviceInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use DeviceInfo.ProtoReflect.Descriptor instead. func (*DeviceInfo) Descriptor() ([]byte, []int) { - return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{3} + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{4} } func (x *DeviceInfo) GetDeviceType() string { @@ -311,7 +381,7 @@ type CreateSessionResponse struct { func (x *CreateSessionResponse) Reset() { *x = CreateSessionResponse{} - mi := &file_api_proto_session_coordinator_proto_msgTypes[4] + mi := &file_api_proto_session_coordinator_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -323,7 +393,7 @@ func (x *CreateSessionResponse) String() string { func (*CreateSessionResponse) ProtoMessage() {} func (x *CreateSessionResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_session_coordinator_proto_msgTypes[4] + mi := &file_api_proto_session_coordinator_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -336,7 +406,7 @@ func (x *CreateSessionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateSessionResponse.ProtoReflect.Descriptor instead. func (*CreateSessionResponse) Descriptor() ([]byte, []int) { - return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{4} + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{5} } func (x *CreateSessionResponse) GetSessionId() string { @@ -387,7 +457,7 @@ type JoinSessionRequest struct { func (x *JoinSessionRequest) Reset() { *x = JoinSessionRequest{} - mi := &file_api_proto_session_coordinator_proto_msgTypes[5] + mi := &file_api_proto_session_coordinator_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -399,7 +469,7 @@ func (x *JoinSessionRequest) String() string { func (*JoinSessionRequest) ProtoMessage() {} func (x *JoinSessionRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_session_coordinator_proto_msgTypes[5] + mi := &file_api_proto_session_coordinator_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -412,7 +482,7 @@ func (x *JoinSessionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use JoinSessionRequest.ProtoReflect.Descriptor instead. func (*JoinSessionRequest) Descriptor() ([]byte, []int) { - return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{5} + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{6} } func (x *JoinSessionRequest) GetSessionId() string { @@ -455,7 +525,7 @@ type JoinSessionResponse struct { func (x *JoinSessionResponse) Reset() { *x = JoinSessionResponse{} - mi := &file_api_proto_session_coordinator_proto_msgTypes[6] + mi := &file_api_proto_session_coordinator_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -467,7 +537,7 @@ func (x *JoinSessionResponse) String() string { func (*JoinSessionResponse) ProtoMessage() {} func (x *JoinSessionResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_session_coordinator_proto_msgTypes[6] + mi := &file_api_proto_session_coordinator_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -480,7 +550,7 @@ func (x *JoinSessionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use JoinSessionResponse.ProtoReflect.Descriptor instead. func (*JoinSessionResponse) Descriptor() ([]byte, []int) { - return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{6} + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{7} } func (x *JoinSessionResponse) GetSuccess() bool { @@ -519,7 +589,7 @@ type SessionInfo struct { func (x *SessionInfo) Reset() { *x = SessionInfo{} - mi := &file_api_proto_session_coordinator_proto_msgTypes[7] + mi := &file_api_proto_session_coordinator_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -531,7 +601,7 @@ func (x *SessionInfo) String() string { func (*SessionInfo) ProtoMessage() {} func (x *SessionInfo) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_session_coordinator_proto_msgTypes[7] + mi := &file_api_proto_session_coordinator_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -544,7 +614,7 @@ func (x *SessionInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use SessionInfo.ProtoReflect.Descriptor instead. func (*SessionInfo) Descriptor() ([]byte, []int) { - return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{7} + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{8} } func (x *SessionInfo) GetSessionId() string { @@ -601,7 +671,7 @@ type PartyInfo struct { func (x *PartyInfo) Reset() { *x = PartyInfo{} - mi := &file_api_proto_session_coordinator_proto_msgTypes[8] + mi := &file_api_proto_session_coordinator_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -613,7 +683,7 @@ func (x *PartyInfo) String() string { func (*PartyInfo) ProtoMessage() {} func (x *PartyInfo) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_session_coordinator_proto_msgTypes[8] + mi := &file_api_proto_session_coordinator_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -626,7 +696,7 @@ func (x *PartyInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use PartyInfo.ProtoReflect.Descriptor instead. func (*PartyInfo) Descriptor() ([]byte, []int) { - return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{8} + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{9} } func (x *PartyInfo) GetPartyId() string { @@ -660,7 +730,7 @@ type GetSessionStatusRequest struct { func (x *GetSessionStatusRequest) Reset() { *x = GetSessionStatusRequest{} - mi := &file_api_proto_session_coordinator_proto_msgTypes[9] + mi := &file_api_proto_session_coordinator_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -672,7 +742,7 @@ func (x *GetSessionStatusRequest) String() string { func (*GetSessionStatusRequest) ProtoMessage() {} func (x *GetSessionStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_session_coordinator_proto_msgTypes[9] + mi := &file_api_proto_session_coordinator_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -685,7 +755,7 @@ func (x *GetSessionStatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSessionStatusRequest.ProtoReflect.Descriptor instead. func (*GetSessionStatusRequest) Descriptor() ([]byte, []int) { - return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{9} + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{10} } func (x *GetSessionStatusRequest) GetSessionId() string { @@ -701,15 +771,22 @@ type GetSessionStatusResponse struct { Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` CompletedParties int32 `protobuf:"varint,2,opt,name=completed_parties,json=completedParties,proto3" json:"completed_parties,omitempty"` TotalParties int32 `protobuf:"varint,3,opt,name=total_parties,json=totalParties,proto3" json:"total_parties,omitempty"` - PublicKey []byte `protobuf:"bytes,4,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` // For completed keygen - Signature []byte `protobuf:"bytes,5,opt,name=signature,proto3" json:"signature,omitempty"` // For completed sign - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + SessionType string `protobuf:"bytes,4,opt,name=session_type,json=sessionType,proto3" json:"session_type,omitempty"` // "keygen" or "sign" + PublicKey []byte `protobuf:"bytes,5,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` // For completed keygen + Signature []byte `protobuf:"bytes,6,opt,name=signature,proto3" json:"signature,omitempty"` // For completed sign + // has_delegate indicates whether this keygen session has a delegate party + // Only meaningful for keygen sessions. Always false for sign sessions. + HasDelegate bool `protobuf:"varint,7,opt,name=has_delegate,json=hasDelegate,proto3" json:"has_delegate,omitempty"` + // Delegate share info (returned when keygen session completed and delegate party submitted share) + // Only populated if session_type="keygen" AND has_delegate=true AND session is completed + DelegateShare *DelegateShareInfo `protobuf:"bytes,8,opt,name=delegate_share,json=delegateShare,proto3" json:"delegate_share,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *GetSessionStatusResponse) Reset() { *x = GetSessionStatusResponse{} - mi := &file_api_proto_session_coordinator_proto_msgTypes[10] + mi := &file_api_proto_session_coordinator_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -721,7 +798,7 @@ func (x *GetSessionStatusResponse) String() string { func (*GetSessionStatusResponse) ProtoMessage() {} func (x *GetSessionStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_session_coordinator_proto_msgTypes[10] + mi := &file_api_proto_session_coordinator_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -734,7 +811,7 @@ func (x *GetSessionStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSessionStatusResponse.ProtoReflect.Descriptor instead. func (*GetSessionStatusResponse) Descriptor() ([]byte, []int) { - return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{10} + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{11} } func (x *GetSessionStatusResponse) GetStatus() string { @@ -758,6 +835,13 @@ func (x *GetSessionStatusResponse) GetTotalParties() int32 { return 0 } +func (x *GetSessionStatusResponse) GetSessionType() string { + if x != nil { + return x.SessionType + } + return "" +} + func (x *GetSessionStatusResponse) GetPublicKey() []byte { if x != nil { return x.PublicKey @@ -772,6 +856,81 @@ func (x *GetSessionStatusResponse) GetSignature() []byte { return nil } +func (x *GetSessionStatusResponse) GetHasDelegate() bool { + if x != nil { + return x.HasDelegate + } + return false +} + +func (x *GetSessionStatusResponse) GetDelegateShare() *DelegateShareInfo { + if x != nil { + return x.DelegateShare + } + return nil +} + +// DelegateShareInfo contains the delegate party's share for user +type DelegateShareInfo struct { + state protoimpl.MessageState `protogen:"open.v1"` + EncryptedShare []byte `protobuf:"bytes,1,opt,name=encrypted_share,json=encryptedShare,proto3" json:"encrypted_share,omitempty"` // Encrypted share for user + PartyIndex int32 `protobuf:"varint,2,opt,name=party_index,json=partyIndex,proto3" json:"party_index,omitempty"` // Party's index in the session + PartyId string `protobuf:"bytes,3,opt,name=party_id,json=partyId,proto3" json:"party_id,omitempty"` // Delegate party ID + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DelegateShareInfo) Reset() { + *x = DelegateShareInfo{} + mi := &file_api_proto_session_coordinator_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DelegateShareInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DelegateShareInfo) ProtoMessage() {} + +func (x *DelegateShareInfo) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_session_coordinator_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DelegateShareInfo.ProtoReflect.Descriptor instead. +func (*DelegateShareInfo) Descriptor() ([]byte, []int) { + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{12} +} + +func (x *DelegateShareInfo) GetEncryptedShare() []byte { + if x != nil { + return x.EncryptedShare + } + return nil +} + +func (x *DelegateShareInfo) GetPartyIndex() int32 { + if x != nil { + return x.PartyIndex + } + return 0 +} + +func (x *DelegateShareInfo) GetPartyId() string { + if x != nil { + return x.PartyId + } + return "" +} + // ReportCompletionRequest reports that a participant has completed type ReportCompletionRequest struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -785,7 +944,7 @@ type ReportCompletionRequest struct { func (x *ReportCompletionRequest) Reset() { *x = ReportCompletionRequest{} - mi := &file_api_proto_session_coordinator_proto_msgTypes[11] + mi := &file_api_proto_session_coordinator_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -797,7 +956,7 @@ func (x *ReportCompletionRequest) String() string { func (*ReportCompletionRequest) ProtoMessage() {} func (x *ReportCompletionRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_session_coordinator_proto_msgTypes[11] + mi := &file_api_proto_session_coordinator_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -810,7 +969,7 @@ func (x *ReportCompletionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportCompletionRequest.ProtoReflect.Descriptor instead. func (*ReportCompletionRequest) Descriptor() ([]byte, []int) { - return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{11} + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{13} } func (x *ReportCompletionRequest) GetSessionId() string { @@ -852,7 +1011,7 @@ type ReportCompletionResponse struct { func (x *ReportCompletionResponse) Reset() { *x = ReportCompletionResponse{} - mi := &file_api_proto_session_coordinator_proto_msgTypes[12] + mi := &file_api_proto_session_coordinator_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -864,7 +1023,7 @@ func (x *ReportCompletionResponse) String() string { func (*ReportCompletionResponse) ProtoMessage() {} func (x *ReportCompletionResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_session_coordinator_proto_msgTypes[12] + mi := &file_api_proto_session_coordinator_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -877,7 +1036,7 @@ func (x *ReportCompletionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportCompletionResponse.ProtoReflect.Descriptor instead. func (*ReportCompletionResponse) Descriptor() ([]byte, []int) { - return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{12} + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{14} } func (x *ReportCompletionResponse) GetSuccess() bool { @@ -904,7 +1063,7 @@ type CloseSessionRequest struct { func (x *CloseSessionRequest) Reset() { *x = CloseSessionRequest{} - mi := &file_api_proto_session_coordinator_proto_msgTypes[13] + mi := &file_api_proto_session_coordinator_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -916,7 +1075,7 @@ func (x *CloseSessionRequest) String() string { func (*CloseSessionRequest) ProtoMessage() {} func (x *CloseSessionRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_session_coordinator_proto_msgTypes[13] + mi := &file_api_proto_session_coordinator_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -929,7 +1088,7 @@ func (x *CloseSessionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CloseSessionRequest.ProtoReflect.Descriptor instead. func (*CloseSessionRequest) Descriptor() ([]byte, []int) { - return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{13} + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{15} } func (x *CloseSessionRequest) GetSessionId() string { @@ -949,7 +1108,7 @@ type CloseSessionResponse struct { func (x *CloseSessionResponse) Reset() { *x = CloseSessionResponse{} - mi := &file_api_proto_session_coordinator_proto_msgTypes[14] + mi := &file_api_proto_session_coordinator_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -961,7 +1120,7 @@ func (x *CloseSessionResponse) String() string { func (*CloseSessionResponse) ProtoMessage() {} func (x *CloseSessionResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_session_coordinator_proto_msgTypes[14] + mi := &file_api_proto_session_coordinator_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -974,7 +1133,7 @@ func (x *CloseSessionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CloseSessionResponse.ProtoReflect.Descriptor instead. func (*CloseSessionResponse) Descriptor() ([]byte, []int) { - return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{14} + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{16} } func (x *CloseSessionResponse) GetSuccess() bool { @@ -995,7 +1154,7 @@ type MarkPartyReadyRequest struct { func (x *MarkPartyReadyRequest) Reset() { *x = MarkPartyReadyRequest{} - mi := &file_api_proto_session_coordinator_proto_msgTypes[15] + mi := &file_api_proto_session_coordinator_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1007,7 +1166,7 @@ func (x *MarkPartyReadyRequest) String() string { func (*MarkPartyReadyRequest) ProtoMessage() {} func (x *MarkPartyReadyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_session_coordinator_proto_msgTypes[15] + mi := &file_api_proto_session_coordinator_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1020,7 +1179,7 @@ func (x *MarkPartyReadyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MarkPartyReadyRequest.ProtoReflect.Descriptor instead. func (*MarkPartyReadyRequest) Descriptor() ([]byte, []int) { - return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{15} + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{17} } func (x *MarkPartyReadyRequest) GetSessionId() string { @@ -1050,7 +1209,7 @@ type MarkPartyReadyResponse struct { func (x *MarkPartyReadyResponse) Reset() { *x = MarkPartyReadyResponse{} - mi := &file_api_proto_session_coordinator_proto_msgTypes[16] + mi := &file_api_proto_session_coordinator_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1062,7 +1221,7 @@ func (x *MarkPartyReadyResponse) String() string { func (*MarkPartyReadyResponse) ProtoMessage() {} func (x *MarkPartyReadyResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_session_coordinator_proto_msgTypes[16] + mi := &file_api_proto_session_coordinator_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1075,7 +1234,7 @@ func (x *MarkPartyReadyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MarkPartyReadyResponse.ProtoReflect.Descriptor instead. func (*MarkPartyReadyResponse) Descriptor() ([]byte, []int) { - return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{16} + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{18} } func (x *MarkPartyReadyResponse) GetSuccess() bool { @@ -1116,7 +1275,7 @@ type StartSessionRequest struct { func (x *StartSessionRequest) Reset() { *x = StartSessionRequest{} - mi := &file_api_proto_session_coordinator_proto_msgTypes[17] + mi := &file_api_proto_session_coordinator_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1128,7 +1287,7 @@ func (x *StartSessionRequest) String() string { func (*StartSessionRequest) ProtoMessage() {} func (x *StartSessionRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_session_coordinator_proto_msgTypes[17] + mi := &file_api_proto_session_coordinator_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1141,7 +1300,7 @@ func (x *StartSessionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StartSessionRequest.ProtoReflect.Descriptor instead. func (*StartSessionRequest) Descriptor() ([]byte, []int) { - return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{17} + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{19} } func (x *StartSessionRequest) GetSessionId() string { @@ -1162,7 +1321,7 @@ type StartSessionResponse struct { func (x *StartSessionResponse) Reset() { *x = StartSessionResponse{} - mi := &file_api_proto_session_coordinator_proto_msgTypes[18] + mi := &file_api_proto_session_coordinator_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1174,7 +1333,7 @@ func (x *StartSessionResponse) String() string { func (*StartSessionResponse) ProtoMessage() {} func (x *StartSessionResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_session_coordinator_proto_msgTypes[18] + mi := &file_api_proto_session_coordinator_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1187,7 +1346,7 @@ func (x *StartSessionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StartSessionResponse.ProtoReflect.Descriptor instead. func (*StartSessionResponse) Descriptor() ([]byte, []int) { - return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{18} + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{20} } func (x *StartSessionResponse) GetSuccess() bool { @@ -1204,11 +1363,133 @@ func (x *StartSessionResponse) GetStatus() string { return "" } +// SubmitDelegateShareRequest submits user's share from delegate party +type SubmitDelegateShareRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` + PartyId string `protobuf:"bytes,2,opt,name=party_id,json=partyId,proto3" json:"party_id,omitempty"` + EncryptedShare []byte `protobuf:"bytes,3,opt,name=encrypted_share,json=encryptedShare,proto3" json:"encrypted_share,omitempty"` // Encrypted share for user + PublicKey []byte `protobuf:"bytes,4,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` // Public key from keygen + PartyIndex int32 `protobuf:"varint,5,opt,name=party_index,json=partyIndex,proto3" json:"party_index,omitempty"` // Party's index in the session + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubmitDelegateShareRequest) Reset() { + *x = SubmitDelegateShareRequest{} + mi := &file_api_proto_session_coordinator_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubmitDelegateShareRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitDelegateShareRequest) ProtoMessage() {} + +func (x *SubmitDelegateShareRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_session_coordinator_proto_msgTypes[21] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubmitDelegateShareRequest.ProtoReflect.Descriptor instead. +func (*SubmitDelegateShareRequest) Descriptor() ([]byte, []int) { + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{21} +} + +func (x *SubmitDelegateShareRequest) GetSessionId() string { + if x != nil { + return x.SessionId + } + return "" +} + +func (x *SubmitDelegateShareRequest) GetPartyId() string { + if x != nil { + return x.PartyId + } + return "" +} + +func (x *SubmitDelegateShareRequest) GetEncryptedShare() []byte { + if x != nil { + return x.EncryptedShare + } + return nil +} + +func (x *SubmitDelegateShareRequest) GetPublicKey() []byte { + if x != nil { + return x.PublicKey + } + return nil +} + +func (x *SubmitDelegateShareRequest) GetPartyIndex() int32 { + if x != nil { + return x.PartyIndex + } + return 0 +} + +// SubmitDelegateShareResponse contains result of share submission +type SubmitDelegateShareResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubmitDelegateShareResponse) Reset() { + *x = SubmitDelegateShareResponse{} + mi := &file_api_proto_session_coordinator_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubmitDelegateShareResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitDelegateShareResponse) ProtoMessage() {} + +func (x *SubmitDelegateShareResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_session_coordinator_proto_msgTypes[22] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubmitDelegateShareResponse.ProtoReflect.Descriptor instead. +func (*SubmitDelegateShareResponse) Descriptor() ([]byte, []int) { + return file_api_proto_session_coordinator_proto_rawDescGZIP(), []int{22} +} + +func (x *SubmitDelegateShareResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + var File_api_proto_session_coordinator_proto protoreflect.FileDescriptor const file_api_proto_session_coordinator_proto_rawDesc = "" + "\n" + - "#api/proto/session_coordinator.proto\x12\x12mpc.coordinator.v1\"\xe8\x02\n" + + "#api/proto/session_coordinator.proto\x12\x12mpc.coordinator.v1\"\xbf\x03\n" + "\x14CreateSessionRequest\x12!\n" + "\fsession_type\x18\x01 \x01(\tR\vsessionType\x12\x1f\n" + "\vthreshold_n\x18\x02 \x01(\x05R\n" + @@ -1218,7 +1499,13 @@ const file_api_proto_session_coordinator_proto_rawDesc = "" + "\fparticipants\x18\x04 \x03(\v2#.mpc.coordinator.v1.ParticipantInfoR\fparticipants\x12!\n" + "\fmessage_hash\x18\x05 \x01(\fR\vmessageHash\x12,\n" + "\x12expires_in_seconds\x18\x06 \x01(\x03R\x10expiresInSeconds\x12Q\n" + - "\x11party_composition\x18\a \x01(\v2$.mpc.coordinator.v1.PartyCompositionR\x10partyComposition\"\x8d\x01\n" + + "\x11party_composition\x18\a \x01(\v2$.mpc.coordinator.v1.PartyCompositionR\x10partyComposition\x12U\n" + + "\x13delegate_user_share\x18\b \x01(\v2%.mpc.coordinator.v1.DelegateUserShareR\x11delegateUserShare\"\x89\x01\n" + + "\x11DelegateUserShare\x12*\n" + + "\x11delegate_party_id\x18\x01 \x01(\tR\x0fdelegatePartyId\x12'\n" + + "\x0fencrypted_share\x18\x02 \x01(\fR\x0eencryptedShare\x12\x1f\n" + + "\vparty_index\x18\x03 \x01(\x05R\n" + + "partyIndex\"\x8d\x01\n" + "\x10PartyComposition\x12)\n" + "\x10persistent_count\x18\x01 \x01(\x05R\x0fpersistentCount\x12%\n" + "\x0edelegate_count\x18\x02 \x01(\x05R\rdelegateCount\x12'\n" + @@ -1277,14 +1564,22 @@ const file_api_proto_session_coordinator_proto_rawDesc = "" + "deviceInfo\"8\n" + "\x17GetSessionStatusRequest\x12\x1d\n" + "\n" + - "session_id\x18\x01 \x01(\tR\tsessionId\"\xc1\x01\n" + + "session_id\x18\x01 \x01(\tR\tsessionId\"\xd5\x02\n" + "\x18GetSessionStatusResponse\x12\x16\n" + "\x06status\x18\x01 \x01(\tR\x06status\x12+\n" + "\x11completed_parties\x18\x02 \x01(\x05R\x10completedParties\x12#\n" + - "\rtotal_parties\x18\x03 \x01(\x05R\ftotalParties\x12\x1d\n" + + "\rtotal_parties\x18\x03 \x01(\x05R\ftotalParties\x12!\n" + + "\fsession_type\x18\x04 \x01(\tR\vsessionType\x12\x1d\n" + "\n" + - "public_key\x18\x04 \x01(\fR\tpublicKey\x12\x1c\n" + - "\tsignature\x18\x05 \x01(\fR\tsignature\"\x90\x01\n" + + "public_key\x18\x05 \x01(\fR\tpublicKey\x12\x1c\n" + + "\tsignature\x18\x06 \x01(\fR\tsignature\x12!\n" + + "\fhas_delegate\x18\a \x01(\bR\vhasDelegate\x12L\n" + + "\x0edelegate_share\x18\b \x01(\v2%.mpc.coordinator.v1.DelegateShareInfoR\rdelegateShare\"x\n" + + "\x11DelegateShareInfo\x12'\n" + + "\x0fencrypted_share\x18\x01 \x01(\fR\x0eencryptedShare\x12\x1f\n" + + "\vparty_index\x18\x02 \x01(\x05R\n" + + "partyIndex\x12\x19\n" + + "\bparty_id\x18\x03 \x01(\tR\apartyId\"\x90\x01\n" + "\x17ReportCompletionRequest\x12\x1d\n" + "\n" + "session_id\x18\x01 \x01(\tR\tsessionId\x12\x19\n" + @@ -1315,7 +1610,18 @@ const file_api_proto_session_coordinator_proto_rawDesc = "" + "session_id\x18\x01 \x01(\tR\tsessionId\"H\n" + "\x14StartSessionResponse\x12\x18\n" + "\asuccess\x18\x01 \x01(\bR\asuccess\x12\x16\n" + - "\x06status\x18\x02 \x01(\tR\x06status2\xe7\x05\n" + + "\x06status\x18\x02 \x01(\tR\x06status\"\xbf\x01\n" + + "\x1aSubmitDelegateShareRequest\x12\x1d\n" + + "\n" + + "session_id\x18\x01 \x01(\tR\tsessionId\x12\x19\n" + + "\bparty_id\x18\x02 \x01(\tR\apartyId\x12'\n" + + "\x0fencrypted_share\x18\x03 \x01(\fR\x0eencryptedShare\x12\x1d\n" + + "\n" + + "public_key\x18\x04 \x01(\fR\tpublicKey\x12\x1f\n" + + "\vparty_index\x18\x05 \x01(\x05R\n" + + "partyIndex\"7\n" + + "\x1bSubmitDelegateShareResponse\x12\x18\n" + + "\asuccess\x18\x01 \x01(\bR\asuccess2\xdf\x06\n" + "\x12SessionCoordinator\x12d\n" + "\rCreateSession\x12(.mpc.coordinator.v1.CreateSessionRequest\x1a).mpc.coordinator.v1.CreateSessionResponse\x12^\n" + "\vJoinSession\x12&.mpc.coordinator.v1.JoinSessionRequest\x1a'.mpc.coordinator.v1.JoinSessionResponse\x12m\n" + @@ -1323,7 +1629,8 @@ const file_api_proto_session_coordinator_proto_rawDesc = "" + "\x0eMarkPartyReady\x12).mpc.coordinator.v1.MarkPartyReadyRequest\x1a*.mpc.coordinator.v1.MarkPartyReadyResponse\x12a\n" + "\fStartSession\x12'.mpc.coordinator.v1.StartSessionRequest\x1a(.mpc.coordinator.v1.StartSessionResponse\x12m\n" + "\x10ReportCompletion\x12+.mpc.coordinator.v1.ReportCompletionRequest\x1a,.mpc.coordinator.v1.ReportCompletionResponse\x12a\n" + - "\fCloseSession\x12'.mpc.coordinator.v1.CloseSessionRequest\x1a(.mpc.coordinator.v1.CloseSessionResponseBEZCgithub.com/rwadurian/mpc-system/api/grpc/coordinator/v1;coordinatorb\x06proto3" + "\fCloseSession\x12'.mpc.coordinator.v1.CloseSessionRequest\x1a(.mpc.coordinator.v1.CloseSessionResponse\x12v\n" + + "\x13SubmitDelegateShare\x12..mpc.coordinator.v1.SubmitDelegateShareRequest\x1a/.mpc.coordinator.v1.SubmitDelegateShareResponseBEZCgithub.com/rwadurian/mpc-system/api/grpc/coordinator/v1;coordinatorb\x06proto3" var ( file_api_proto_session_coordinator_proto_rawDescOnce sync.Once @@ -1337,57 +1644,65 @@ func file_api_proto_session_coordinator_proto_rawDescGZIP() []byte { return file_api_proto_session_coordinator_proto_rawDescData } -var file_api_proto_session_coordinator_proto_msgTypes = make([]protoimpl.MessageInfo, 20) +var file_api_proto_session_coordinator_proto_msgTypes = make([]protoimpl.MessageInfo, 24) var file_api_proto_session_coordinator_proto_goTypes = []any{ - (*CreateSessionRequest)(nil), // 0: mpc.coordinator.v1.CreateSessionRequest - (*PartyComposition)(nil), // 1: mpc.coordinator.v1.PartyComposition - (*ParticipantInfo)(nil), // 2: mpc.coordinator.v1.ParticipantInfo - (*DeviceInfo)(nil), // 3: mpc.coordinator.v1.DeviceInfo - (*CreateSessionResponse)(nil), // 4: mpc.coordinator.v1.CreateSessionResponse - (*JoinSessionRequest)(nil), // 5: mpc.coordinator.v1.JoinSessionRequest - (*JoinSessionResponse)(nil), // 6: mpc.coordinator.v1.JoinSessionResponse - (*SessionInfo)(nil), // 7: mpc.coordinator.v1.SessionInfo - (*PartyInfo)(nil), // 8: mpc.coordinator.v1.PartyInfo - (*GetSessionStatusRequest)(nil), // 9: mpc.coordinator.v1.GetSessionStatusRequest - (*GetSessionStatusResponse)(nil), // 10: mpc.coordinator.v1.GetSessionStatusResponse - (*ReportCompletionRequest)(nil), // 11: mpc.coordinator.v1.ReportCompletionRequest - (*ReportCompletionResponse)(nil), // 12: mpc.coordinator.v1.ReportCompletionResponse - (*CloseSessionRequest)(nil), // 13: mpc.coordinator.v1.CloseSessionRequest - (*CloseSessionResponse)(nil), // 14: mpc.coordinator.v1.CloseSessionResponse - (*MarkPartyReadyRequest)(nil), // 15: mpc.coordinator.v1.MarkPartyReadyRequest - (*MarkPartyReadyResponse)(nil), // 16: mpc.coordinator.v1.MarkPartyReadyResponse - (*StartSessionRequest)(nil), // 17: mpc.coordinator.v1.StartSessionRequest - (*StartSessionResponse)(nil), // 18: mpc.coordinator.v1.StartSessionResponse - nil, // 19: mpc.coordinator.v1.CreateSessionResponse.JoinTokensEntry + (*CreateSessionRequest)(nil), // 0: mpc.coordinator.v1.CreateSessionRequest + (*DelegateUserShare)(nil), // 1: mpc.coordinator.v1.DelegateUserShare + (*PartyComposition)(nil), // 2: mpc.coordinator.v1.PartyComposition + (*ParticipantInfo)(nil), // 3: mpc.coordinator.v1.ParticipantInfo + (*DeviceInfo)(nil), // 4: mpc.coordinator.v1.DeviceInfo + (*CreateSessionResponse)(nil), // 5: mpc.coordinator.v1.CreateSessionResponse + (*JoinSessionRequest)(nil), // 6: mpc.coordinator.v1.JoinSessionRequest + (*JoinSessionResponse)(nil), // 7: mpc.coordinator.v1.JoinSessionResponse + (*SessionInfo)(nil), // 8: mpc.coordinator.v1.SessionInfo + (*PartyInfo)(nil), // 9: mpc.coordinator.v1.PartyInfo + (*GetSessionStatusRequest)(nil), // 10: mpc.coordinator.v1.GetSessionStatusRequest + (*GetSessionStatusResponse)(nil), // 11: mpc.coordinator.v1.GetSessionStatusResponse + (*DelegateShareInfo)(nil), // 12: mpc.coordinator.v1.DelegateShareInfo + (*ReportCompletionRequest)(nil), // 13: mpc.coordinator.v1.ReportCompletionRequest + (*ReportCompletionResponse)(nil), // 14: mpc.coordinator.v1.ReportCompletionResponse + (*CloseSessionRequest)(nil), // 15: mpc.coordinator.v1.CloseSessionRequest + (*CloseSessionResponse)(nil), // 16: mpc.coordinator.v1.CloseSessionResponse + (*MarkPartyReadyRequest)(nil), // 17: mpc.coordinator.v1.MarkPartyReadyRequest + (*MarkPartyReadyResponse)(nil), // 18: mpc.coordinator.v1.MarkPartyReadyResponse + (*StartSessionRequest)(nil), // 19: mpc.coordinator.v1.StartSessionRequest + (*StartSessionResponse)(nil), // 20: mpc.coordinator.v1.StartSessionResponse + (*SubmitDelegateShareRequest)(nil), // 21: mpc.coordinator.v1.SubmitDelegateShareRequest + (*SubmitDelegateShareResponse)(nil), // 22: mpc.coordinator.v1.SubmitDelegateShareResponse + nil, // 23: mpc.coordinator.v1.CreateSessionResponse.JoinTokensEntry } var file_api_proto_session_coordinator_proto_depIdxs = []int32{ - 2, // 0: mpc.coordinator.v1.CreateSessionRequest.participants:type_name -> mpc.coordinator.v1.ParticipantInfo - 1, // 1: mpc.coordinator.v1.CreateSessionRequest.party_composition:type_name -> mpc.coordinator.v1.PartyComposition - 3, // 2: mpc.coordinator.v1.ParticipantInfo.device_info:type_name -> mpc.coordinator.v1.DeviceInfo - 19, // 3: mpc.coordinator.v1.CreateSessionResponse.join_tokens:type_name -> mpc.coordinator.v1.CreateSessionResponse.JoinTokensEntry - 3, // 4: mpc.coordinator.v1.JoinSessionRequest.device_info:type_name -> mpc.coordinator.v1.DeviceInfo - 7, // 5: mpc.coordinator.v1.JoinSessionResponse.session_info:type_name -> mpc.coordinator.v1.SessionInfo - 8, // 6: mpc.coordinator.v1.JoinSessionResponse.other_parties:type_name -> mpc.coordinator.v1.PartyInfo - 3, // 7: mpc.coordinator.v1.PartyInfo.device_info:type_name -> mpc.coordinator.v1.DeviceInfo - 0, // 8: mpc.coordinator.v1.SessionCoordinator.CreateSession:input_type -> mpc.coordinator.v1.CreateSessionRequest - 5, // 9: mpc.coordinator.v1.SessionCoordinator.JoinSession:input_type -> mpc.coordinator.v1.JoinSessionRequest - 9, // 10: mpc.coordinator.v1.SessionCoordinator.GetSessionStatus:input_type -> mpc.coordinator.v1.GetSessionStatusRequest - 15, // 11: mpc.coordinator.v1.SessionCoordinator.MarkPartyReady:input_type -> mpc.coordinator.v1.MarkPartyReadyRequest - 17, // 12: mpc.coordinator.v1.SessionCoordinator.StartSession:input_type -> mpc.coordinator.v1.StartSessionRequest - 11, // 13: mpc.coordinator.v1.SessionCoordinator.ReportCompletion:input_type -> mpc.coordinator.v1.ReportCompletionRequest - 13, // 14: mpc.coordinator.v1.SessionCoordinator.CloseSession:input_type -> mpc.coordinator.v1.CloseSessionRequest - 4, // 15: mpc.coordinator.v1.SessionCoordinator.CreateSession:output_type -> mpc.coordinator.v1.CreateSessionResponse - 6, // 16: mpc.coordinator.v1.SessionCoordinator.JoinSession:output_type -> mpc.coordinator.v1.JoinSessionResponse - 10, // 17: mpc.coordinator.v1.SessionCoordinator.GetSessionStatus:output_type -> mpc.coordinator.v1.GetSessionStatusResponse - 16, // 18: mpc.coordinator.v1.SessionCoordinator.MarkPartyReady:output_type -> mpc.coordinator.v1.MarkPartyReadyResponse - 18, // 19: mpc.coordinator.v1.SessionCoordinator.StartSession:output_type -> mpc.coordinator.v1.StartSessionResponse - 12, // 20: mpc.coordinator.v1.SessionCoordinator.ReportCompletion:output_type -> mpc.coordinator.v1.ReportCompletionResponse - 14, // 21: mpc.coordinator.v1.SessionCoordinator.CloseSession:output_type -> mpc.coordinator.v1.CloseSessionResponse - 15, // [15:22] is the sub-list for method output_type - 8, // [8:15] is the sub-list for method input_type - 8, // [8:8] is the sub-list for extension type_name - 8, // [8:8] is the sub-list for extension extendee - 0, // [0:8] is the sub-list for field type_name + 3, // 0: mpc.coordinator.v1.CreateSessionRequest.participants:type_name -> mpc.coordinator.v1.ParticipantInfo + 2, // 1: mpc.coordinator.v1.CreateSessionRequest.party_composition:type_name -> mpc.coordinator.v1.PartyComposition + 1, // 2: mpc.coordinator.v1.CreateSessionRequest.delegate_user_share:type_name -> mpc.coordinator.v1.DelegateUserShare + 4, // 3: mpc.coordinator.v1.ParticipantInfo.device_info:type_name -> mpc.coordinator.v1.DeviceInfo + 23, // 4: mpc.coordinator.v1.CreateSessionResponse.join_tokens:type_name -> mpc.coordinator.v1.CreateSessionResponse.JoinTokensEntry + 4, // 5: mpc.coordinator.v1.JoinSessionRequest.device_info:type_name -> mpc.coordinator.v1.DeviceInfo + 8, // 6: mpc.coordinator.v1.JoinSessionResponse.session_info:type_name -> mpc.coordinator.v1.SessionInfo + 9, // 7: mpc.coordinator.v1.JoinSessionResponse.other_parties:type_name -> mpc.coordinator.v1.PartyInfo + 4, // 8: mpc.coordinator.v1.PartyInfo.device_info:type_name -> mpc.coordinator.v1.DeviceInfo + 12, // 9: mpc.coordinator.v1.GetSessionStatusResponse.delegate_share:type_name -> mpc.coordinator.v1.DelegateShareInfo + 0, // 10: mpc.coordinator.v1.SessionCoordinator.CreateSession:input_type -> mpc.coordinator.v1.CreateSessionRequest + 6, // 11: mpc.coordinator.v1.SessionCoordinator.JoinSession:input_type -> mpc.coordinator.v1.JoinSessionRequest + 10, // 12: mpc.coordinator.v1.SessionCoordinator.GetSessionStatus:input_type -> mpc.coordinator.v1.GetSessionStatusRequest + 17, // 13: mpc.coordinator.v1.SessionCoordinator.MarkPartyReady:input_type -> mpc.coordinator.v1.MarkPartyReadyRequest + 19, // 14: mpc.coordinator.v1.SessionCoordinator.StartSession:input_type -> mpc.coordinator.v1.StartSessionRequest + 13, // 15: mpc.coordinator.v1.SessionCoordinator.ReportCompletion:input_type -> mpc.coordinator.v1.ReportCompletionRequest + 15, // 16: mpc.coordinator.v1.SessionCoordinator.CloseSession:input_type -> mpc.coordinator.v1.CloseSessionRequest + 21, // 17: mpc.coordinator.v1.SessionCoordinator.SubmitDelegateShare:input_type -> mpc.coordinator.v1.SubmitDelegateShareRequest + 5, // 18: mpc.coordinator.v1.SessionCoordinator.CreateSession:output_type -> mpc.coordinator.v1.CreateSessionResponse + 7, // 19: mpc.coordinator.v1.SessionCoordinator.JoinSession:output_type -> mpc.coordinator.v1.JoinSessionResponse + 11, // 20: mpc.coordinator.v1.SessionCoordinator.GetSessionStatus:output_type -> mpc.coordinator.v1.GetSessionStatusResponse + 18, // 21: mpc.coordinator.v1.SessionCoordinator.MarkPartyReady:output_type -> mpc.coordinator.v1.MarkPartyReadyResponse + 20, // 22: mpc.coordinator.v1.SessionCoordinator.StartSession:output_type -> mpc.coordinator.v1.StartSessionResponse + 14, // 23: mpc.coordinator.v1.SessionCoordinator.ReportCompletion:output_type -> mpc.coordinator.v1.ReportCompletionResponse + 16, // 24: mpc.coordinator.v1.SessionCoordinator.CloseSession:output_type -> mpc.coordinator.v1.CloseSessionResponse + 22, // 25: mpc.coordinator.v1.SessionCoordinator.SubmitDelegateShare:output_type -> mpc.coordinator.v1.SubmitDelegateShareResponse + 18, // [18:26] is the sub-list for method output_type + 10, // [10:18] is the sub-list for method input_type + 10, // [10:10] is the sub-list for extension type_name + 10, // [10:10] is the sub-list for extension extendee + 0, // [0:10] is the sub-list for field type_name } func init() { file_api_proto_session_coordinator_proto_init() } @@ -1401,7 +1716,7 @@ func file_api_proto_session_coordinator_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_proto_session_coordinator_proto_rawDesc), len(file_api_proto_session_coordinator_proto_rawDesc)), NumEnums: 0, - NumMessages: 20, + NumMessages: 24, NumExtensions: 0, NumServices: 1, }, diff --git a/backend/mpc-system/api/grpc/coordinator/v1/session_coordinator_grpc.pb.go b/backend/mpc-system/api/grpc/coordinator/v1/session_coordinator_grpc.pb.go index 58638ed1..ec50e1d7 100644 --- a/backend/mpc-system/api/grpc/coordinator/v1/session_coordinator_grpc.pb.go +++ b/backend/mpc-system/api/grpc/coordinator/v1/session_coordinator_grpc.pb.go @@ -19,13 +19,14 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - SessionCoordinator_CreateSession_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/CreateSession" - SessionCoordinator_JoinSession_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/JoinSession" - SessionCoordinator_GetSessionStatus_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/GetSessionStatus" - SessionCoordinator_MarkPartyReady_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/MarkPartyReady" - SessionCoordinator_StartSession_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/StartSession" - SessionCoordinator_ReportCompletion_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/ReportCompletion" - SessionCoordinator_CloseSession_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/CloseSession" + SessionCoordinator_CreateSession_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/CreateSession" + SessionCoordinator_JoinSession_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/JoinSession" + SessionCoordinator_GetSessionStatus_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/GetSessionStatus" + SessionCoordinator_MarkPartyReady_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/MarkPartyReady" + SessionCoordinator_StartSession_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/StartSession" + SessionCoordinator_ReportCompletion_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/ReportCompletion" + SessionCoordinator_CloseSession_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/CloseSession" + SessionCoordinator_SubmitDelegateShare_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/SubmitDelegateShare" ) // SessionCoordinatorClient is the client API for SessionCoordinator service. @@ -42,6 +43,8 @@ type SessionCoordinatorClient interface { StartSession(ctx context.Context, in *StartSessionRequest, opts ...grpc.CallOption) (*StartSessionResponse, error) ReportCompletion(ctx context.Context, in *ReportCompletionRequest, opts ...grpc.CallOption) (*ReportCompletionResponse, error) CloseSession(ctx context.Context, in *CloseSessionRequest, opts ...grpc.CallOption) (*CloseSessionResponse, error) + // Delegate party share submission (delegate party submits user's share after keygen) + SubmitDelegateShare(ctx context.Context, in *SubmitDelegateShareRequest, opts ...grpc.CallOption) (*SubmitDelegateShareResponse, error) } type sessionCoordinatorClient struct { @@ -122,6 +125,16 @@ func (c *sessionCoordinatorClient) CloseSession(ctx context.Context, in *CloseSe return out, nil } +func (c *sessionCoordinatorClient) SubmitDelegateShare(ctx context.Context, in *SubmitDelegateShareRequest, opts ...grpc.CallOption) (*SubmitDelegateShareResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SubmitDelegateShareResponse) + err := c.cc.Invoke(ctx, SessionCoordinator_SubmitDelegateShare_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // SessionCoordinatorServer is the server API for SessionCoordinator service. // All implementations must embed UnimplementedSessionCoordinatorServer // for forward compatibility. @@ -136,6 +149,8 @@ type SessionCoordinatorServer interface { StartSession(context.Context, *StartSessionRequest) (*StartSessionResponse, error) ReportCompletion(context.Context, *ReportCompletionRequest) (*ReportCompletionResponse, error) CloseSession(context.Context, *CloseSessionRequest) (*CloseSessionResponse, error) + // Delegate party share submission (delegate party submits user's share after keygen) + SubmitDelegateShare(context.Context, *SubmitDelegateShareRequest) (*SubmitDelegateShareResponse, error) mustEmbedUnimplementedSessionCoordinatorServer() } @@ -167,6 +182,9 @@ func (UnimplementedSessionCoordinatorServer) ReportCompletion(context.Context, * func (UnimplementedSessionCoordinatorServer) CloseSession(context.Context, *CloseSessionRequest) (*CloseSessionResponse, error) { return nil, status.Error(codes.Unimplemented, "method CloseSession not implemented") } +func (UnimplementedSessionCoordinatorServer) SubmitDelegateShare(context.Context, *SubmitDelegateShareRequest) (*SubmitDelegateShareResponse, error) { + return nil, status.Error(codes.Unimplemented, "method SubmitDelegateShare not implemented") +} func (UnimplementedSessionCoordinatorServer) mustEmbedUnimplementedSessionCoordinatorServer() {} func (UnimplementedSessionCoordinatorServer) testEmbeddedByValue() {} @@ -314,6 +332,24 @@ func _SessionCoordinator_CloseSession_Handler(srv interface{}, ctx context.Conte return interceptor(ctx, in, info, handler) } +func _SessionCoordinator_SubmitDelegateShare_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SubmitDelegateShareRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SessionCoordinatorServer).SubmitDelegateShare(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SessionCoordinator_SubmitDelegateShare_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SessionCoordinatorServer).SubmitDelegateShare(ctx, req.(*SubmitDelegateShareRequest)) + } + return interceptor(ctx, in, info, handler) +} + // SessionCoordinator_ServiceDesc is the grpc.ServiceDesc for SessionCoordinator service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -349,6 +385,10 @@ var SessionCoordinator_ServiceDesc = grpc.ServiceDesc{ MethodName: "CloseSession", Handler: _SessionCoordinator_CloseSession_Handler, }, + { + MethodName: "SubmitDelegateShare", + Handler: _SessionCoordinator_SubmitDelegateShare_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "api/proto/session_coordinator.proto", diff --git a/backend/mpc-system/api/grpc/router/v1/message_router.pb.go b/backend/mpc-system/api/grpc/router/v1/message_router.pb.go index 6fbc1d3f..251b7a78 100644 --- a/backend/mpc-system/api/grpc/router/v1/message_router.pb.go +++ b/backend/mpc-system/api/grpc/router/v1/message_router.pb.go @@ -678,8 +678,10 @@ type SessionEvent struct { MessageHash []byte `protobuf:"bytes,8,opt,name=message_hash,json=messageHash,proto3" json:"message_hash,omitempty"` // For sign sessions CreatedAt int64 `protobuf:"varint,9,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` // Unix timestamp milliseconds ExpiresAt int64 `protobuf:"varint,10,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"` // Unix timestamp milliseconds - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // For sign sessions with delegate party: user's share for delegate to use + DelegateUserShare *DelegateUserShare `protobuf:"bytes,11,opt,name=delegate_user_share,json=delegateUserShare,proto3" json:"delegate_user_share,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SessionEvent) Reset() { @@ -782,6 +784,74 @@ func (x *SessionEvent) GetExpiresAt() int64 { return 0 } +func (x *SessionEvent) GetDelegateUserShare() *DelegateUserShare { + if x != nil { + return x.DelegateUserShare + } + return nil +} + +// DelegateUserShare contains user's share for delegate party to use in signing +type DelegateUserShare struct { + state protoimpl.MessageState `protogen:"open.v1"` + DelegatePartyId string `protobuf:"bytes,1,opt,name=delegate_party_id,json=delegatePartyId,proto3" json:"delegate_party_id,omitempty"` // The delegate party that will use this share + EncryptedShare []byte `protobuf:"bytes,2,opt,name=encrypted_share,json=encryptedShare,proto3" json:"encrypted_share,omitempty"` // User's encrypted share + PartyIndex int32 `protobuf:"varint,3,opt,name=party_index,json=partyIndex,proto3" json:"party_index,omitempty"` // Party index for this share + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DelegateUserShare) Reset() { + *x = DelegateUserShare{} + mi := &file_api_proto_message_router_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DelegateUserShare) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DelegateUserShare) ProtoMessage() {} + +func (x *DelegateUserShare) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_message_router_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DelegateUserShare.ProtoReflect.Descriptor instead. +func (*DelegateUserShare) Descriptor() ([]byte, []int) { + return file_api_proto_message_router_proto_rawDescGZIP(), []int{11} +} + +func (x *DelegateUserShare) GetDelegatePartyId() string { + if x != nil { + return x.DelegatePartyId + } + return "" +} + +func (x *DelegateUserShare) GetEncryptedShare() []byte { + if x != nil { + return x.EncryptedShare + } + return nil +} + +func (x *DelegateUserShare) GetPartyIndex() int32 { + if x != nil { + return x.PartyIndex + } + return 0 +} + // PublishSessionEventRequest publishes a session event type PublishSessionEventRequest struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -792,7 +862,7 @@ type PublishSessionEventRequest struct { func (x *PublishSessionEventRequest) Reset() { *x = PublishSessionEventRequest{} - mi := &file_api_proto_message_router_proto_msgTypes[11] + mi := &file_api_proto_message_router_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -804,7 +874,7 @@ func (x *PublishSessionEventRequest) String() string { func (*PublishSessionEventRequest) ProtoMessage() {} func (x *PublishSessionEventRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_message_router_proto_msgTypes[11] + mi := &file_api_proto_message_router_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -817,7 +887,7 @@ func (x *PublishSessionEventRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PublishSessionEventRequest.ProtoReflect.Descriptor instead. func (*PublishSessionEventRequest) Descriptor() ([]byte, []int) { - return file_api_proto_message_router_proto_rawDescGZIP(), []int{11} + return file_api_proto_message_router_proto_rawDescGZIP(), []int{12} } func (x *PublishSessionEventRequest) GetEvent() *SessionEvent { @@ -838,7 +908,7 @@ type PublishSessionEventResponse struct { func (x *PublishSessionEventResponse) Reset() { *x = PublishSessionEventResponse{} - mi := &file_api_proto_message_router_proto_msgTypes[12] + mi := &file_api_proto_message_router_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -850,7 +920,7 @@ func (x *PublishSessionEventResponse) String() string { func (*PublishSessionEventResponse) ProtoMessage() {} func (x *PublishSessionEventResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_message_router_proto_msgTypes[12] + mi := &file_api_proto_message_router_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -863,7 +933,7 @@ func (x *PublishSessionEventResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PublishSessionEventResponse.ProtoReflect.Descriptor instead. func (*PublishSessionEventResponse) Descriptor() ([]byte, []int) { - return file_api_proto_message_router_proto_rawDescGZIP(), []int{12} + return file_api_proto_message_router_proto_rawDescGZIP(), []int{13} } func (x *PublishSessionEventResponse) GetSuccess() bool { @@ -891,7 +961,7 @@ type GetRegisteredPartiesRequest struct { func (x *GetRegisteredPartiesRequest) Reset() { *x = GetRegisteredPartiesRequest{} - mi := &file_api_proto_message_router_proto_msgTypes[13] + mi := &file_api_proto_message_router_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -903,7 +973,7 @@ func (x *GetRegisteredPartiesRequest) String() string { func (*GetRegisteredPartiesRequest) ProtoMessage() {} func (x *GetRegisteredPartiesRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_message_router_proto_msgTypes[13] + mi := &file_api_proto_message_router_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -916,7 +986,7 @@ func (x *GetRegisteredPartiesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRegisteredPartiesRequest.ProtoReflect.Descriptor instead. func (*GetRegisteredPartiesRequest) Descriptor() ([]byte, []int) { - return file_api_proto_message_router_proto_rawDescGZIP(), []int{13} + return file_api_proto_message_router_proto_rawDescGZIP(), []int{14} } func (x *GetRegisteredPartiesRequest) GetRoleFilter() string { @@ -948,7 +1018,7 @@ type RegisteredParty struct { func (x *RegisteredParty) Reset() { *x = RegisteredParty{} - mi := &file_api_proto_message_router_proto_msgTypes[14] + mi := &file_api_proto_message_router_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -960,7 +1030,7 @@ func (x *RegisteredParty) String() string { func (*RegisteredParty) ProtoMessage() {} func (x *RegisteredParty) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_message_router_proto_msgTypes[14] + mi := &file_api_proto_message_router_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -973,7 +1043,7 @@ func (x *RegisteredParty) ProtoReflect() protoreflect.Message { // Deprecated: Use RegisteredParty.ProtoReflect.Descriptor instead. func (*RegisteredParty) Descriptor() ([]byte, []int) { - return file_api_proto_message_router_proto_rawDescGZIP(), []int{14} + return file_api_proto_message_router_proto_rawDescGZIP(), []int{15} } func (x *RegisteredParty) GetPartyId() string { @@ -1029,7 +1099,7 @@ type GetRegisteredPartiesResponse struct { func (x *GetRegisteredPartiesResponse) Reset() { *x = GetRegisteredPartiesResponse{} - mi := &file_api_proto_message_router_proto_msgTypes[15] + mi := &file_api_proto_message_router_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1041,7 +1111,7 @@ func (x *GetRegisteredPartiesResponse) String() string { func (*GetRegisteredPartiesResponse) ProtoMessage() {} func (x *GetRegisteredPartiesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_message_router_proto_msgTypes[15] + mi := &file_api_proto_message_router_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1054,7 +1124,7 @@ func (x *GetRegisteredPartiesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRegisteredPartiesResponse.ProtoReflect.Descriptor instead. func (*GetRegisteredPartiesResponse) Descriptor() ([]byte, []int) { - return file_api_proto_message_router_proto_rawDescGZIP(), []int{15} + return file_api_proto_message_router_proto_rawDescGZIP(), []int{16} } func (x *GetRegisteredPartiesResponse) GetParties() []*RegisteredParty { @@ -1085,7 +1155,7 @@ type AcknowledgeMessageRequest struct { func (x *AcknowledgeMessageRequest) Reset() { *x = AcknowledgeMessageRequest{} - mi := &file_api_proto_message_router_proto_msgTypes[16] + mi := &file_api_proto_message_router_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1097,7 +1167,7 @@ func (x *AcknowledgeMessageRequest) String() string { func (*AcknowledgeMessageRequest) ProtoMessage() {} func (x *AcknowledgeMessageRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_message_router_proto_msgTypes[16] + mi := &file_api_proto_message_router_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1110,7 +1180,7 @@ func (x *AcknowledgeMessageRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AcknowledgeMessageRequest.ProtoReflect.Descriptor instead. func (*AcknowledgeMessageRequest) Descriptor() ([]byte, []int) { - return file_api_proto_message_router_proto_rawDescGZIP(), []int{16} + return file_api_proto_message_router_proto_rawDescGZIP(), []int{17} } func (x *AcknowledgeMessageRequest) GetMessageId() string { @@ -1159,7 +1229,7 @@ type AcknowledgeMessageResponse struct { func (x *AcknowledgeMessageResponse) Reset() { *x = AcknowledgeMessageResponse{} - mi := &file_api_proto_message_router_proto_msgTypes[17] + mi := &file_api_proto_message_router_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1171,7 +1241,7 @@ func (x *AcknowledgeMessageResponse) String() string { func (*AcknowledgeMessageResponse) ProtoMessage() {} func (x *AcknowledgeMessageResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_message_router_proto_msgTypes[17] + mi := &file_api_proto_message_router_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1184,7 +1254,7 @@ func (x *AcknowledgeMessageResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AcknowledgeMessageResponse.ProtoReflect.Descriptor instead. func (*AcknowledgeMessageResponse) Descriptor() ([]byte, []int) { - return file_api_proto_message_router_proto_rawDescGZIP(), []int{17} + return file_api_proto_message_router_proto_rawDescGZIP(), []int{18} } func (x *AcknowledgeMessageResponse) GetSuccess() bool { @@ -1212,7 +1282,7 @@ type GetMessageStatusRequest struct { func (x *GetMessageStatusRequest) Reset() { *x = GetMessageStatusRequest{} - mi := &file_api_proto_message_router_proto_msgTypes[18] + mi := &file_api_proto_message_router_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1224,7 +1294,7 @@ func (x *GetMessageStatusRequest) String() string { func (*GetMessageStatusRequest) ProtoMessage() {} func (x *GetMessageStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_message_router_proto_msgTypes[18] + mi := &file_api_proto_message_router_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1237,7 +1307,7 @@ func (x *GetMessageStatusRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMessageStatusRequest.ProtoReflect.Descriptor instead. func (*GetMessageStatusRequest) Descriptor() ([]byte, []int) { - return file_api_proto_message_router_proto_rawDescGZIP(), []int{18} + return file_api_proto_message_router_proto_rawDescGZIP(), []int{19} } func (x *GetMessageStatusRequest) GetMessageId() string { @@ -1268,7 +1338,7 @@ type MessageDeliveryStatus struct { func (x *MessageDeliveryStatus) Reset() { *x = MessageDeliveryStatus{} - mi := &file_api_proto_message_router_proto_msgTypes[19] + mi := &file_api_proto_message_router_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1280,7 +1350,7 @@ func (x *MessageDeliveryStatus) String() string { func (*MessageDeliveryStatus) ProtoMessage() {} func (x *MessageDeliveryStatus) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_message_router_proto_msgTypes[19] + mi := &file_api_proto_message_router_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1293,7 +1363,7 @@ func (x *MessageDeliveryStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use MessageDeliveryStatus.ProtoReflect.Descriptor instead. func (*MessageDeliveryStatus) Descriptor() ([]byte, []int) { - return file_api_proto_message_router_proto_rawDescGZIP(), []int{19} + return file_api_proto_message_router_proto_rawDescGZIP(), []int{20} } func (x *MessageDeliveryStatus) GetPartyId() string { @@ -1344,7 +1414,7 @@ type GetMessageStatusResponse struct { func (x *GetMessageStatusResponse) Reset() { *x = GetMessageStatusResponse{} - mi := &file_api_proto_message_router_proto_msgTypes[20] + mi := &file_api_proto_message_router_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1356,7 +1426,7 @@ func (x *GetMessageStatusResponse) String() string { func (*GetMessageStatusResponse) ProtoMessage() {} func (x *GetMessageStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_message_router_proto_msgTypes[20] + mi := &file_api_proto_message_router_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1369,7 +1439,7 @@ func (x *GetMessageStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMessageStatusResponse.ProtoReflect.Descriptor instead. func (*GetMessageStatusResponse) Descriptor() ([]byte, []int) { - return file_api_proto_message_router_proto_rawDescGZIP(), []int{20} + return file_api_proto_message_router_proto_rawDescGZIP(), []int{21} } func (x *GetMessageStatusResponse) GetMessageId() string { @@ -1411,7 +1481,7 @@ type HeartbeatRequest struct { func (x *HeartbeatRequest) Reset() { *x = HeartbeatRequest{} - mi := &file_api_proto_message_router_proto_msgTypes[21] + mi := &file_api_proto_message_router_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1423,7 +1493,7 @@ func (x *HeartbeatRequest) String() string { func (*HeartbeatRequest) ProtoMessage() {} func (x *HeartbeatRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_message_router_proto_msgTypes[21] + mi := &file_api_proto_message_router_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1436,7 +1506,7 @@ func (x *HeartbeatRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use HeartbeatRequest.ProtoReflect.Descriptor instead. func (*HeartbeatRequest) Descriptor() ([]byte, []int) { - return file_api_proto_message_router_proto_rawDescGZIP(), []int{21} + return file_api_proto_message_router_proto_rawDescGZIP(), []int{22} } func (x *HeartbeatRequest) GetPartyId() string { @@ -1465,7 +1535,7 @@ type HeartbeatResponse struct { func (x *HeartbeatResponse) Reset() { *x = HeartbeatResponse{} - mi := &file_api_proto_message_router_proto_msgTypes[22] + mi := &file_api_proto_message_router_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1477,7 +1547,7 @@ func (x *HeartbeatResponse) String() string { func (*HeartbeatResponse) ProtoMessage() {} func (x *HeartbeatResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_message_router_proto_msgTypes[22] + mi := &file_api_proto_message_router_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1490,7 +1560,7 @@ func (x *HeartbeatResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use HeartbeatResponse.ProtoReflect.Descriptor instead. func (*HeartbeatResponse) Descriptor() ([]byte, []int) { - return file_api_proto_message_router_proto_rawDescGZIP(), []int{22} + return file_api_proto_message_router_proto_rawDescGZIP(), []int{23} } func (x *HeartbeatResponse) GetSuccess() bool { @@ -1514,6 +1584,831 @@ func (x *HeartbeatResponse) GetPendingMessages() int32 { return 0 } +// DeviceInfo contains device information +type DeviceInfo struct { + state protoimpl.MessageState `protogen:"open.v1"` + DeviceType string `protobuf:"bytes,1,opt,name=device_type,json=deviceType,proto3" json:"device_type,omitempty"` // server, mobile, web + DeviceId string `protobuf:"bytes,2,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` + Platform string `protobuf:"bytes,3,opt,name=platform,proto3" json:"platform,omitempty"` + AppVersion string `protobuf:"bytes,4,opt,name=app_version,json=appVersion,proto3" json:"app_version,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeviceInfo) Reset() { + *x = DeviceInfo{} + mi := &file_api_proto_message_router_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeviceInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeviceInfo) ProtoMessage() {} + +func (x *DeviceInfo) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_message_router_proto_msgTypes[24] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeviceInfo.ProtoReflect.Descriptor instead. +func (*DeviceInfo) Descriptor() ([]byte, []int) { + return file_api_proto_message_router_proto_rawDescGZIP(), []int{24} +} + +func (x *DeviceInfo) GetDeviceType() string { + if x != nil { + return x.DeviceType + } + return "" +} + +func (x *DeviceInfo) GetDeviceId() string { + if x != nil { + return x.DeviceId + } + return "" +} + +func (x *DeviceInfo) GetPlatform() string { + if x != nil { + return x.Platform + } + return "" +} + +func (x *DeviceInfo) GetAppVersion() string { + if x != nil { + return x.AppVersion + } + return "" +} + +// PartyInfo contains party information +type PartyInfo struct { + state protoimpl.MessageState `protogen:"open.v1"` + PartyId string `protobuf:"bytes,1,opt,name=party_id,json=partyId,proto3" json:"party_id,omitempty"` + PartyIndex int32 `protobuf:"varint,2,opt,name=party_index,json=partyIndex,proto3" json:"party_index,omitempty"` + DeviceInfo *DeviceInfo `protobuf:"bytes,3,opt,name=device_info,json=deviceInfo,proto3" json:"device_info,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PartyInfo) Reset() { + *x = PartyInfo{} + mi := &file_api_proto_message_router_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PartyInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PartyInfo) ProtoMessage() {} + +func (x *PartyInfo) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_message_router_proto_msgTypes[25] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PartyInfo.ProtoReflect.Descriptor instead. +func (*PartyInfo) Descriptor() ([]byte, []int) { + return file_api_proto_message_router_proto_rawDescGZIP(), []int{25} +} + +func (x *PartyInfo) GetPartyId() string { + if x != nil { + return x.PartyId + } + return "" +} + +func (x *PartyInfo) GetPartyIndex() int32 { + if x != nil { + return x.PartyIndex + } + return 0 +} + +func (x *PartyInfo) GetDeviceInfo() *DeviceInfo { + if x != nil { + return x.DeviceInfo + } + return nil +} + +// SessionInfo contains session information +type SessionInfo struct { + state protoimpl.MessageState `protogen:"open.v1"` + SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` + SessionType string `protobuf:"bytes,2,opt,name=session_type,json=sessionType,proto3" json:"session_type,omitempty"` // keygen, sign + ThresholdN int32 `protobuf:"varint,3,opt,name=threshold_n,json=thresholdN,proto3" json:"threshold_n,omitempty"` + ThresholdT int32 `protobuf:"varint,4,opt,name=threshold_t,json=thresholdT,proto3" json:"threshold_t,omitempty"` + MessageHash []byte `protobuf:"bytes,5,opt,name=message_hash,json=messageHash,proto3" json:"message_hash,omitempty"` // For sign sessions + Status string `protobuf:"bytes,6,opt,name=status,proto3" json:"status,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SessionInfo) Reset() { + *x = SessionInfo{} + mi := &file_api_proto_message_router_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SessionInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SessionInfo) ProtoMessage() {} + +func (x *SessionInfo) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_message_router_proto_msgTypes[26] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SessionInfo.ProtoReflect.Descriptor instead. +func (*SessionInfo) Descriptor() ([]byte, []int) { + return file_api_proto_message_router_proto_rawDescGZIP(), []int{26} +} + +func (x *SessionInfo) GetSessionId() string { + if x != nil { + return x.SessionId + } + return "" +} + +func (x *SessionInfo) GetSessionType() string { + if x != nil { + return x.SessionType + } + return "" +} + +func (x *SessionInfo) GetThresholdN() int32 { + if x != nil { + return x.ThresholdN + } + return 0 +} + +func (x *SessionInfo) GetThresholdT() int32 { + if x != nil { + return x.ThresholdT + } + return 0 +} + +func (x *SessionInfo) GetMessageHash() []byte { + if x != nil { + return x.MessageHash + } + return nil +} + +func (x *SessionInfo) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +// JoinSessionRequest joins a session +type JoinSessionRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` + PartyId string `protobuf:"bytes,2,opt,name=party_id,json=partyId,proto3" json:"party_id,omitempty"` + JoinToken string `protobuf:"bytes,3,opt,name=join_token,json=joinToken,proto3" json:"join_token,omitempty"` + DeviceInfo *DeviceInfo `protobuf:"bytes,4,opt,name=device_info,json=deviceInfo,proto3" json:"device_info,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *JoinSessionRequest) Reset() { + *x = JoinSessionRequest{} + mi := &file_api_proto_message_router_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JoinSessionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinSessionRequest) ProtoMessage() {} + +func (x *JoinSessionRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_message_router_proto_msgTypes[27] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinSessionRequest.ProtoReflect.Descriptor instead. +func (*JoinSessionRequest) Descriptor() ([]byte, []int) { + return file_api_proto_message_router_proto_rawDescGZIP(), []int{27} +} + +func (x *JoinSessionRequest) GetSessionId() string { + if x != nil { + return x.SessionId + } + return "" +} + +func (x *JoinSessionRequest) GetPartyId() string { + if x != nil { + return x.PartyId + } + return "" +} + +func (x *JoinSessionRequest) GetJoinToken() string { + if x != nil { + return x.JoinToken + } + return "" +} + +func (x *JoinSessionRequest) GetDeviceInfo() *DeviceInfo { + if x != nil { + return x.DeviceInfo + } + return nil +} + +// JoinSessionResponse returns session info after joining +type JoinSessionResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + SessionInfo *SessionInfo `protobuf:"bytes,2,opt,name=session_info,json=sessionInfo,proto3" json:"session_info,omitempty"` + OtherParties []*PartyInfo `protobuf:"bytes,3,rep,name=other_parties,json=otherParties,proto3" json:"other_parties,omitempty"` // All participants in the session + PartyIndex int32 `protobuf:"varint,4,opt,name=party_index,json=partyIndex,proto3" json:"party_index,omitempty"` // This party's index + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *JoinSessionResponse) Reset() { + *x = JoinSessionResponse{} + mi := &file_api_proto_message_router_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *JoinSessionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinSessionResponse) ProtoMessage() {} + +func (x *JoinSessionResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_message_router_proto_msgTypes[28] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinSessionResponse.ProtoReflect.Descriptor instead. +func (*JoinSessionResponse) Descriptor() ([]byte, []int) { + return file_api_proto_message_router_proto_rawDescGZIP(), []int{28} +} + +func (x *JoinSessionResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *JoinSessionResponse) GetSessionInfo() *SessionInfo { + if x != nil { + return x.SessionInfo + } + return nil +} + +func (x *JoinSessionResponse) GetOtherParties() []*PartyInfo { + if x != nil { + return x.OtherParties + } + return nil +} + +func (x *JoinSessionResponse) GetPartyIndex() int32 { + if x != nil { + return x.PartyIndex + } + return 0 +} + +// MarkPartyReadyRequest marks a party as ready +type MarkPartyReadyRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` + PartyId string `protobuf:"bytes,2,opt,name=party_id,json=partyId,proto3" json:"party_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MarkPartyReadyRequest) Reset() { + *x = MarkPartyReadyRequest{} + mi := &file_api_proto_message_router_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MarkPartyReadyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MarkPartyReadyRequest) ProtoMessage() {} + +func (x *MarkPartyReadyRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_message_router_proto_msgTypes[29] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MarkPartyReadyRequest.ProtoReflect.Descriptor instead. +func (*MarkPartyReadyRequest) Descriptor() ([]byte, []int) { + return file_api_proto_message_router_proto_rawDescGZIP(), []int{29} +} + +func (x *MarkPartyReadyRequest) GetSessionId() string { + if x != nil { + return x.SessionId + } + return "" +} + +func (x *MarkPartyReadyRequest) GetPartyId() string { + if x != nil { + return x.PartyId + } + return "" +} + +// MarkPartyReadyResponse confirms ready status +type MarkPartyReadyResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + AllReady bool `protobuf:"varint,2,opt,name=all_ready,json=allReady,proto3" json:"all_ready,omitempty"` // True if all parties are ready + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MarkPartyReadyResponse) Reset() { + *x = MarkPartyReadyResponse{} + mi := &file_api_proto_message_router_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MarkPartyReadyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MarkPartyReadyResponse) ProtoMessage() {} + +func (x *MarkPartyReadyResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_message_router_proto_msgTypes[30] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MarkPartyReadyResponse.ProtoReflect.Descriptor instead. +func (*MarkPartyReadyResponse) Descriptor() ([]byte, []int) { + return file_api_proto_message_router_proto_rawDescGZIP(), []int{30} +} + +func (x *MarkPartyReadyResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *MarkPartyReadyResponse) GetAllReady() bool { + if x != nil { + return x.AllReady + } + return false +} + +// ReportCompletionRequest reports protocol completion +type ReportCompletionRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` + PartyId string `protobuf:"bytes,2,opt,name=party_id,json=partyId,proto3" json:"party_id,omitempty"` + PublicKey []byte `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` // For keygen: resulting public key + Signature []byte `protobuf:"bytes,4,opt,name=signature,proto3" json:"signature,omitempty"` // For signing: resulting signature + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReportCompletionRequest) Reset() { + *x = ReportCompletionRequest{} + mi := &file_api_proto_message_router_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReportCompletionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReportCompletionRequest) ProtoMessage() {} + +func (x *ReportCompletionRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_message_router_proto_msgTypes[31] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReportCompletionRequest.ProtoReflect.Descriptor instead. +func (*ReportCompletionRequest) Descriptor() ([]byte, []int) { + return file_api_proto_message_router_proto_rawDescGZIP(), []int{31} +} + +func (x *ReportCompletionRequest) GetSessionId() string { + if x != nil { + return x.SessionId + } + return "" +} + +func (x *ReportCompletionRequest) GetPartyId() string { + if x != nil { + return x.PartyId + } + return "" +} + +func (x *ReportCompletionRequest) GetPublicKey() []byte { + if x != nil { + return x.PublicKey + } + return nil +} + +func (x *ReportCompletionRequest) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + +// ReportCompletionResponse confirms completion report +type ReportCompletionResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + AllCompleted bool `protobuf:"varint,2,opt,name=all_completed,json=allCompleted,proto3" json:"all_completed,omitempty"` // True if all parties completed + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReportCompletionResponse) Reset() { + *x = ReportCompletionResponse{} + mi := &file_api_proto_message_router_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReportCompletionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReportCompletionResponse) ProtoMessage() {} + +func (x *ReportCompletionResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_message_router_proto_msgTypes[32] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReportCompletionResponse.ProtoReflect.Descriptor instead. +func (*ReportCompletionResponse) Descriptor() ([]byte, []int) { + return file_api_proto_message_router_proto_rawDescGZIP(), []int{32} +} + +func (x *ReportCompletionResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *ReportCompletionResponse) GetAllCompleted() bool { + if x != nil { + return x.AllCompleted + } + return false +} + +// GetSessionStatusRequest requests session status +type GetSessionStatusRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetSessionStatusRequest) Reset() { + *x = GetSessionStatusRequest{} + mi := &file_api_proto_message_router_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetSessionStatusRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSessionStatusRequest) ProtoMessage() {} + +func (x *GetSessionStatusRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_message_router_proto_msgTypes[33] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSessionStatusRequest.ProtoReflect.Descriptor instead. +func (*GetSessionStatusRequest) Descriptor() ([]byte, []int) { + return file_api_proto_message_router_proto_rawDescGZIP(), []int{33} +} + +func (x *GetSessionStatusRequest) GetSessionId() string { + if x != nil { + return x.SessionId + } + return "" +} + +// GetSessionStatusResponse returns session status +type GetSessionStatusResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` // created, in_progress, completed, failed, expired + ThresholdN int32 `protobuf:"varint,3,opt,name=threshold_n,json=thresholdN,proto3" json:"threshold_n,omitempty"` + ThresholdT int32 `protobuf:"varint,4,opt,name=threshold_t,json=thresholdT,proto3" json:"threshold_t,omitempty"` + Participants []*PartyInfo `protobuf:"bytes,5,rep,name=participants,proto3" json:"participants,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetSessionStatusResponse) Reset() { + *x = GetSessionStatusResponse{} + mi := &file_api_proto_message_router_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetSessionStatusResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSessionStatusResponse) ProtoMessage() {} + +func (x *GetSessionStatusResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_message_router_proto_msgTypes[34] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSessionStatusResponse.ProtoReflect.Descriptor instead. +func (*GetSessionStatusResponse) Descriptor() ([]byte, []int) { + return file_api_proto_message_router_proto_rawDescGZIP(), []int{34} +} + +func (x *GetSessionStatusResponse) GetSessionId() string { + if x != nil { + return x.SessionId + } + return "" +} + +func (x *GetSessionStatusResponse) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *GetSessionStatusResponse) GetThresholdN() int32 { + if x != nil { + return x.ThresholdN + } + return 0 +} + +func (x *GetSessionStatusResponse) GetThresholdT() int32 { + if x != nil { + return x.ThresholdT + } + return 0 +} + +func (x *GetSessionStatusResponse) GetParticipants() []*PartyInfo { + if x != nil { + return x.Participants + } + return nil +} + +// SubmitDelegateShareRequest submits user's share from delegate party +type SubmitDelegateShareRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` + PartyId string `protobuf:"bytes,2,opt,name=party_id,json=partyId,proto3" json:"party_id,omitempty"` + EncryptedShare []byte `protobuf:"bytes,3,opt,name=encrypted_share,json=encryptedShare,proto3" json:"encrypted_share,omitempty"` // Encrypted share for user + PublicKey []byte `protobuf:"bytes,4,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` // Public key from keygen + PartyIndex int32 `protobuf:"varint,5,opt,name=party_index,json=partyIndex,proto3" json:"party_index,omitempty"` // Party's index in the session + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubmitDelegateShareRequest) Reset() { + *x = SubmitDelegateShareRequest{} + mi := &file_api_proto_message_router_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubmitDelegateShareRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitDelegateShareRequest) ProtoMessage() {} + +func (x *SubmitDelegateShareRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_message_router_proto_msgTypes[35] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubmitDelegateShareRequest.ProtoReflect.Descriptor instead. +func (*SubmitDelegateShareRequest) Descriptor() ([]byte, []int) { + return file_api_proto_message_router_proto_rawDescGZIP(), []int{35} +} + +func (x *SubmitDelegateShareRequest) GetSessionId() string { + if x != nil { + return x.SessionId + } + return "" +} + +func (x *SubmitDelegateShareRequest) GetPartyId() string { + if x != nil { + return x.PartyId + } + return "" +} + +func (x *SubmitDelegateShareRequest) GetEncryptedShare() []byte { + if x != nil { + return x.EncryptedShare + } + return nil +} + +func (x *SubmitDelegateShareRequest) GetPublicKey() []byte { + if x != nil { + return x.PublicKey + } + return nil +} + +func (x *SubmitDelegateShareRequest) GetPartyIndex() int32 { + if x != nil { + return x.PartyIndex + } + return 0 +} + +// SubmitDelegateShareResponse contains result of share submission +type SubmitDelegateShareResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubmitDelegateShareResponse) Reset() { + *x = SubmitDelegateShareResponse{} + mi := &file_api_proto_message_router_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubmitDelegateShareResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitDelegateShareResponse) ProtoMessage() {} + +func (x *SubmitDelegateShareResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_message_router_proto_msgTypes[36] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubmitDelegateShareResponse.ProtoReflect.Descriptor instead. +func (*SubmitDelegateShareResponse) Descriptor() ([]byte, []int) { + return file_api_proto_message_router_proto_rawDescGZIP(), []int{36} +} + +func (x *SubmitDelegateShareResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + var File_api_proto_message_router_proto protoreflect.FileDescriptor const file_api_proto_message_router_proto_rawDesc = "" + @@ -1576,7 +2471,7 @@ const file_api_proto_message_router_proto_rawDesc = "" + "\x1dSubscribeSessionEventsRequest\x12\x19\n" + "\bparty_id\x18\x01 \x01(\tR\apartyId\x12\x1f\n" + "\vevent_types\x18\x02 \x03(\tR\n" + - "eventTypes\"\xc2\x03\n" + + "eventTypes\"\x94\x04\n" + "\fSessionEvent\x12\x19\n" + "\bevent_id\x18\x01 \x01(\tR\aeventId\x12\x1d\n" + "\n" + @@ -1595,10 +2490,16 @@ const file_api_proto_message_router_proto_rawDesc = "" + "created_at\x18\t \x01(\x03R\tcreatedAt\x12\x1d\n" + "\n" + "expires_at\x18\n" + - " \x01(\x03R\texpiresAt\x1a=\n" + + " \x01(\x03R\texpiresAt\x12P\n" + + "\x13delegate_user_share\x18\v \x01(\v2 .mpc.router.v1.DelegateUserShareR\x11delegateUserShare\x1a=\n" + "\x0fJoinTokensEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"O\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x89\x01\n" + + "\x11DelegateUserShare\x12*\n" + + "\x11delegate_party_id\x18\x01 \x01(\tR\x0fdelegatePartyId\x12'\n" + + "\x0fencrypted_share\x18\x02 \x01(\fR\x0eencryptedShare\x12\x1f\n" + + "\vparty_index\x18\x03 \x01(\x05R\n" + + "partyIndex\"O\n" + "\x1aPublishSessionEventRequest\x121\n" + "\x05event\x18\x01 \x01(\v2\x1b.mpc.router.v1.SessionEventR\x05event\"b\n" + "\x1bPublishSessionEventResponse\x12\x18\n" + @@ -1659,7 +2560,85 @@ const file_api_proto_message_router_proto_rawDesc = "" + "\x11HeartbeatResponse\x12\x18\n" + "\asuccess\x18\x01 \x01(\bR\asuccess\x12)\n" + "\x10server_timestamp\x18\x02 \x01(\x03R\x0fserverTimestamp\x12)\n" + - "\x10pending_messages\x18\x03 \x01(\x05R\x0fpendingMessages2\xf0\a\n" + + "\x10pending_messages\x18\x03 \x01(\x05R\x0fpendingMessages\"\x87\x01\n" + + "\n" + + "DeviceInfo\x12\x1f\n" + + "\vdevice_type\x18\x01 \x01(\tR\n" + + "deviceType\x12\x1b\n" + + "\tdevice_id\x18\x02 \x01(\tR\bdeviceId\x12\x1a\n" + + "\bplatform\x18\x03 \x01(\tR\bplatform\x12\x1f\n" + + "\vapp_version\x18\x04 \x01(\tR\n" + + "appVersion\"\x83\x01\n" + + "\tPartyInfo\x12\x19\n" + + "\bparty_id\x18\x01 \x01(\tR\apartyId\x12\x1f\n" + + "\vparty_index\x18\x02 \x01(\x05R\n" + + "partyIndex\x12:\n" + + "\vdevice_info\x18\x03 \x01(\v2\x19.mpc.router.v1.DeviceInfoR\n" + + "deviceInfo\"\xcc\x01\n" + + "\vSessionInfo\x12\x1d\n" + + "\n" + + "session_id\x18\x01 \x01(\tR\tsessionId\x12!\n" + + "\fsession_type\x18\x02 \x01(\tR\vsessionType\x12\x1f\n" + + "\vthreshold_n\x18\x03 \x01(\x05R\n" + + "thresholdN\x12\x1f\n" + + "\vthreshold_t\x18\x04 \x01(\x05R\n" + + "thresholdT\x12!\n" + + "\fmessage_hash\x18\x05 \x01(\fR\vmessageHash\x12\x16\n" + + "\x06status\x18\x06 \x01(\tR\x06status\"\xa9\x01\n" + + "\x12JoinSessionRequest\x12\x1d\n" + + "\n" + + "session_id\x18\x01 \x01(\tR\tsessionId\x12\x19\n" + + "\bparty_id\x18\x02 \x01(\tR\apartyId\x12\x1d\n" + + "\n" + + "join_token\x18\x03 \x01(\tR\tjoinToken\x12:\n" + + "\vdevice_info\x18\x04 \x01(\v2\x19.mpc.router.v1.DeviceInfoR\n" + + "deviceInfo\"\xce\x01\n" + + "\x13JoinSessionResponse\x12\x18\n" + + "\asuccess\x18\x01 \x01(\bR\asuccess\x12=\n" + + "\fsession_info\x18\x02 \x01(\v2\x1a.mpc.router.v1.SessionInfoR\vsessionInfo\x12=\n" + + "\rother_parties\x18\x03 \x03(\v2\x18.mpc.router.v1.PartyInfoR\fotherParties\x12\x1f\n" + + "\vparty_index\x18\x04 \x01(\x05R\n" + + "partyIndex\"Q\n" + + "\x15MarkPartyReadyRequest\x12\x1d\n" + + "\n" + + "session_id\x18\x01 \x01(\tR\tsessionId\x12\x19\n" + + "\bparty_id\x18\x02 \x01(\tR\apartyId\"O\n" + + "\x16MarkPartyReadyResponse\x12\x18\n" + + "\asuccess\x18\x01 \x01(\bR\asuccess\x12\x1b\n" + + "\tall_ready\x18\x02 \x01(\bR\ballReady\"\x90\x01\n" + + "\x17ReportCompletionRequest\x12\x1d\n" + + "\n" + + "session_id\x18\x01 \x01(\tR\tsessionId\x12\x19\n" + + "\bparty_id\x18\x02 \x01(\tR\apartyId\x12\x1d\n" + + "\n" + + "public_key\x18\x03 \x01(\fR\tpublicKey\x12\x1c\n" + + "\tsignature\x18\x04 \x01(\fR\tsignature\"Y\n" + + "\x18ReportCompletionResponse\x12\x18\n" + + "\asuccess\x18\x01 \x01(\bR\asuccess\x12#\n" + + "\rall_completed\x18\x02 \x01(\bR\fallCompleted\"8\n" + + "\x17GetSessionStatusRequest\x12\x1d\n" + + "\n" + + "session_id\x18\x01 \x01(\tR\tsessionId\"\xd1\x01\n" + + "\x18GetSessionStatusResponse\x12\x1d\n" + + "\n" + + "session_id\x18\x01 \x01(\tR\tsessionId\x12\x16\n" + + "\x06status\x18\x02 \x01(\tR\x06status\x12\x1f\n" + + "\vthreshold_n\x18\x03 \x01(\x05R\n" + + "thresholdN\x12\x1f\n" + + "\vthreshold_t\x18\x04 \x01(\x05R\n" + + "thresholdT\x12<\n" + + "\fparticipants\x18\x05 \x03(\v2\x18.mpc.router.v1.PartyInfoR\fparticipants\"\xbf\x01\n" + + "\x1aSubmitDelegateShareRequest\x12\x1d\n" + + "\n" + + "session_id\x18\x01 \x01(\tR\tsessionId\x12\x19\n" + + "\bparty_id\x18\x02 \x01(\tR\apartyId\x12'\n" + + "\x0fencrypted_share\x18\x03 \x01(\fR\x0eencryptedShare\x12\x1d\n" + + "\n" + + "public_key\x18\x04 \x01(\fR\tpublicKey\x12\x1f\n" + + "\vparty_index\x18\x05 \x01(\x05R\n" + + "partyIndex\"7\n" + + "\x1bSubmitDelegateShareResponse\x12\x18\n" + + "\asuccess\x18\x01 \x01(\bR\asuccess2\xdd\v\n" + "\rMessageRouter\x12W\n" + "\fRouteMessage\x12\".mpc.router.v1.RouteMessageRequest\x1a#.mpc.router.v1.RouteMessageResponse\x12Y\n" + "\x11SubscribeMessages\x12'.mpc.router.v1.SubscribeMessagesRequest\x1a\x19.mpc.router.v1.MPCMessage0\x01\x12i\n" + @@ -1670,7 +2649,12 @@ const file_api_proto_message_router_proto_rawDesc = "" + "\tHeartbeat\x12\x1f.mpc.router.v1.HeartbeatRequest\x1a .mpc.router.v1.HeartbeatResponse\x12e\n" + "\x16SubscribeSessionEvents\x12,.mpc.router.v1.SubscribeSessionEventsRequest\x1a\x1b.mpc.router.v1.SessionEvent0\x01\x12l\n" + "\x13PublishSessionEvent\x12).mpc.router.v1.PublishSessionEventRequest\x1a*.mpc.router.v1.PublishSessionEventResponse\x12o\n" + - "\x14GetRegisteredParties\x12*.mpc.router.v1.GetRegisteredPartiesRequest\x1a+.mpc.router.v1.GetRegisteredPartiesResponseB;Z9github.com/rwadurian/mpc-system/api/grpc/router/v1;routerb\x06proto3" + "\x14GetRegisteredParties\x12*.mpc.router.v1.GetRegisteredPartiesRequest\x1a+.mpc.router.v1.GetRegisteredPartiesResponse\x12T\n" + + "\vJoinSession\x12!.mpc.router.v1.JoinSessionRequest\x1a\".mpc.router.v1.JoinSessionResponse\x12]\n" + + "\x0eMarkPartyReady\x12$.mpc.router.v1.MarkPartyReadyRequest\x1a%.mpc.router.v1.MarkPartyReadyResponse\x12c\n" + + "\x10ReportCompletion\x12&.mpc.router.v1.ReportCompletionRequest\x1a'.mpc.router.v1.ReportCompletionResponse\x12c\n" + + "\x10GetSessionStatus\x12&.mpc.router.v1.GetSessionStatusRequest\x1a'.mpc.router.v1.GetSessionStatusResponse\x12l\n" + + "\x13SubmitDelegateShare\x12).mpc.router.v1.SubmitDelegateShareRequest\x1a*.mpc.router.v1.SubmitDelegateShareResponseB;Z9github.com/rwadurian/mpc-system/api/grpc/router/v1;routerb\x06proto3" var ( file_api_proto_message_router_proto_rawDescOnce sync.Once @@ -1684,7 +2668,7 @@ func file_api_proto_message_router_proto_rawDescGZIP() []byte { return file_api_proto_message_router_proto_rawDescData } -var file_api_proto_message_router_proto_msgTypes = make([]protoimpl.MessageInfo, 24) +var file_api_proto_message_router_proto_msgTypes = make([]protoimpl.MessageInfo, 38) var file_api_proto_message_router_proto_goTypes = []any{ (*RouteMessageRequest)(nil), // 0: mpc.router.v1.RouteMessageRequest (*RouteMessageResponse)(nil), // 1: mpc.router.v1.RouteMessageResponse @@ -1697,53 +2681,83 @@ var file_api_proto_message_router_proto_goTypes = []any{ (*RegisterPartyResponse)(nil), // 8: mpc.router.v1.RegisterPartyResponse (*SubscribeSessionEventsRequest)(nil), // 9: mpc.router.v1.SubscribeSessionEventsRequest (*SessionEvent)(nil), // 10: mpc.router.v1.SessionEvent - (*PublishSessionEventRequest)(nil), // 11: mpc.router.v1.PublishSessionEventRequest - (*PublishSessionEventResponse)(nil), // 12: mpc.router.v1.PublishSessionEventResponse - (*GetRegisteredPartiesRequest)(nil), // 13: mpc.router.v1.GetRegisteredPartiesRequest - (*RegisteredParty)(nil), // 14: mpc.router.v1.RegisteredParty - (*GetRegisteredPartiesResponse)(nil), // 15: mpc.router.v1.GetRegisteredPartiesResponse - (*AcknowledgeMessageRequest)(nil), // 16: mpc.router.v1.AcknowledgeMessageRequest - (*AcknowledgeMessageResponse)(nil), // 17: mpc.router.v1.AcknowledgeMessageResponse - (*GetMessageStatusRequest)(nil), // 18: mpc.router.v1.GetMessageStatusRequest - (*MessageDeliveryStatus)(nil), // 19: mpc.router.v1.MessageDeliveryStatus - (*GetMessageStatusResponse)(nil), // 20: mpc.router.v1.GetMessageStatusResponse - (*HeartbeatRequest)(nil), // 21: mpc.router.v1.HeartbeatRequest - (*HeartbeatResponse)(nil), // 22: mpc.router.v1.HeartbeatResponse - nil, // 23: mpc.router.v1.SessionEvent.JoinTokensEntry + (*DelegateUserShare)(nil), // 11: mpc.router.v1.DelegateUserShare + (*PublishSessionEventRequest)(nil), // 12: mpc.router.v1.PublishSessionEventRequest + (*PublishSessionEventResponse)(nil), // 13: mpc.router.v1.PublishSessionEventResponse + (*GetRegisteredPartiesRequest)(nil), // 14: mpc.router.v1.GetRegisteredPartiesRequest + (*RegisteredParty)(nil), // 15: mpc.router.v1.RegisteredParty + (*GetRegisteredPartiesResponse)(nil), // 16: mpc.router.v1.GetRegisteredPartiesResponse + (*AcknowledgeMessageRequest)(nil), // 17: mpc.router.v1.AcknowledgeMessageRequest + (*AcknowledgeMessageResponse)(nil), // 18: mpc.router.v1.AcknowledgeMessageResponse + (*GetMessageStatusRequest)(nil), // 19: mpc.router.v1.GetMessageStatusRequest + (*MessageDeliveryStatus)(nil), // 20: mpc.router.v1.MessageDeliveryStatus + (*GetMessageStatusResponse)(nil), // 21: mpc.router.v1.GetMessageStatusResponse + (*HeartbeatRequest)(nil), // 22: mpc.router.v1.HeartbeatRequest + (*HeartbeatResponse)(nil), // 23: mpc.router.v1.HeartbeatResponse + (*DeviceInfo)(nil), // 24: mpc.router.v1.DeviceInfo + (*PartyInfo)(nil), // 25: mpc.router.v1.PartyInfo + (*SessionInfo)(nil), // 26: mpc.router.v1.SessionInfo + (*JoinSessionRequest)(nil), // 27: mpc.router.v1.JoinSessionRequest + (*JoinSessionResponse)(nil), // 28: mpc.router.v1.JoinSessionResponse + (*MarkPartyReadyRequest)(nil), // 29: mpc.router.v1.MarkPartyReadyRequest + (*MarkPartyReadyResponse)(nil), // 30: mpc.router.v1.MarkPartyReadyResponse + (*ReportCompletionRequest)(nil), // 31: mpc.router.v1.ReportCompletionRequest + (*ReportCompletionResponse)(nil), // 32: mpc.router.v1.ReportCompletionResponse + (*GetSessionStatusRequest)(nil), // 33: mpc.router.v1.GetSessionStatusRequest + (*GetSessionStatusResponse)(nil), // 34: mpc.router.v1.GetSessionStatusResponse + (*SubmitDelegateShareRequest)(nil), // 35: mpc.router.v1.SubmitDelegateShareRequest + (*SubmitDelegateShareResponse)(nil), // 36: mpc.router.v1.SubmitDelegateShareResponse + nil, // 37: mpc.router.v1.SessionEvent.JoinTokensEntry } var file_api_proto_message_router_proto_depIdxs = []int32{ 3, // 0: mpc.router.v1.GetPendingMessagesResponse.messages:type_name -> mpc.router.v1.MPCMessage 6, // 1: mpc.router.v1.RegisterPartyRequest.notification:type_name -> mpc.router.v1.NotificationChannel - 23, // 2: mpc.router.v1.SessionEvent.join_tokens:type_name -> mpc.router.v1.SessionEvent.JoinTokensEntry - 10, // 3: mpc.router.v1.PublishSessionEventRequest.event:type_name -> mpc.router.v1.SessionEvent - 6, // 4: mpc.router.v1.RegisteredParty.notification:type_name -> mpc.router.v1.NotificationChannel - 14, // 5: mpc.router.v1.GetRegisteredPartiesResponse.parties:type_name -> mpc.router.v1.RegisteredParty - 19, // 6: mpc.router.v1.GetMessageStatusResponse.deliveries:type_name -> mpc.router.v1.MessageDeliveryStatus - 0, // 7: mpc.router.v1.MessageRouter.RouteMessage:input_type -> mpc.router.v1.RouteMessageRequest - 2, // 8: mpc.router.v1.MessageRouter.SubscribeMessages:input_type -> mpc.router.v1.SubscribeMessagesRequest - 4, // 9: mpc.router.v1.MessageRouter.GetPendingMessages:input_type -> mpc.router.v1.GetPendingMessagesRequest - 16, // 10: mpc.router.v1.MessageRouter.AcknowledgeMessage:input_type -> mpc.router.v1.AcknowledgeMessageRequest - 18, // 11: mpc.router.v1.MessageRouter.GetMessageStatus:input_type -> mpc.router.v1.GetMessageStatusRequest - 7, // 12: mpc.router.v1.MessageRouter.RegisterParty:input_type -> mpc.router.v1.RegisterPartyRequest - 21, // 13: mpc.router.v1.MessageRouter.Heartbeat:input_type -> mpc.router.v1.HeartbeatRequest - 9, // 14: mpc.router.v1.MessageRouter.SubscribeSessionEvents:input_type -> mpc.router.v1.SubscribeSessionEventsRequest - 11, // 15: mpc.router.v1.MessageRouter.PublishSessionEvent:input_type -> mpc.router.v1.PublishSessionEventRequest - 13, // 16: mpc.router.v1.MessageRouter.GetRegisteredParties:input_type -> mpc.router.v1.GetRegisteredPartiesRequest - 1, // 17: mpc.router.v1.MessageRouter.RouteMessage:output_type -> mpc.router.v1.RouteMessageResponse - 3, // 18: mpc.router.v1.MessageRouter.SubscribeMessages:output_type -> mpc.router.v1.MPCMessage - 5, // 19: mpc.router.v1.MessageRouter.GetPendingMessages:output_type -> mpc.router.v1.GetPendingMessagesResponse - 17, // 20: mpc.router.v1.MessageRouter.AcknowledgeMessage:output_type -> mpc.router.v1.AcknowledgeMessageResponse - 20, // 21: mpc.router.v1.MessageRouter.GetMessageStatus:output_type -> mpc.router.v1.GetMessageStatusResponse - 8, // 22: mpc.router.v1.MessageRouter.RegisterParty:output_type -> mpc.router.v1.RegisterPartyResponse - 22, // 23: mpc.router.v1.MessageRouter.Heartbeat:output_type -> mpc.router.v1.HeartbeatResponse - 10, // 24: mpc.router.v1.MessageRouter.SubscribeSessionEvents:output_type -> mpc.router.v1.SessionEvent - 12, // 25: mpc.router.v1.MessageRouter.PublishSessionEvent:output_type -> mpc.router.v1.PublishSessionEventResponse - 15, // 26: mpc.router.v1.MessageRouter.GetRegisteredParties:output_type -> mpc.router.v1.GetRegisteredPartiesResponse - 17, // [17:27] is the sub-list for method output_type - 7, // [7:17] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 37, // 2: mpc.router.v1.SessionEvent.join_tokens:type_name -> mpc.router.v1.SessionEvent.JoinTokensEntry + 11, // 3: mpc.router.v1.SessionEvent.delegate_user_share:type_name -> mpc.router.v1.DelegateUserShare + 10, // 4: mpc.router.v1.PublishSessionEventRequest.event:type_name -> mpc.router.v1.SessionEvent + 6, // 5: mpc.router.v1.RegisteredParty.notification:type_name -> mpc.router.v1.NotificationChannel + 15, // 6: mpc.router.v1.GetRegisteredPartiesResponse.parties:type_name -> mpc.router.v1.RegisteredParty + 20, // 7: mpc.router.v1.GetMessageStatusResponse.deliveries:type_name -> mpc.router.v1.MessageDeliveryStatus + 24, // 8: mpc.router.v1.PartyInfo.device_info:type_name -> mpc.router.v1.DeviceInfo + 24, // 9: mpc.router.v1.JoinSessionRequest.device_info:type_name -> mpc.router.v1.DeviceInfo + 26, // 10: mpc.router.v1.JoinSessionResponse.session_info:type_name -> mpc.router.v1.SessionInfo + 25, // 11: mpc.router.v1.JoinSessionResponse.other_parties:type_name -> mpc.router.v1.PartyInfo + 25, // 12: mpc.router.v1.GetSessionStatusResponse.participants:type_name -> mpc.router.v1.PartyInfo + 0, // 13: mpc.router.v1.MessageRouter.RouteMessage:input_type -> mpc.router.v1.RouteMessageRequest + 2, // 14: mpc.router.v1.MessageRouter.SubscribeMessages:input_type -> mpc.router.v1.SubscribeMessagesRequest + 4, // 15: mpc.router.v1.MessageRouter.GetPendingMessages:input_type -> mpc.router.v1.GetPendingMessagesRequest + 17, // 16: mpc.router.v1.MessageRouter.AcknowledgeMessage:input_type -> mpc.router.v1.AcknowledgeMessageRequest + 19, // 17: mpc.router.v1.MessageRouter.GetMessageStatus:input_type -> mpc.router.v1.GetMessageStatusRequest + 7, // 18: mpc.router.v1.MessageRouter.RegisterParty:input_type -> mpc.router.v1.RegisterPartyRequest + 22, // 19: mpc.router.v1.MessageRouter.Heartbeat:input_type -> mpc.router.v1.HeartbeatRequest + 9, // 20: mpc.router.v1.MessageRouter.SubscribeSessionEvents:input_type -> mpc.router.v1.SubscribeSessionEventsRequest + 12, // 21: mpc.router.v1.MessageRouter.PublishSessionEvent:input_type -> mpc.router.v1.PublishSessionEventRequest + 14, // 22: mpc.router.v1.MessageRouter.GetRegisteredParties:input_type -> mpc.router.v1.GetRegisteredPartiesRequest + 27, // 23: mpc.router.v1.MessageRouter.JoinSession:input_type -> mpc.router.v1.JoinSessionRequest + 29, // 24: mpc.router.v1.MessageRouter.MarkPartyReady:input_type -> mpc.router.v1.MarkPartyReadyRequest + 31, // 25: mpc.router.v1.MessageRouter.ReportCompletion:input_type -> mpc.router.v1.ReportCompletionRequest + 33, // 26: mpc.router.v1.MessageRouter.GetSessionStatus:input_type -> mpc.router.v1.GetSessionStatusRequest + 35, // 27: mpc.router.v1.MessageRouter.SubmitDelegateShare:input_type -> mpc.router.v1.SubmitDelegateShareRequest + 1, // 28: mpc.router.v1.MessageRouter.RouteMessage:output_type -> mpc.router.v1.RouteMessageResponse + 3, // 29: mpc.router.v1.MessageRouter.SubscribeMessages:output_type -> mpc.router.v1.MPCMessage + 5, // 30: mpc.router.v1.MessageRouter.GetPendingMessages:output_type -> mpc.router.v1.GetPendingMessagesResponse + 18, // 31: mpc.router.v1.MessageRouter.AcknowledgeMessage:output_type -> mpc.router.v1.AcknowledgeMessageResponse + 21, // 32: mpc.router.v1.MessageRouter.GetMessageStatus:output_type -> mpc.router.v1.GetMessageStatusResponse + 8, // 33: mpc.router.v1.MessageRouter.RegisterParty:output_type -> mpc.router.v1.RegisterPartyResponse + 23, // 34: mpc.router.v1.MessageRouter.Heartbeat:output_type -> mpc.router.v1.HeartbeatResponse + 10, // 35: mpc.router.v1.MessageRouter.SubscribeSessionEvents:output_type -> mpc.router.v1.SessionEvent + 13, // 36: mpc.router.v1.MessageRouter.PublishSessionEvent:output_type -> mpc.router.v1.PublishSessionEventResponse + 16, // 37: mpc.router.v1.MessageRouter.GetRegisteredParties:output_type -> mpc.router.v1.GetRegisteredPartiesResponse + 28, // 38: mpc.router.v1.MessageRouter.JoinSession:output_type -> mpc.router.v1.JoinSessionResponse + 30, // 39: mpc.router.v1.MessageRouter.MarkPartyReady:output_type -> mpc.router.v1.MarkPartyReadyResponse + 32, // 40: mpc.router.v1.MessageRouter.ReportCompletion:output_type -> mpc.router.v1.ReportCompletionResponse + 34, // 41: mpc.router.v1.MessageRouter.GetSessionStatus:output_type -> mpc.router.v1.GetSessionStatusResponse + 36, // 42: mpc.router.v1.MessageRouter.SubmitDelegateShare:output_type -> mpc.router.v1.SubmitDelegateShareResponse + 28, // [28:43] is the sub-list for method output_type + 13, // [13:28] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name } func init() { file_api_proto_message_router_proto_init() } @@ -1757,7 +2771,7 @@ func file_api_proto_message_router_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_proto_message_router_proto_rawDesc), len(file_api_proto_message_router_proto_rawDesc)), NumEnums: 0, - NumMessages: 24, + NumMessages: 38, NumExtensions: 0, NumServices: 1, }, diff --git a/backend/mpc-system/api/grpc/router/v1/message_router_grpc.pb.go b/backend/mpc-system/api/grpc/router/v1/message_router_grpc.pb.go index d35f02ba..7ed924ac 100644 --- a/backend/mpc-system/api/grpc/router/v1/message_router_grpc.pb.go +++ b/backend/mpc-system/api/grpc/router/v1/message_router_grpc.pb.go @@ -29,6 +29,11 @@ const ( MessageRouter_SubscribeSessionEvents_FullMethodName = "/mpc.router.v1.MessageRouter/SubscribeSessionEvents" MessageRouter_PublishSessionEvent_FullMethodName = "/mpc.router.v1.MessageRouter/PublishSessionEvent" MessageRouter_GetRegisteredParties_FullMethodName = "/mpc.router.v1.MessageRouter/GetRegisteredParties" + MessageRouter_JoinSession_FullMethodName = "/mpc.router.v1.MessageRouter/JoinSession" + MessageRouter_MarkPartyReady_FullMethodName = "/mpc.router.v1.MessageRouter/MarkPartyReady" + MessageRouter_ReportCompletion_FullMethodName = "/mpc.router.v1.MessageRouter/ReportCompletion" + MessageRouter_GetSessionStatus_FullMethodName = "/mpc.router.v1.MessageRouter/GetSessionStatus" + MessageRouter_SubmitDelegateShare_FullMethodName = "/mpc.router.v1.MessageRouter/SubmitDelegateShare" ) // MessageRouterClient is the client API for MessageRouter service. @@ -36,6 +41,8 @@ const ( // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // // MessageRouter service handles MPC message routing +// This is the ONLY service that server-parties need to connect to. +// All session operations are proxied through Message Router to Session Coordinator. type MessageRouterClient interface { // RouteMessage routes a message from one party to others RouteMessage(ctx context.Context, in *RouteMessageRequest, opts ...grpc.CallOption) (*RouteMessageResponse, error) @@ -58,6 +65,16 @@ type MessageRouterClient interface { PublishSessionEvent(ctx context.Context, in *PublishSessionEventRequest, opts ...grpc.CallOption) (*PublishSessionEventResponse, error) // GetRegisteredParties returns all registered parties (for Session Coordinator party discovery) GetRegisteredParties(ctx context.Context, in *GetRegisteredPartiesRequest, opts ...grpc.CallOption) (*GetRegisteredPartiesResponse, error) + // JoinSession joins a session (proxied to Session Coordinator) + JoinSession(ctx context.Context, in *JoinSessionRequest, opts ...grpc.CallOption) (*JoinSessionResponse, error) + // MarkPartyReady marks a party as ready (proxied to Session Coordinator) + MarkPartyReady(ctx context.Context, in *MarkPartyReadyRequest, opts ...grpc.CallOption) (*MarkPartyReadyResponse, error) + // ReportCompletion reports completion (proxied to Session Coordinator) + ReportCompletion(ctx context.Context, in *ReportCompletionRequest, opts ...grpc.CallOption) (*ReportCompletionResponse, error) + // GetSessionStatus gets session status (proxied to Session Coordinator) + GetSessionStatus(ctx context.Context, in *GetSessionStatusRequest, opts ...grpc.CallOption) (*GetSessionStatusResponse, error) + // SubmitDelegateShare submits user's share from delegate party (proxied to Session Coordinator) + SubmitDelegateShare(ctx context.Context, in *SubmitDelegateShareRequest, opts ...grpc.CallOption) (*SubmitDelegateShareResponse, error) } type messageRouterClient struct { @@ -186,11 +203,63 @@ func (c *messageRouterClient) GetRegisteredParties(ctx context.Context, in *GetR return out, nil } +func (c *messageRouterClient) JoinSession(ctx context.Context, in *JoinSessionRequest, opts ...grpc.CallOption) (*JoinSessionResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(JoinSessionResponse) + err := c.cc.Invoke(ctx, MessageRouter_JoinSession_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *messageRouterClient) MarkPartyReady(ctx context.Context, in *MarkPartyReadyRequest, opts ...grpc.CallOption) (*MarkPartyReadyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MarkPartyReadyResponse) + err := c.cc.Invoke(ctx, MessageRouter_MarkPartyReady_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *messageRouterClient) ReportCompletion(ctx context.Context, in *ReportCompletionRequest, opts ...grpc.CallOption) (*ReportCompletionResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ReportCompletionResponse) + err := c.cc.Invoke(ctx, MessageRouter_ReportCompletion_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *messageRouterClient) GetSessionStatus(ctx context.Context, in *GetSessionStatusRequest, opts ...grpc.CallOption) (*GetSessionStatusResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetSessionStatusResponse) + err := c.cc.Invoke(ctx, MessageRouter_GetSessionStatus_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *messageRouterClient) SubmitDelegateShare(ctx context.Context, in *SubmitDelegateShareRequest, opts ...grpc.CallOption) (*SubmitDelegateShareResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SubmitDelegateShareResponse) + err := c.cc.Invoke(ctx, MessageRouter_SubmitDelegateShare_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // MessageRouterServer is the server API for MessageRouter service. // All implementations must embed UnimplementedMessageRouterServer // for forward compatibility. // // MessageRouter service handles MPC message routing +// This is the ONLY service that server-parties need to connect to. +// All session operations are proxied through Message Router to Session Coordinator. type MessageRouterServer interface { // RouteMessage routes a message from one party to others RouteMessage(context.Context, *RouteMessageRequest) (*RouteMessageResponse, error) @@ -213,6 +282,16 @@ type MessageRouterServer interface { PublishSessionEvent(context.Context, *PublishSessionEventRequest) (*PublishSessionEventResponse, error) // GetRegisteredParties returns all registered parties (for Session Coordinator party discovery) GetRegisteredParties(context.Context, *GetRegisteredPartiesRequest) (*GetRegisteredPartiesResponse, error) + // JoinSession joins a session (proxied to Session Coordinator) + JoinSession(context.Context, *JoinSessionRequest) (*JoinSessionResponse, error) + // MarkPartyReady marks a party as ready (proxied to Session Coordinator) + MarkPartyReady(context.Context, *MarkPartyReadyRequest) (*MarkPartyReadyResponse, error) + // ReportCompletion reports completion (proxied to Session Coordinator) + ReportCompletion(context.Context, *ReportCompletionRequest) (*ReportCompletionResponse, error) + // GetSessionStatus gets session status (proxied to Session Coordinator) + GetSessionStatus(context.Context, *GetSessionStatusRequest) (*GetSessionStatusResponse, error) + // SubmitDelegateShare submits user's share from delegate party (proxied to Session Coordinator) + SubmitDelegateShare(context.Context, *SubmitDelegateShareRequest) (*SubmitDelegateShareResponse, error) mustEmbedUnimplementedMessageRouterServer() } @@ -253,6 +332,21 @@ func (UnimplementedMessageRouterServer) PublishSessionEvent(context.Context, *Pu func (UnimplementedMessageRouterServer) GetRegisteredParties(context.Context, *GetRegisteredPartiesRequest) (*GetRegisteredPartiesResponse, error) { return nil, status.Error(codes.Unimplemented, "method GetRegisteredParties not implemented") } +func (UnimplementedMessageRouterServer) JoinSession(context.Context, *JoinSessionRequest) (*JoinSessionResponse, error) { + return nil, status.Error(codes.Unimplemented, "method JoinSession not implemented") +} +func (UnimplementedMessageRouterServer) MarkPartyReady(context.Context, *MarkPartyReadyRequest) (*MarkPartyReadyResponse, error) { + return nil, status.Error(codes.Unimplemented, "method MarkPartyReady not implemented") +} +func (UnimplementedMessageRouterServer) ReportCompletion(context.Context, *ReportCompletionRequest) (*ReportCompletionResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ReportCompletion not implemented") +} +func (UnimplementedMessageRouterServer) GetSessionStatus(context.Context, *GetSessionStatusRequest) (*GetSessionStatusResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetSessionStatus not implemented") +} +func (UnimplementedMessageRouterServer) SubmitDelegateShare(context.Context, *SubmitDelegateShareRequest) (*SubmitDelegateShareResponse, error) { + return nil, status.Error(codes.Unimplemented, "method SubmitDelegateShare not implemented") +} func (UnimplementedMessageRouterServer) mustEmbedUnimplementedMessageRouterServer() {} func (UnimplementedMessageRouterServer) testEmbeddedByValue() {} @@ -440,6 +534,96 @@ func _MessageRouter_GetRegisteredParties_Handler(srv interface{}, ctx context.Co return interceptor(ctx, in, info, handler) } +func _MessageRouter_JoinSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(JoinSessionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MessageRouterServer).JoinSession(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: MessageRouter_JoinSession_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MessageRouterServer).JoinSession(ctx, req.(*JoinSessionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _MessageRouter_MarkPartyReady_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MarkPartyReadyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MessageRouterServer).MarkPartyReady(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: MessageRouter_MarkPartyReady_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MessageRouterServer).MarkPartyReady(ctx, req.(*MarkPartyReadyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _MessageRouter_ReportCompletion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReportCompletionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MessageRouterServer).ReportCompletion(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: MessageRouter_ReportCompletion_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MessageRouterServer).ReportCompletion(ctx, req.(*ReportCompletionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _MessageRouter_GetSessionStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetSessionStatusRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MessageRouterServer).GetSessionStatus(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: MessageRouter_GetSessionStatus_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MessageRouterServer).GetSessionStatus(ctx, req.(*GetSessionStatusRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _MessageRouter_SubmitDelegateShare_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SubmitDelegateShareRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MessageRouterServer).SubmitDelegateShare(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: MessageRouter_SubmitDelegateShare_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MessageRouterServer).SubmitDelegateShare(ctx, req.(*SubmitDelegateShareRequest)) + } + return interceptor(ctx, in, info, handler) +} + // MessageRouter_ServiceDesc is the grpc.ServiceDesc for MessageRouter service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -479,6 +663,26 @@ var MessageRouter_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetRegisteredParties", Handler: _MessageRouter_GetRegisteredParties_Handler, }, + { + MethodName: "JoinSession", + Handler: _MessageRouter_JoinSession_Handler, + }, + { + MethodName: "MarkPartyReady", + Handler: _MessageRouter_MarkPartyReady_Handler, + }, + { + MethodName: "ReportCompletion", + Handler: _MessageRouter_ReportCompletion_Handler, + }, + { + MethodName: "GetSessionStatus", + Handler: _MessageRouter_GetSessionStatus_Handler, + }, + { + MethodName: "SubmitDelegateShare", + Handler: _MessageRouter_SubmitDelegateShare_Handler, + }, }, Streams: []grpc.StreamDesc{ { diff --git a/backend/mpc-system/api/proto/message_router.proto b/backend/mpc-system/api/proto/message_router.proto index 88b4da47..a8ae5054 100644 --- a/backend/mpc-system/api/proto/message_router.proto +++ b/backend/mpc-system/api/proto/message_router.proto @@ -5,7 +5,13 @@ package mpc.router.v1; option go_package = "github.com/rwadurian/mpc-system/api/grpc/router/v1;router"; // MessageRouter service handles MPC message routing +// This is the ONLY service that server-parties need to connect to. +// All session operations are proxied through Message Router to Session Coordinator. service MessageRouter { + // ============================================ + // Message Routing + // ============================================ + // RouteMessage routes a message from one party to others rpc RouteMessage(RouteMessageRequest) returns (RouteMessageResponse); @@ -22,20 +28,52 @@ service MessageRouter { // GetMessageStatus gets the delivery status of a message rpc GetMessageStatus(GetMessageStatusRequest) returns (GetMessageStatusResponse); + // ============================================ + // Party Registration & Heartbeat + // ============================================ + // RegisterParty registers a party with the message router (party actively connects) rpc RegisterParty(RegisterPartyRequest) returns (RegisterPartyResponse); // Heartbeat sends a heartbeat to keep the party alive rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse); + // ============================================ + // Session Events (Push from Coordinator) + // ============================================ + // SubscribeSessionEvents subscribes to session lifecycle events (session start, etc.) rpc SubscribeSessionEvents(SubscribeSessionEventsRequest) returns (stream SessionEvent); // PublishSessionEvent publishes a session event (called by Session Coordinator) rpc PublishSessionEvent(PublishSessionEventRequest) returns (PublishSessionEventResponse); + // ============================================ + // Party Discovery (for Session Coordinator) + // ============================================ + // GetRegisteredParties returns all registered parties (for Session Coordinator party discovery) rpc GetRegisteredParties(GetRegisteredPartiesRequest) returns (GetRegisteredPartiesResponse); + + // ============================================ + // Session Operations (Proxied to Coordinator) + // These allow server-parties to only connect to Message Router + // ============================================ + + // JoinSession joins a session (proxied to Session Coordinator) + rpc JoinSession(JoinSessionRequest) returns (JoinSessionResponse); + + // MarkPartyReady marks a party as ready (proxied to Session Coordinator) + rpc MarkPartyReady(MarkPartyReadyRequest) returns (MarkPartyReadyResponse); + + // ReportCompletion reports completion (proxied to Session Coordinator) + rpc ReportCompletion(ReportCompletionRequest) returns (ReportCompletionResponse); + + // GetSessionStatus gets session status (proxied to Session Coordinator) + rpc GetSessionStatus(GetSessionStatusRequest) returns (GetSessionStatusResponse); + + // SubmitDelegateShare submits user's share from delegate party (proxied to Session Coordinator) + rpc SubmitDelegateShare(SubmitDelegateShareRequest) returns (SubmitDelegateShareResponse); } // RouteMessageRequest routes an MPC message @@ -126,6 +164,15 @@ message SessionEvent { bytes message_hash = 8; // For sign sessions int64 created_at = 9; // Unix timestamp milliseconds int64 expires_at = 10; // Unix timestamp milliseconds + // For sign sessions with delegate party: user's share for delegate to use + DelegateUserShare delegate_user_share = 11; +} + +// DelegateUserShare contains user's share for delegate party to use in signing +message DelegateUserShare { + string delegate_party_id = 1; // The delegate party that will use this share + bytes encrypted_share = 2; // User's encrypted share + int32 party_index = 3; // Party index for this share } // PublishSessionEventRequest publishes a session event @@ -211,3 +258,102 @@ message HeartbeatResponse { int64 server_timestamp = 2; // Server timestamp for clock sync int32 pending_messages = 3; // Number of pending messages for this party } + +// ============================================ +// Session Operations (Proxied to Coordinator) +// ============================================ + +// DeviceInfo contains device information +message DeviceInfo { + string device_type = 1; // server, mobile, web + string device_id = 2; + string platform = 3; + string app_version = 4; +} + +// PartyInfo contains party information +message PartyInfo { + string party_id = 1; + int32 party_index = 2; + DeviceInfo device_info = 3; +} + +// SessionInfo contains session information +message SessionInfo { + string session_id = 1; + string session_type = 2; // keygen, sign + int32 threshold_n = 3; + int32 threshold_t = 4; + bytes message_hash = 5; // For sign sessions + string status = 6; +} + +// JoinSessionRequest joins a session +message JoinSessionRequest { + string session_id = 1; + string party_id = 2; + string join_token = 3; + DeviceInfo device_info = 4; +} + +// JoinSessionResponse returns session info after joining +message JoinSessionResponse { + bool success = 1; + SessionInfo session_info = 2; + repeated PartyInfo other_parties = 3; // All participants in the session + int32 party_index = 4; // This party's index +} + +// MarkPartyReadyRequest marks a party as ready +message MarkPartyReadyRequest { + string session_id = 1; + string party_id = 2; +} + +// MarkPartyReadyResponse confirms ready status +message MarkPartyReadyResponse { + bool success = 1; + bool all_ready = 2; // True if all parties are ready +} + +// ReportCompletionRequest reports protocol completion +message ReportCompletionRequest { + string session_id = 1; + string party_id = 2; + bytes public_key = 3; // For keygen: resulting public key + bytes signature = 4; // For signing: resulting signature +} + +// ReportCompletionResponse confirms completion report +message ReportCompletionResponse { + bool success = 1; + bool all_completed = 2; // True if all parties completed +} + +// GetSessionStatusRequest requests session status +message GetSessionStatusRequest { + string session_id = 1; +} + +// GetSessionStatusResponse returns session status +message GetSessionStatusResponse { + string session_id = 1; + string status = 2; // created, in_progress, completed, failed, expired + int32 threshold_n = 3; + int32 threshold_t = 4; + repeated PartyInfo participants = 5; +} + +// SubmitDelegateShareRequest submits user's share from delegate party +message SubmitDelegateShareRequest { + string session_id = 1; + string party_id = 2; + bytes encrypted_share = 3; // Encrypted share for user + bytes public_key = 4; // Public key from keygen + int32 party_index = 5; // Party's index in the session +} + +// SubmitDelegateShareResponse contains result of share submission +message SubmitDelegateShareResponse { + bool success = 1; +} diff --git a/backend/mpc-system/api/proto/session_coordinator.proto b/backend/mpc-system/api/proto/session_coordinator.proto index 4ac10b39..c1aceb1c 100644 --- a/backend/mpc-system/api/proto/session_coordinator.proto +++ b/backend/mpc-system/api/proto/session_coordinator.proto @@ -14,6 +14,9 @@ service SessionCoordinator { rpc StartSession(StartSessionRequest) returns (StartSessionResponse); rpc ReportCompletion(ReportCompletionRequest) returns (ReportCompletionResponse); rpc CloseSession(CloseSessionRequest) returns (CloseSessionResponse); + + // Delegate party share submission (delegate party submits user's share after keygen) + rpc SubmitDelegateShare(SubmitDelegateShareRequest) returns (SubmitDelegateShareResponse); } // CreateSessionRequest creates a new MPC session @@ -25,6 +28,15 @@ message CreateSessionRequest { bytes message_hash = 5; // Required for sign sessions int64 expires_in_seconds = 6; // Session expiration time PartyComposition party_composition = 7; // Optional: party composition requirements for auto-selection + // For sign sessions with delegate party: user must provide their encrypted share + DelegateUserShare delegate_user_share = 8; +} + +// DelegateUserShare contains user's share for delegate party to use in signing +message DelegateUserShare { + string delegate_party_id = 1; // The delegate party that will use this share + bytes encrypted_share = 2; // User's encrypted share (same as received from keygen) + int32 party_index = 3; // Party index for this share } // PartyComposition specifies requirements for automatic party selection @@ -99,8 +111,22 @@ message GetSessionStatusResponse { string status = 1; int32 completed_parties = 2; int32 total_parties = 3; - bytes public_key = 4; // For completed keygen - bytes signature = 5; // For completed sign + string session_type = 4; // "keygen" or "sign" + bytes public_key = 5; // For completed keygen + bytes signature = 6; // For completed sign + // has_delegate indicates whether this keygen session has a delegate party + // Only meaningful for keygen sessions. Always false for sign sessions. + bool has_delegate = 7; + // Delegate share info (returned when keygen session completed and delegate party submitted share) + // Only populated if session_type="keygen" AND has_delegate=true AND session is completed + DelegateShareInfo delegate_share = 8; +} + +// DelegateShareInfo contains the delegate party's share for user +message DelegateShareInfo { + bytes encrypted_share = 1; // Encrypted share for user + int32 party_index = 2; // Party's index in the session + string party_id = 3; // Delegate party ID } // ReportCompletionRequest reports that a participant has completed @@ -151,3 +177,17 @@ message StartSessionResponse { bool success = 1; string status = 2; // New session status } + +// SubmitDelegateShareRequest submits user's share from delegate party +message SubmitDelegateShareRequest { + string session_id = 1; + string party_id = 2; + bytes encrypted_share = 3; // Encrypted share for user + bytes public_key = 4; // Public key from keygen + int32 party_index = 5; // Party's index in the session +} + +// SubmitDelegateShareResponse contains result of share submission +message SubmitDelegateShareResponse { + bool success = 1; +} diff --git a/backend/mpc-system/deploy.sh b/backend/mpc-system/deploy.sh index e3da2c0c..0f599a26 100755 --- a/backend/mpc-system/deploy.sh +++ b/backend/mpc-system/deploy.sh @@ -4,11 +4,23 @@ # ============================================================================= # This script manages the MPC System Docker services # -# External Ports: +# Deployment Modes: +# 1. Development (default): All services on one machine (docker-compose.yml) +# 2. Production Central: Central services only (docker-compose.prod.yml) +# 3. Production Party: Standalone party (docker-compose.party.yml) +# +# External Ports (Development): # 4000 - Account Service HTTP API # 8081 - Session Coordinator API -# 8082 - Message Router WebSocket -# 8083 - Server Party API (user share generation) +# 8082 - Message Router HTTP +# 8083 - Server Party API +# +# External Ports (Production Central): +# 50051 - Message Router gRPC (for party connections) +# 50052 - Session Coordinator gRPC (for party connections) +# 4000 - Account Service HTTP API +# 8081 - Session Coordinator HTTP API +# 8082 - Message Router HTTP API # ============================================================================= set -e @@ -18,226 +30,488 @@ RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' +CYAN='\033[0;36m' NC='\033[0m' log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_success() { echo -e "${GREEN}[OK]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } +log_header() { echo -e "${CYAN}=== $1 ===${NC}"; } SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" -# Load environment -if [ -f ".env" ]; then - log_info "Loading environment from .env file" - set -a - source .env - set +a -elif [ ! -f ".env" ] && [ -f ".env.example" ]; then - log_warn ".env file not found. Creating from .env.example" - log_warn "Please edit .env and configure for your environment!" - cp .env.example .env - log_error "Please configure .env file and run again" - exit 1 -fi +# Determine which environment file to load +load_env() { + local env_file="$1" + if [ -f "$env_file" ]; then + log_info "Loading environment from $env_file" + set -a + source "$env_file" + set +a + return 0 + fi + return 1 +} -# Core services list -CORE_SERVICES="postgres redis rabbitmq" -MPC_SERVICES="session-coordinator message-router server-party-1 server-party-2 server-party-3 server-party-api account-service" -ALL_SERVICES="$CORE_SERVICES $MPC_SERVICES" +# Load environment based on mode +load_environment() { + local mode="$1" + case "$mode" in + prod) + load_env ".env.prod" || load_env ".env" || { + log_error "No .env.prod or .env file found" + exit 1 + } + ;; + party) + load_env ".env.party" || { + log_error "No .env.party file found. Create from .env.party.example" + exit 1 + } + ;; + *) + load_env ".env" || { + if [ -f ".env.example" ]; then + log_warn ".env file not found. Creating from .env.example" + cp .env.example .env + log_error "Please configure .env file and run again" + fi + exit 1 + } + ;; + esac +} +# Service lists +CORE_SERVICES="postgres" +DEV_MPC_SERVICES="session-coordinator message-router server-party-1 server-party-2 server-party-3 server-party-api account-service" +PROD_CENTRAL_SERVICES="postgres message-router session-coordinator account-service server-party-api" + +# ============================================ +# Development Mode Commands (docker-compose.yml) +# ============================================ +dev_commands() { + load_environment "dev" + + case "$1" in + build) + log_info "Building MPC System services..." + docker compose build + log_success "MPC System built successfully" + ;; + + build-no-cache) + log_info "Building MPC System (no cache)..." + docker compose build --no-cache + log_success "MPC System built successfully" + ;; + + up|start) + log_info "Starting MPC System (Development)..." + docker compose up -d + log_success "MPC System started" + echo "" + log_info "Services status:" + docker compose ps + ;; + + down|stop) + log_info "Stopping MPC System..." + docker compose down + log_success "MPC System stopped" + ;; + + restart) + log_info "Restarting MPC System..." + docker compose down + docker compose up -d + log_success "MPC System restarted" + ;; + + logs) + if [ -n "$2" ]; then + docker compose logs -f "$2" + else + docker compose logs -f + fi + ;; + + logs-tail) + if [ -n "$2" ]; then + docker compose logs --tail 100 "$2" + else + docker compose logs --tail 100 + fi + ;; + + status|ps) + log_info "MPC System status:" + docker compose ps + ;; + + health) + log_info "Checking MPC System health..." + echo "" + log_header "Infrastructure" + for svc in $CORE_SERVICES; do + if docker compose ps "$svc" --format json 2>/dev/null | grep -q '"Health":"healthy"'; then + log_success "$svc is healthy" + else + log_warn "$svc is not healthy" + fi + done + + echo "" + log_header "MPC Services" + for svc in $DEV_MPC_SERVICES; do + if docker compose ps "$svc" --format json 2>/dev/null | grep -q '"Health":"healthy"'; then + log_success "$svc is healthy" + else + log_warn "$svc is not healthy" + fi + done + + echo "" + log_header "External API" + if curl -sf "http://localhost:4000/health" > /dev/null 2>&1; then + log_success "Account Service API (port 4000) is accessible" + else + log_error "Account Service API (port 4000) is not accessible" + fi + ;; + + clean) + log_warn "This will remove all containers and volumes!" + read -p "Are you sure? (y/N) " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + docker compose down -v + log_success "MPC System cleaned" + else + log_info "Cancelled" + fi + ;; + + shell) + if [ -n "$2" ]; then + log_info "Opening shell in $2..." + docker compose exec "$2" sh + else + log_info "Opening shell in account-service..." + docker compose exec account-service sh + fi + ;; + + test-api) + log_info "Testing Account Service API..." + echo "" + echo "Health check:" + curl -s "http://localhost:4000/health" | jq . 2>/dev/null || curl -s "http://localhost:4000/health" + echo "" + ;; + + *) + return 1 + ;; + esac +} + +# ============================================ +# Production Central Commands (docker-compose.prod.yml) +# ============================================ +prod_commands() { + load_environment "prod" + + case "$1" in + build) + log_info "Building Production Central services..." + docker compose -f docker-compose.prod.yml build + log_success "Production services built" + ;; + + up|start) + log_info "Starting Production Central services..." + docker compose -f docker-compose.prod.yml up -d + log_success "Production Central services started" + echo "" + log_header "Services Status" + docker compose -f docker-compose.prod.yml ps + echo "" + log_header "Public Endpoints" + echo " Message Router gRPC: ${MESSAGE_ROUTER_GRPC_PORT:-50051}" + echo " Session Coordinator gRPC: ${SESSION_COORDINATOR_GRPC_PORT:-50052}" + echo " Account Service HTTP: ${ACCOUNT_SERVICE_PORT:-4000}" + ;; + + down|stop) + log_info "Stopping Production Central services..." + docker compose -f docker-compose.prod.yml down + log_success "Production Central services stopped" + ;; + + restart) + log_info "Restarting Production Central services..." + docker compose -f docker-compose.prod.yml down + docker compose -f docker-compose.prod.yml up -d + log_success "Production Central services restarted" + ;; + + logs) + if [ -n "$2" ]; then + docker compose -f docker-compose.prod.yml logs -f "$2" + else + docker compose -f docker-compose.prod.yml logs -f + fi + ;; + + status|ps) + log_info "Production Central status:" + docker compose -f docker-compose.prod.yml ps + ;; + + health) + log_info "Checking Production Central health..." + echo "" + log_header "Central Services" + for svc in $PROD_CENTRAL_SERVICES; do + if docker compose -f docker-compose.prod.yml ps "$svc" --format json 2>/dev/null | grep -q '"Health":"healthy"'; then + log_success "$svc is healthy" + else + log_warn "$svc is not healthy" + fi + done + + echo "" + log_header "Public Endpoints" + if curl -sf "http://localhost:${MESSAGE_ROUTER_HTTP_PORT:-8082}/health" > /dev/null 2>&1; then + log_success "Message Router (port ${MESSAGE_ROUTER_HTTP_PORT:-8082}) is accessible" + else + log_error "Message Router is not accessible" + fi + if curl -sf "http://localhost:${SESSION_COORDINATOR_HTTP_PORT:-8081}/health" > /dev/null 2>&1; then + log_success "Session Coordinator (port ${SESSION_COORDINATOR_HTTP_PORT:-8081}) is accessible" + else + log_error "Session Coordinator is not accessible" + fi + if curl -sf "http://localhost:${ACCOUNT_SERVICE_PORT:-4000}/health" > /dev/null 2>&1; then + log_success "Account Service (port ${ACCOUNT_SERVICE_PORT:-4000}) is accessible" + else + log_error "Account Service is not accessible" + fi + ;; + + clean) + log_warn "This will remove all Production Central containers and volumes!" + read -p "Are you sure? (y/N) " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + docker compose -f docker-compose.prod.yml down -v + log_success "Production Central cleaned" + else + log_info "Cancelled" + fi + ;; + + *) + return 1 + ;; + esac +} + +# ============================================ +# Production Party Commands (docker-compose.party.yml) +# ============================================ +party_commands() { + load_environment "party" + + # Validate required environment variables + if [ -z "$PARTY_ID" ]; then + log_error "PARTY_ID must be set (e.g., server-party-1)" + exit 1 + fi + if [ -z "$MESSAGE_ROUTER_ADDR" ]; then + log_error "MESSAGE_ROUTER_ADDR must be set (e.g., grpc.mpc.example.com:50051)" + exit 1 + fi + + case "$1" in + build) + log_info "Building Party ($PARTY_ID)..." + docker compose -f docker-compose.party.yml build + log_success "Party built" + ;; + + up|start) + log_info "Starting Party: $PARTY_ID" + log_info "Connecting to Message Router: $MESSAGE_ROUTER_ADDR" + docker compose -f docker-compose.party.yml up -d + log_success "Party $PARTY_ID started" + echo "" + docker compose -f docker-compose.party.yml ps + ;; + + down|stop) + log_info "Stopping Party: $PARTY_ID..." + docker compose -f docker-compose.party.yml down + log_success "Party stopped" + ;; + + restart) + log_info "Restarting Party: $PARTY_ID..." + docker compose -f docker-compose.party.yml down + docker compose -f docker-compose.party.yml up -d + log_success "Party restarted" + ;; + + logs) + docker compose -f docker-compose.party.yml logs -f server-party + ;; + + status|ps) + log_info "Party $PARTY_ID status:" + docker compose -f docker-compose.party.yml ps + ;; + + health) + log_info "Checking Party $PARTY_ID health..." + echo "" + log_header "Local Services" + if docker compose -f docker-compose.party.yml ps postgres --format json 2>/dev/null | grep -q '"Health":"healthy"'; then + log_success "Local PostgreSQL is healthy" + else + log_warn "Local PostgreSQL is not healthy" + fi + if docker compose -f docker-compose.party.yml ps server-party --format json 2>/dev/null | grep -q '"Health":"healthy"'; then + log_success "Server Party is healthy" + else + log_warn "Server Party is not healthy" + fi + + echo "" + log_header "Central Service Connectivity" + # Extract host and port from MESSAGE_ROUTER_ADDR + MR_HOST=$(echo "$MESSAGE_ROUTER_ADDR" | cut -d: -f1) + MR_PORT=$(echo "$MESSAGE_ROUTER_ADDR" | cut -d: -f2) + if timeout 5 bash -c "echo >/dev/tcp/$MR_HOST/$MR_PORT" 2>/dev/null; then + log_success "Message Router ($MESSAGE_ROUTER_ADDR) is reachable" + else + log_error "Message Router ($MESSAGE_ROUTER_ADDR) is NOT reachable" + fi + ;; + + clean) + log_warn "This will remove Party $PARTY_ID containers and LOCAL KEY STORAGE!" + log_warn "Your encrypted key shares will be DELETED!" + read -p "Are you absolutely sure? (yes/N) " confirm + echo + if [ "$confirm" = "yes" ]; then + docker compose -f docker-compose.party.yml down -v + log_success "Party $PARTY_ID cleaned" + else + log_info "Cancelled" + fi + ;; + + *) + return 1 + ;; + esac +} + +# ============================================ +# Main Command Router +# ============================================ +show_help() { + echo "MPC System Deployment Script" + echo "" + echo "Usage: $0 [options]" + echo "" + echo "Deployment Modes:" + echo " (default) Development mode - all services on one machine" + echo " prod Production Central - Message Router, Session Coordinator, Account" + echo " party Production Party - standalone server-party (distributed)" + echo "" + echo "Development Commands (default mode):" + echo " $0 build Build all Docker images" + echo " $0 up|start Start all services" + echo " $0 down|stop Stop all services" + echo " $0 restart Restart all services" + echo " $0 logs [service] Follow logs" + echo " $0 status|ps Show services status" + echo " $0 health Check all services health" + echo " $0 clean Remove containers and volumes" + echo "" + echo "Production Central Commands:" + echo " $0 prod build Build central services" + echo " $0 prod up Start central services" + echo " $0 prod down Stop central services" + echo " $0 prod logs Follow central logs" + echo " $0 prod health Check central health" + echo "" + echo "Production Party Commands (run on each party machine):" + echo " $0 party build Build party service" + echo " $0 party up Start party (connects to central)" + echo " $0 party down Stop party" + echo " $0 party logs Follow party logs" + echo " $0 party health Check party health and connectivity" + echo "" + echo "Environment Files:" + echo " .env Development configuration" + echo " .env.prod Production Central configuration" + echo " .env.party Production Party configuration" + echo "" + echo "Examples:" + echo " # Development (all on one machine)" + echo " $0 up" + echo "" + echo " # Production Central (on central server)" + echo " $0 prod up" + echo "" + echo " # Production Party (on each party machine)" + echo " PARTY_ID=server-party-1 $0 party up" + echo " PARTY_ID=server-party-2 $0 party up" + echo " PARTY_ID=server-party-3 $0 party up" +} + +# Route commands based on first argument case "$1" in - build) - log_info "Building MPC System services..." - docker compose build - log_success "MPC System built successfully" + prod) + shift + prod_commands "$@" || { + echo "Usage: $0 prod {build|up|down|restart|logs|status|health|clean}" + exit 1 + } ;; - build-no-cache) - log_info "Building MPC System (no cache)..." - docker compose build --no-cache - log_success "MPC System built successfully" + party) + shift + party_commands "$@" || { + echo "Usage: $0 party {build|up|down|restart|logs|status|health|clean}" + echo "" + echo "Required environment variables:" + echo " PARTY_ID Unique party identifier" + echo " MESSAGE_ROUTER_ADDR Central Message Router address (only connection needed)" + echo " CRYPTO_MASTER_KEY Encryption key for key shares" + exit 1 + } ;; - up|start) - log_info "Starting MPC System..." - docker compose up -d - log_success "MPC System started" - echo "" - log_info "Services status:" - docker compose ps + help|--help|-h) + show_help ;; - down|stop) - log_info "Stopping MPC System..." - docker compose down - log_success "MPC System stopped" - ;; - - restart) - log_info "Restarting MPC System..." - docker compose down - docker compose up -d - log_success "MPC System restarted" - ;; - - logs) - if [ -n "$2" ]; then - docker compose logs -f "$2" - else - docker compose logs -f - fi - ;; - - logs-tail) - if [ -n "$2" ]; then - docker compose logs --tail 100 "$2" - else - docker compose logs --tail 100 - fi - ;; - - status|ps) - log_info "MPC System status:" - docker compose ps - ;; - - health) - log_info "Checking MPC System health..." - - # Check infrastructure - echo "" - echo "=== Infrastructure ===" - for svc in $CORE_SERVICES; do - if docker compose ps "$svc" --format json 2>/dev/null | grep -q '"Health":"healthy"'; then - log_success "$svc is healthy" - else - log_warn "$svc is not healthy" - fi - done - - # Check MPC services - echo "" - echo "=== MPC Services ===" - for svc in $MPC_SERVICES; do - if docker compose ps "$svc" --format json 2>/dev/null | grep -q '"Health":"healthy"'; then - log_success "$svc is healthy" - else - log_warn "$svc is not healthy" - fi - done - - # Check external API - echo "" - echo "=== External API ===" - if curl -sf "http://localhost:4000/health" > /dev/null 2>&1; then - log_success "Account Service API (port 4000) is accessible" - else - log_error "Account Service API (port 4000) is not accessible" - fi - ;; - - infra) - case "$2" in - up) - log_info "Starting infrastructure services..." - docker compose up -d $CORE_SERVICES - log_success "Infrastructure started" - ;; - down) - log_info "Stopping infrastructure services..." - docker compose stop $CORE_SERVICES - log_success "Infrastructure stopped" - ;; - *) - echo "Usage: $0 infra {up|down}" - exit 1 - ;; - esac - ;; - - mpc) - case "$2" in - up) - log_info "Starting MPC services..." - docker compose up -d $MPC_SERVICES - log_success "MPC services started" - ;; - down) - log_info "Stopping MPC services..." - docker compose stop $MPC_SERVICES - log_success "MPC services stopped" - ;; - restart) - log_info "Restarting MPC services..." - docker compose stop $MPC_SERVICES - docker compose up -d $MPC_SERVICES - log_success "MPC services restarted" - ;; - *) - echo "Usage: $0 mpc {up|down|restart}" - exit 1 - ;; - esac - ;; - - clean) - log_warn "This will remove all containers and volumes!" - read -p "Are you sure? (y/N) " -n 1 -r - echo - if [[ $REPLY =~ ^[Yy]$ ]]; then - docker compose down -v - log_success "MPC System cleaned" - else - log_info "Cancelled" - fi - ;; - - shell) - if [ -n "$2" ]; then - log_info "Opening shell in $2..." - docker compose exec "$2" sh - else - log_info "Opening shell in account-service..." - docker compose exec account-service sh - fi - ;; - - test-api) - log_info "Testing Account Service API..." - echo "" - echo "Health check:" - curl -s "http://localhost:4000/health" | jq . 2>/dev/null || curl -s "http://localhost:4000/health" - echo "" + "") + show_help + exit 1 ;; *) - echo "MPC System Deployment Script" - echo "" - echo "Usage: $0 [options]" - echo "" - echo "Commands:" - echo " build - Build all Docker images" - echo " build-no-cache - Build images without cache" - echo " up|start - Start all services" - echo " down|stop - Stop all services" - echo " restart - Restart all services" - echo " logs [service] - Follow logs (all or specific service)" - echo " logs-tail [svc] - Show last 100 log lines" - echo " status|ps - Show services status" - echo " health - Check all services health" - echo "" - echo " infra up|down - Start/stop infrastructure only" - echo " mpc up|down|restart - Start/stop/restart MPC services only" - echo "" - echo " shell [service] - Open shell in container" - echo " test-api - Test Account Service API" - echo " clean - Remove all containers and volumes" - echo "" - echo "Services:" - echo " Infrastructure: $CORE_SERVICES" - echo " MPC Services: $MPC_SERVICES" - exit 1 + # Default to development mode + dev_commands "$@" || { + show_help + exit 1 + } ;; esac diff --git a/backend/mpc-system/docker-compose.party.yml b/backend/mpc-system/docker-compose.party.yml new file mode 100644 index 00000000..4baad07f --- /dev/null +++ b/backend/mpc-system/docker-compose.party.yml @@ -0,0 +1,127 @@ +# ============================================================================= +# MPC-System Production Deployment - Standalone Server Party +# ============================================================================= +# Purpose: Deploy a single server-party that connects to central Message Router +# This configuration is used for distributed deployment where parties run on +# different physical machines, possibly behind NAT. +# +# Usage: +# # On each party machine: +# PARTY_ID=server-party-1 ./deploy.sh party up +# PARTY_ID=server-party-2 ./deploy.sh party up +# PARTY_ID=server-party-3 ./deploy.sh party up +# +# Required Environment Variables: +# PARTY_ID - Unique party identifier (e.g., server-party-1) +# MESSAGE_ROUTER_ADDR - Public address of Message Router (e.g., grpc.mpc.example.com:50051) +# CRYPTO_MASTER_KEY - 64-character hex key for share encryption +# +# Architecture: +# This Party (NAT OK) --[outbound gRPC]--> Message Router (Public Internet) +# +# Note: Parties ONLY connect to Message Router. Session operations are +# proxied through Message Router to Session Coordinator internally. +# +# NAT Traversal: +# - Party initiates single outbound connection (no inbound ports needed) +# - gRPC keepalive maintains connection through NAT +# - Heartbeat every 30 seconds keeps connection alive +# ============================================================================= + +services: + # ============================================ + # PostgreSQL for Party's Local Key Storage + # ============================================ + postgres: + image: postgres:15-alpine + container_name: mpc-party-postgres-${PARTY_ID:-party} + environment: + POSTGRES_DB: mpc_party + POSTGRES_USER: ${POSTGRES_USER:-mpc_user} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set} + volumes: + - party-postgres-data:/var/lib/postgresql/data + - ./migrations:/docker-entrypoint-initdb.d:ro + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-mpc_user} -d mpc_party"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + networks: + - party-network + restart: unless-stopped + + # ============================================ + # Server Party - Connects to Central Services + # ============================================ + server-party: + build: + context: . + dockerfile: services/server-party/Dockerfile + container_name: mpc-${PARTY_ID:-server-party} + # No ports exposed - party connects outbound to Message Router + # HTTP port is optional for local health checks + ports: + - "${PARTY_HTTP_PORT:-8080}:8080" # Optional: local health check only + environment: + # Party Identity + PARTY_ID: ${PARTY_ID:?PARTY_ID must be set (e.g., server-party-1)} + PARTY_ROLE: ${PARTY_ROLE:-persistent} + + # Server Configuration + MPC_SERVER_GRPC_PORT: 50051 + MPC_SERVER_HTTP_PORT: 8080 + MPC_SERVER_ENVIRONMENT: ${ENVIRONMENT:-production} + + # Local Database for Key Storage + MPC_DATABASE_HOST: postgres + MPC_DATABASE_PORT: 5432 + MPC_DATABASE_USER: ${POSTGRES_USER:-mpc_user} + MPC_DATABASE_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set} + MPC_DATABASE_DBNAME: mpc_party + MPC_DATABASE_SSLMODE: disable + + # Central Service (PUBLIC address - accessible from this party's location) + # Parties ONLY connect to Message Router (session ops proxied internally) + MESSAGE_ROUTER_ADDR: ${MESSAGE_ROUTER_ADDR:?MESSAGE_ROUTER_ADDR must be set (e.g., grpc.mpc.example.com:50051)} + + # Encryption Key for Key Shares + MPC_CRYPTO_MASTER_KEY: ${CRYPTO_MASTER_KEY:?CRYPTO_MASTER_KEY must be set (64 hex characters)} + + # Optional: Notification channels for offline mode + NOTIFICATION_EMAIL: ${NOTIFICATION_EMAIL:-} + NOTIFICATION_PHONE: ${NOTIFICATION_PHONE:-} + NOTIFICATION_PUSH_TOKEN: ${NOTIFICATION_PUSH_TOKEN:-} + depends_on: + postgres: + condition: service_healthy + healthcheck: + test: ["CMD", "curl", "-sf", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + networks: + - party-network + restart: unless-stopped + # Important: Allow container to resolve external DNS + dns: + - 8.8.8.8 + - 8.8.4.4 + +# ============================================ +# Networks +# ============================================ +networks: + party-network: + driver: bridge + +# ============================================ +# Volumes - Party's Local Key Storage +# IMPORTANT: Back up this volume! It contains encrypted key shares. +# ============================================ +volumes: + party-postgres-data: + driver: local + name: mpc-party-${PARTY_ID:-party}-postgres-data diff --git a/backend/mpc-system/docker-compose.prod.yml b/backend/mpc-system/docker-compose.prod.yml new file mode 100644 index 00000000..ccc1b0fd --- /dev/null +++ b/backend/mpc-system/docker-compose.prod.yml @@ -0,0 +1,206 @@ +# ============================================================================= +# MPC-System Production Deployment - Central Services +# ============================================================================= +# Purpose: Deploy central infrastructure (Message Router, Session Coordinator, Account) +# Server Parties are deployed separately on different machines/locations +# +# Usage: +# ./deploy.sh prod up # Start central services +# ./deploy.sh prod down # Stop central services +# +# External Ports (must be accessible from server-parties): +# 50051 - Message Router gRPC (for party connections) +# 50052 - Session Coordinator gRPC (for party connections) +# 4000 - Account Service HTTP API (for backend integration) +# 8081 - Session Coordinator HTTP API (for backend integration) +# 8082 - Message Router HTTP API (health checks) +# +# Architecture: +# Server Parties (NAT-friendly) --> Message Router (Public) --> Session Coordinator +# --> PostgreSQL (Internal) +# ============================================================================= + +services: + # ============================================ + # Infrastructure Services (Internal Only) + # ============================================ + + postgres: + image: postgres:15-alpine + container_name: mpc-postgres + environment: + POSTGRES_DB: mpc_system + POSTGRES_USER: ${POSTGRES_USER:-mpc_user} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set in .env} + volumes: + - postgres-data:/var/lib/postgresql/data + - ./migrations:/docker-entrypoint-initdb.d:ro + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-mpc_user} -d mpc_system"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + networks: + - mpc-internal + restart: unless-stopped + + # ============================================ + # Message Router - Public gRPC Endpoint + # Server Parties connect here from anywhere + # ============================================ + message-router: + build: + context: . + dockerfile: services/message-router/Dockerfile + container_name: mpc-message-router + ports: + - "${MESSAGE_ROUTER_GRPC_PORT:-50051}:50051" # gRPC for party connections (PUBLIC) + - "${MESSAGE_ROUTER_HTTP_PORT:-8082}:8080" # HTTP for health checks + environment: + MPC_SERVER_GRPC_PORT: 50051 + MPC_SERVER_HTTP_PORT: 8080 + MPC_SERVER_ENVIRONMENT: ${ENVIRONMENT:-production} + MPC_DATABASE_HOST: postgres + MPC_DATABASE_PORT: 5432 + MPC_DATABASE_USER: ${POSTGRES_USER:-mpc_user} + MPC_DATABASE_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set} + MPC_DATABASE_DBNAME: mpc_system + MPC_DATABASE_SSLMODE: disable + depends_on: + postgres: + condition: service_healthy + healthcheck: + test: ["CMD", "curl", "-sf", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + networks: + - mpc-internal + restart: unless-stopped + + # ============================================ + # Session Coordinator - Public gRPC Endpoint + # ============================================ + session-coordinator: + build: + context: . + dockerfile: services/session-coordinator/Dockerfile + container_name: mpc-session-coordinator + ports: + - "${SESSION_COORDINATOR_GRPC_PORT:-50052}:50051" # gRPC for party connections (PUBLIC) + - "${SESSION_COORDINATOR_HTTP_PORT:-8081}:8080" # HTTP API + environment: + MPC_SERVER_GRPC_PORT: 50051 + MPC_SERVER_HTTP_PORT: 8080 + MPC_SERVER_ENVIRONMENT: ${ENVIRONMENT:-production} + MPC_DATABASE_HOST: postgres + MPC_DATABASE_PORT: 5432 + MPC_DATABASE_USER: ${POSTGRES_USER:-mpc_user} + MPC_DATABASE_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set} + MPC_DATABASE_DBNAME: mpc_system + MPC_DATABASE_SSLMODE: disable + MPC_JWT_SECRET_KEY: ${JWT_SECRET_KEY} + MPC_JWT_ISSUER: mpc-system + MESSAGE_ROUTER_ADDR: message-router:50051 + depends_on: + postgres: + condition: service_healthy + message-router: + condition: service_healthy + healthcheck: + test: ["CMD", "curl", "-sf", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + networks: + - mpc-internal + restart: unless-stopped + + # ============================================ + # Account Service - External API Entry Point + # ============================================ + account-service: + build: + context: . + dockerfile: services/account/Dockerfile + container_name: mpc-account-service + ports: + - "${ACCOUNT_SERVICE_PORT:-4000}:8080" # HTTP API for external access + environment: + MPC_SERVER_GRPC_PORT: 50051 + MPC_SERVER_HTTP_PORT: 8080 + MPC_SERVER_ENVIRONMENT: ${ENVIRONMENT:-production} + MPC_DATABASE_HOST: postgres + MPC_DATABASE_PORT: 5432 + MPC_DATABASE_USER: ${POSTGRES_USER:-mpc_user} + MPC_DATABASE_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set} + MPC_DATABASE_DBNAME: mpc_system + MPC_DATABASE_SSLMODE: disable + MPC_COORDINATOR_URL: session-coordinator:50051 + MPC_JWT_SECRET_KEY: ${JWT_SECRET_KEY} + MPC_API_KEY: ${MPC_API_KEY} + ALLOWED_IPS: ${ALLOWED_IPS:-} + depends_on: + postgres: + condition: service_healthy + session-coordinator: + condition: service_healthy + healthcheck: + test: ["CMD", "curl", "-sf", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + networks: + - mpc-internal + restart: unless-stopped + + # ============================================ + # Server Party API - User Share Generation + # (Optional: only needed if generating user shares) + # ============================================ + server-party-api: + build: + context: . + dockerfile: services/server-party-api/Dockerfile + container_name: mpc-server-party-api + ports: + - "${SERVER_PARTY_API_PORT:-8083}:8080" + environment: + MPC_SERVER_HTTP_PORT: 8080 + MPC_SERVER_ENVIRONMENT: ${ENVIRONMENT:-production} + SESSION_COORDINATOR_ADDR: session-coordinator:50051 + MESSAGE_ROUTER_ADDR: message-router:50051 + MPC_CRYPTO_MASTER_KEY: ${CRYPTO_MASTER_KEY} + MPC_API_KEY: ${MPC_API_KEY} + depends_on: + session-coordinator: + condition: service_healthy + message-router: + condition: service_healthy + healthcheck: + test: ["CMD", "curl", "-sf", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + networks: + - mpc-internal + restart: unless-stopped + +# ============================================ +# Networks +# ============================================ +networks: + mpc-internal: + driver: bridge + +# ============================================ +# Volumes +# ============================================ +volumes: + postgres-data: + driver: local diff --git a/backend/mpc-system/migrations/002_add_signing_parties.down.sql b/backend/mpc-system/migrations/002_add_signing_parties.down.sql new file mode 100644 index 00000000..f5867345 --- /dev/null +++ b/backend/mpc-system/migrations/002_add_signing_parties.down.sql @@ -0,0 +1,6 @@ +-- Rollback: Remove signing_parties column from accounts table + +DROP INDEX IF EXISTS idx_accounts_signing_parties; + +ALTER TABLE accounts +DROP COLUMN IF EXISTS signing_parties; diff --git a/backend/mpc-system/migrations/002_add_signing_parties.up.sql b/backend/mpc-system/migrations/002_add_signing_parties.up.sql new file mode 100644 index 00000000..d9a078d8 --- /dev/null +++ b/backend/mpc-system/migrations/002_add_signing_parties.up.sql @@ -0,0 +1,11 @@ +-- Add signing_parties column to accounts table +-- This column stores configured party IDs for signing operations +-- If NULL or empty, all active parties will be used for signing + +ALTER TABLE accounts +ADD COLUMN signing_parties TEXT[]; + +-- Add index for querying accounts with configured signing parties +CREATE INDEX idx_accounts_signing_parties ON accounts USING GIN(signing_parties) WHERE signing_parties IS NOT NULL; + +COMMENT ON COLUMN accounts.signing_parties IS 'Configured party IDs for signing operations. NULL means use all active parties.'; diff --git a/backend/mpc-system/services/account/adapters/input/http/account_handler.go b/backend/mpc-system/services/account/adapters/input/http/account_handler.go index 00f7e556..cefd8465 100644 --- a/backend/mpc-system/services/account/adapters/input/http/account_handler.go +++ b/backend/mpc-system/services/account/adapters/input/http/account_handler.go @@ -9,9 +9,10 @@ import ( "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/rwadurian/mpc-system/pkg/logger" - "github.com/rwadurian/mpc-system/services/account/adapters/output/grpc" + grpcclient "github.com/rwadurian/mpc-system/services/account/adapters/output/grpc" "github.com/rwadurian/mpc-system/services/account/application/ports" "github.com/rwadurian/mpc-system/services/account/application/use_cases" + "github.com/rwadurian/mpc-system/services/account/domain/entities" "github.com/rwadurian/mpc-system/services/account/domain/value_objects" "go.uber.org/zap" ) @@ -31,7 +32,7 @@ type AccountHTTPHandler struct { completeRecoveryUC *use_cases.CompleteRecoveryUseCase getRecoveryStatusUC *use_cases.GetRecoveryStatusUseCase cancelRecoveryUC *use_cases.CancelRecoveryUseCase - sessionCoordinatorClient *grpc.SessionCoordinatorClient + sessionCoordinatorClient *grpcclient.SessionCoordinatorClient } // NewAccountHTTPHandler creates a new AccountHTTPHandler @@ -49,7 +50,7 @@ func NewAccountHTTPHandler( completeRecoveryUC *use_cases.CompleteRecoveryUseCase, getRecoveryStatusUC *use_cases.GetRecoveryStatusUseCase, cancelRecoveryUC *use_cases.CancelRecoveryUseCase, - sessionCoordinatorClient *grpc.SessionCoordinatorClient, + sessionCoordinatorClient *grpcclient.SessionCoordinatorClient, ) *AccountHTTPHandler { return &AccountHTTPHandler{ createAccountUC: createAccountUC, @@ -80,6 +81,11 @@ func (h *AccountHTTPHandler) RegisterRoutes(router *gin.RouterGroup) { accounts.PUT("/:id", h.UpdateAccount) accounts.GET("/:id/shares", h.GetAccountShares) accounts.DELETE("/:id/shares/:shareId", h.DeactivateShare) + // Signing parties configuration + accounts.POST("/:id/signing-config", h.SetSigningParties) + accounts.PUT("/:id/signing-config", h.UpdateSigningParties) + accounts.DELETE("/:id/signing-config", h.ClearSigningParties) + accounts.GET("/:id/signing-config", h.GetSigningParties) } auth := router.Group("/auth") @@ -613,9 +619,9 @@ func (h *AccountHTTPHandler) CreateKeygenSession(c *gin.Context) { // CreateSigningSessionRequest represents the request for creating a signing session // Coordinator will automatically select parties based on account's registered shares type CreateSigningSessionRequest struct { - AccountID string `json:"account_id" binding:"required"` // Account to sign for - MessageHash string `json:"message_hash" binding:"required"` // SHA-256 hash to sign (hex encoded) - UserShare string `json:"user_share"` // Optional: user's encrypted share (hex) if delegate party is used + AccountID string `json:"account_id" binding:"required"` // Account to sign for + MessageHash string `json:"message_hash" binding:"required"` // SHA-256 hash to sign (hex encoded) + UserShare string `json:"user_share"` // Required if account has delegate share: user's encrypted share (hex) } // CreateSigningSession handles creating a new signing session @@ -655,20 +661,80 @@ func (h *AccountHTTPHandler) CreateSigningSession(c *gin.Context) { return } - // Get the party IDs from account shares - var partyIDs []string + // Build a map of active shares for validation + activeSharesMap := make(map[string]*entities.AccountShare) + var allActivePartyIDs []string + var delegateShare *entities.AccountShare for _, share := range accountOutput.Shares { if share.IsActive { - partyIDs = append(partyIDs, share.PartyID) + activeSharesMap[share.PartyID] = share + allActivePartyIDs = append(allActivePartyIDs, share.PartyID) + // Check if this is a delegate share + if share.ShareType == value_objects.ShareTypeDelegate { + delegateShare = share + } } } - // Validate we have enough active shares + // Determine which parties to use for signing + var partyIDs []string + if accountOutput.Account.HasSigningPartiesConfig() { + // Use configured signing parties + configuredParties := accountOutput.Account.GetSigningParties() + + // Validate all configured parties are still active + for _, partyID := range configuredParties { + if _, exists := activeSharesMap[partyID]; !exists { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "configured signing party is no longer active", + "party_id": partyID, + "hint": "update signing-config with active parties", + }) + return + } + } + partyIDs = configuredParties + + logger.Info("Using configured signing parties", + zap.String("account_id", req.AccountID), + zap.Strings("configured_parties", partyIDs)) + } else { + // Use all active parties (original behavior) + partyIDs = allActivePartyIDs + + logger.Info("Using all active parties for signing", + zap.String("account_id", req.AccountID), + zap.Strings("active_parties", partyIDs)) + } + + // Check if any of the selected parties is a delegate + // (delegate share might not be in the configured signing parties) + var selectedDelegateShare *entities.AccountShare + for _, partyID := range partyIDs { + if share := activeSharesMap[partyID]; share != nil && share.ShareType == value_objects.ShareTypeDelegate { + selectedDelegateShare = share + break + } + } + + // If selected parties include delegate share, user_share is required + if selectedDelegateShare != nil && req.UserShare == "" { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "user_share is required for accounts with delegate party", + "delegate_party_id": selectedDelegateShare.PartyID, + }) + return + } + + // Use the selected delegate for signing (not necessarily the account's delegate) + delegateShare = selectedDelegateShare + + // Validate we have enough parties if len(partyIDs) < accountOutput.Account.ThresholdT { c.JSON(http.StatusBadRequest, gin.H{ - "error": "insufficient active shares for signing", + "error": "insufficient parties for signing", "required": accountOutput.Account.ThresholdT, - "active": len(partyIDs), + "selected": len(partyIDs), }) return } @@ -677,10 +743,30 @@ func (h *AccountHTTPHandler) CreateSigningSession(c *gin.Context) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - logger.Info("Calling CreateSigningSession via gRPC (auto party selection)", - zap.String("account_id", req.AccountID), - zap.Int("threshold_t", accountOutput.Account.ThresholdT), - zap.Int("available_parties", len(partyIDs))) + // Prepare delegate user share if needed + var delegateUserShare *grpcclient.DelegateUserShareInput + if delegateShare != nil { + userShareBytes, err := hex.DecodeString(req.UserShare) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid user_share format (expected hex)"}) + return + } + delegateUserShare = &grpcclient.DelegateUserShareInput{ + DelegatePartyID: delegateShare.PartyID, + EncryptedShare: userShareBytes, + PartyIndex: int32(delegateShare.PartyIndex), + } + logger.Info("Calling CreateSigningSession with delegate user share", + zap.String("account_id", req.AccountID), + zap.String("delegate_party_id", delegateShare.PartyID), + zap.Int("threshold_t", accountOutput.Account.ThresholdT), + zap.Int("available_parties", len(partyIDs))) + } else { + logger.Info("Calling CreateSigningSession via gRPC (auto party selection)", + zap.String("account_id", req.AccountID), + zap.Int("threshold_t", accountOutput.Account.ThresholdT), + zap.Int("available_parties", len(partyIDs))) + } resp, err := h.sessionCoordinatorClient.CreateSigningSessionAuto( ctx, @@ -688,6 +774,7 @@ func (h *AccountHTTPHandler) CreateSigningSession(c *gin.Context) { partyIDs, messageHash, 600, // 10 minutes expiry + delegateUserShare, ) if err != nil { @@ -700,7 +787,7 @@ func (h *AccountHTTPHandler) CreateSigningSession(c *gin.Context) { zap.String("session_id", resp.SessionID), zap.Int("num_parties", len(resp.SelectedParties))) - c.JSON(http.StatusCreated, gin.H{ + response := gin.H{ "session_id": resp.SessionID, "session_type": "sign", "account_id": req.AccountID, @@ -708,7 +795,17 @@ func (h *AccountHTTPHandler) CreateSigningSession(c *gin.Context) { "threshold_t": accountOutput.Account.ThresholdT, "selected_parties": resp.SelectedParties, "status": "created", - }) + } + + // Include has_delegate for sign sessions too + if delegateShare != nil { + response["has_delegate"] = true + response["delegate_party_id"] = delegateShare.PartyID + } else { + response["has_delegate"] = false + } + + c.JSON(http.StatusCreated, response) } // GetSessionStatus handles getting session status @@ -734,16 +831,37 @@ func (h *AccountHTTPHandler) GetSessionStatus(c *gin.Context) { response := gin.H{ "session_id": sessionID, "status": resp.Status, + "session_type": resp.SessionType, // "keygen" or "sign" "completed_parties": resp.CompletedParties, "total_parties": resp.TotalParties, } - if len(resp.PublicKey) > 0 { - response["public_key"] = hex.EncodeToString(resp.PublicKey) + // Keygen-specific fields + if resp.SessionType == "keygen" { + // has_delegate only meaningful for keygen sessions + response["has_delegate"] = resp.HasDelegate + + if len(resp.PublicKey) > 0 { + response["public_key"] = hex.EncodeToString(resp.PublicKey) + } + + // Include delegate share if keygen session has delegate party + // has_delegate=true + delegate_share=null means share was already retrieved (one-time) + // has_delegate=false means no delegate party (pure persistent keygen) + if resp.HasDelegate && resp.DelegateShare != nil { + response["delegate_share"] = gin.H{ + "encrypted_share": hex.EncodeToString(resp.DelegateShare.EncryptedShare), + "party_index": resp.DelegateShare.PartyIndex, + "party_id": resp.DelegateShare.PartyID, + } + } } - if len(resp.Signature) > 0 { - response["signature"] = hex.EncodeToString(resp.Signature) + // Sign-specific fields + if resp.SessionType == "sign" { + if len(resp.Signature) > 0 { + response["signature"] = hex.EncodeToString(resp.Signature) + } } c.JSON(http.StatusOK, response) @@ -857,3 +975,256 @@ func (h *AccountHTTPHandler) CreateAccountFromKeygen(c *gin.Context) { "public_key": hex.EncodeToString(output.Account.PublicKey), }) } + +// ============================================ +// Signing Parties Configuration Endpoints +// ============================================ + +// SigningPartiesRequest represents the request for setting/updating signing parties +type SigningPartiesRequest struct { + PartyIDs []string `json:"party_ids" binding:"required,min=1"` +} + +// SetSigningParties handles setting signing parties for the first time +// POST /accounts/:id/signing-config +func (h *AccountHTTPHandler) SetSigningParties(c *gin.Context) { + idStr := c.Param("id") + accountID, err := value_objects.AccountIDFromString(idStr) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account ID"}) + return + } + + var req SigningPartiesRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // Get account + accountOutput, err := h.getAccountUC.Execute(c.Request.Context(), ports.GetAccountInput{ + AccountID: &accountID, + }) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "account not found"}) + return + } + + // Check if signing parties already configured + if accountOutput.Account.HasSigningPartiesConfig() { + c.JSON(http.StatusConflict, gin.H{ + "error": "signing parties already configured, use PUT to update", + "current_signing_parties": accountOutput.Account.GetSigningParties(), + }) + return + } + + // Validate that all party IDs are valid shares for this account + validPartyIDs := make(map[string]bool) + for _, share := range accountOutput.Shares { + if share.IsActive { + validPartyIDs[share.PartyID] = true + } + } + + for _, partyID := range req.PartyIDs { + if !validPartyIDs[partyID] { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "invalid party ID - not an active share for this account", + "party_id": partyID, + }) + return + } + } + + // Set signing parties + if err := accountOutput.Account.SetSigningParties(req.PartyIDs); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // Update account in database + _, err = h.updateAccountUC.Execute(c.Request.Context(), ports.UpdateAccountInput{ + AccountID: accountID, + SigningParties: req.PartyIDs, + }) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + logger.Info("Signing parties configured", + zap.String("account_id", idStr), + zap.Strings("signing_parties", req.PartyIDs)) + + c.JSON(http.StatusCreated, gin.H{ + "message": "signing parties configured successfully", + "signing_parties": req.PartyIDs, + "threshold_t": accountOutput.Account.ThresholdT, + }) +} + +// UpdateSigningParties handles updating existing signing parties configuration +// PUT /accounts/:id/signing-config +func (h *AccountHTTPHandler) UpdateSigningParties(c *gin.Context) { + idStr := c.Param("id") + accountID, err := value_objects.AccountIDFromString(idStr) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account ID"}) + return + } + + var req SigningPartiesRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // Get account + accountOutput, err := h.getAccountUC.Execute(c.Request.Context(), ports.GetAccountInput{ + AccountID: &accountID, + }) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "account not found"}) + return + } + + // Check if signing parties are configured (must exist to update) + if !accountOutput.Account.HasSigningPartiesConfig() { + c.JSON(http.StatusNotFound, gin.H{ + "error": "signing parties not configured, use POST to set", + }) + return + } + + // Validate that all party IDs are valid shares for this account + validPartyIDs := make(map[string]bool) + for _, share := range accountOutput.Shares { + if share.IsActive { + validPartyIDs[share.PartyID] = true + } + } + + for _, partyID := range req.PartyIDs { + if !validPartyIDs[partyID] { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "invalid party ID - not an active share for this account", + "party_id": partyID, + }) + return + } + } + + // Set signing parties (validates count matches threshold) + if err := accountOutput.Account.SetSigningParties(req.PartyIDs); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // Update account in database + _, err = h.updateAccountUC.Execute(c.Request.Context(), ports.UpdateAccountInput{ + AccountID: accountID, + SigningParties: req.PartyIDs, + }) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + logger.Info("Signing parties updated", + zap.String("account_id", idStr), + zap.Strings("signing_parties", req.PartyIDs)) + + c.JSON(http.StatusOK, gin.H{ + "message": "signing parties updated successfully", + "signing_parties": req.PartyIDs, + "threshold_t": accountOutput.Account.ThresholdT, + }) +} + +// ClearSigningParties handles clearing signing parties configuration +// DELETE /accounts/:id/signing-config +func (h *AccountHTTPHandler) ClearSigningParties(c *gin.Context) { + idStr := c.Param("id") + accountID, err := value_objects.AccountIDFromString(idStr) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account ID"}) + return + } + + // Get account + accountOutput, err := h.getAccountUC.Execute(c.Request.Context(), ports.GetAccountInput{ + AccountID: &accountID, + }) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "account not found"}) + return + } + + // Check if signing parties are configured + if !accountOutput.Account.HasSigningPartiesConfig() { + c.JSON(http.StatusNotFound, gin.H{ + "error": "signing parties not configured", + }) + return + } + + // Clear signing parties - pass empty slice + _, err = h.updateAccountUC.Execute(c.Request.Context(), ports.UpdateAccountInput{ + AccountID: accountID, + ClearSigningParties: true, + }) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + logger.Info("Signing parties cleared", + zap.String("account_id", idStr)) + + c.JSON(http.StatusOK, gin.H{ + "message": "signing parties cleared - all active parties will be used for signing", + }) +} + +// GetSigningParties handles getting current signing parties configuration +// GET /accounts/:id/signing-config +func (h *AccountHTTPHandler) GetSigningParties(c *gin.Context) { + idStr := c.Param("id") + accountID, err := value_objects.AccountIDFromString(idStr) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account ID"}) + return + } + + // Get account + accountOutput, err := h.getAccountUC.Execute(c.Request.Context(), ports.GetAccountInput{ + AccountID: &accountID, + }) + if err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "account not found"}) + return + } + + if accountOutput.Account.HasSigningPartiesConfig() { + c.JSON(http.StatusOK, gin.H{ + "configured": true, + "signing_parties": accountOutput.Account.GetSigningParties(), + "threshold_t": accountOutput.Account.ThresholdT, + }) + } else { + // Get all active parties + var activeParties []string + for _, share := range accountOutput.Shares { + if share.IsActive { + activeParties = append(activeParties, share.PartyID) + } + } + c.JSON(http.StatusOK, gin.H{ + "configured": false, + "message": "no signing parties configured - all active parties will be used", + "active_parties": activeParties, + "threshold_t": accountOutput.Account.ThresholdT, + }) + } +} diff --git a/backend/mpc-system/services/account/adapters/output/grpc/session_coordinator_client.go b/backend/mpc-system/services/account/adapters/output/grpc/session_coordinator_client.go index f3140951..b5c41688 100644 --- a/backend/mpc-system/services/account/adapters/output/grpc/session_coordinator_client.go +++ b/backend/mpc-system/services/account/adapters/output/grpc/session_coordinator_client.go @@ -121,14 +121,23 @@ func (c *SessionCoordinatorClient) CreateKeygenSessionAuto( }, nil } +// DelegateUserShareInput contains user's share for delegate party in signing +type DelegateUserShareInput struct { + DelegatePartyID string + EncryptedShare []byte + PartyIndex int32 +} + // CreateSigningSessionAuto creates a new signing session with automatic party selection // Coordinator will select parties from the provided party IDs (from account shares) +// delegateUserShare is required if any of the parties is a delegate party func (c *SessionCoordinatorClient) CreateSigningSessionAuto( ctx context.Context, thresholdT int32, partyIDs []string, messageHash []byte, expiresInSeconds int64, + delegateUserShare *DelegateUserShareInput, ) (*CreateSessionAutoResponse, error) { // Convert party IDs to participant info (minimal info, coordinator will fill in details) pbParticipants := make([]*coordinatorpb.ParticipantInfo, len(partyIDs)) @@ -147,9 +156,22 @@ func (c *SessionCoordinatorClient) CreateSigningSessionAuto( ExpiresInSeconds: expiresInSeconds, } - logger.Info("Sending CreateSigningSession gRPC request", - zap.Int32("threshold_t", thresholdT), - zap.Int("num_parties", len(partyIDs))) + // Add delegate user share if provided + if delegateUserShare != nil { + req.DelegateUserShare = &coordinatorpb.DelegateUserShare{ + DelegatePartyId: delegateUserShare.DelegatePartyID, + EncryptedShare: delegateUserShare.EncryptedShare, + PartyIndex: delegateUserShare.PartyIndex, + } + logger.Info("Sending CreateSigningSession gRPC request with delegate user share", + zap.Int32("threshold_t", thresholdT), + zap.Int("num_parties", len(partyIDs)), + zap.String("delegate_party_id", delegateUserShare.DelegatePartyID)) + } else { + logger.Info("Sending CreateSigningSession gRPC request", + zap.Int32("threshold_t", thresholdT), + zap.Int("num_parties", len(partyIDs))) + } resp, err := c.client.CreateSession(ctx, req) if err != nil { @@ -170,6 +192,7 @@ func (c *SessionCoordinatorClient) CreateSigningSessionAuto( return &CreateSessionAutoResponse{ SessionID: resp.SessionId, SelectedParties: selectedParties, + DelegateParty: resp.DelegatePartyId, JoinTokens: resp.JoinTokens, ExpiresAt: resp.ExpiresAt, }, nil @@ -189,13 +212,26 @@ func (c *SessionCoordinatorClient) GetSessionStatus( return nil, fmt.Errorf("failed to get session status: %w", err) } - return &SessionStatusResponse{ + result := &SessionStatusResponse{ Status: resp.Status, CompletedParties: resp.CompletedParties, TotalParties: resp.TotalParties, + SessionType: resp.SessionType, PublicKey: resp.PublicKey, Signature: resp.Signature, - }, nil + HasDelegate: resp.HasDelegate, + } + + // Include delegate share if present + if resp.DelegateShare != nil { + result.DelegateShare = &DelegateShareInfo{ + EncryptedShare: resp.DelegateShare.EncryptedShare, + PartyIndex: resp.DelegateShare.PartyIndex, + PartyID: resp.DelegateShare.PartyId, + } + } + + return result, nil } // Close closes the gRPC connection @@ -236,6 +272,20 @@ type SessionStatusResponse struct { Status string CompletedParties int32 TotalParties int32 + SessionType string // "keygen" or "sign" PublicKey []byte Signature []byte + // HasDelegate indicates whether this keygen session has a delegate party + // Only meaningful for keygen sessions. Always false for sign sessions. + HasDelegate bool + // DelegateShare is non-nil when session_type="keygen" AND has_delegate=true AND session is completed + // nil with has_delegate=true means share was already retrieved (one-time retrieval) + DelegateShare *DelegateShareInfo +} + +// DelegateShareInfo contains the delegate party's share for user +type DelegateShareInfo struct { + EncryptedShare []byte + PartyIndex int32 + PartyID string } diff --git a/backend/mpc-system/services/account/adapters/output/postgres/account_repo.go b/backend/mpc-system/services/account/adapters/output/postgres/account_repo.go index b961e620..5cb54f80 100644 --- a/backend/mpc-system/services/account/adapters/output/postgres/account_repo.go +++ b/backend/mpc-system/services/account/adapters/output/postgres/account_repo.go @@ -6,6 +6,7 @@ import ( "errors" "github.com/google/uuid" + "github.com/lib/pq" "github.com/rwadurian/mpc-system/services/account/domain/entities" "github.com/rwadurian/mpc-system/services/account/domain/repositories" "github.com/rwadurian/mpc-system/services/account/domain/value_objects" @@ -25,8 +26,8 @@ func NewAccountPostgresRepo(db *sql.DB) repositories.AccountRepository { func (r *AccountPostgresRepo) Create(ctx context.Context, account *entities.Account) error { query := ` INSERT INTO accounts (id, username, email, phone, public_key, keygen_session_id, - threshold_n, threshold_t, status, created_at, updated_at, last_login_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) + threshold_n, threshold_t, status, created_at, updated_at, last_login_at, signing_parties) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) ` _, err := r.db.ExecContext(ctx, query, @@ -42,6 +43,7 @@ func (r *AccountPostgresRepo) Create(ctx context.Context, account *entities.Acco account.CreatedAt, account.UpdatedAt, account.LastLoginAt, + pq.Array(account.SigningParties), ) return err @@ -51,7 +53,7 @@ func (r *AccountPostgresRepo) Create(ctx context.Context, account *entities.Acco func (r *AccountPostgresRepo) GetByID(ctx context.Context, id value_objects.AccountID) (*entities.Account, error) { query := ` SELECT id, username, email, phone, public_key, keygen_session_id, - threshold_n, threshold_t, status, created_at, updated_at, last_login_at + threshold_n, threshold_t, status, created_at, updated_at, last_login_at, signing_parties FROM accounts WHERE id = $1 ` @@ -63,7 +65,7 @@ func (r *AccountPostgresRepo) GetByID(ctx context.Context, id value_objects.Acco func (r *AccountPostgresRepo) GetByUsername(ctx context.Context, username string) (*entities.Account, error) { query := ` SELECT id, username, email, phone, public_key, keygen_session_id, - threshold_n, threshold_t, status, created_at, updated_at, last_login_at + threshold_n, threshold_t, status, created_at, updated_at, last_login_at, signing_parties FROM accounts WHERE username = $1 ` @@ -75,7 +77,7 @@ func (r *AccountPostgresRepo) GetByUsername(ctx context.Context, username string func (r *AccountPostgresRepo) GetByEmail(ctx context.Context, email string) (*entities.Account, error) { query := ` SELECT id, username, email, phone, public_key, keygen_session_id, - threshold_n, threshold_t, status, created_at, updated_at, last_login_at + threshold_n, threshold_t, status, created_at, updated_at, last_login_at, signing_parties FROM accounts WHERE email = $1 ` @@ -87,7 +89,7 @@ func (r *AccountPostgresRepo) GetByEmail(ctx context.Context, email string) (*en func (r *AccountPostgresRepo) GetByPublicKey(ctx context.Context, publicKey []byte) (*entities.Account, error) { query := ` SELECT id, username, email, phone, public_key, keygen_session_id, - threshold_n, threshold_t, status, created_at, updated_at, last_login_at + threshold_n, threshold_t, status, created_at, updated_at, last_login_at, signing_parties FROM accounts WHERE public_key = $1 ` @@ -100,7 +102,8 @@ func (r *AccountPostgresRepo) Update(ctx context.Context, account *entities.Acco query := ` UPDATE accounts SET username = $2, email = $3, phone = $4, public_key = $5, keygen_session_id = $6, - threshold_n = $7, threshold_t = $8, status = $9, updated_at = $10, last_login_at = $11 + threshold_n = $7, threshold_t = $8, status = $9, updated_at = $10, last_login_at = $11, + signing_parties = $12 WHERE id = $1 ` @@ -116,6 +119,7 @@ func (r *AccountPostgresRepo) Update(ctx context.Context, account *entities.Acco account.Status.String(), account.UpdatedAt, account.LastLoginAt, + pq.Array(account.SigningParties), ) if err != nil { @@ -177,7 +181,7 @@ func (r *AccountPostgresRepo) ExistsByEmail(ctx context.Context, email string) ( func (r *AccountPostgresRepo) List(ctx context.Context, offset, limit int) ([]*entities.Account, error) { query := ` SELECT id, username, email, phone, public_key, keygen_session_id, - threshold_n, threshold_t, status, created_at, updated_at, last_login_at + threshold_n, threshold_t, status, created_at, updated_at, last_login_at, signing_parties FROM accounts ORDER BY created_at DESC LIMIT $1 OFFSET $2 @@ -222,6 +226,7 @@ func (r *AccountPostgresRepo) scanAccount(row *sql.Row) (*entities.Account, erro thresholdN int thresholdT int status string + signingParties pq.StringArray account entities.Account ) @@ -238,6 +243,7 @@ func (r *AccountPostgresRepo) scanAccount(row *sql.Row) (*entities.Account, erro &account.CreatedAt, &account.UpdatedAt, &account.LastLoginAt, + &signingParties, ) if err != nil { @@ -260,6 +266,7 @@ func (r *AccountPostgresRepo) scanAccount(row *sql.Row) (*entities.Account, erro account.ThresholdN = thresholdN account.ThresholdT = thresholdT account.Status = value_objects.AccountStatus(status) + account.SigningParties = signingParties return &account, nil } @@ -276,6 +283,7 @@ func (r *AccountPostgresRepo) scanAccountFromRows(rows *sql.Rows) (*entities.Acc thresholdN int thresholdT int status string + signingParties pq.StringArray account entities.Account ) @@ -292,6 +300,7 @@ func (r *AccountPostgresRepo) scanAccountFromRows(rows *sql.Rows) (*entities.Acc &account.CreatedAt, &account.UpdatedAt, &account.LastLoginAt, + &signingParties, ) if err != nil { @@ -311,6 +320,7 @@ func (r *AccountPostgresRepo) scanAccountFromRows(rows *sql.Rows) (*entities.Acc account.ThresholdN = thresholdN account.ThresholdT = thresholdT account.Status = value_objects.AccountStatus(status) + account.SigningParties = signingParties return &account, nil } diff --git a/backend/mpc-system/services/account/application/ports/input_ports.go b/backend/mpc-system/services/account/application/ports/input_ports.go index 79d90a3f..34ca77ce 100644 --- a/backend/mpc-system/services/account/application/ports/input_ports.go +++ b/backend/mpc-system/services/account/application/ports/input_ports.go @@ -114,8 +114,10 @@ type CompleteRecoveryPort interface { // UpdateAccountInput represents input for updating an account type UpdateAccountInput struct { - AccountID value_objects.AccountID - Phone *string + AccountID value_objects.AccountID + Phone *string + SigningParties []string // Set/update signing parties (nil means no change) + ClearSigningParties bool // If true, clear signing parties configuration } // UpdateAccountOutput represents output from updating an account diff --git a/backend/mpc-system/services/account/application/use_cases/create_account.go b/backend/mpc-system/services/account/application/use_cases/create_account.go index 540cb875..fc9f616a 100644 --- a/backend/mpc-system/services/account/application/use_cases/create_account.go +++ b/backend/mpc-system/services/account/application/use_cases/create_account.go @@ -165,6 +165,15 @@ func (uc *UpdateAccountUseCase) Execute(ctx context.Context, input ports.UpdateA account.SetPhone(*input.Phone) } + // Handle signing parties update + if input.ClearSigningParties { + account.ClearSigningParties() + } else if len(input.SigningParties) > 0 { + if err := account.SetSigningParties(input.SigningParties); err != nil { + return nil, err + } + } + if err := uc.accountRepo.Update(ctx, account); err != nil { return nil, err } diff --git a/backend/mpc-system/services/account/domain/entities/account.go b/backend/mpc-system/services/account/domain/entities/account.go index 2dd08dd7..a717a809 100644 --- a/backend/mpc-system/services/account/domain/entities/account.go +++ b/backend/mpc-system/services/account/domain/entities/account.go @@ -21,6 +21,10 @@ type Account struct { CreatedAt time.Time UpdatedAt time.Time LastLoginAt *time.Time + // SigningParties is a list of party IDs designated for signing operations. + // If nil or empty, all active parties will be used for signing. + // Must contain exactly ThresholdT parties when set. + SigningParties []string } // NewAccount creates a new Account @@ -135,18 +139,67 @@ func (a *Account) Validate() error { return nil } +// SetSigningParties sets the designated signing parties for this account +// partyIDs must contain exactly ThresholdT parties +func (a *Account) SetSigningParties(partyIDs []string) error { + if len(partyIDs) != a.ThresholdT { + return ErrInvalidSigningPartiesCount + } + // Check for duplicates + seen := make(map[string]bool) + for _, id := range partyIDs { + if id == "" { + return ErrInvalidPartyID + } + if seen[id] { + return ErrDuplicatePartyID + } + seen[id] = true + } + a.SigningParties = partyIDs + a.UpdatedAt = time.Now().UTC() + return nil +} + +// ClearSigningParties removes the signing parties configuration +// After clearing, all active parties will be used for signing +func (a *Account) ClearSigningParties() { + a.SigningParties = nil + a.UpdatedAt = time.Now().UTC() +} + +// HasSigningPartiesConfig returns true if signing parties are configured +func (a *Account) HasSigningPartiesConfig() bool { + return len(a.SigningParties) > 0 +} + +// GetSigningParties returns the configured signing parties +// Returns nil if not configured (meaning all active parties should be used) +func (a *Account) GetSigningParties() []string { + if len(a.SigningParties) == 0 { + return nil + } + // Return a copy to prevent modification + result := make([]string, len(a.SigningParties)) + copy(result, a.SigningParties) + return result +} + // Account errors var ( - ErrInvalidUsername = &AccountError{Code: "INVALID_USERNAME", Message: "username is required"} - ErrInvalidEmail = &AccountError{Code: "INVALID_EMAIL", Message: "email is required"} - ErrInvalidPublicKey = &AccountError{Code: "INVALID_PUBLIC_KEY", Message: "public key is required"} - ErrInvalidThreshold = &AccountError{Code: "INVALID_THRESHOLD", Message: "invalid threshold configuration"} - ErrAccountInRecovery = &AccountError{Code: "ACCOUNT_IN_RECOVERY", Message: "account is in recovery mode"} - ErrCannotInitiateRecovery = &AccountError{Code: "CANNOT_INITIATE_RECOVERY", Message: "cannot initiate recovery in current state"} - ErrAccountNotActive = &AccountError{Code: "ACCOUNT_NOT_ACTIVE", Message: "account is not active"} - ErrAccountNotFound = &AccountError{Code: "ACCOUNT_NOT_FOUND", Message: "account not found"} - ErrDuplicateUsername = &AccountError{Code: "DUPLICATE_USERNAME", Message: "username already exists"} - ErrDuplicateEmail = &AccountError{Code: "DUPLICATE_EMAIL", Message: "email already exists"} + ErrInvalidUsername = &AccountError{Code: "INVALID_USERNAME", Message: "username is required"} + ErrInvalidEmail = &AccountError{Code: "INVALID_EMAIL", Message: "email is required"} + ErrInvalidPublicKey = &AccountError{Code: "INVALID_PUBLIC_KEY", Message: "public key is required"} + ErrInvalidThreshold = &AccountError{Code: "INVALID_THRESHOLD", Message: "invalid threshold configuration"} + ErrAccountInRecovery = &AccountError{Code: "ACCOUNT_IN_RECOVERY", Message: "account is in recovery mode"} + ErrCannotInitiateRecovery = &AccountError{Code: "CANNOT_INITIATE_RECOVERY", Message: "cannot initiate recovery in current state"} + ErrAccountNotActive = &AccountError{Code: "ACCOUNT_NOT_ACTIVE", Message: "account is not active"} + ErrAccountNotFound = &AccountError{Code: "ACCOUNT_NOT_FOUND", Message: "account not found"} + ErrDuplicateUsername = &AccountError{Code: "DUPLICATE_USERNAME", Message: "username already exists"} + ErrDuplicateEmail = &AccountError{Code: "DUPLICATE_EMAIL", Message: "email already exists"} + ErrInvalidSigningPartiesCount = &AccountError{Code: "INVALID_SIGNING_PARTIES_COUNT", Message: "signing parties count must equal threshold T"} + ErrInvalidPartyID = &AccountError{Code: "INVALID_PARTY_ID", Message: "party ID cannot be empty"} + ErrDuplicatePartyID = &AccountError{Code: "DUPLICATE_PARTY_ID", Message: "duplicate party ID in signing parties"} ) // AccountError represents an account domain error diff --git a/backend/mpc-system/services/account/domain/entities/account_share.go b/backend/mpc-system/services/account/domain/entities/account_share.go index 50b58fc3..fffc16e7 100644 --- a/backend/mpc-system/services/account/domain/entities/account_share.go +++ b/backend/mpc-system/services/account/domain/entities/account_share.go @@ -77,6 +77,16 @@ func (s *AccountShare) IsRecoveryShare() bool { return s.ShareType == value_objects.ShareTypeRecovery } +// IsDelegateShare checks if this is a delegate share +func (s *AccountShare) IsDelegateShare() bool { + return s.ShareType == value_objects.ShareTypeDelegate +} + +// RequiresUserShare checks if this share type requires user to provide share for signing +func (s *AccountShare) RequiresUserShare() bool { + return s.ShareType.RequiresUserShare() +} + // Validate validates the account share func (s *AccountShare) Validate() error { if s.AccountID.IsZero() { diff --git a/backend/mpc-system/services/account/domain/value_objects/account_status.go b/backend/mpc-system/services/account/domain/value_objects/account_status.go index bcea5fc4..5849775e 100644 --- a/backend/mpc-system/services/account/domain/value_objects/account_status.go +++ b/backend/mpc-system/services/account/domain/value_objects/account_status.go @@ -39,9 +39,10 @@ func (s AccountStatus) CanInitiateRecovery() bool { type ShareType string const ( - ShareTypeUserDevice ShareType = "user_device" - ShareTypeServer ShareType = "server" - ShareTypeRecovery ShareType = "recovery" + ShareTypeUserDevice ShareType = "user_device" // Share stored on user's device + ShareTypeServer ShareType = "server" // Share stored by persistent server party (in DB) + ShareTypeRecovery ShareType = "recovery" // Share stored for recovery + ShareTypeDelegate ShareType = "delegate" // Share returned to user via delegate party (not stored in party DB) ) // String returns the string representation @@ -52,13 +53,18 @@ func (st ShareType) String() string { // IsValid checks if the share type is valid func (st ShareType) IsValid() bool { switch st { - case ShareTypeUserDevice, ShareTypeServer, ShareTypeRecovery: + case ShareTypeUserDevice, ShareTypeServer, ShareTypeRecovery, ShareTypeDelegate: return true default: return false } } +// RequiresUserShare checks if this share type requires user to provide share for signing +func (st ShareType) RequiresUserShare() bool { + return st == ShareTypeDelegate || st == ShareTypeUserDevice +} + // RecoveryType represents the type of account recovery type RecoveryType string diff --git a/backend/mpc-system/services/message-router/adapters/input/grpc/message_grpc_handler.go b/backend/mpc-system/services/message-router/adapters/input/grpc/message_grpc_handler.go index 3338a5c8..7d51fd19 100644 --- a/backend/mpc-system/services/message-router/adapters/input/grpc/message_grpc_handler.go +++ b/backend/mpc-system/services/message-router/adapters/input/grpc/message_grpc_handler.go @@ -4,6 +4,7 @@ import ( "context" "time" + coordinator "github.com/rwadurian/mpc-system/api/grpc/coordinator/v1" pb "github.com/rwadurian/mpc-system/api/grpc/router/v1" "github.com/rwadurian/mpc-system/pkg/logger" "github.com/rwadurian/mpc-system/services/message-router/application/use_cases" @@ -11,6 +12,7 @@ import ( "github.com/rwadurian/mpc-system/services/message-router/domain/entities" "github.com/rwadurian/mpc-system/services/message-router/domain/repositories" "go.uber.org/zap" + "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -30,6 +32,7 @@ type MessageRouterServer struct { partyRegistry *domain.PartyRegistry eventBroadcaster *domain.SessionEventBroadcaster messageRepo repositories.MessageRepository + coordinatorConn *grpc.ClientConn // Connection to Session Coordinator for proxying } // NewMessageRouterServer creates a new gRPC server @@ -51,6 +54,13 @@ func NewMessageRouterServer( } } +// SetCoordinatorConnection sets the Session Coordinator gRPC connection for proxying +// This allows server-parties to only connect to Message Router +func (s *MessageRouterServer) SetCoordinatorConnection(conn *grpc.ClientConn) { + s.coordinatorConn = conn + logger.Info("Session Coordinator connection configured for proxying") +} + // RouteMessage routes an MPC message func (s *MessageRouterServer) RouteMessage( ctx context.Context, @@ -452,3 +462,218 @@ func toGRPCError(err error) error { return status.Error(codes.Internal, err.Error()) } } + +// ============================================ +// Session Operations (Proxied to Coordinator) +// These methods allow server-parties to only connect to Message Router +// ============================================ + +// JoinSession proxies the join session request to Session Coordinator +func (s *MessageRouterServer) JoinSession( + ctx context.Context, + req *pb.JoinSessionRequest, +) (*pb.JoinSessionResponse, error) { + if s.coordinatorConn == nil { + return nil, status.Error(codes.Unavailable, "session coordinator not configured") + } + + // Convert to coordinator request + coordReq := &coordinator.JoinSessionRequest{ + SessionId: req.SessionId, + PartyId: req.PartyId, + JoinToken: req.JoinToken, + } + if req.DeviceInfo != nil { + coordReq.DeviceInfo = &coordinator.DeviceInfo{ + DeviceType: req.DeviceInfo.DeviceType, + DeviceId: req.DeviceInfo.DeviceId, + Platform: req.DeviceInfo.Platform, + AppVersion: req.DeviceInfo.AppVersion, + } + } + + // Call Session Coordinator + coordResp := &coordinator.JoinSessionResponse{} + err := s.coordinatorConn.Invoke(ctx, "/mpc.coordinator.v1.SessionCoordinator/JoinSession", coordReq, coordResp) + if err != nil { + logger.Error("Failed to proxy JoinSession to coordinator", + zap.String("session_id", req.SessionId), + zap.String("party_id", req.PartyId), + zap.Error(err)) + return nil, err + } + + // Convert response + resp := &pb.JoinSessionResponse{ + Success: coordResp.Success, + } + + if coordResp.SessionInfo != nil { + resp.SessionInfo = &pb.SessionInfo{ + SessionId: req.SessionId, + SessionType: coordResp.SessionInfo.SessionType, + ThresholdN: coordResp.SessionInfo.ThresholdN, + ThresholdT: coordResp.SessionInfo.ThresholdT, + MessageHash: coordResp.SessionInfo.MessageHash, + } + } + + if len(coordResp.OtherParties) > 0 { + resp.OtherParties = make([]*pb.PartyInfo, len(coordResp.OtherParties)) + for i, p := range coordResp.OtherParties { + resp.OtherParties[i] = &pb.PartyInfo{ + PartyId: p.PartyId, + PartyIndex: p.PartyIndex, + } + // Find this party's index + if p.PartyId == req.PartyId { + resp.PartyIndex = p.PartyIndex + } + } + } + + logger.Info("Proxied JoinSession to coordinator", + zap.String("session_id", req.SessionId), + zap.String("party_id", req.PartyId), + zap.Bool("success", resp.Success)) + + return resp, nil +} + +// MarkPartyReady proxies the mark ready request to Session Coordinator +func (s *MessageRouterServer) MarkPartyReady( + ctx context.Context, + req *pb.MarkPartyReadyRequest, +) (*pb.MarkPartyReadyResponse, error) { + if s.coordinatorConn == nil { + return nil, status.Error(codes.Unavailable, "session coordinator not configured") + } + + coordReq := &coordinator.MarkPartyReadyRequest{ + SessionId: req.SessionId, + PartyId: req.PartyId, + } + + coordResp := &coordinator.MarkPartyReadyResponse{} + err := s.coordinatorConn.Invoke(ctx, "/mpc.coordinator.v1.SessionCoordinator/MarkPartyReady", coordReq, coordResp) + if err != nil { + logger.Error("Failed to proxy MarkPartyReady to coordinator", + zap.String("session_id", req.SessionId), + zap.String("party_id", req.PartyId), + zap.Error(err)) + return nil, err + } + + logger.Debug("Proxied MarkPartyReady to coordinator", + zap.String("session_id", req.SessionId), + zap.String("party_id", req.PartyId), + zap.Bool("all_ready", coordResp.AllReady)) + + return &pb.MarkPartyReadyResponse{ + Success: true, + AllReady: coordResp.AllReady, + }, nil +} + +// ReportCompletion proxies the completion report to Session Coordinator +func (s *MessageRouterServer) ReportCompletion( + ctx context.Context, + req *pb.ReportCompletionRequest, +) (*pb.ReportCompletionResponse, error) { + if s.coordinatorConn == nil { + return nil, status.Error(codes.Unavailable, "session coordinator not configured") + } + + coordReq := &coordinator.ReportCompletionRequest{ + SessionId: req.SessionId, + PartyId: req.PartyId, + PublicKey: req.PublicKey, + } + + coordResp := &coordinator.ReportCompletionResponse{} + err := s.coordinatorConn.Invoke(ctx, "/mpc.coordinator.v1.SessionCoordinator/ReportCompletion", coordReq, coordResp) + if err != nil { + logger.Error("Failed to proxy ReportCompletion to coordinator", + zap.String("session_id", req.SessionId), + zap.String("party_id", req.PartyId), + zap.Error(err)) + return nil, err + } + + logger.Info("Proxied ReportCompletion to coordinator", + zap.String("session_id", req.SessionId), + zap.String("party_id", req.PartyId), + zap.Bool("all_completed", coordResp.AllCompleted)) + + return &pb.ReportCompletionResponse{ + Success: true, + AllCompleted: coordResp.AllCompleted, + }, nil +} + +// GetSessionStatus proxies the status request to Session Coordinator +func (s *MessageRouterServer) GetSessionStatus( + ctx context.Context, + req *pb.GetSessionStatusRequest, +) (*pb.GetSessionStatusResponse, error) { + if s.coordinatorConn == nil { + return nil, status.Error(codes.Unavailable, "session coordinator not configured") + } + + coordReq := &coordinator.GetSessionStatusRequest{ + SessionId: req.SessionId, + } + + coordResp := &coordinator.GetSessionStatusResponse{} + err := s.coordinatorConn.Invoke(ctx, "/mpc.coordinator.v1.SessionCoordinator/GetSessionStatus", coordReq, coordResp) + if err != nil { + logger.Error("Failed to proxy GetSessionStatus to coordinator", + zap.String("session_id", req.SessionId), + zap.Error(err)) + return nil, err + } + + return &pb.GetSessionStatusResponse{ + SessionId: req.SessionId, + Status: coordResp.Status, + ThresholdN: coordResp.TotalParties, // Use TotalParties as N + ThresholdT: coordResp.CompletedParties, // Return completed count in ThresholdT for info + }, nil +} + +// SubmitDelegateShare proxies the delegate share submission to Session Coordinator +func (s *MessageRouterServer) SubmitDelegateShare( + ctx context.Context, + req *pb.SubmitDelegateShareRequest, +) (*pb.SubmitDelegateShareResponse, error) { + if s.coordinatorConn == nil { + return nil, status.Error(codes.Unavailable, "session coordinator not configured") + } + + coordReq := &coordinator.SubmitDelegateShareRequest{ + SessionId: req.SessionId, + PartyId: req.PartyId, + EncryptedShare: req.EncryptedShare, + PublicKey: req.PublicKey, + PartyIndex: req.PartyIndex, + } + + coordResp := &coordinator.SubmitDelegateShareResponse{} + err := s.coordinatorConn.Invoke(ctx, "/mpc.coordinator.v1.SessionCoordinator/SubmitDelegateShare", coordReq, coordResp) + if err != nil { + logger.Error("Failed to proxy SubmitDelegateShare to coordinator", + zap.String("session_id", req.SessionId), + zap.String("party_id", req.PartyId), + zap.Error(err)) + return nil, err + } + + logger.Info("Proxied SubmitDelegateShare to coordinator", + zap.String("session_id", req.SessionId), + zap.String("party_id", req.PartyId), + zap.Bool("success", coordResp.Success)) + + return &pb.SubmitDelegateShareResponse{ + Success: coordResp.Success, + }, nil +} diff --git a/backend/mpc-system/services/message-router/cmd/server/main.go b/backend/mpc-system/services/message-router/cmd/server/main.go index 52e8792c..b1ba1a8e 100644 --- a/backend/mpc-system/services/message-router/cmd/server/main.go +++ b/backend/mpc-system/services/message-router/cmd/server/main.go @@ -15,6 +15,7 @@ import ( "github.com/gin-gonic/gin" _ "github.com/lib/pq" "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/reflection" pb "github.com/rwadurian/mpc-system/api/grpc/router/v1" @@ -77,6 +78,26 @@ func main() { routeMessageUC := use_cases.NewRouteMessageUseCase(messageRepo, messageBroker) getPendingMessagesUC := use_cases.NewGetPendingMessagesUseCase(messageRepo) + // Connect to Session Coordinator for proxying session operations + // This allows server-parties to only connect to Message Router + coordinatorAddr := os.Getenv("SESSION_COORDINATOR_ADDR") + if coordinatorAddr == "" { + coordinatorAddr = "session-coordinator:50051" // Default in docker-compose + } + var coordinatorConn *grpc.ClientConn + coordinatorConn, err = grpc.NewClient(coordinatorAddr, + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + logger.Warn("Failed to connect to Session Coordinator for proxying (session operations will be unavailable)", + zap.String("address", coordinatorAddr), + zap.Error(err)) + } else { + defer coordinatorConn.Close() + logger.Info("Connected to Session Coordinator for proxying session operations", + zap.String("address", coordinatorAddr)) + } + // Start message cleanup background job go runMessageCleanup(messageRepo) @@ -92,7 +113,7 @@ func main() { // Start gRPC server go func() { - if err := startGRPCServer(cfg, routeMessageUC, getPendingMessagesUC, messageBroker, partyRegistry, eventBroadcaster, messageRepo); err != nil { + if err := startGRPCServer(cfg, routeMessageUC, getPendingMessagesUC, messageBroker, partyRegistry, eventBroadcaster, messageRepo, coordinatorConn); err != nil { errChan <- fmt.Errorf("gRPC server error: %w", err) } }() @@ -189,6 +210,7 @@ func startGRPCServer( partyRegistry *domain.PartyRegistry, eventBroadcaster *domain.SessionEventBroadcaster, messageRepo *postgres.MessagePostgresRepo, + coordinatorConn *grpc.ClientConn, ) error { listener, err := net.Listen("tcp", fmt.Sprintf(":%d", cfg.Server.GRPCPort)) if err != nil { @@ -206,6 +228,13 @@ func startGRPCServer( eventBroadcaster, messageRepo, ) + + // Set coordinator connection for proxying session operations + // This allows server-parties to only connect to Message Router + if coordinatorConn != nil { + messageRouterServer.SetCoordinatorConnection(coordinatorConn) + } + pb.RegisterMessageRouterServer(grpcServer, messageRouterServer) // Enable reflection for debugging diff --git a/backend/mpc-system/services/server-party-api/cmd/server/main.go b/backend/mpc-system/services/server-party-api/cmd/server/main.go index 66c6515a..578d965b 100644 --- a/backend/mpc-system/services/server-party-api/cmd/server/main.go +++ b/backend/mpc-system/services/server-party-api/cmd/server/main.go @@ -14,18 +14,15 @@ import ( "github.com/gin-gonic/gin" "github.com/google/uuid" + router "github.com/rwadurian/mpc-system/api/grpc/router/v1" "github.com/rwadurian/mpc-system/pkg/config" "github.com/rwadurian/mpc-system/pkg/crypto" "github.com/rwadurian/mpc-system/pkg/logger" grpcclient "github.com/rwadurian/mpc-system/services/server-party/adapters/output/grpc" "github.com/rwadurian/mpc-system/services/server-party/application/use_cases" - "github.com/rwadurian/mpc-system/services/server-party/infrastructure/cache" "go.uber.org/zap" ) -// Global share cache for delegate parties -var globalShareCache *cache.ShareCache - func main() { // Parse flags configPath := flag.String("config", "", "Path to config file") @@ -48,14 +45,10 @@ func main() { } defer logger.Sync() - logger.Info("Starting Server Party API Service (Delegate Mode)", + logger.Info("Starting Delegate Party Service", zap.String("environment", cfg.Server.Environment), zap.Int("http_port", cfg.Server.HTTPPort)) - // Initialize share cache for delegate parties (15 minute TTL) - globalShareCache = cache.NewShareCache(15 * time.Minute) - logger.Info("Share cache initialized", zap.Duration("ttl", 15*time.Minute)) - // Initialize crypto service with master key from environment masterKeyHex := os.Getenv("MPC_CRYPTO_MASTER_KEY") if masterKeyHex == "" { @@ -70,29 +63,14 @@ func main() { logger.Fatal("Failed to create crypto service", zap.Error(err)) } - // Get API key for authentication - apiKey := os.Getenv("MPC_API_KEY") - if apiKey == "" { - logger.Warn("MPC_API_KEY not set, API will be unprotected") - } - - // Get gRPC service addresses from environment - coordinatorAddr := os.Getenv("SESSION_COORDINATOR_ADDR") - if coordinatorAddr == "" { - coordinatorAddr = "session-coordinator:50051" - } + // Get Message Router address from environment + // Delegate party (like all parties) ONLY connects to Message Router routerAddr := os.Getenv("MESSAGE_ROUTER_ADDR") if routerAddr == "" { routerAddr = "message-router:50051" } - // Initialize gRPC clients - sessionClient, err := grpcclient.NewSessionCoordinatorClient(coordinatorAddr) - if err != nil { - logger.Fatal("Failed to connect to session coordinator", zap.Error(err)) - } - defer sessionClient.Close() - + // Initialize Message Router client (the only gRPC connection needed) messageRouter, err := grpcclient.NewMessageRouterClient(routerAddr) if err != nil { logger.Fatal("Failed to connect to message router", zap.Error(err)) @@ -106,44 +84,74 @@ func main() { // Get party ID from environment (or use default) partyID := os.Getenv("PARTY_ID") if partyID == "" { - partyID = "server-party-api" + partyID = "delegate-party" } // Force PARTY_ROLE to delegate for this service - os.Setenv("PARTY_ROLE", "delegate") + partyRole := "delegate" // Register this party as a delegate party with Message Router logger.Info("Registering party with Message Router", zap.String("party_id", partyID), - zap.String("role", "delegate")) + zap.String("role", partyRole)) - if err := messageRouter.RegisterParty(ctx, partyID, "delegate", "1.0.0"); err != nil { + if err := messageRouter.RegisterParty(ctx, partyID, partyRole, "1.0.0"); err != nil { logger.Fatal("Failed to register party", zap.Error(err)) } logger.Info("Party registered successfully", zap.String("party_id", partyID), - zap.String("role", "delegate")) + zap.String("role", partyRole)) + + // Start heartbeat to keep party registered + heartbeatCancel := messageRouter.StartHeartbeat(ctx, partyID, 30*time.Second, func(pendingCount int32) { + if pendingCount > 0 { + logger.Info("Pending messages detected via heartbeat", + zap.String("party_id", partyID), + zap.Int32("pending_count", pendingCount)) + } + }) + defer heartbeatCancel() + logger.Info("Heartbeat started", zap.String("party_id", partyID), zap.Duration("interval", 30*time.Second)) // Initialize use cases with nil keyShareRepo (delegate doesn't use DB) - // The use cases check PARTY_ROLE env var to determine behavior + // MessageRouter handles both messaging AND session operations (proxied to coordinator) participateKeygenUC := use_cases.NewParticipateKeygenUseCase( nil, // No database storage for delegate - sessionClient, + messageRouter, messageRouter, cryptoService, ) participateSigningUC := use_cases.NewParticipateSigningUseCase( nil, // No database storage for delegate - sessionClient, + messageRouter, messageRouter, cryptoService, ) - // Start HTTP server + // Subscribe to session events and handle them automatically (SAME AS SERVER-PARTY) + logger.Info("Subscribing to session events", zap.String("party_id", partyID)) + + eventHandler := createSessionEventHandler( + ctx, + partyID, + participateKeygenUC, + participateSigningUC, + messageRouter, + ) + + if err := messageRouter.SubscribeSessionEvents(ctx, partyID, eventHandler); err != nil { + logger.Fatal("Failed to subscribe to session events", zap.Error(err)) + } + + logger.Info("Delegate party initialized successfully (party-driven architecture)", + zap.String("party_id", partyID), + zap.String("role", partyRole)) + + // Start HTTP server (health check only) errChan := make(chan error, 1) go func() { - if err := startHTTPServer(cfg, participateKeygenUC, participateSigningUC, cryptoService, apiKey); err != nil { + if err := startHTTPServer(cfg); err != nil { errChan <- fmt.Errorf("HTTP server error: %w", err) } }() @@ -165,258 +173,162 @@ func main() { time.Sleep(5 * time.Second) logger.Info("Shutdown complete") - - _ = ctx } -func startHTTPServer( - cfg *config.Config, - participateKeygenUC *use_cases.ParticipateKeygenUseCase, - participateSigningUC *use_cases.ParticipateSigningUseCase, - cryptoService *crypto.CryptoService, - apiKey string, -) error { +// startHTTPServer starts HTTP server for health checks only +func startHTTPServer(cfg *config.Config) error { if cfg.Server.Environment == "production" { gin.SetMode(gin.ReleaseMode) } - router := gin.New() - router.Use(gin.Recovery()) - router.Use(gin.Logger()) + r := gin.New() + r.Use(gin.Recovery()) - // Health check - router.GET("/health", func(c *gin.Context) { + // Health check only + r.GET("/health", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "status": "healthy", - "service": "server-party-api", + "service": "delegate-party", "role": "delegate", }) }) - // API routes with optional authentication - api := router.Group("/api/v1") - if apiKey != "" { - api.Use(apiKeyAuth(apiKey)) - } - - { - // Keygen participation - same as server-party but returns share - api.POST("/keygen/participate", func(c *gin.Context) { - var req struct { - SessionID string `json:"session_id" binding:"required"` - PartyID string `json:"party_id" binding:"required"` - JoinToken string `json:"join_token" binding:"required"` - } - - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - sessionID, err := uuid.Parse(req.SessionID) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "invalid session_id format"}) - return - } - - logger.Info("Starting keygen participation (delegate)", - zap.String("session_id", req.SessionID), - zap.String("party_id", req.PartyID)) - - // Execute keygen synchronously for delegate party - ctx, cancel := context.WithTimeout(c.Request.Context(), 10*time.Minute) - defer cancel() - - input := use_cases.ParticipateKeygenInput{ - SessionID: sessionID, - PartyID: req.PartyID, - JoinToken: req.JoinToken, - } - - output, err := participateKeygenUC.Execute(ctx, input) - if err != nil { - logger.Error("Keygen participation failed", - zap.String("session_id", req.SessionID), - zap.String("party_id", req.PartyID), - zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{ - "error": "keygen failed", - "details": err.Error(), - "session_id": req.SessionID, - "party_id": req.PartyID, - }) - return - } - - logger.Info("Keygen participation completed (delegate)", - zap.String("session_id", req.SessionID), - zap.String("party_id", req.PartyID), - zap.Bool("success", output.Success)) - - // For delegate party, ShareForUser contains the encrypted share - if len(output.ShareForUser) == 0 { - c.JSON(http.StatusInternalServerError, gin.H{ - "error": "share not generated for delegate party", - }) - return - } - - // Store in cache for retrieval (optional, for async pattern) - globalShareCache.Store(sessionID, req.PartyID, output.ShareForUser, output.PublicKey) - - // Return share directly - c.JSON(http.StatusOK, gin.H{ - "success": true, - "session_id": req.SessionID, - "party_id": req.PartyID, - "party_index": output.KeyShare.PartyIndex, - "share_data": hex.EncodeToString(output.ShareForUser), - "public_key": hex.EncodeToString(output.PublicKey), - }) - }) - - // Signing with user-provided share - api.POST("/sign/participate", func(c *gin.Context) { - var req struct { - SessionID string `json:"session_id" binding:"required"` - PartyID string `json:"party_id" binding:"required"` - JoinToken string `json:"join_token" binding:"required"` - ShareData string `json:"share_data" binding:"required"` // User's encrypted share - MessageHash string `json:"message_hash"` - } - - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - sessionID, err := uuid.Parse(req.SessionID) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "invalid session_id format"}) - return - } - - shareData, err := hex.DecodeString(req.ShareData) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "invalid share_data format (expected hex)"}) - return - } - - var messageHash []byte - if req.MessageHash != "" { - messageHash, err = hex.DecodeString(req.MessageHash) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "invalid message_hash format (expected hex)"}) - return - } - } - - logger.Info("Starting signing participation (delegate)", - zap.String("session_id", req.SessionID), - zap.String("party_id", req.PartyID)) - - // Execute signing synchronously - ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Minute) - defer cancel() - - input := use_cases.ParticipateSigningInput{ - SessionID: sessionID, - PartyID: req.PartyID, - JoinToken: req.JoinToken, - MessageHash: messageHash, - UserShareData: shareData, // Pass user's share - } - - output, err := participateSigningUC.Execute(ctx, input) - if err != nil { - logger.Error("Signing participation failed", - zap.String("session_id", req.SessionID), - zap.String("party_id", req.PartyID), - zap.Error(err)) - c.JSON(http.StatusInternalServerError, gin.H{ - "error": "signing failed", - "details": err.Error(), - "session_id": req.SessionID, - "party_id": req.PartyID, - }) - return - } - - logger.Info("Signing participation completed (delegate)", - zap.String("session_id", req.SessionID), - zap.String("party_id", req.PartyID), - zap.Bool("success", output.Success)) - - // Return signature - var rHex, sHex string - if output.R != nil { - rHex = hex.EncodeToString(output.R.Bytes()) - } - if output.S != nil { - sHex = hex.EncodeToString(output.S.Bytes()) - } - - c.JSON(http.StatusOK, gin.H{ - "success": true, - "session_id": req.SessionID, - "party_id": req.PartyID, - "signature": hex.EncodeToString(output.Signature), - "r": rHex, - "s": sHex, - }) - }) - - // Get user share from cache (for async keygen pattern) - api.GET("/sessions/:session_id/user-share", func(c *gin.Context) { - sessionIDStr := c.Param("session_id") - - sessionID, err := uuid.Parse(sessionIDStr) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{ - "error": "invalid session_id format", - }) - return - } - - // Retrieve and delete share from cache (one-time retrieval) - entry, exists := globalShareCache.GetAndDelete(sessionID) - if !exists { - c.JSON(http.StatusNotFound, gin.H{ - "error": "Share not found or already retrieved", - "note": "Shares can only be retrieved once and expire after 15 minutes", - }) - return - } - - logger.Info("User share retrieved successfully", - zap.String("session_id", sessionIDStr), - zap.String("party_id", entry.PartyID)) - - c.JSON(http.StatusOK, gin.H{ - "session_id": sessionIDStr, - "party_id": entry.PartyID, - "share": hex.EncodeToString(entry.Share), - "public_key": hex.EncodeToString(entry.PublicKey), - "note": "This share has been deleted from memory and cannot be retrieved again", - }) - }) - } - - logger.Info("Starting HTTP server", zap.Int("port", cfg.Server.HTTPPort)) - return router.Run(fmt.Sprintf(":%d", cfg.Server.HTTPPort)) + logger.Info("Starting HTTP server (health check only)", zap.Int("port", cfg.Server.HTTPPort)) + return r.Run(fmt.Sprintf(":%d", cfg.Server.HTTPPort)) } -func apiKeyAuth(expectedKey string) gin.HandlerFunc { - return func(c *gin.Context) { - apiKey := c.GetHeader("X-API-Key") - if apiKey == "" { - apiKey = c.Query("api_key") +// createSessionEventHandler creates a handler for session events (party-driven architecture) +// Delegate party automatically responds to session creation events by joining keygen or signing sessions +// After keygen, it submits the user's share to Session Coordinator (instead of saving to DB) +func createSessionEventHandler( + ctx context.Context, + partyID string, + participateKeygenUC *use_cases.ParticipateKeygenUseCase, + participateSigningUC *use_cases.ParticipateSigningUseCase, + messageRouter *grpcclient.MessageRouterClient, +) func(*router.SessionEvent) { + return func(event *router.SessionEvent) { + // Check if this party is selected for the session + isSelected := false + for _, selectedParty := range event.SelectedParties { + if selectedParty == partyID { + isSelected = true + break + } } - if apiKey != expectedKey { - c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid or missing API key"}) - c.Abort() + + if !isSelected { + logger.Debug("Party not selected for this session", + zap.String("session_id", event.SessionId), + zap.String("party_id", partyID)) return } - c.Next() + + // Get join token for this party + joinToken, exists := event.JoinTokens[partyID] + if !exists { + logger.Error("No join token found for party", + zap.String("session_id", event.SessionId), + zap.String("party_id", partyID)) + return + } + + logger.Info("Delegate party selected for session, auto-participating", + zap.String("session_id", event.SessionId), + zap.String("party_id", partyID), + zap.String("event_type", event.EventType)) + + // Parse session ID + sessionID, err := uuid.Parse(event.SessionId) + if err != nil { + logger.Error("Invalid session ID", zap.Error(err)) + return + } + + // Automatically participate based on session type + go func() { + ctx := context.Background() + + // Determine session type from event + if event.EventType == "session_created" { + // Check if it's keygen or sign based on message_hash + if len(event.MessageHash) == 0 { + // Keygen session + logger.Info("Auto-participating in keygen session (delegate)", + zap.String("session_id", event.SessionId), + zap.String("party_id", partyID)) + + input := use_cases.ParticipateKeygenInput{ + SessionID: sessionID, + PartyID: partyID, + JoinToken: joinToken, + } + + result, err := participateKeygenUC.Execute(ctx, input) + if err != nil { + logger.Error("Keygen participation failed", + zap.Error(err), + zap.String("session_id", event.SessionId)) + return + } + + logger.Info("Keygen participation completed (delegate)", + zap.String("session_id", event.SessionId), + zap.String("public_key", hex.EncodeToString(result.PublicKey))) + + // Delegate party: Submit share to Session Coordinator (via Message Router) + // This is the key difference from persistent party which saves to DB + if len(result.ShareForUser) > 0 { + logger.Info("Submitting delegate share to Session Coordinator", + zap.String("session_id", event.SessionId), + zap.String("party_id", partyID), + zap.Int("share_size", len(result.ShareForUser))) + + if err := messageRouter.SubmitDelegateShare( + ctx, + event.SessionId, + partyID, + result.ShareForUser, + result.PublicKey, + int32(result.KeyShare.PartyIndex), + ); err != nil { + logger.Error("Failed to submit delegate share", + zap.Error(err), + zap.String("session_id", event.SessionId)) + } else { + logger.Info("Delegate share submitted successfully", + zap.String("session_id", event.SessionId), + zap.String("party_id", partyID)) + } + } + } else { + // Sign session + logger.Info("Auto-participating in sign session (delegate)", + zap.String("session_id", event.SessionId), + zap.String("party_id", partyID)) + + input := use_cases.ParticipateSigningInput{ + SessionID: sessionID, + PartyID: partyID, + JoinToken: joinToken, + MessageHash: event.MessageHash, + // Note: For signing, user must provide their share via Account Service + // This will be passed through the session event or retrieved separately + } + + result, err := participateSigningUC.Execute(ctx, input) + if err != nil { + logger.Error("Signing participation failed", + zap.Error(err), + zap.String("session_id", event.SessionId)) + return + } + + logger.Info("Signing participation completed (delegate)", + zap.String("session_id", event.SessionId), + zap.String("signature", hex.EncodeToString(result.Signature))) + } + } + }() } } diff --git a/backend/mpc-system/services/server-party/adapters/output/grpc/message_router_client.go b/backend/mpc-system/services/server-party/adapters/output/grpc/message_router_client.go index e011df4d..d69c3f4e 100644 --- a/backend/mpc-system/services/server-party/adapters/output/grpc/message_router_client.go +++ b/backend/mpc-system/services/server-party/adapters/output/grpc/message_router_client.go @@ -599,3 +599,194 @@ func (c *MessageRouterClient) StartHeartbeat( return cancel } + +// ============================================ +// Session Operations (Proxied through Message Router) +// These methods allow server-parties to only connect to Message Router +// ============================================ + +// JoinSession joins an MPC session via Message Router +// Includes automatic retry with exponential backoff for transient failures +func (c *MessageRouterClient) JoinSession( + ctx context.Context, + sessionID uuid.UUID, + partyID, joinToken string, +) (*use_cases.SessionInfo, error) { + req := &router.JoinSessionRequest{ + SessionId: sessionID.String(), + PartyId: partyID, + JoinToken: joinToken, + DeviceInfo: &router.DeviceInfo{ + DeviceType: "server", + DeviceId: partyID, + Platform: "linux", + AppVersion: "1.0.0", + }, + } + + return retry.Do(ctx, c.retryCfg, "JoinSession", func() (*use_cases.SessionInfo, error) { + resp := &router.JoinSessionResponse{} + err := c.getConn().Invoke(ctx, "/mpc.router.v1.MessageRouter/JoinSession", req, resp) + if err != nil { + return nil, err + } + + if !resp.Success { + logger.Error("Join session failed", zap.String("session_id", sessionID.String())) + return nil, use_cases.ErrInvalidSession + } + + // Convert response to SessionInfo + participants := make([]use_cases.ParticipantInfo, len(resp.OtherParties)) + for i, p := range resp.OtherParties { + logger.Debug("Received party_index from Message Router", + zap.String("party_id", p.PartyId), + zap.Int32("party_index", p.PartyIndex)) + + participants[i] = use_cases.ParticipantInfo{ + PartyID: p.PartyId, + PartyIndex: int(p.PartyIndex), + } + } + + sessionInfo := &use_cases.SessionInfo{ + SessionID: sessionID, + Participants: participants, + } + + if resp.SessionInfo != nil { + sessionInfo.SessionType = resp.SessionInfo.SessionType + sessionInfo.ThresholdN = int(resp.SessionInfo.ThresholdN) + sessionInfo.ThresholdT = int(resp.SessionInfo.ThresholdT) + sessionInfo.MessageHash = resp.SessionInfo.MessageHash + } + + logger.Info("Joined session via Message Router", + zap.String("session_id", sessionID.String()), + zap.String("party_id", partyID), + zap.String("session_type", sessionInfo.SessionType)) + + return sessionInfo, nil + }) +} + +// ReportCompletion reports protocol completion via Message Router +// Includes automatic retry with exponential backoff for transient failures +func (c *MessageRouterClient) ReportCompletion( + ctx context.Context, + sessionID uuid.UUID, + partyID string, + resultData []byte, +) error { + req := &router.ReportCompletionRequest{ + SessionId: sessionID.String(), + PartyId: partyID, + PublicKey: resultData, // For keygen: public key; for signing: signature + } + + return retry.DoVoid(ctx, c.retryCfg, "ReportCompletion", func() error { + resp := &router.ReportCompletionResponse{} + err := c.getConn().Invoke(ctx, "/mpc.router.v1.MessageRouter/ReportCompletion", req, resp) + if err != nil { + return err + } + + logger.Info("Reported completion via Message Router", + zap.String("session_id", sessionID.String()), + zap.String("party_id", partyID), + zap.Bool("all_completed", resp.AllCompleted)) + + return nil + }) +} + +// MarkPartyReady marks the party as ready to start the protocol via Message Router +// Includes automatic retry with exponential backoff for transient failures +func (c *MessageRouterClient) MarkPartyReady( + ctx context.Context, + sessionID uuid.UUID, + partyID string, +) (bool, error) { + req := &router.MarkPartyReadyRequest{ + SessionId: sessionID.String(), + PartyId: partyID, + } + + return retry.Do(ctx, c.retryCfg, "MarkPartyReady", func() (bool, error) { + resp := &router.MarkPartyReadyResponse{} + err := c.getConn().Invoke(ctx, "/mpc.router.v1.MessageRouter/MarkPartyReady", req, resp) + if err != nil { + return false, err + } + + logger.Info("Marked party ready via Message Router", + zap.String("session_id", sessionID.String()), + zap.String("party_id", partyID), + zap.Bool("all_ready", resp.AllReady)) + + return resp.AllReady, nil + }) +} + +// GetSessionStatus gets the current session status via Message Router +// Includes automatic retry with exponential backoff for transient failures +func (c *MessageRouterClient) GetSessionStatus( + ctx context.Context, + sessionID uuid.UUID, +) (string, error) { + req := &router.GetSessionStatusRequest{ + SessionId: sessionID.String(), + } + + return retry.Do(ctx, c.retryCfg, "GetSessionStatus", func() (string, error) { + resp := &router.GetSessionStatusResponse{} + err := c.getConn().Invoke(ctx, "/mpc.router.v1.MessageRouter/GetSessionStatus", req, resp) + if err != nil { + return "", err + } + + return resp.Status, nil + }) +} + +// SubmitDelegateShare submits user's share from delegate party to Session Coordinator +// This is called after keygen by delegate parties to return the share for user retrieval +// Includes automatic retry with exponential backoff for transient failures +func (c *MessageRouterClient) SubmitDelegateShare( + ctx context.Context, + sessionID string, + partyID string, + encryptedShare []byte, + publicKey []byte, + partyIndex int32, +) error { + req := &router.SubmitDelegateShareRequest{ + SessionId: sessionID, + PartyId: partyID, + EncryptedShare: encryptedShare, + PublicKey: publicKey, + PartyIndex: partyIndex, + } + + return retry.DoVoid(ctx, c.retryCfg, "SubmitDelegateShare", func() error { + resp := &router.SubmitDelegateShareResponse{} + err := c.getConn().Invoke(ctx, "/mpc.router.v1.MessageRouter/SubmitDelegateShare", req, resp) + if err != nil { + return err + } + + if !resp.Success { + logger.Error("Submit delegate share failed", + zap.String("session_id", sessionID), + zap.String("party_id", partyID)) + return use_cases.ErrKeygenFailed + } + + logger.Info("Delegate share submitted successfully via Message Router", + zap.String("session_id", sessionID), + zap.String("party_id", partyID), + zap.Int("share_size", len(encryptedShare))) + + return nil + }) +} diff --git a/backend/mpc-system/services/server-party/cmd/server/main.go b/backend/mpc-system/services/server-party/cmd/server/main.go index 11900617..e504f29d 100644 --- a/backend/mpc-system/services/server-party/cmd/server/main.go +++ b/backend/mpc-system/services/server-party/cmd/server/main.go @@ -81,23 +81,15 @@ func main() { logger.Fatal("Failed to create crypto service", zap.Error(err)) } - // Get gRPC service addresses from environment - coordinatorAddr := os.Getenv("SESSION_COORDINATOR_ADDR") - if coordinatorAddr == "" { - coordinatorAddr = "localhost:9091" - } + // Get Message Router address from environment + // Server-parties ONLY connect to Message Router (not Session Coordinator) + // Message Router proxies session operations to Session Coordinator routerAddr := os.Getenv("MESSAGE_ROUTER_ADDR") if routerAddr == "" { routerAddr = "localhost:9092" } - // Initialize gRPC clients - sessionClient, err := grpcclient.NewSessionCoordinatorClient(coordinatorAddr) - if err != nil { - logger.Fatal("Failed to connect to session coordinator", zap.Error(err)) - } - defer sessionClient.Close() - + // Initialize Message Router client (the only gRPC connection needed) messageRouter, err := grpcclient.NewMessageRouterClient(routerAddr) if err != nil { logger.Fatal("Failed to connect to message router", zap.Error(err)) @@ -107,16 +99,17 @@ func main() { // Initialize repositories keyShareRepo := postgres.NewKeySharePostgresRepo(db) - // Initialize use cases with real gRPC clients + // Initialize use cases with Message Router client + // Message Router handles both messaging AND session operations (proxied to coordinator) participateKeygenUC := use_cases.NewParticipateKeygenUseCase( keyShareRepo, - sessionClient, + messageRouter, // MessageRouterClient implements SessionCoordinatorClient interface messageRouter, cryptoService, ) participateSigningUC := use_cases.NewParticipateSigningUseCase( keyShareRepo, - sessionClient, + messageRouter, // MessageRouterClient implements SessionCoordinatorClient interface messageRouter, cryptoService, ) @@ -192,7 +185,6 @@ func main() { partyID, participateKeygenUC, participateSigningUC, - sessionClient, ) if err := messageRouter.SubscribeSessionEvents(ctx, partyID, eventHandler); err != nil { @@ -535,7 +527,6 @@ func createSessionEventHandler( partyID string, participateKeygenUC *use_cases.ParticipateKeygenUseCase, participateSigningUC *use_cases.ParticipateSigningUseCase, - sessionClient *grpcclient.SessionCoordinatorClient, ) func(*router.SessionEvent) { return func(event *router.SessionEvent) { // Check if this party is selected for the session @@ -617,6 +608,15 @@ func createSessionEventHandler( MessageHash: event.MessageHash, } + // If this party is the delegate party and user share is provided, use it + if event.DelegateUserShare != nil && event.DelegateUserShare.DelegatePartyId == partyID { + input.UserShareData = event.DelegateUserShare.EncryptedShare + logger.Info("Using user-provided share for delegate party signing", + zap.String("session_id", event.SessionId), + zap.String("party_id", partyID), + zap.Int32("party_index", event.DelegateUserShare.PartyIndex)) + } + result, err := participateSigningUC.Execute(ctx, input) if err != nil { logger.Error("Signing participation failed", diff --git a/backend/mpc-system/services/session-coordinator/adapters/input/grpc/session_grpc_handler.go b/backend/mpc-system/services/session-coordinator/adapters/input/grpc/session_grpc_handler.go index f0b38954..2f986ed9 100644 --- a/backend/mpc-system/services/session-coordinator/adapters/input/grpc/session_grpc_handler.go +++ b/backend/mpc-system/services/session-coordinator/adapters/input/grpc/session_grpc_handler.go @@ -2,6 +2,7 @@ package grpc import ( "context" + "sync" "time" "github.com/google/uuid" @@ -17,6 +18,79 @@ import ( "google.golang.org/grpc/status" ) +// DelegateShareEntry represents a delegate party's share for user retrieval +type DelegateShareEntry struct { + PartyID string + EncryptedShare []byte + PublicKey []byte + PartyIndex int32 + CreatedAt time.Time +} + +// DelegateShareCache stores delegate shares temporarily for user retrieval +type DelegateShareCache struct { + mu sync.RWMutex + shares map[string]*DelegateShareEntry // sessionID -> share + ttl time.Duration +} + +// NewDelegateShareCache creates a new cache with TTL +func NewDelegateShareCache(ttl time.Duration) *DelegateShareCache { + cache := &DelegateShareCache{ + shares: make(map[string]*DelegateShareEntry), + ttl: ttl, + } + // Start cleanup goroutine + go cache.cleanupLoop() + return cache +} + +// Store stores a delegate share +func (c *DelegateShareCache) Store(sessionID string, entry *DelegateShareEntry) { + c.mu.Lock() + defer c.mu.Unlock() + entry.CreatedAt = time.Now() + c.shares[sessionID] = entry + logger.Info("Delegate share stored", + zap.String("session_id", sessionID), + zap.String("party_id", entry.PartyID)) +} + +// Get retrieves and deletes a delegate share (one-time retrieval) +func (c *DelegateShareCache) Get(sessionID string) (*DelegateShareEntry, bool) { + c.mu.Lock() + defer c.mu.Unlock() + entry, exists := c.shares[sessionID] + if exists { + delete(c.shares, sessionID) + logger.Info("Delegate share retrieved and deleted", + zap.String("session_id", sessionID), + zap.String("party_id", entry.PartyID)) + } + return entry, exists +} + +// cleanupLoop removes expired entries +func (c *DelegateShareCache) cleanupLoop() { + ticker := time.NewTicker(c.ttl / 2) + defer ticker.Stop() + for range ticker.C { + c.mu.Lock() + now := time.Now() + for sessionID, entry := range c.shares { + if now.Sub(entry.CreatedAt) > c.ttl { + delete(c.shares, sessionID) + logger.Debug("Expired delegate share removed", + zap.String("session_id", sessionID)) + } + } + c.mu.Unlock() + } +} + +// Global delegate share cache (15 minute TTL) +var delegateShareCache = NewDelegateShareCache(15 * time.Minute) + // SessionCoordinatorServer implements the gRPC SessionCoordinator service type SessionCoordinatorServer struct { pb.UnimplementedSessionCoordinatorServer @@ -86,6 +160,15 @@ func (s *SessionCoordinatorServer) CreateSession( ExpiresIn: time.Duration(req.ExpiresInSeconds) * time.Second, } + // Add delegate user share if provided (for sign sessions with delegate party) + if req.DelegateUserShare != nil { + inputData.DelegateUserShare = &input.DelegateUserShare{ + DelegatePartyID: req.DelegateUserShare.DelegatePartyId, + EncryptedShare: req.DelegateUserShare.EncryptedShare, + PartyIndex: req.DelegateUserShare.PartyIndex, + } + } + // Execute use case output, err := s.createSessionUC.Execute(ctx, inputData) if err != nil { @@ -194,13 +277,34 @@ func (s *SessionCoordinatorServer) GetSessionStatus( } } - return &pb.GetSessionStatusResponse{ + resp := &pb.GetSessionStatusResponse{ Status: output.Status, CompletedParties: int32(completedParties), TotalParties: int32(len(output.Participants)), + SessionType: output.SessionType, PublicKey: output.PublicKey, Signature: output.Signature, - }, nil + HasDelegate: output.HasDelegate, // Only meaningful for keygen sessions + } + + // Try to get delegate share from cache + // Only include if session has delegate and is completed + if output.HasDelegate && output.Status == "completed" { + entry, found := delegateShareCache.Get(req.SessionId) + if found { + resp.DelegateShare = &pb.DelegateShareInfo{ + EncryptedShare: entry.EncryptedShare, + PartyIndex: entry.PartyIndex, + PartyId: entry.PartyID, + } + logger.Info("Delegate share included in GetSessionStatus response", + zap.String("session_id", req.SessionId), + zap.String("party_id", entry.PartyID)) + } + // If has_delegate=true but delegate_share=nil, share was already retrieved + } + + return resp, nil } // ReportCompletion reports that a participant has completed @@ -323,6 +427,40 @@ func (s *SessionCoordinatorServer) StartSession( }, nil } +// SubmitDelegateShare stores a delegate party's share for user retrieval +func (s *SessionCoordinatorServer) SubmitDelegateShare( + ctx context.Context, + req *pb.SubmitDelegateShareRequest, +) (*pb.SubmitDelegateShareResponse, error) { + if req.SessionId == "" { + return nil, status.Error(codes.InvalidArgument, "session_id is required") + } + if req.PartyId == "" { + return nil, status.Error(codes.InvalidArgument, "party_id is required") + } + if len(req.EncryptedShare) == 0 { + return nil, status.Error(codes.InvalidArgument, "encrypted_share is required") + } + + // Store in cache + entry := &DelegateShareEntry{ + PartyID: req.PartyId, + EncryptedShare: req.EncryptedShare, + PublicKey: req.PublicKey, + PartyIndex: req.PartyIndex, + } + delegateShareCache.Store(req.SessionId, entry) + + logger.Info("Delegate share submitted", + zap.String("session_id", req.SessionId), + zap.String("party_id", req.PartyId), + zap.Int("share_size", len(req.EncryptedShare))) + + return &pb.SubmitDelegateShareResponse{ + Success: true, + }, nil +} + // toGRPCError converts domain errors to gRPC errors func toGRPCError(err error) error { switch err { diff --git a/backend/mpc-system/services/session-coordinator/adapters/input/http/session_http_handler.go b/backend/mpc-system/services/session-coordinator/adapters/input/http/session_http_handler.go index 69892dd8..93a46685 100644 --- a/backend/mpc-system/services/session-coordinator/adapters/input/http/session_http_handler.go +++ b/backend/mpc-system/services/session-coordinator/adapters/input/http/session_http_handler.go @@ -128,7 +128,7 @@ func (h *SessionHTTPHandler) CreateSession(c *gin.Context) { expiresIn := time.Duration(req.ExpiresIn) * time.Second if expiresIn == 0 { - expiresIn = 10 * time.Minute // Default + expiresIn = 24 * time.Hour // Default: 24-hour session validity } inputData := input.CreateSessionInput{ diff --git a/backend/mpc-system/services/session-coordinator/adapters/output/grpc/message_router_client.go b/backend/mpc-system/services/session-coordinator/adapters/output/grpc/message_router_client.go index 7019366e..8b3f6351 100644 --- a/backend/mpc-system/services/session-coordinator/adapters/output/grpc/message_router_client.go +++ b/backend/mpc-system/services/session-coordinator/adapters/output/grpc/message_router_client.go @@ -8,6 +8,7 @@ import ( "github.com/rwadurian/mpc-system/pkg/grpcutil" "github.com/rwadurian/mpc-system/pkg/logger" "github.com/rwadurian/mpc-system/pkg/retry" + "github.com/rwadurian/mpc-system/services/session-coordinator/application/use_cases" "go.uber.org/zap" ) @@ -120,6 +121,7 @@ func (c *MessageRouterClient) PublishSessionCreated( messageHash []byte, createdAt int64, expiresAt int64, + delegateUserShare *use_cases.DelegateUserShareInfo, ) error { event := &router.SessionEvent{ EventId: uuid.New().String(), @@ -134,5 +136,14 @@ func (c *MessageRouterClient) PublishSessionCreated( ExpiresAt: expiresAt, } + // Add delegate user share if provided (for sign sessions with delegate) + if delegateUserShare != nil { + event.DelegateUserShare = &router.DelegateUserShare{ + DelegatePartyId: delegateUserShare.DelegatePartyID, + EncryptedShare: delegateUserShare.EncryptedShare, + PartyIndex: delegateUserShare.PartyIndex, + } + } + return c.PublishSessionEvent(ctx, event) } diff --git a/backend/mpc-system/services/session-coordinator/application/ports/input/session_management_port.go b/backend/mpc-system/services/session-coordinator/application/ports/input/session_management_port.go index 1c13c182..c72216c5 100644 --- a/backend/mpc-system/services/session-coordinator/application/ports/input/session_management_port.go +++ b/backend/mpc-system/services/session-coordinator/application/ports/input/session_management_port.go @@ -36,6 +36,13 @@ type PartyComposition struct { CustomFilters []output.PartySelectionFilter // Custom party selection filters } +// DelegateUserShare contains user's share for delegate party to use in signing +type DelegateUserShare struct { + DelegatePartyID string // The delegate party that will use this share + EncryptedShare []byte // User's encrypted share + PartyIndex int32 // Party index for this share +} + // CreateSessionInput contains input for creating a session type CreateSessionInput struct { InitiatorID string @@ -43,9 +50,10 @@ type CreateSessionInput struct { ThresholdN int ThresholdT int Participants []ParticipantInfo - PartyComposition *PartyComposition // Optional: specify party composition by role - MessageHash []byte // For sign sessions + PartyComposition *PartyComposition // Optional: specify party composition by role + MessageHash []byte // For sign sessions ExpiresIn time.Duration + DelegateUserShare *DelegateUserShare // For sign sessions with delegate party } // ParticipantInfo contains information about a participant @@ -96,13 +104,16 @@ type PartyInfo struct { // SessionStatusOutput contains session status information type SessionStatusOutput struct { - SessionID uuid.UUID - Status string - ThresholdT int - ThresholdN int - Participants []ParticipantStatus - PublicKey []byte // For completed keygen - Signature []byte // For completed sign + SessionID uuid.UUID + Status string + SessionType string // "keygen" or "sign" + ThresholdT int + ThresholdN int + Participants []ParticipantStatus + PublicKey []byte // For completed keygen + Signature []byte // For completed sign + HasDelegate bool // True if keygen session has a delegate party (always false for sign) + DelegatePartyID string // The delegate party ID (if any, only for keygen) } // ParticipantStatus contains participant status information diff --git a/backend/mpc-system/services/session-coordinator/application/use_cases/create_session.go b/backend/mpc-system/services/session-coordinator/application/use_cases/create_session.go index d07325e4..de70aae1 100644 --- a/backend/mpc-system/services/session-coordinator/application/use_cases/create_session.go +++ b/backend/mpc-system/services/session-coordinator/application/use_cases/create_session.go @@ -16,6 +16,13 @@ import ( "go.uber.org/zap" ) +// DelegateUserShareInfo contains user's share for delegate party +type DelegateUserShareInfo struct { + DelegatePartyID string + EncryptedShare []byte + PartyIndex int32 +} + // MessageRouterClient interface for publishing session events type MessageRouterClient interface { PublishSessionCreated( @@ -28,6 +35,7 @@ type MessageRouterClient interface { messageHash []byte, createdAt int64, expiresAt int64, + delegateUserShare *DelegateUserShareInfo, ) error } @@ -286,6 +294,17 @@ func (uc *CreateSessionUseCase) Execute( // Only publish if parties were selected from pool if len(session.GetPartyIDs()) > 0 && uc.messageRouterClient != nil { selectedParties := session.GetPartyIDs() + + // Convert delegate user share if provided (for sign sessions with delegate) + var delegateUserShare *DelegateUserShareInfo + if req.DelegateUserShare != nil { + delegateUserShare = &DelegateUserShareInfo{ + DelegatePartyID: req.DelegateUserShare.DelegatePartyID, + EncryptedShare: req.DelegateUserShare.EncryptedShare, + PartyIndex: req.DelegateUserShare.PartyIndex, + } + } + err := uc.messageRouterClient.PublishSessionCreated( ctx, session.ID.UUID(), @@ -296,6 +315,7 @@ func (uc *CreateSessionUseCase) Execute( session.MessageHash, session.CreatedAt.UnixMilli(), session.ExpiresAt.UnixMilli(), + delegateUserShare, ) if err != nil { // Log error but don't fail the operation diff --git a/backend/mpc-system/services/session-coordinator/application/use_cases/get_session_status.go b/backend/mpc-system/services/session-coordinator/application/use_cases/get_session_status.go index 2fa67b29..c093136b 100644 --- a/backend/mpc-system/services/session-coordinator/application/use_cases/get_session_status.go +++ b/backend/mpc-system/services/session-coordinator/application/use_cases/get_session_status.go @@ -46,12 +46,17 @@ func (uc *GetSessionStatusUseCase) Execute( } // 3. Build response + // has_delegate is only meaningful for keygen sessions + hasDelegate := session.DelegatePartyID != "" && string(session.SessionType) == "keygen" return &input.SessionStatusOutput{ - SessionID: session.ID.UUID(), - Status: session.Status.String(), - ThresholdT: session.Threshold.T(), - ThresholdN: session.Threshold.N(), - Participants: participants, - PublicKey: session.PublicKey, + SessionID: session.ID.UUID(), + Status: session.Status.String(), + SessionType: string(session.SessionType), + ThresholdT: session.Threshold.T(), + ThresholdN: session.Threshold.N(), + Participants: participants, + PublicKey: session.PublicKey, + HasDelegate: hasDelegate, + DelegatePartyID: session.DelegatePartyID, }, nil } diff --git a/backend/mpc-system/services/session-coordinator/domain/services/session_coordinator.go b/backend/mpc-system/services/session-coordinator/domain/services/session_coordinator.go index 43970538..2018a3b8 100644 --- a/backend/mpc-system/services/session-coordinator/domain/services/session_coordinator.go +++ b/backend/mpc-system/services/session-coordinator/domain/services/session_coordinator.go @@ -84,15 +84,11 @@ func (s *SessionCoordinatorService) ShouldExpireSession(session *entities.MPCSes } // CalculateSessionTimeout calculates the timeout for a session type +// Sessions are valid for 24 hours to allow users sufficient time to complete MPC operations +// Note: This is separate from reliability timeouts (party heartbeat/activity) which remain at 2 minutes func (s *SessionCoordinatorService) CalculateSessionTimeout(sessionType entities.SessionType) time.Duration { - switch sessionType { - case entities.SessionTypeKeygen: - return 10 * time.Minute - case entities.SessionTypeSign: - return 5 * time.Minute - default: - return 10 * time.Minute - } + // All session types have a 24-hour validity period + return 24 * time.Hour } // ValidateMessageRouting validates if a message can be routed