diff --git a/backend/mpc-system/services/service-party-app/electron/modules/grpc-client.ts b/backend/mpc-system/services/service-party-app/electron/modules/grpc-client.ts index 7f17736b..190b3fd3 100644 --- a/backend/mpc-system/services/service-party-app/electron/modules/grpc-client.ts +++ b/backend/mpc-system/services/service-party-app/electron/modules/grpc-client.ts @@ -2,9 +2,19 @@ import * as grpc from '@grpc/grpc-js'; import * as protoLoader from '@grpc/proto-loader'; import * as path from 'path'; import { EventEmitter } from 'events'; +import { app } from 'electron'; -// Proto 文件路径 -const PROTO_PATH = path.join(__dirname, '../../proto/message_router.proto'); +// Proto 文件路径 - 在打包后需要从 app.asar.unpacked 或 resources 目录加载 +function getProtoPath(): string { + // 开发环境 + if (!app.isPackaged) { + return path.join(__dirname, '../../proto/message_router.proto'); + } + // 生产环境 - proto 文件需要解包 + return path.join(process.resourcesPath, 'proto/message_router.proto'); +} + +const PROTO_PATH = getProtoPath(); // 定义 proto 包结构类型 interface ProtoPackage { diff --git a/backend/mpc-system/services/service-party-app/package.json b/backend/mpc-system/services/service-party-app/package.json index 97bfd55f..855d2a7c 100644 --- a/backend/mpc-system/services/service-party-app/package.json +++ b/backend/mpc-system/services/service-party-app/package.json @@ -55,8 +55,14 @@ "files": [ "dist/**/*", "dist-electron/**/*", - "wasm/**/*", - "proto/**/*" + "wasm/**/*" + ], + "extraResources": [ + { + "from": "proto", + "to": "proto", + "filter": ["**/*"] + } ], "win": { "target": [ diff --git a/backend/mpc-system/services/service-party-app/proto/message_router.proto b/backend/mpc-system/services/service-party-app/proto/message_router.proto new file mode 100644 index 00000000..656dfc61 --- /dev/null +++ b/backend/mpc-system/services/service-party-app/proto/message_router.proto @@ -0,0 +1,360 @@ +syntax = "proto3"; + +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); + + // SubscribeMessages subscribes to messages for a party (streaming) + rpc SubscribeMessages(SubscribeMessagesRequest) returns (stream MPCMessage); + + // GetPendingMessages retrieves pending messages (polling alternative) + rpc GetPendingMessages(GetPendingMessagesRequest) returns (GetPendingMessagesResponse); + + // AcknowledgeMessage acknowledges receipt of a message + // Must be called after processing a message to confirm delivery + rpc AcknowledgeMessage(AcknowledgeMessageRequest) returns (AcknowledgeMessageResponse); + + // 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 +message RouteMessageRequest { + string session_id = 1; + string from_party = 2; + repeated string to_parties = 3; // Empty for broadcast + int32 round_number = 4; + string message_type = 5; + bytes payload = 6; // Encrypted MPC message +} + +// RouteMessageResponse confirms message routing +message RouteMessageResponse { + bool success = 1; + string message_id = 2; +} + +// SubscribeMessagesRequest subscribes to messages for a party +message SubscribeMessagesRequest { + string session_id = 1; + string party_id = 2; +} + +// MPCMessage represents an MPC protocol message +message MPCMessage { + string message_id = 1; + string session_id = 2; + string from_party = 3; + bool is_broadcast = 4; + int32 round_number = 5; + string message_type = 6; + bytes payload = 7; + int64 created_at = 8; // Unix timestamp milliseconds +} + +// GetPendingMessagesRequest retrieves pending messages +message GetPendingMessagesRequest { + string session_id = 1; + string party_id = 2; + int64 after_timestamp = 3; // Get messages after this timestamp +} + +// GetPendingMessagesResponse contains pending messages +message GetPendingMessagesResponse { + repeated MPCMessage messages = 1; +} + +// NotificationChannel represents a notification channel for offline parties +// If a party has notification channels, it operates in offline mode (24h async) +// If no notification channels, it operates in real-time mode (Message Router push) +message NotificationChannel { + string email = 1; // Optional: email address for notifications + string phone = 2; // Optional: phone number for SMS notifications + string push_token = 3; // Optional: push notification token (FCM/APNs) +} + +// RegisterPartyRequest registers a party with the router +message RegisterPartyRequest { + string party_id = 1; // Unique party identifier + string party_role = 2; // persistent, delegate, or temporary + string version = 3; // Party software version + NotificationChannel notification = 4; // Optional: notification channel for offline mode +} + +// RegisterPartyResponse confirms party registration +message RegisterPartyResponse { + bool success = 1; + string message = 2; + int64 registered_at = 3; // Unix timestamp milliseconds +} + +// SubscribeSessionEventsRequest subscribes to session events +message SubscribeSessionEventsRequest { + string party_id = 1; // Party ID subscribing to events + repeated string event_types = 2; // Event types to subscribe (empty = all) +} + +// SessionEvent represents a session lifecycle event +message SessionEvent { + string event_id = 1; + string event_type = 2; // session_created, session_started, etc. + string session_id = 3; + int32 threshold_n = 4; + int32 threshold_t = 5; + repeated string selected_parties = 6; // PartyIDs selected for this session + map join_tokens = 7; // PartyID -> JoinToken mapping + 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 +message PublishSessionEventRequest { + SessionEvent event = 1; +} + +// PublishSessionEventResponse confirms event publication +message PublishSessionEventResponse { + bool success = 1; + int32 subscriber_count = 2; // Number of parties that received the event +} + +// GetRegisteredPartiesRequest requests registered parties list +message GetRegisteredPartiesRequest { + string role_filter = 1; // Optional: filter by role (persistent, delegate, temporary) + bool only_online = 2; // Optional: only return online parties +} + +// RegisteredParty represents a registered party +message RegisteredParty { + string party_id = 1; // Unique party identifier + string role = 2; // persistent, delegate, or temporary + bool online = 3; // Whether party is currently connected + int64 registered_at = 4; // Unix timestamp milliseconds + int64 last_seen_at = 5; // Unix timestamp milliseconds + NotificationChannel notification = 6; // Optional: notification channel (if set, party is offline mode) +} + +// GetRegisteredPartiesResponse returns registered parties +message GetRegisteredPartiesResponse { + repeated RegisteredParty parties = 1; + int32 total_count = 2; +} + +// AcknowledgeMessageRequest acknowledges message receipt +message AcknowledgeMessageRequest { + string message_id = 1; // ID of the message being acknowledged + string party_id = 2; // ID of the party acknowledging + string session_id = 3; // Session the message belongs to + bool success = 4; // True if message was processed successfully + string error_message = 5; // Error message if processing failed +} + +// AcknowledgeMessageResponse confirms acknowledgment +message AcknowledgeMessageResponse { + bool success = 1; + string message = 2; +} + +// GetMessageStatusRequest requests message delivery status +message GetMessageStatusRequest { + string message_id = 1; + string session_id = 2; +} + +// MessageDeliveryStatus represents delivery status to a single party +message MessageDeliveryStatus { + string party_id = 1; + string status = 2; // pending, delivered, acknowledged, failed + int64 delivered_at = 3; // Unix timestamp milliseconds + int64 acknowledged_at = 4; // Unix timestamp milliseconds + int32 retry_count = 5; // Number of delivery retries +} + +// GetMessageStatusResponse returns message delivery status +message GetMessageStatusResponse { + string message_id = 1; + string session_id = 2; + repeated MessageDeliveryStatus deliveries = 3; + bool all_acknowledged = 4; // True if all recipients acknowledged +} + +// HeartbeatRequest sends a heartbeat to keep the party alive +message HeartbeatRequest { + string party_id = 1; + int64 timestamp = 2; // Unix timestamp milliseconds +} + +// HeartbeatResponse confirms heartbeat receipt +message HeartbeatResponse { + bool success = 1; + 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; + string keygen_session_id = 7; // For sign sessions: which keygen session's shares to use +} + +// 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; +}