feat(mpc-system): add signing parties configuration and delegate signing support
- Add signing-config API endpoints (POST/PUT/DELETE/GET) for configuring which parties should participate in signing operations - Add SigningParties field to Account entity with database migration - Modify CreateSigningSession to use configured parties if set, otherwise use all active parties (backward compatible) - Add delegate party signing support: user provides encrypted share at sign time for delegate party to use - Update protobuf definitions for DelegateUserShare in session events - Add ShareTypeDelegate to support hybrid custody model API endpoints: - POST /accounts/:id/signing-config - Set signing parties (first time) - PUT /accounts/:id/signing-config - Update signing parties - DELETE /accounts/:id/signing-config - Clear config (use all parties) - GET /accounts/:id/signing-config - Get current configuration 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
55f5ec49f2
commit
aa74e2b2e2
|
|
@ -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
|
||||||
|
|
@ -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=
|
||||||
|
|
@ -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
|
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
|
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
|
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
|
// For sign sessions with delegate party: user must provide their encrypted share
|
||||||
sizeCache protoimpl.SizeCache
|
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() {
|
func (x *CreateSessionRequest) Reset() {
|
||||||
|
|
@ -114,6 +116,74 @@ func (x *CreateSessionRequest) GetPartyComposition() *PartyComposition {
|
||||||
return nil
|
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
|
// PartyComposition specifies requirements for automatic party selection
|
||||||
type PartyComposition struct {
|
type PartyComposition struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
|
@ -126,7 +196,7 @@ type PartyComposition struct {
|
||||||
|
|
||||||
func (x *PartyComposition) Reset() {
|
func (x *PartyComposition) Reset() {
|
||||||
*x = PartyComposition{}
|
*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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
@ -138,7 +208,7 @@ func (x *PartyComposition) String() string {
|
||||||
func (*PartyComposition) ProtoMessage() {}
|
func (*PartyComposition) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *PartyComposition) ProtoReflect() protoreflect.Message {
|
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 {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
|
@ -151,7 +221,7 @@ func (x *PartyComposition) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use PartyComposition.ProtoReflect.Descriptor instead.
|
// Deprecated: Use PartyComposition.ProtoReflect.Descriptor instead.
|
||||||
func (*PartyComposition) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *PartyComposition) GetPersistentCount() int32 {
|
||||||
|
|
@ -186,7 +256,7 @@ type ParticipantInfo struct {
|
||||||
|
|
||||||
func (x *ParticipantInfo) Reset() {
|
func (x *ParticipantInfo) Reset() {
|
||||||
*x = ParticipantInfo{}
|
*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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
@ -198,7 +268,7 @@ func (x *ParticipantInfo) String() string {
|
||||||
func (*ParticipantInfo) ProtoMessage() {}
|
func (*ParticipantInfo) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *ParticipantInfo) ProtoReflect() protoreflect.Message {
|
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 {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
|
@ -211,7 +281,7 @@ func (x *ParticipantInfo) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use ParticipantInfo.ProtoReflect.Descriptor instead.
|
// Deprecated: Use ParticipantInfo.ProtoReflect.Descriptor instead.
|
||||||
func (*ParticipantInfo) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *ParticipantInfo) GetPartyId() string {
|
||||||
|
|
@ -241,7 +311,7 @@ type DeviceInfo struct {
|
||||||
|
|
||||||
func (x *DeviceInfo) Reset() {
|
func (x *DeviceInfo) Reset() {
|
||||||
*x = DeviceInfo{}
|
*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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
@ -253,7 +323,7 @@ func (x *DeviceInfo) String() string {
|
||||||
func (*DeviceInfo) ProtoMessage() {}
|
func (*DeviceInfo) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *DeviceInfo) ProtoReflect() protoreflect.Message {
|
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 {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
|
@ -266,7 +336,7 @@ func (x *DeviceInfo) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use DeviceInfo.ProtoReflect.Descriptor instead.
|
// Deprecated: Use DeviceInfo.ProtoReflect.Descriptor instead.
|
||||||
func (*DeviceInfo) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *DeviceInfo) GetDeviceType() string {
|
||||||
|
|
@ -311,7 +381,7 @@ type CreateSessionResponse struct {
|
||||||
|
|
||||||
func (x *CreateSessionResponse) Reset() {
|
func (x *CreateSessionResponse) Reset() {
|
||||||
*x = CreateSessionResponse{}
|
*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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
@ -323,7 +393,7 @@ func (x *CreateSessionResponse) String() string {
|
||||||
func (*CreateSessionResponse) ProtoMessage() {}
|
func (*CreateSessionResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *CreateSessionResponse) ProtoReflect() protoreflect.Message {
|
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 {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
|
@ -336,7 +406,7 @@ func (x *CreateSessionResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use CreateSessionResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use CreateSessionResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*CreateSessionResponse) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *CreateSessionResponse) GetSessionId() string {
|
||||||
|
|
@ -387,7 +457,7 @@ type JoinSessionRequest struct {
|
||||||
|
|
||||||
func (x *JoinSessionRequest) Reset() {
|
func (x *JoinSessionRequest) Reset() {
|
||||||
*x = JoinSessionRequest{}
|
*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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
@ -399,7 +469,7 @@ func (x *JoinSessionRequest) String() string {
|
||||||
func (*JoinSessionRequest) ProtoMessage() {}
|
func (*JoinSessionRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *JoinSessionRequest) ProtoReflect() protoreflect.Message {
|
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 {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
|
@ -412,7 +482,7 @@ func (x *JoinSessionRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use JoinSessionRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use JoinSessionRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*JoinSessionRequest) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *JoinSessionRequest) GetSessionId() string {
|
||||||
|
|
@ -455,7 +525,7 @@ type JoinSessionResponse struct {
|
||||||
|
|
||||||
func (x *JoinSessionResponse) Reset() {
|
func (x *JoinSessionResponse) Reset() {
|
||||||
*x = JoinSessionResponse{}
|
*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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
@ -467,7 +537,7 @@ func (x *JoinSessionResponse) String() string {
|
||||||
func (*JoinSessionResponse) ProtoMessage() {}
|
func (*JoinSessionResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *JoinSessionResponse) ProtoReflect() protoreflect.Message {
|
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 {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
|
@ -480,7 +550,7 @@ func (x *JoinSessionResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use JoinSessionResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use JoinSessionResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*JoinSessionResponse) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *JoinSessionResponse) GetSuccess() bool {
|
||||||
|
|
@ -519,7 +589,7 @@ type SessionInfo struct {
|
||||||
|
|
||||||
func (x *SessionInfo) Reset() {
|
func (x *SessionInfo) Reset() {
|
||||||
*x = SessionInfo{}
|
*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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
@ -531,7 +601,7 @@ func (x *SessionInfo) String() string {
|
||||||
func (*SessionInfo) ProtoMessage() {}
|
func (*SessionInfo) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *SessionInfo) ProtoReflect() protoreflect.Message {
|
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 {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
|
@ -544,7 +614,7 @@ func (x *SessionInfo) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use SessionInfo.ProtoReflect.Descriptor instead.
|
// Deprecated: Use SessionInfo.ProtoReflect.Descriptor instead.
|
||||||
func (*SessionInfo) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *SessionInfo) GetSessionId() string {
|
||||||
|
|
@ -601,7 +671,7 @@ type PartyInfo struct {
|
||||||
|
|
||||||
func (x *PartyInfo) Reset() {
|
func (x *PartyInfo) Reset() {
|
||||||
*x = PartyInfo{}
|
*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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
@ -613,7 +683,7 @@ func (x *PartyInfo) String() string {
|
||||||
func (*PartyInfo) ProtoMessage() {}
|
func (*PartyInfo) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *PartyInfo) ProtoReflect() protoreflect.Message {
|
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 {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
|
@ -626,7 +696,7 @@ func (x *PartyInfo) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use PartyInfo.ProtoReflect.Descriptor instead.
|
// Deprecated: Use PartyInfo.ProtoReflect.Descriptor instead.
|
||||||
func (*PartyInfo) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *PartyInfo) GetPartyId() string {
|
||||||
|
|
@ -660,7 +730,7 @@ type GetSessionStatusRequest struct {
|
||||||
|
|
||||||
func (x *GetSessionStatusRequest) Reset() {
|
func (x *GetSessionStatusRequest) Reset() {
|
||||||
*x = GetSessionStatusRequest{}
|
*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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
@ -672,7 +742,7 @@ func (x *GetSessionStatusRequest) String() string {
|
||||||
func (*GetSessionStatusRequest) ProtoMessage() {}
|
func (*GetSessionStatusRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *GetSessionStatusRequest) ProtoReflect() protoreflect.Message {
|
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 {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
|
@ -685,7 +755,7 @@ func (x *GetSessionStatusRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use GetSessionStatusRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use GetSessionStatusRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*GetSessionStatusRequest) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *GetSessionStatusRequest) GetSessionId() string {
|
||||||
|
|
@ -701,15 +771,22 @@ type GetSessionStatusResponse struct {
|
||||||
Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"`
|
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"`
|
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"`
|
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
|
SessionType string `protobuf:"bytes,4,opt,name=session_type,json=sessionType,proto3" json:"session_type,omitempty"` // "keygen" or "sign"
|
||||||
Signature []byte `protobuf:"bytes,5,opt,name=signature,proto3" json:"signature,omitempty"` // For completed sign
|
PublicKey []byte `protobuf:"bytes,5,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` // For completed keygen
|
||||||
unknownFields protoimpl.UnknownFields
|
Signature []byte `protobuf:"bytes,6,opt,name=signature,proto3" json:"signature,omitempty"` // For completed sign
|
||||||
sizeCache protoimpl.SizeCache
|
// 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() {
|
func (x *GetSessionStatusResponse) Reset() {
|
||||||
*x = GetSessionStatusResponse{}
|
*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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
@ -721,7 +798,7 @@ func (x *GetSessionStatusResponse) String() string {
|
||||||
func (*GetSessionStatusResponse) ProtoMessage() {}
|
func (*GetSessionStatusResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *GetSessionStatusResponse) ProtoReflect() protoreflect.Message {
|
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 {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
|
@ -734,7 +811,7 @@ func (x *GetSessionStatusResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use GetSessionStatusResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use GetSessionStatusResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*GetSessionStatusResponse) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *GetSessionStatusResponse) GetStatus() string {
|
||||||
|
|
@ -758,6 +835,13 @@ func (x *GetSessionStatusResponse) GetTotalParties() int32 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *GetSessionStatusResponse) GetSessionType() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.SessionType
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (x *GetSessionStatusResponse) GetPublicKey() []byte {
|
func (x *GetSessionStatusResponse) GetPublicKey() []byte {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.PublicKey
|
return x.PublicKey
|
||||||
|
|
@ -772,6 +856,81 @@ func (x *GetSessionStatusResponse) GetSignature() []byte {
|
||||||
return nil
|
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
|
// ReportCompletionRequest reports that a participant has completed
|
||||||
type ReportCompletionRequest struct {
|
type ReportCompletionRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
|
@ -785,7 +944,7 @@ type ReportCompletionRequest struct {
|
||||||
|
|
||||||
func (x *ReportCompletionRequest) Reset() {
|
func (x *ReportCompletionRequest) Reset() {
|
||||||
*x = ReportCompletionRequest{}
|
*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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
@ -797,7 +956,7 @@ func (x *ReportCompletionRequest) String() string {
|
||||||
func (*ReportCompletionRequest) ProtoMessage() {}
|
func (*ReportCompletionRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *ReportCompletionRequest) ProtoReflect() protoreflect.Message {
|
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 {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
|
@ -810,7 +969,7 @@ func (x *ReportCompletionRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use ReportCompletionRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use ReportCompletionRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*ReportCompletionRequest) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *ReportCompletionRequest) GetSessionId() string {
|
||||||
|
|
@ -852,7 +1011,7 @@ type ReportCompletionResponse struct {
|
||||||
|
|
||||||
func (x *ReportCompletionResponse) Reset() {
|
func (x *ReportCompletionResponse) Reset() {
|
||||||
*x = ReportCompletionResponse{}
|
*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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
@ -864,7 +1023,7 @@ func (x *ReportCompletionResponse) String() string {
|
||||||
func (*ReportCompletionResponse) ProtoMessage() {}
|
func (*ReportCompletionResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *ReportCompletionResponse) ProtoReflect() protoreflect.Message {
|
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 {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
|
@ -877,7 +1036,7 @@ func (x *ReportCompletionResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use ReportCompletionResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use ReportCompletionResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*ReportCompletionResponse) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *ReportCompletionResponse) GetSuccess() bool {
|
||||||
|
|
@ -904,7 +1063,7 @@ type CloseSessionRequest struct {
|
||||||
|
|
||||||
func (x *CloseSessionRequest) Reset() {
|
func (x *CloseSessionRequest) Reset() {
|
||||||
*x = CloseSessionRequest{}
|
*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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
@ -916,7 +1075,7 @@ func (x *CloseSessionRequest) String() string {
|
||||||
func (*CloseSessionRequest) ProtoMessage() {}
|
func (*CloseSessionRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *CloseSessionRequest) ProtoReflect() protoreflect.Message {
|
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 {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
|
@ -929,7 +1088,7 @@ func (x *CloseSessionRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use CloseSessionRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use CloseSessionRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*CloseSessionRequest) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *CloseSessionRequest) GetSessionId() string {
|
||||||
|
|
@ -949,7 +1108,7 @@ type CloseSessionResponse struct {
|
||||||
|
|
||||||
func (x *CloseSessionResponse) Reset() {
|
func (x *CloseSessionResponse) Reset() {
|
||||||
*x = CloseSessionResponse{}
|
*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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
@ -961,7 +1120,7 @@ func (x *CloseSessionResponse) String() string {
|
||||||
func (*CloseSessionResponse) ProtoMessage() {}
|
func (*CloseSessionResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *CloseSessionResponse) ProtoReflect() protoreflect.Message {
|
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 {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
|
@ -974,7 +1133,7 @@ func (x *CloseSessionResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use CloseSessionResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use CloseSessionResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*CloseSessionResponse) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *CloseSessionResponse) GetSuccess() bool {
|
||||||
|
|
@ -995,7 +1154,7 @@ type MarkPartyReadyRequest struct {
|
||||||
|
|
||||||
func (x *MarkPartyReadyRequest) Reset() {
|
func (x *MarkPartyReadyRequest) Reset() {
|
||||||
*x = MarkPartyReadyRequest{}
|
*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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
@ -1007,7 +1166,7 @@ func (x *MarkPartyReadyRequest) String() string {
|
||||||
func (*MarkPartyReadyRequest) ProtoMessage() {}
|
func (*MarkPartyReadyRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *MarkPartyReadyRequest) ProtoReflect() protoreflect.Message {
|
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 {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
|
@ -1020,7 +1179,7 @@ func (x *MarkPartyReadyRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use MarkPartyReadyRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use MarkPartyReadyRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*MarkPartyReadyRequest) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *MarkPartyReadyRequest) GetSessionId() string {
|
||||||
|
|
@ -1050,7 +1209,7 @@ type MarkPartyReadyResponse struct {
|
||||||
|
|
||||||
func (x *MarkPartyReadyResponse) Reset() {
|
func (x *MarkPartyReadyResponse) Reset() {
|
||||||
*x = MarkPartyReadyResponse{}
|
*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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
@ -1062,7 +1221,7 @@ func (x *MarkPartyReadyResponse) String() string {
|
||||||
func (*MarkPartyReadyResponse) ProtoMessage() {}
|
func (*MarkPartyReadyResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *MarkPartyReadyResponse) ProtoReflect() protoreflect.Message {
|
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 {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
|
@ -1075,7 +1234,7 @@ func (x *MarkPartyReadyResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use MarkPartyReadyResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use MarkPartyReadyResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*MarkPartyReadyResponse) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *MarkPartyReadyResponse) GetSuccess() bool {
|
||||||
|
|
@ -1116,7 +1275,7 @@ type StartSessionRequest struct {
|
||||||
|
|
||||||
func (x *StartSessionRequest) Reset() {
|
func (x *StartSessionRequest) Reset() {
|
||||||
*x = StartSessionRequest{}
|
*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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
@ -1128,7 +1287,7 @@ func (x *StartSessionRequest) String() string {
|
||||||
func (*StartSessionRequest) ProtoMessage() {}
|
func (*StartSessionRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *StartSessionRequest) ProtoReflect() protoreflect.Message {
|
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 {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
|
@ -1141,7 +1300,7 @@ func (x *StartSessionRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use StartSessionRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use StartSessionRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*StartSessionRequest) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *StartSessionRequest) GetSessionId() string {
|
||||||
|
|
@ -1162,7 +1321,7 @@ type StartSessionResponse struct {
|
||||||
|
|
||||||
func (x *StartSessionResponse) Reset() {
|
func (x *StartSessionResponse) Reset() {
|
||||||
*x = StartSessionResponse{}
|
*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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
|
@ -1174,7 +1333,7 @@ func (x *StartSessionResponse) String() string {
|
||||||
func (*StartSessionResponse) ProtoMessage() {}
|
func (*StartSessionResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *StartSessionResponse) ProtoReflect() protoreflect.Message {
|
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 {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
|
@ -1187,7 +1346,7 @@ func (x *StartSessionResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use StartSessionResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use StartSessionResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*StartSessionResponse) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *StartSessionResponse) GetSuccess() bool {
|
||||||
|
|
@ -1204,11 +1363,133 @@ func (x *StartSessionResponse) GetStatus() string {
|
||||||
return ""
|
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
|
var File_api_proto_session_coordinator_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
const file_api_proto_session_coordinator_proto_rawDesc = "" +
|
const file_api_proto_session_coordinator_proto_rawDesc = "" +
|
||||||
"\n" +
|
"\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" +
|
"\x14CreateSessionRequest\x12!\n" +
|
||||||
"\fsession_type\x18\x01 \x01(\tR\vsessionType\x12\x1f\n" +
|
"\fsession_type\x18\x01 \x01(\tR\vsessionType\x12\x1f\n" +
|
||||||
"\vthreshold_n\x18\x02 \x01(\x05R\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" +
|
"\fparticipants\x18\x04 \x03(\v2#.mpc.coordinator.v1.ParticipantInfoR\fparticipants\x12!\n" +
|
||||||
"\fmessage_hash\x18\x05 \x01(\fR\vmessageHash\x12,\n" +
|
"\fmessage_hash\x18\x05 \x01(\fR\vmessageHash\x12,\n" +
|
||||||
"\x12expires_in_seconds\x18\x06 \x01(\x03R\x10expiresInSeconds\x12Q\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" +
|
"\x10PartyComposition\x12)\n" +
|
||||||
"\x10persistent_count\x18\x01 \x01(\x05R\x0fpersistentCount\x12%\n" +
|
"\x10persistent_count\x18\x01 \x01(\x05R\x0fpersistentCount\x12%\n" +
|
||||||
"\x0edelegate_count\x18\x02 \x01(\x05R\rdelegateCount\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" +
|
"deviceInfo\"8\n" +
|
||||||
"\x17GetSessionStatusRequest\x12\x1d\n" +
|
"\x17GetSessionStatusRequest\x12\x1d\n" +
|
||||||
"\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" +
|
"\x18GetSessionStatusResponse\x12\x16\n" +
|
||||||
"\x06status\x18\x01 \x01(\tR\x06status\x12+\n" +
|
"\x06status\x18\x01 \x01(\tR\x06status\x12+\n" +
|
||||||
"\x11completed_parties\x18\x02 \x01(\x05R\x10completedParties\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" +
|
"\n" +
|
||||||
"public_key\x18\x04 \x01(\fR\tpublicKey\x12\x1c\n" +
|
"public_key\x18\x05 \x01(\fR\tpublicKey\x12\x1c\n" +
|
||||||
"\tsignature\x18\x05 \x01(\fR\tsignature\"\x90\x01\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" +
|
"\x17ReportCompletionRequest\x12\x1d\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"session_id\x18\x01 \x01(\tR\tsessionId\x12\x19\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" +
|
"session_id\x18\x01 \x01(\tR\tsessionId\"H\n" +
|
||||||
"\x14StartSessionResponse\x12\x18\n" +
|
"\x14StartSessionResponse\x12\x18\n" +
|
||||||
"\asuccess\x18\x01 \x01(\bR\asuccess\x12\x16\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" +
|
"\x12SessionCoordinator\x12d\n" +
|
||||||
"\rCreateSession\x12(.mpc.coordinator.v1.CreateSessionRequest\x1a).mpc.coordinator.v1.CreateSessionResponse\x12^\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" +
|
"\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" +
|
"\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" +
|
"\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" +
|
"\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 (
|
var (
|
||||||
file_api_proto_session_coordinator_proto_rawDescOnce sync.Once
|
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
|
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{
|
var file_api_proto_session_coordinator_proto_goTypes = []any{
|
||||||
(*CreateSessionRequest)(nil), // 0: mpc.coordinator.v1.CreateSessionRequest
|
(*CreateSessionRequest)(nil), // 0: mpc.coordinator.v1.CreateSessionRequest
|
||||||
(*PartyComposition)(nil), // 1: mpc.coordinator.v1.PartyComposition
|
(*DelegateUserShare)(nil), // 1: mpc.coordinator.v1.DelegateUserShare
|
||||||
(*ParticipantInfo)(nil), // 2: mpc.coordinator.v1.ParticipantInfo
|
(*PartyComposition)(nil), // 2: mpc.coordinator.v1.PartyComposition
|
||||||
(*DeviceInfo)(nil), // 3: mpc.coordinator.v1.DeviceInfo
|
(*ParticipantInfo)(nil), // 3: mpc.coordinator.v1.ParticipantInfo
|
||||||
(*CreateSessionResponse)(nil), // 4: mpc.coordinator.v1.CreateSessionResponse
|
(*DeviceInfo)(nil), // 4: mpc.coordinator.v1.DeviceInfo
|
||||||
(*JoinSessionRequest)(nil), // 5: mpc.coordinator.v1.JoinSessionRequest
|
(*CreateSessionResponse)(nil), // 5: mpc.coordinator.v1.CreateSessionResponse
|
||||||
(*JoinSessionResponse)(nil), // 6: mpc.coordinator.v1.JoinSessionResponse
|
(*JoinSessionRequest)(nil), // 6: mpc.coordinator.v1.JoinSessionRequest
|
||||||
(*SessionInfo)(nil), // 7: mpc.coordinator.v1.SessionInfo
|
(*JoinSessionResponse)(nil), // 7: mpc.coordinator.v1.JoinSessionResponse
|
||||||
(*PartyInfo)(nil), // 8: mpc.coordinator.v1.PartyInfo
|
(*SessionInfo)(nil), // 8: mpc.coordinator.v1.SessionInfo
|
||||||
(*GetSessionStatusRequest)(nil), // 9: mpc.coordinator.v1.GetSessionStatusRequest
|
(*PartyInfo)(nil), // 9: mpc.coordinator.v1.PartyInfo
|
||||||
(*GetSessionStatusResponse)(nil), // 10: mpc.coordinator.v1.GetSessionStatusResponse
|
(*GetSessionStatusRequest)(nil), // 10: mpc.coordinator.v1.GetSessionStatusRequest
|
||||||
(*ReportCompletionRequest)(nil), // 11: mpc.coordinator.v1.ReportCompletionRequest
|
(*GetSessionStatusResponse)(nil), // 11: mpc.coordinator.v1.GetSessionStatusResponse
|
||||||
(*ReportCompletionResponse)(nil), // 12: mpc.coordinator.v1.ReportCompletionResponse
|
(*DelegateShareInfo)(nil), // 12: mpc.coordinator.v1.DelegateShareInfo
|
||||||
(*CloseSessionRequest)(nil), // 13: mpc.coordinator.v1.CloseSessionRequest
|
(*ReportCompletionRequest)(nil), // 13: mpc.coordinator.v1.ReportCompletionRequest
|
||||||
(*CloseSessionResponse)(nil), // 14: mpc.coordinator.v1.CloseSessionResponse
|
(*ReportCompletionResponse)(nil), // 14: mpc.coordinator.v1.ReportCompletionResponse
|
||||||
(*MarkPartyReadyRequest)(nil), // 15: mpc.coordinator.v1.MarkPartyReadyRequest
|
(*CloseSessionRequest)(nil), // 15: mpc.coordinator.v1.CloseSessionRequest
|
||||||
(*MarkPartyReadyResponse)(nil), // 16: mpc.coordinator.v1.MarkPartyReadyResponse
|
(*CloseSessionResponse)(nil), // 16: mpc.coordinator.v1.CloseSessionResponse
|
||||||
(*StartSessionRequest)(nil), // 17: mpc.coordinator.v1.StartSessionRequest
|
(*MarkPartyReadyRequest)(nil), // 17: mpc.coordinator.v1.MarkPartyReadyRequest
|
||||||
(*StartSessionResponse)(nil), // 18: mpc.coordinator.v1.StartSessionResponse
|
(*MarkPartyReadyResponse)(nil), // 18: mpc.coordinator.v1.MarkPartyReadyResponse
|
||||||
nil, // 19: mpc.coordinator.v1.CreateSessionResponse.JoinTokensEntry
|
(*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{
|
var file_api_proto_session_coordinator_proto_depIdxs = []int32{
|
||||||
2, // 0: mpc.coordinator.v1.CreateSessionRequest.participants:type_name -> mpc.coordinator.v1.ParticipantInfo
|
3, // 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
|
2, // 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
|
1, // 2: mpc.coordinator.v1.CreateSessionRequest.delegate_user_share:type_name -> mpc.coordinator.v1.DelegateUserShare
|
||||||
19, // 3: mpc.coordinator.v1.CreateSessionResponse.join_tokens:type_name -> mpc.coordinator.v1.CreateSessionResponse.JoinTokensEntry
|
4, // 3: mpc.coordinator.v1.ParticipantInfo.device_info:type_name -> mpc.coordinator.v1.DeviceInfo
|
||||||
3, // 4: mpc.coordinator.v1.JoinSessionRequest.device_info:type_name -> mpc.coordinator.v1.DeviceInfo
|
23, // 4: mpc.coordinator.v1.CreateSessionResponse.join_tokens:type_name -> mpc.coordinator.v1.CreateSessionResponse.JoinTokensEntry
|
||||||
7, // 5: mpc.coordinator.v1.JoinSessionResponse.session_info:type_name -> mpc.coordinator.v1.SessionInfo
|
4, // 5: mpc.coordinator.v1.JoinSessionRequest.device_info:type_name -> mpc.coordinator.v1.DeviceInfo
|
||||||
8, // 6: mpc.coordinator.v1.JoinSessionResponse.other_parties:type_name -> mpc.coordinator.v1.PartyInfo
|
8, // 6: mpc.coordinator.v1.JoinSessionResponse.session_info:type_name -> mpc.coordinator.v1.SessionInfo
|
||||||
3, // 7: mpc.coordinator.v1.PartyInfo.device_info:type_name -> mpc.coordinator.v1.DeviceInfo
|
9, // 7: mpc.coordinator.v1.JoinSessionResponse.other_parties:type_name -> mpc.coordinator.v1.PartyInfo
|
||||||
0, // 8: mpc.coordinator.v1.SessionCoordinator.CreateSession:input_type -> mpc.coordinator.v1.CreateSessionRequest
|
4, // 8: mpc.coordinator.v1.PartyInfo.device_info:type_name -> mpc.coordinator.v1.DeviceInfo
|
||||||
5, // 9: mpc.coordinator.v1.SessionCoordinator.JoinSession:input_type -> mpc.coordinator.v1.JoinSessionRequest
|
12, // 9: mpc.coordinator.v1.GetSessionStatusResponse.delegate_share:type_name -> mpc.coordinator.v1.DelegateShareInfo
|
||||||
9, // 10: mpc.coordinator.v1.SessionCoordinator.GetSessionStatus:input_type -> mpc.coordinator.v1.GetSessionStatusRequest
|
0, // 10: mpc.coordinator.v1.SessionCoordinator.CreateSession:input_type -> mpc.coordinator.v1.CreateSessionRequest
|
||||||
15, // 11: mpc.coordinator.v1.SessionCoordinator.MarkPartyReady:input_type -> mpc.coordinator.v1.MarkPartyReadyRequest
|
6, // 11: mpc.coordinator.v1.SessionCoordinator.JoinSession:input_type -> mpc.coordinator.v1.JoinSessionRequest
|
||||||
17, // 12: mpc.coordinator.v1.SessionCoordinator.StartSession:input_type -> mpc.coordinator.v1.StartSessionRequest
|
10, // 12: mpc.coordinator.v1.SessionCoordinator.GetSessionStatus:input_type -> mpc.coordinator.v1.GetSessionStatusRequest
|
||||||
11, // 13: mpc.coordinator.v1.SessionCoordinator.ReportCompletion:input_type -> mpc.coordinator.v1.ReportCompletionRequest
|
17, // 13: mpc.coordinator.v1.SessionCoordinator.MarkPartyReady:input_type -> mpc.coordinator.v1.MarkPartyReadyRequest
|
||||||
13, // 14: mpc.coordinator.v1.SessionCoordinator.CloseSession:input_type -> mpc.coordinator.v1.CloseSessionRequest
|
19, // 14: mpc.coordinator.v1.SessionCoordinator.StartSession:input_type -> mpc.coordinator.v1.StartSessionRequest
|
||||||
4, // 15: mpc.coordinator.v1.SessionCoordinator.CreateSession:output_type -> mpc.coordinator.v1.CreateSessionResponse
|
13, // 15: mpc.coordinator.v1.SessionCoordinator.ReportCompletion:input_type -> mpc.coordinator.v1.ReportCompletionRequest
|
||||||
6, // 16: mpc.coordinator.v1.SessionCoordinator.JoinSession:output_type -> mpc.coordinator.v1.JoinSessionResponse
|
15, // 16: mpc.coordinator.v1.SessionCoordinator.CloseSession:input_type -> mpc.coordinator.v1.CloseSessionRequest
|
||||||
10, // 17: mpc.coordinator.v1.SessionCoordinator.GetSessionStatus:output_type -> mpc.coordinator.v1.GetSessionStatusResponse
|
21, // 17: mpc.coordinator.v1.SessionCoordinator.SubmitDelegateShare:input_type -> mpc.coordinator.v1.SubmitDelegateShareRequest
|
||||||
16, // 18: mpc.coordinator.v1.SessionCoordinator.MarkPartyReady:output_type -> mpc.coordinator.v1.MarkPartyReadyResponse
|
5, // 18: mpc.coordinator.v1.SessionCoordinator.CreateSession:output_type -> mpc.coordinator.v1.CreateSessionResponse
|
||||||
18, // 19: mpc.coordinator.v1.SessionCoordinator.StartSession:output_type -> mpc.coordinator.v1.StartSessionResponse
|
7, // 19: mpc.coordinator.v1.SessionCoordinator.JoinSession:output_type -> mpc.coordinator.v1.JoinSessionResponse
|
||||||
12, // 20: mpc.coordinator.v1.SessionCoordinator.ReportCompletion:output_type -> mpc.coordinator.v1.ReportCompletionResponse
|
11, // 20: mpc.coordinator.v1.SessionCoordinator.GetSessionStatus:output_type -> mpc.coordinator.v1.GetSessionStatusResponse
|
||||||
14, // 21: mpc.coordinator.v1.SessionCoordinator.CloseSession:output_type -> mpc.coordinator.v1.CloseSessionResponse
|
18, // 21: mpc.coordinator.v1.SessionCoordinator.MarkPartyReady:output_type -> mpc.coordinator.v1.MarkPartyReadyResponse
|
||||||
15, // [15:22] is the sub-list for method output_type
|
20, // 22: mpc.coordinator.v1.SessionCoordinator.StartSession:output_type -> mpc.coordinator.v1.StartSessionResponse
|
||||||
8, // [8:15] is the sub-list for method input_type
|
14, // 23: mpc.coordinator.v1.SessionCoordinator.ReportCompletion:output_type -> mpc.coordinator.v1.ReportCompletionResponse
|
||||||
8, // [8:8] is the sub-list for extension type_name
|
16, // 24: mpc.coordinator.v1.SessionCoordinator.CloseSession:output_type -> mpc.coordinator.v1.CloseSessionResponse
|
||||||
8, // [8:8] is the sub-list for extension extendee
|
22, // 25: mpc.coordinator.v1.SessionCoordinator.SubmitDelegateShare:output_type -> mpc.coordinator.v1.SubmitDelegateShareResponse
|
||||||
0, // [0:8] is the sub-list for field type_name
|
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() }
|
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(),
|
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)),
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_proto_session_coordinator_proto_rawDesc), len(file_api_proto_session_coordinator_proto_rawDesc)),
|
||||||
NumEnums: 0,
|
NumEnums: 0,
|
||||||
NumMessages: 20,
|
NumMessages: 24,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 1,
|
NumServices: 1,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,14 @@ import (
|
||||||
const _ = grpc.SupportPackageIsVersion9
|
const _ = grpc.SupportPackageIsVersion9
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SessionCoordinator_CreateSession_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/CreateSession"
|
SessionCoordinator_CreateSession_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/CreateSession"
|
||||||
SessionCoordinator_JoinSession_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/JoinSession"
|
SessionCoordinator_JoinSession_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/JoinSession"
|
||||||
SessionCoordinator_GetSessionStatus_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/GetSessionStatus"
|
SessionCoordinator_GetSessionStatus_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/GetSessionStatus"
|
||||||
SessionCoordinator_MarkPartyReady_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/MarkPartyReady"
|
SessionCoordinator_MarkPartyReady_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/MarkPartyReady"
|
||||||
SessionCoordinator_StartSession_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/StartSession"
|
SessionCoordinator_StartSession_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/StartSession"
|
||||||
SessionCoordinator_ReportCompletion_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/ReportCompletion"
|
SessionCoordinator_ReportCompletion_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/ReportCompletion"
|
||||||
SessionCoordinator_CloseSession_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/CloseSession"
|
SessionCoordinator_CloseSession_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/CloseSession"
|
||||||
|
SessionCoordinator_SubmitDelegateShare_FullMethodName = "/mpc.coordinator.v1.SessionCoordinator/SubmitDelegateShare"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SessionCoordinatorClient is the client API for SessionCoordinator service.
|
// 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)
|
StartSession(ctx context.Context, in *StartSessionRequest, opts ...grpc.CallOption) (*StartSessionResponse, error)
|
||||||
ReportCompletion(ctx context.Context, in *ReportCompletionRequest, opts ...grpc.CallOption) (*ReportCompletionResponse, error)
|
ReportCompletion(ctx context.Context, in *ReportCompletionRequest, opts ...grpc.CallOption) (*ReportCompletionResponse, error)
|
||||||
CloseSession(ctx context.Context, in *CloseSessionRequest, opts ...grpc.CallOption) (*CloseSessionResponse, 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 {
|
type sessionCoordinatorClient struct {
|
||||||
|
|
@ -122,6 +125,16 @@ func (c *sessionCoordinatorClient) CloseSession(ctx context.Context, in *CloseSe
|
||||||
return out, nil
|
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.
|
// SessionCoordinatorServer is the server API for SessionCoordinator service.
|
||||||
// All implementations must embed UnimplementedSessionCoordinatorServer
|
// All implementations must embed UnimplementedSessionCoordinatorServer
|
||||||
// for forward compatibility.
|
// for forward compatibility.
|
||||||
|
|
@ -136,6 +149,8 @@ type SessionCoordinatorServer interface {
|
||||||
StartSession(context.Context, *StartSessionRequest) (*StartSessionResponse, error)
|
StartSession(context.Context, *StartSessionRequest) (*StartSessionResponse, error)
|
||||||
ReportCompletion(context.Context, *ReportCompletionRequest) (*ReportCompletionResponse, error)
|
ReportCompletion(context.Context, *ReportCompletionRequest) (*ReportCompletionResponse, error)
|
||||||
CloseSession(context.Context, *CloseSessionRequest) (*CloseSessionResponse, 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()
|
mustEmbedUnimplementedSessionCoordinatorServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,6 +182,9 @@ func (UnimplementedSessionCoordinatorServer) ReportCompletion(context.Context, *
|
||||||
func (UnimplementedSessionCoordinatorServer) CloseSession(context.Context, *CloseSessionRequest) (*CloseSessionResponse, error) {
|
func (UnimplementedSessionCoordinatorServer) CloseSession(context.Context, *CloseSessionRequest) (*CloseSessionResponse, error) {
|
||||||
return nil, status.Error(codes.Unimplemented, "method CloseSession not implemented")
|
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) mustEmbedUnimplementedSessionCoordinatorServer() {}
|
||||||
func (UnimplementedSessionCoordinatorServer) testEmbeddedByValue() {}
|
func (UnimplementedSessionCoordinatorServer) testEmbeddedByValue() {}
|
||||||
|
|
||||||
|
|
@ -314,6 +332,24 @@ func _SessionCoordinator_CloseSession_Handler(srv interface{}, ctx context.Conte
|
||||||
return interceptor(ctx, in, info, handler)
|
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.
|
// SessionCoordinator_ServiceDesc is the grpc.ServiceDesc for SessionCoordinator service.
|
||||||
// It's only intended for direct use with grpc.RegisterService,
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
// and not to be introspected or modified (even as a copy)
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
|
@ -349,6 +385,10 @@ var SessionCoordinator_ServiceDesc = grpc.ServiceDesc{
|
||||||
MethodName: "CloseSession",
|
MethodName: "CloseSession",
|
||||||
Handler: _SessionCoordinator_CloseSession_Handler,
|
Handler: _SessionCoordinator_CloseSession_Handler,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MethodName: "SubmitDelegateShare",
|
||||||
|
Handler: _SessionCoordinator_SubmitDelegateShare_Handler,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Streams: []grpc.StreamDesc{},
|
Streams: []grpc.StreamDesc{},
|
||||||
Metadata: "api/proto/session_coordinator.proto",
|
Metadata: "api/proto/session_coordinator.proto",
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -29,6 +29,11 @@ const (
|
||||||
MessageRouter_SubscribeSessionEvents_FullMethodName = "/mpc.router.v1.MessageRouter/SubscribeSessionEvents"
|
MessageRouter_SubscribeSessionEvents_FullMethodName = "/mpc.router.v1.MessageRouter/SubscribeSessionEvents"
|
||||||
MessageRouter_PublishSessionEvent_FullMethodName = "/mpc.router.v1.MessageRouter/PublishSessionEvent"
|
MessageRouter_PublishSessionEvent_FullMethodName = "/mpc.router.v1.MessageRouter/PublishSessionEvent"
|
||||||
MessageRouter_GetRegisteredParties_FullMethodName = "/mpc.router.v1.MessageRouter/GetRegisteredParties"
|
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.
|
// 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.
|
// 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
|
// 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 {
|
type MessageRouterClient interface {
|
||||||
// RouteMessage routes a message from one party to others
|
// RouteMessage routes a message from one party to others
|
||||||
RouteMessage(ctx context.Context, in *RouteMessageRequest, opts ...grpc.CallOption) (*RouteMessageResponse, error)
|
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)
|
PublishSessionEvent(ctx context.Context, in *PublishSessionEventRequest, opts ...grpc.CallOption) (*PublishSessionEventResponse, error)
|
||||||
// GetRegisteredParties returns all registered parties (for Session Coordinator party discovery)
|
// GetRegisteredParties returns all registered parties (for Session Coordinator party discovery)
|
||||||
GetRegisteredParties(ctx context.Context, in *GetRegisteredPartiesRequest, opts ...grpc.CallOption) (*GetRegisteredPartiesResponse, error)
|
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 {
|
type messageRouterClient struct {
|
||||||
|
|
@ -186,11 +203,63 @@ func (c *messageRouterClient) GetRegisteredParties(ctx context.Context, in *GetR
|
||||||
return out, nil
|
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.
|
// MessageRouterServer is the server API for MessageRouter service.
|
||||||
// All implementations must embed UnimplementedMessageRouterServer
|
// All implementations must embed UnimplementedMessageRouterServer
|
||||||
// for forward compatibility.
|
// for forward compatibility.
|
||||||
//
|
//
|
||||||
// MessageRouter service handles MPC message routing
|
// 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 {
|
type MessageRouterServer interface {
|
||||||
// RouteMessage routes a message from one party to others
|
// RouteMessage routes a message from one party to others
|
||||||
RouteMessage(context.Context, *RouteMessageRequest) (*RouteMessageResponse, error)
|
RouteMessage(context.Context, *RouteMessageRequest) (*RouteMessageResponse, error)
|
||||||
|
|
@ -213,6 +282,16 @@ type MessageRouterServer interface {
|
||||||
PublishSessionEvent(context.Context, *PublishSessionEventRequest) (*PublishSessionEventResponse, error)
|
PublishSessionEvent(context.Context, *PublishSessionEventRequest) (*PublishSessionEventResponse, error)
|
||||||
// GetRegisteredParties returns all registered parties (for Session Coordinator party discovery)
|
// GetRegisteredParties returns all registered parties (for Session Coordinator party discovery)
|
||||||
GetRegisteredParties(context.Context, *GetRegisteredPartiesRequest) (*GetRegisteredPartiesResponse, error)
|
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()
|
mustEmbedUnimplementedMessageRouterServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -253,6 +332,21 @@ func (UnimplementedMessageRouterServer) PublishSessionEvent(context.Context, *Pu
|
||||||
func (UnimplementedMessageRouterServer) GetRegisteredParties(context.Context, *GetRegisteredPartiesRequest) (*GetRegisteredPartiesResponse, error) {
|
func (UnimplementedMessageRouterServer) GetRegisteredParties(context.Context, *GetRegisteredPartiesRequest) (*GetRegisteredPartiesResponse, error) {
|
||||||
return nil, status.Error(codes.Unimplemented, "method GetRegisteredParties not implemented")
|
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) mustEmbedUnimplementedMessageRouterServer() {}
|
||||||
func (UnimplementedMessageRouterServer) testEmbeddedByValue() {}
|
func (UnimplementedMessageRouterServer) testEmbeddedByValue() {}
|
||||||
|
|
||||||
|
|
@ -440,6 +534,96 @@ func _MessageRouter_GetRegisteredParties_Handler(srv interface{}, ctx context.Co
|
||||||
return interceptor(ctx, in, info, handler)
|
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.
|
// MessageRouter_ServiceDesc is the grpc.ServiceDesc for MessageRouter service.
|
||||||
// It's only intended for direct use with grpc.RegisterService,
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
// and not to be introspected or modified (even as a copy)
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
|
@ -479,6 +663,26 @@ var MessageRouter_ServiceDesc = grpc.ServiceDesc{
|
||||||
MethodName: "GetRegisteredParties",
|
MethodName: "GetRegisteredParties",
|
||||||
Handler: _MessageRouter_GetRegisteredParties_Handler,
|
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{
|
Streams: []grpc.StreamDesc{
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,13 @@ package mpc.router.v1;
|
||||||
option go_package = "github.com/rwadurian/mpc-system/api/grpc/router/v1;router";
|
option go_package = "github.com/rwadurian/mpc-system/api/grpc/router/v1;router";
|
||||||
|
|
||||||
// MessageRouter service handles MPC message routing
|
// 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 {
|
service MessageRouter {
|
||||||
|
// ============================================
|
||||||
|
// Message Routing
|
||||||
|
// ============================================
|
||||||
|
|
||||||
// RouteMessage routes a message from one party to others
|
// RouteMessage routes a message from one party to others
|
||||||
rpc RouteMessage(RouteMessageRequest) returns (RouteMessageResponse);
|
rpc RouteMessage(RouteMessageRequest) returns (RouteMessageResponse);
|
||||||
|
|
||||||
|
|
@ -22,20 +28,52 @@ service MessageRouter {
|
||||||
// GetMessageStatus gets the delivery status of a message
|
// GetMessageStatus gets the delivery status of a message
|
||||||
rpc GetMessageStatus(GetMessageStatusRequest) returns (GetMessageStatusResponse);
|
rpc GetMessageStatus(GetMessageStatusRequest) returns (GetMessageStatusResponse);
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// Party Registration & Heartbeat
|
||||||
|
// ============================================
|
||||||
|
|
||||||
// RegisterParty registers a party with the message router (party actively connects)
|
// RegisterParty registers a party with the message router (party actively connects)
|
||||||
rpc RegisterParty(RegisterPartyRequest) returns (RegisterPartyResponse);
|
rpc RegisterParty(RegisterPartyRequest) returns (RegisterPartyResponse);
|
||||||
|
|
||||||
// Heartbeat sends a heartbeat to keep the party alive
|
// Heartbeat sends a heartbeat to keep the party alive
|
||||||
rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse);
|
rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse);
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// Session Events (Push from Coordinator)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
// SubscribeSessionEvents subscribes to session lifecycle events (session start, etc.)
|
// SubscribeSessionEvents subscribes to session lifecycle events (session start, etc.)
|
||||||
rpc SubscribeSessionEvents(SubscribeSessionEventsRequest) returns (stream SessionEvent);
|
rpc SubscribeSessionEvents(SubscribeSessionEventsRequest) returns (stream SessionEvent);
|
||||||
|
|
||||||
// PublishSessionEvent publishes a session event (called by Session Coordinator)
|
// PublishSessionEvent publishes a session event (called by Session Coordinator)
|
||||||
rpc PublishSessionEvent(PublishSessionEventRequest) returns (PublishSessionEventResponse);
|
rpc PublishSessionEvent(PublishSessionEventRequest) returns (PublishSessionEventResponse);
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// Party Discovery (for Session Coordinator)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
// GetRegisteredParties returns all registered parties (for Session Coordinator party discovery)
|
// GetRegisteredParties returns all registered parties (for Session Coordinator party discovery)
|
||||||
rpc GetRegisteredParties(GetRegisteredPartiesRequest) returns (GetRegisteredPartiesResponse);
|
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
|
// RouteMessageRequest routes an MPC message
|
||||||
|
|
@ -126,6 +164,15 @@ message SessionEvent {
|
||||||
bytes message_hash = 8; // For sign sessions
|
bytes message_hash = 8; // For sign sessions
|
||||||
int64 created_at = 9; // Unix timestamp milliseconds
|
int64 created_at = 9; // Unix timestamp milliseconds
|
||||||
int64 expires_at = 10; // 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
|
// PublishSessionEventRequest publishes a session event
|
||||||
|
|
@ -211,3 +258,102 @@ message HeartbeatResponse {
|
||||||
int64 server_timestamp = 2; // Server timestamp for clock sync
|
int64 server_timestamp = 2; // Server timestamp for clock sync
|
||||||
int32 pending_messages = 3; // Number of pending messages for this party
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@ service SessionCoordinator {
|
||||||
rpc StartSession(StartSessionRequest) returns (StartSessionResponse);
|
rpc StartSession(StartSessionRequest) returns (StartSessionResponse);
|
||||||
rpc ReportCompletion(ReportCompletionRequest) returns (ReportCompletionResponse);
|
rpc ReportCompletion(ReportCompletionRequest) returns (ReportCompletionResponse);
|
||||||
rpc CloseSession(CloseSessionRequest) returns (CloseSessionResponse);
|
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
|
// CreateSessionRequest creates a new MPC session
|
||||||
|
|
@ -25,6 +28,15 @@ message CreateSessionRequest {
|
||||||
bytes message_hash = 5; // Required for sign sessions
|
bytes message_hash = 5; // Required for sign sessions
|
||||||
int64 expires_in_seconds = 6; // Session expiration time
|
int64 expires_in_seconds = 6; // Session expiration time
|
||||||
PartyComposition party_composition = 7; // Optional: party composition requirements for auto-selection
|
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
|
// PartyComposition specifies requirements for automatic party selection
|
||||||
|
|
@ -99,8 +111,22 @@ message GetSessionStatusResponse {
|
||||||
string status = 1;
|
string status = 1;
|
||||||
int32 completed_parties = 2;
|
int32 completed_parties = 2;
|
||||||
int32 total_parties = 3;
|
int32 total_parties = 3;
|
||||||
bytes public_key = 4; // For completed keygen
|
string session_type = 4; // "keygen" or "sign"
|
||||||
bytes signature = 5; // For completed 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
|
// ReportCompletionRequest reports that a participant has completed
|
||||||
|
|
@ -151,3 +177,17 @@ message StartSessionResponse {
|
||||||
bool success = 1;
|
bool success = 1;
|
||||||
string status = 2; // New session status
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,23 @@
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# This script manages the MPC System Docker services
|
# 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
|
# 4000 - Account Service HTTP API
|
||||||
# 8081 - Session Coordinator API
|
# 8081 - Session Coordinator API
|
||||||
# 8082 - Message Router WebSocket
|
# 8082 - Message Router HTTP
|
||||||
# 8083 - Server Party API (user share generation)
|
# 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
|
set -e
|
||||||
|
|
@ -18,226 +30,488 @@ RED='\033[0;31m'
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
YELLOW='\033[1;33m'
|
YELLOW='\033[1;33m'
|
||||||
BLUE='\033[0;34m'
|
BLUE='\033[0;34m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
NC='\033[0m'
|
NC='\033[0m'
|
||||||
|
|
||||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||||
log_success() { echo -e "${GREEN}[OK]${NC} $1"; }
|
log_success() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||||
log_error() { echo -e "${RED}[ERROR]${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)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
cd "$SCRIPT_DIR"
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
# Load environment
|
# Determine which environment file to load
|
||||||
if [ -f ".env" ]; then
|
load_env() {
|
||||||
log_info "Loading environment from .env file"
|
local env_file="$1"
|
||||||
set -a
|
if [ -f "$env_file" ]; then
|
||||||
source .env
|
log_info "Loading environment from $env_file"
|
||||||
set +a
|
set -a
|
||||||
elif [ ! -f ".env" ] && [ -f ".env.example" ]; then
|
source "$env_file"
|
||||||
log_warn ".env file not found. Creating from .env.example"
|
set +a
|
||||||
log_warn "Please edit .env and configure for your environment!"
|
return 0
|
||||||
cp .env.example .env
|
fi
|
||||||
log_error "Please configure .env file and run again"
|
return 1
|
||||||
exit 1
|
}
|
||||||
fi
|
|
||||||
|
|
||||||
# Core services list
|
# Load environment based on mode
|
||||||
CORE_SERVICES="postgres redis rabbitmq"
|
load_environment() {
|
||||||
MPC_SERVICES="session-coordinator message-router server-party-1 server-party-2 server-party-3 server-party-api account-service"
|
local mode="$1"
|
||||||
ALL_SERVICES="$CORE_SERVICES $MPC_SERVICES"
|
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 <mode> <command> [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
|
case "$1" in
|
||||||
build)
|
prod)
|
||||||
log_info "Building MPC System services..."
|
shift
|
||||||
docker compose build
|
prod_commands "$@" || {
|
||||||
log_success "MPC System built successfully"
|
echo "Usage: $0 prod {build|up|down|restart|logs|status|health|clean}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
;;
|
;;
|
||||||
|
|
||||||
build-no-cache)
|
party)
|
||||||
log_info "Building MPC System (no cache)..."
|
shift
|
||||||
docker compose build --no-cache
|
party_commands "$@" || {
|
||||||
log_success "MPC System built successfully"
|
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)
|
help|--help|-h)
|
||||||
log_info "Starting MPC System..."
|
show_help
|
||||||
docker compose up -d
|
|
||||||
log_success "MPC System started"
|
|
||||||
echo ""
|
|
||||||
log_info "Services status:"
|
|
||||||
docker compose ps
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
down|stop)
|
"")
|
||||||
log_info "Stopping MPC System..."
|
show_help
|
||||||
docker compose down
|
exit 1
|
||||||
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 ""
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
echo "MPC System Deployment Script"
|
# Default to development mode
|
||||||
echo ""
|
dev_commands "$@" || {
|
||||||
echo "Usage: $0 <command> [options]"
|
show_help
|
||||||
echo ""
|
exit 1
|
||||||
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
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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.';
|
||||||
|
|
@ -9,9 +9,10 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/rwadurian/mpc-system/pkg/logger"
|
"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/ports"
|
||||||
"github.com/rwadurian/mpc-system/services/account/application/use_cases"
|
"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"
|
"github.com/rwadurian/mpc-system/services/account/domain/value_objects"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
@ -31,7 +32,7 @@ type AccountHTTPHandler struct {
|
||||||
completeRecoveryUC *use_cases.CompleteRecoveryUseCase
|
completeRecoveryUC *use_cases.CompleteRecoveryUseCase
|
||||||
getRecoveryStatusUC *use_cases.GetRecoveryStatusUseCase
|
getRecoveryStatusUC *use_cases.GetRecoveryStatusUseCase
|
||||||
cancelRecoveryUC *use_cases.CancelRecoveryUseCase
|
cancelRecoveryUC *use_cases.CancelRecoveryUseCase
|
||||||
sessionCoordinatorClient *grpc.SessionCoordinatorClient
|
sessionCoordinatorClient *grpcclient.SessionCoordinatorClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAccountHTTPHandler creates a new AccountHTTPHandler
|
// NewAccountHTTPHandler creates a new AccountHTTPHandler
|
||||||
|
|
@ -49,7 +50,7 @@ func NewAccountHTTPHandler(
|
||||||
completeRecoveryUC *use_cases.CompleteRecoveryUseCase,
|
completeRecoveryUC *use_cases.CompleteRecoveryUseCase,
|
||||||
getRecoveryStatusUC *use_cases.GetRecoveryStatusUseCase,
|
getRecoveryStatusUC *use_cases.GetRecoveryStatusUseCase,
|
||||||
cancelRecoveryUC *use_cases.CancelRecoveryUseCase,
|
cancelRecoveryUC *use_cases.CancelRecoveryUseCase,
|
||||||
sessionCoordinatorClient *grpc.SessionCoordinatorClient,
|
sessionCoordinatorClient *grpcclient.SessionCoordinatorClient,
|
||||||
) *AccountHTTPHandler {
|
) *AccountHTTPHandler {
|
||||||
return &AccountHTTPHandler{
|
return &AccountHTTPHandler{
|
||||||
createAccountUC: createAccountUC,
|
createAccountUC: createAccountUC,
|
||||||
|
|
@ -80,6 +81,11 @@ func (h *AccountHTTPHandler) RegisterRoutes(router *gin.RouterGroup) {
|
||||||
accounts.PUT("/:id", h.UpdateAccount)
|
accounts.PUT("/:id", h.UpdateAccount)
|
||||||
accounts.GET("/:id/shares", h.GetAccountShares)
|
accounts.GET("/:id/shares", h.GetAccountShares)
|
||||||
accounts.DELETE("/:id/shares/:shareId", h.DeactivateShare)
|
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")
|
auth := router.Group("/auth")
|
||||||
|
|
@ -613,9 +619,9 @@ func (h *AccountHTTPHandler) CreateKeygenSession(c *gin.Context) {
|
||||||
// CreateSigningSessionRequest represents the request for creating a signing session
|
// CreateSigningSessionRequest represents the request for creating a signing session
|
||||||
// Coordinator will automatically select parties based on account's registered shares
|
// Coordinator will automatically select parties based on account's registered shares
|
||||||
type CreateSigningSessionRequest struct {
|
type CreateSigningSessionRequest struct {
|
||||||
AccountID string `json:"account_id" binding:"required"` // Account to sign for
|
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)
|
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
|
UserShare string `json:"user_share"` // Required if account has delegate share: user's encrypted share (hex)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateSigningSession handles creating a new signing session
|
// CreateSigningSession handles creating a new signing session
|
||||||
|
|
@ -655,20 +661,80 @@ func (h *AccountHTTPHandler) CreateSigningSession(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the party IDs from account shares
|
// Build a map of active shares for validation
|
||||||
var partyIDs []string
|
activeSharesMap := make(map[string]*entities.AccountShare)
|
||||||
|
var allActivePartyIDs []string
|
||||||
|
var delegateShare *entities.AccountShare
|
||||||
for _, share := range accountOutput.Shares {
|
for _, share := range accountOutput.Shares {
|
||||||
if share.IsActive {
|
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 {
|
if len(partyIDs) < accountOutput.Account.ThresholdT {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
"error": "insufficient active shares for signing",
|
"error": "insufficient parties for signing",
|
||||||
"required": accountOutput.Account.ThresholdT,
|
"required": accountOutput.Account.ThresholdT,
|
||||||
"active": len(partyIDs),
|
"selected": len(partyIDs),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -677,10 +743,30 @@ func (h *AccountHTTPHandler) CreateSigningSession(c *gin.Context) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
logger.Info("Calling CreateSigningSession via gRPC (auto party selection)",
|
// Prepare delegate user share if needed
|
||||||
zap.String("account_id", req.AccountID),
|
var delegateUserShare *grpcclient.DelegateUserShareInput
|
||||||
zap.Int("threshold_t", accountOutput.Account.ThresholdT),
|
if delegateShare != nil {
|
||||||
zap.Int("available_parties", len(partyIDs)))
|
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(
|
resp, err := h.sessionCoordinatorClient.CreateSigningSessionAuto(
|
||||||
ctx,
|
ctx,
|
||||||
|
|
@ -688,6 +774,7 @@ func (h *AccountHTTPHandler) CreateSigningSession(c *gin.Context) {
|
||||||
partyIDs,
|
partyIDs,
|
||||||
messageHash,
|
messageHash,
|
||||||
600, // 10 minutes expiry
|
600, // 10 minutes expiry
|
||||||
|
delegateUserShare,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -700,7 +787,7 @@ func (h *AccountHTTPHandler) CreateSigningSession(c *gin.Context) {
|
||||||
zap.String("session_id", resp.SessionID),
|
zap.String("session_id", resp.SessionID),
|
||||||
zap.Int("num_parties", len(resp.SelectedParties)))
|
zap.Int("num_parties", len(resp.SelectedParties)))
|
||||||
|
|
||||||
c.JSON(http.StatusCreated, gin.H{
|
response := gin.H{
|
||||||
"session_id": resp.SessionID,
|
"session_id": resp.SessionID,
|
||||||
"session_type": "sign",
|
"session_type": "sign",
|
||||||
"account_id": req.AccountID,
|
"account_id": req.AccountID,
|
||||||
|
|
@ -708,7 +795,17 @@ func (h *AccountHTTPHandler) CreateSigningSession(c *gin.Context) {
|
||||||
"threshold_t": accountOutput.Account.ThresholdT,
|
"threshold_t": accountOutput.Account.ThresholdT,
|
||||||
"selected_parties": resp.SelectedParties,
|
"selected_parties": resp.SelectedParties,
|
||||||
"status": "created",
|
"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
|
// GetSessionStatus handles getting session status
|
||||||
|
|
@ -734,16 +831,37 @@ func (h *AccountHTTPHandler) GetSessionStatus(c *gin.Context) {
|
||||||
response := gin.H{
|
response := gin.H{
|
||||||
"session_id": sessionID,
|
"session_id": sessionID,
|
||||||
"status": resp.Status,
|
"status": resp.Status,
|
||||||
|
"session_type": resp.SessionType, // "keygen" or "sign"
|
||||||
"completed_parties": resp.CompletedParties,
|
"completed_parties": resp.CompletedParties,
|
||||||
"total_parties": resp.TotalParties,
|
"total_parties": resp.TotalParties,
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(resp.PublicKey) > 0 {
|
// Keygen-specific fields
|
||||||
response["public_key"] = hex.EncodeToString(resp.PublicKey)
|
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 {
|
// Sign-specific fields
|
||||||
response["signature"] = hex.EncodeToString(resp.Signature)
|
if resp.SessionType == "sign" {
|
||||||
|
if len(resp.Signature) > 0 {
|
||||||
|
response["signature"] = hex.EncodeToString(resp.Signature)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, response)
|
c.JSON(http.StatusOK, response)
|
||||||
|
|
@ -857,3 +975,256 @@ func (h *AccountHTTPHandler) CreateAccountFromKeygen(c *gin.Context) {
|
||||||
"public_key": hex.EncodeToString(output.Account.PublicKey),
|
"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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -121,14 +121,23 @@ func (c *SessionCoordinatorClient) CreateKeygenSessionAuto(
|
||||||
}, nil
|
}, 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
|
// CreateSigningSessionAuto creates a new signing session with automatic party selection
|
||||||
// Coordinator will select parties from the provided party IDs (from account shares)
|
// 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(
|
func (c *SessionCoordinatorClient) CreateSigningSessionAuto(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
thresholdT int32,
|
thresholdT int32,
|
||||||
partyIDs []string,
|
partyIDs []string,
|
||||||
messageHash []byte,
|
messageHash []byte,
|
||||||
expiresInSeconds int64,
|
expiresInSeconds int64,
|
||||||
|
delegateUserShare *DelegateUserShareInput,
|
||||||
) (*CreateSessionAutoResponse, error) {
|
) (*CreateSessionAutoResponse, error) {
|
||||||
// Convert party IDs to participant info (minimal info, coordinator will fill in details)
|
// Convert party IDs to participant info (minimal info, coordinator will fill in details)
|
||||||
pbParticipants := make([]*coordinatorpb.ParticipantInfo, len(partyIDs))
|
pbParticipants := make([]*coordinatorpb.ParticipantInfo, len(partyIDs))
|
||||||
|
|
@ -147,9 +156,22 @@ func (c *SessionCoordinatorClient) CreateSigningSessionAuto(
|
||||||
ExpiresInSeconds: expiresInSeconds,
|
ExpiresInSeconds: expiresInSeconds,
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info("Sending CreateSigningSession gRPC request",
|
// Add delegate user share if provided
|
||||||
zap.Int32("threshold_t", thresholdT),
|
if delegateUserShare != nil {
|
||||||
zap.Int("num_parties", len(partyIDs)))
|
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)
|
resp, err := c.client.CreateSession(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -170,6 +192,7 @@ func (c *SessionCoordinatorClient) CreateSigningSessionAuto(
|
||||||
return &CreateSessionAutoResponse{
|
return &CreateSessionAutoResponse{
|
||||||
SessionID: resp.SessionId,
|
SessionID: resp.SessionId,
|
||||||
SelectedParties: selectedParties,
|
SelectedParties: selectedParties,
|
||||||
|
DelegateParty: resp.DelegatePartyId,
|
||||||
JoinTokens: resp.JoinTokens,
|
JoinTokens: resp.JoinTokens,
|
||||||
ExpiresAt: resp.ExpiresAt,
|
ExpiresAt: resp.ExpiresAt,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
@ -189,13 +212,26 @@ func (c *SessionCoordinatorClient) GetSessionStatus(
|
||||||
return nil, fmt.Errorf("failed to get session status: %w", err)
|
return nil, fmt.Errorf("failed to get session status: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SessionStatusResponse{
|
result := &SessionStatusResponse{
|
||||||
Status: resp.Status,
|
Status: resp.Status,
|
||||||
CompletedParties: resp.CompletedParties,
|
CompletedParties: resp.CompletedParties,
|
||||||
TotalParties: resp.TotalParties,
|
TotalParties: resp.TotalParties,
|
||||||
|
SessionType: resp.SessionType,
|
||||||
PublicKey: resp.PublicKey,
|
PublicKey: resp.PublicKey,
|
||||||
Signature: resp.Signature,
|
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
|
// Close closes the gRPC connection
|
||||||
|
|
@ -236,6 +272,20 @@ type SessionStatusResponse struct {
|
||||||
Status string
|
Status string
|
||||||
CompletedParties int32
|
CompletedParties int32
|
||||||
TotalParties int32
|
TotalParties int32
|
||||||
|
SessionType string // "keygen" or "sign"
|
||||||
PublicKey []byte
|
PublicKey []byte
|
||||||
Signature []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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"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/entities"
|
||||||
"github.com/rwadurian/mpc-system/services/account/domain/repositories"
|
"github.com/rwadurian/mpc-system/services/account/domain/repositories"
|
||||||
"github.com/rwadurian/mpc-system/services/account/domain/value_objects"
|
"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 {
|
func (r *AccountPostgresRepo) Create(ctx context.Context, account *entities.Account) error {
|
||||||
query := `
|
query := `
|
||||||
INSERT INTO accounts (id, username, email, phone, public_key, keygen_session_id,
|
INSERT INTO accounts (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)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
||||||
`
|
`
|
||||||
|
|
||||||
_, err := r.db.ExecContext(ctx, query,
|
_, err := r.db.ExecContext(ctx, query,
|
||||||
|
|
@ -42,6 +43,7 @@ func (r *AccountPostgresRepo) Create(ctx context.Context, account *entities.Acco
|
||||||
account.CreatedAt,
|
account.CreatedAt,
|
||||||
account.UpdatedAt,
|
account.UpdatedAt,
|
||||||
account.LastLoginAt,
|
account.LastLoginAt,
|
||||||
|
pq.Array(account.SigningParties),
|
||||||
)
|
)
|
||||||
|
|
||||||
return err
|
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) {
|
func (r *AccountPostgresRepo) GetByID(ctx context.Context, id value_objects.AccountID) (*entities.Account, error) {
|
||||||
query := `
|
query := `
|
||||||
SELECT id, username, email, phone, public_key, keygen_session_id,
|
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
|
FROM accounts
|
||||||
WHERE id = $1
|
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) {
|
func (r *AccountPostgresRepo) GetByUsername(ctx context.Context, username string) (*entities.Account, error) {
|
||||||
query := `
|
query := `
|
||||||
SELECT id, username, email, phone, public_key, keygen_session_id,
|
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
|
FROM accounts
|
||||||
WHERE username = $1
|
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) {
|
func (r *AccountPostgresRepo) GetByEmail(ctx context.Context, email string) (*entities.Account, error) {
|
||||||
query := `
|
query := `
|
||||||
SELECT id, username, email, phone, public_key, keygen_session_id,
|
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
|
FROM accounts
|
||||||
WHERE email = $1
|
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) {
|
func (r *AccountPostgresRepo) GetByPublicKey(ctx context.Context, publicKey []byte) (*entities.Account, error) {
|
||||||
query := `
|
query := `
|
||||||
SELECT id, username, email, phone, public_key, keygen_session_id,
|
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
|
FROM accounts
|
||||||
WHERE public_key = $1
|
WHERE public_key = $1
|
||||||
`
|
`
|
||||||
|
|
@ -100,7 +102,8 @@ func (r *AccountPostgresRepo) Update(ctx context.Context, account *entities.Acco
|
||||||
query := `
|
query := `
|
||||||
UPDATE accounts
|
UPDATE accounts
|
||||||
SET username = $2, email = $3, phone = $4, public_key = $5, keygen_session_id = $6,
|
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
|
WHERE id = $1
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
@ -116,6 +119,7 @@ func (r *AccountPostgresRepo) Update(ctx context.Context, account *entities.Acco
|
||||||
account.Status.String(),
|
account.Status.String(),
|
||||||
account.UpdatedAt,
|
account.UpdatedAt,
|
||||||
account.LastLoginAt,
|
account.LastLoginAt,
|
||||||
|
pq.Array(account.SigningParties),
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
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) {
|
func (r *AccountPostgresRepo) List(ctx context.Context, offset, limit int) ([]*entities.Account, error) {
|
||||||
query := `
|
query := `
|
||||||
SELECT id, username, email, phone, public_key, keygen_session_id,
|
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
|
FROM accounts
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
LIMIT $1 OFFSET $2
|
LIMIT $1 OFFSET $2
|
||||||
|
|
@ -222,6 +226,7 @@ func (r *AccountPostgresRepo) scanAccount(row *sql.Row) (*entities.Account, erro
|
||||||
thresholdN int
|
thresholdN int
|
||||||
thresholdT int
|
thresholdT int
|
||||||
status string
|
status string
|
||||||
|
signingParties pq.StringArray
|
||||||
account entities.Account
|
account entities.Account
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -238,6 +243,7 @@ func (r *AccountPostgresRepo) scanAccount(row *sql.Row) (*entities.Account, erro
|
||||||
&account.CreatedAt,
|
&account.CreatedAt,
|
||||||
&account.UpdatedAt,
|
&account.UpdatedAt,
|
||||||
&account.LastLoginAt,
|
&account.LastLoginAt,
|
||||||
|
&signingParties,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -260,6 +266,7 @@ func (r *AccountPostgresRepo) scanAccount(row *sql.Row) (*entities.Account, erro
|
||||||
account.ThresholdN = thresholdN
|
account.ThresholdN = thresholdN
|
||||||
account.ThresholdT = thresholdT
|
account.ThresholdT = thresholdT
|
||||||
account.Status = value_objects.AccountStatus(status)
|
account.Status = value_objects.AccountStatus(status)
|
||||||
|
account.SigningParties = signingParties
|
||||||
|
|
||||||
return &account, nil
|
return &account, nil
|
||||||
}
|
}
|
||||||
|
|
@ -276,6 +283,7 @@ func (r *AccountPostgresRepo) scanAccountFromRows(rows *sql.Rows) (*entities.Acc
|
||||||
thresholdN int
|
thresholdN int
|
||||||
thresholdT int
|
thresholdT int
|
||||||
status string
|
status string
|
||||||
|
signingParties pq.StringArray
|
||||||
account entities.Account
|
account entities.Account
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -292,6 +300,7 @@ func (r *AccountPostgresRepo) scanAccountFromRows(rows *sql.Rows) (*entities.Acc
|
||||||
&account.CreatedAt,
|
&account.CreatedAt,
|
||||||
&account.UpdatedAt,
|
&account.UpdatedAt,
|
||||||
&account.LastLoginAt,
|
&account.LastLoginAt,
|
||||||
|
&signingParties,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -311,6 +320,7 @@ func (r *AccountPostgresRepo) scanAccountFromRows(rows *sql.Rows) (*entities.Acc
|
||||||
account.ThresholdN = thresholdN
|
account.ThresholdN = thresholdN
|
||||||
account.ThresholdT = thresholdT
|
account.ThresholdT = thresholdT
|
||||||
account.Status = value_objects.AccountStatus(status)
|
account.Status = value_objects.AccountStatus(status)
|
||||||
|
account.SigningParties = signingParties
|
||||||
|
|
||||||
return &account, nil
|
return &account, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -114,8 +114,10 @@ type CompleteRecoveryPort interface {
|
||||||
|
|
||||||
// UpdateAccountInput represents input for updating an account
|
// UpdateAccountInput represents input for updating an account
|
||||||
type UpdateAccountInput struct {
|
type UpdateAccountInput struct {
|
||||||
AccountID value_objects.AccountID
|
AccountID value_objects.AccountID
|
||||||
Phone *string
|
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
|
// UpdateAccountOutput represents output from updating an account
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,15 @@ func (uc *UpdateAccountUseCase) Execute(ctx context.Context, input ports.UpdateA
|
||||||
account.SetPhone(*input.Phone)
|
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 {
|
if err := uc.accountRepo.Update(ctx, account); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,10 @@ type Account struct {
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
LastLoginAt *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
|
// NewAccount creates a new Account
|
||||||
|
|
@ -135,18 +139,67 @@ func (a *Account) Validate() error {
|
||||||
return nil
|
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
|
// Account errors
|
||||||
var (
|
var (
|
||||||
ErrInvalidUsername = &AccountError{Code: "INVALID_USERNAME", Message: "username is required"}
|
ErrInvalidUsername = &AccountError{Code: "INVALID_USERNAME", Message: "username is required"}
|
||||||
ErrInvalidEmail = &AccountError{Code: "INVALID_EMAIL", Message: "email is required"}
|
ErrInvalidEmail = &AccountError{Code: "INVALID_EMAIL", Message: "email is required"}
|
||||||
ErrInvalidPublicKey = &AccountError{Code: "INVALID_PUBLIC_KEY", Message: "public key is required"}
|
ErrInvalidPublicKey = &AccountError{Code: "INVALID_PUBLIC_KEY", Message: "public key is required"}
|
||||||
ErrInvalidThreshold = &AccountError{Code: "INVALID_THRESHOLD", Message: "invalid threshold configuration"}
|
ErrInvalidThreshold = &AccountError{Code: "INVALID_THRESHOLD", Message: "invalid threshold configuration"}
|
||||||
ErrAccountInRecovery = &AccountError{Code: "ACCOUNT_IN_RECOVERY", Message: "account is in recovery mode"}
|
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"}
|
ErrCannotInitiateRecovery = &AccountError{Code: "CANNOT_INITIATE_RECOVERY", Message: "cannot initiate recovery in current state"}
|
||||||
ErrAccountNotActive = &AccountError{Code: "ACCOUNT_NOT_ACTIVE", Message: "account is not active"}
|
ErrAccountNotActive = &AccountError{Code: "ACCOUNT_NOT_ACTIVE", Message: "account is not active"}
|
||||||
ErrAccountNotFound = &AccountError{Code: "ACCOUNT_NOT_FOUND", Message: "account not found"}
|
ErrAccountNotFound = &AccountError{Code: "ACCOUNT_NOT_FOUND", Message: "account not found"}
|
||||||
ErrDuplicateUsername = &AccountError{Code: "DUPLICATE_USERNAME", Message: "username already exists"}
|
ErrDuplicateUsername = &AccountError{Code: "DUPLICATE_USERNAME", Message: "username already exists"}
|
||||||
ErrDuplicateEmail = &AccountError{Code: "DUPLICATE_EMAIL", Message: "email 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
|
// AccountError represents an account domain error
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,16 @@ func (s *AccountShare) IsRecoveryShare() bool {
|
||||||
return s.ShareType == value_objects.ShareTypeRecovery
|
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
|
// Validate validates the account share
|
||||||
func (s *AccountShare) Validate() error {
|
func (s *AccountShare) Validate() error {
|
||||||
if s.AccountID.IsZero() {
|
if s.AccountID.IsZero() {
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,10 @@ func (s AccountStatus) CanInitiateRecovery() bool {
|
||||||
type ShareType string
|
type ShareType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ShareTypeUserDevice ShareType = "user_device"
|
ShareTypeUserDevice ShareType = "user_device" // Share stored on user's device
|
||||||
ShareTypeServer ShareType = "server"
|
ShareTypeServer ShareType = "server" // Share stored by persistent server party (in DB)
|
||||||
ShareTypeRecovery ShareType = "recovery"
|
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
|
// String returns the string representation
|
||||||
|
|
@ -52,13 +53,18 @@ func (st ShareType) String() string {
|
||||||
// IsValid checks if the share type is valid
|
// IsValid checks if the share type is valid
|
||||||
func (st ShareType) IsValid() bool {
|
func (st ShareType) IsValid() bool {
|
||||||
switch st {
|
switch st {
|
||||||
case ShareTypeUserDevice, ShareTypeServer, ShareTypeRecovery:
|
case ShareTypeUserDevice, ShareTypeServer, ShareTypeRecovery, ShareTypeDelegate:
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
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
|
// RecoveryType represents the type of account recovery
|
||||||
type RecoveryType string
|
type RecoveryType string
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
coordinator "github.com/rwadurian/mpc-system/api/grpc/coordinator/v1"
|
||||||
pb "github.com/rwadurian/mpc-system/api/grpc/router/v1"
|
pb "github.com/rwadurian/mpc-system/api/grpc/router/v1"
|
||||||
"github.com/rwadurian/mpc-system/pkg/logger"
|
"github.com/rwadurian/mpc-system/pkg/logger"
|
||||||
"github.com/rwadurian/mpc-system/services/message-router/application/use_cases"
|
"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/entities"
|
||||||
"github.com/rwadurian/mpc-system/services/message-router/domain/repositories"
|
"github.com/rwadurian/mpc-system/services/message-router/domain/repositories"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
@ -30,6 +32,7 @@ type MessageRouterServer struct {
|
||||||
partyRegistry *domain.PartyRegistry
|
partyRegistry *domain.PartyRegistry
|
||||||
eventBroadcaster *domain.SessionEventBroadcaster
|
eventBroadcaster *domain.SessionEventBroadcaster
|
||||||
messageRepo repositories.MessageRepository
|
messageRepo repositories.MessageRepository
|
||||||
|
coordinatorConn *grpc.ClientConn // Connection to Session Coordinator for proxying
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMessageRouterServer creates a new gRPC server
|
// 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
|
// RouteMessage routes an MPC message
|
||||||
func (s *MessageRouterServer) RouteMessage(
|
func (s *MessageRouterServer) RouteMessage(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
|
@ -452,3 +462,218 @@ func toGRPCError(err error) error {
|
||||||
return status.Error(codes.Internal, err.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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
"google.golang.org/grpc/reflection"
|
"google.golang.org/grpc/reflection"
|
||||||
|
|
||||||
pb "github.com/rwadurian/mpc-system/api/grpc/router/v1"
|
pb "github.com/rwadurian/mpc-system/api/grpc/router/v1"
|
||||||
|
|
@ -77,6 +78,26 @@ func main() {
|
||||||
routeMessageUC := use_cases.NewRouteMessageUseCase(messageRepo, messageBroker)
|
routeMessageUC := use_cases.NewRouteMessageUseCase(messageRepo, messageBroker)
|
||||||
getPendingMessagesUC := use_cases.NewGetPendingMessagesUseCase(messageRepo)
|
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
|
// Start message cleanup background job
|
||||||
go runMessageCleanup(messageRepo)
|
go runMessageCleanup(messageRepo)
|
||||||
|
|
||||||
|
|
@ -92,7 +113,7 @@ func main() {
|
||||||
|
|
||||||
// Start gRPC server
|
// Start gRPC server
|
||||||
go func() {
|
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)
|
errChan <- fmt.Errorf("gRPC server error: %w", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
@ -189,6 +210,7 @@ func startGRPCServer(
|
||||||
partyRegistry *domain.PartyRegistry,
|
partyRegistry *domain.PartyRegistry,
|
||||||
eventBroadcaster *domain.SessionEventBroadcaster,
|
eventBroadcaster *domain.SessionEventBroadcaster,
|
||||||
messageRepo *postgres.MessagePostgresRepo,
|
messageRepo *postgres.MessagePostgresRepo,
|
||||||
|
coordinatorConn *grpc.ClientConn,
|
||||||
) error {
|
) error {
|
||||||
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", cfg.Server.GRPCPort))
|
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", cfg.Server.GRPCPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -206,6 +228,13 @@ func startGRPCServer(
|
||||||
eventBroadcaster,
|
eventBroadcaster,
|
||||||
messageRepo,
|
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)
|
pb.RegisterMessageRouterServer(grpcServer, messageRouterServer)
|
||||||
|
|
||||||
// Enable reflection for debugging
|
// Enable reflection for debugging
|
||||||
|
|
|
||||||
|
|
@ -14,18 +14,15 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
"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/config"
|
||||||
"github.com/rwadurian/mpc-system/pkg/crypto"
|
"github.com/rwadurian/mpc-system/pkg/crypto"
|
||||||
"github.com/rwadurian/mpc-system/pkg/logger"
|
"github.com/rwadurian/mpc-system/pkg/logger"
|
||||||
grpcclient "github.com/rwadurian/mpc-system/services/server-party/adapters/output/grpc"
|
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/application/use_cases"
|
||||||
"github.com/rwadurian/mpc-system/services/server-party/infrastructure/cache"
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Global share cache for delegate parties
|
|
||||||
var globalShareCache *cache.ShareCache
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Parse flags
|
// Parse flags
|
||||||
configPath := flag.String("config", "", "Path to config file")
|
configPath := flag.String("config", "", "Path to config file")
|
||||||
|
|
@ -48,14 +45,10 @@ func main() {
|
||||||
}
|
}
|
||||||
defer logger.Sync()
|
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.String("environment", cfg.Server.Environment),
|
||||||
zap.Int("http_port", cfg.Server.HTTPPort))
|
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
|
// Initialize crypto service with master key from environment
|
||||||
masterKeyHex := os.Getenv("MPC_CRYPTO_MASTER_KEY")
|
masterKeyHex := os.Getenv("MPC_CRYPTO_MASTER_KEY")
|
||||||
if masterKeyHex == "" {
|
if masterKeyHex == "" {
|
||||||
|
|
@ -70,29 +63,14 @@ func main() {
|
||||||
logger.Fatal("Failed to create crypto service", zap.Error(err))
|
logger.Fatal("Failed to create crypto service", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get API key for authentication
|
// Get Message Router address from environment
|
||||||
apiKey := os.Getenv("MPC_API_KEY")
|
// Delegate party (like all parties) ONLY connects to Message Router
|
||||||
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"
|
|
||||||
}
|
|
||||||
routerAddr := os.Getenv("MESSAGE_ROUTER_ADDR")
|
routerAddr := os.Getenv("MESSAGE_ROUTER_ADDR")
|
||||||
if routerAddr == "" {
|
if routerAddr == "" {
|
||||||
routerAddr = "message-router:50051"
|
routerAddr = "message-router:50051"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize gRPC clients
|
// Initialize Message Router client (the only gRPC connection needed)
|
||||||
sessionClient, err := grpcclient.NewSessionCoordinatorClient(coordinatorAddr)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal("Failed to connect to session coordinator", zap.Error(err))
|
|
||||||
}
|
|
||||||
defer sessionClient.Close()
|
|
||||||
|
|
||||||
messageRouter, err := grpcclient.NewMessageRouterClient(routerAddr)
|
messageRouter, err := grpcclient.NewMessageRouterClient(routerAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal("Failed to connect to message router", zap.Error(err))
|
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)
|
// Get party ID from environment (or use default)
|
||||||
partyID := os.Getenv("PARTY_ID")
|
partyID := os.Getenv("PARTY_ID")
|
||||||
if partyID == "" {
|
if partyID == "" {
|
||||||
partyID = "server-party-api"
|
partyID = "delegate-party"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force PARTY_ROLE to delegate for this service
|
// 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
|
// Register this party as a delegate party with Message Router
|
||||||
logger.Info("Registering party with Message Router",
|
logger.Info("Registering party with Message Router",
|
||||||
zap.String("party_id", partyID),
|
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.Fatal("Failed to register party", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info("Party registered successfully",
|
logger.Info("Party registered successfully",
|
||||||
zap.String("party_id", partyID),
|
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)
|
// 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(
|
participateKeygenUC := use_cases.NewParticipateKeygenUseCase(
|
||||||
nil, // No database storage for delegate
|
nil, // No database storage for delegate
|
||||||
sessionClient,
|
messageRouter,
|
||||||
messageRouter,
|
messageRouter,
|
||||||
cryptoService,
|
cryptoService,
|
||||||
)
|
)
|
||||||
participateSigningUC := use_cases.NewParticipateSigningUseCase(
|
participateSigningUC := use_cases.NewParticipateSigningUseCase(
|
||||||
nil, // No database storage for delegate
|
nil, // No database storage for delegate
|
||||||
sessionClient,
|
messageRouter,
|
||||||
messageRouter,
|
messageRouter,
|
||||||
cryptoService,
|
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)
|
errChan := make(chan error, 1)
|
||||||
go func() {
|
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)
|
errChan <- fmt.Errorf("HTTP server error: %w", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
@ -165,258 +173,162 @@ func main() {
|
||||||
|
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
logger.Info("Shutdown complete")
|
logger.Info("Shutdown complete")
|
||||||
|
|
||||||
_ = ctx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func startHTTPServer(
|
// startHTTPServer starts HTTP server for health checks only
|
||||||
cfg *config.Config,
|
func startHTTPServer(cfg *config.Config) error {
|
||||||
participateKeygenUC *use_cases.ParticipateKeygenUseCase,
|
|
||||||
participateSigningUC *use_cases.ParticipateSigningUseCase,
|
|
||||||
cryptoService *crypto.CryptoService,
|
|
||||||
apiKey string,
|
|
||||||
) error {
|
|
||||||
if cfg.Server.Environment == "production" {
|
if cfg.Server.Environment == "production" {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
router := gin.New()
|
r := gin.New()
|
||||||
router.Use(gin.Recovery())
|
r.Use(gin.Recovery())
|
||||||
router.Use(gin.Logger())
|
|
||||||
|
|
||||||
// Health check
|
// Health check only
|
||||||
router.GET("/health", func(c *gin.Context) {
|
r.GET("/health", func(c *gin.Context) {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"status": "healthy",
|
"status": "healthy",
|
||||||
"service": "server-party-api",
|
"service": "delegate-party",
|
||||||
"role": "delegate",
|
"role": "delegate",
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// API routes with optional authentication
|
logger.Info("Starting HTTP server (health check only)", zap.Int("port", cfg.Server.HTTPPort))
|
||||||
api := router.Group("/api/v1")
|
return r.Run(fmt.Sprintf(":%d", cfg.Server.HTTPPort))
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiKeyAuth(expectedKey string) gin.HandlerFunc {
|
// createSessionEventHandler creates a handler for session events (party-driven architecture)
|
||||||
return func(c *gin.Context) {
|
// Delegate party automatically responds to session creation events by joining keygen or signing sessions
|
||||||
apiKey := c.GetHeader("X-API-Key")
|
// After keygen, it submits the user's share to Session Coordinator (instead of saving to DB)
|
||||||
if apiKey == "" {
|
func createSessionEventHandler(
|
||||||
apiKey = c.Query("api_key")
|
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"})
|
if !isSelected {
|
||||||
c.Abort()
|
logger.Debug("Party not selected for this session",
|
||||||
|
zap.String("session_id", event.SessionId),
|
||||||
|
zap.String("party_id", partyID))
|
||||||
return
|
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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -599,3 +599,194 @@ func (c *MessageRouterClient) StartHeartbeat(
|
||||||
|
|
||||||
return cancel
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,23 +81,15 @@ func main() {
|
||||||
logger.Fatal("Failed to create crypto service", zap.Error(err))
|
logger.Fatal("Failed to create crypto service", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get gRPC service addresses from environment
|
// Get Message Router address from environment
|
||||||
coordinatorAddr := os.Getenv("SESSION_COORDINATOR_ADDR")
|
// Server-parties ONLY connect to Message Router (not Session Coordinator)
|
||||||
if coordinatorAddr == "" {
|
// Message Router proxies session operations to Session Coordinator
|
||||||
coordinatorAddr = "localhost:9091"
|
|
||||||
}
|
|
||||||
routerAddr := os.Getenv("MESSAGE_ROUTER_ADDR")
|
routerAddr := os.Getenv("MESSAGE_ROUTER_ADDR")
|
||||||
if routerAddr == "" {
|
if routerAddr == "" {
|
||||||
routerAddr = "localhost:9092"
|
routerAddr = "localhost:9092"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize gRPC clients
|
// Initialize Message Router client (the only gRPC connection needed)
|
||||||
sessionClient, err := grpcclient.NewSessionCoordinatorClient(coordinatorAddr)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal("Failed to connect to session coordinator", zap.Error(err))
|
|
||||||
}
|
|
||||||
defer sessionClient.Close()
|
|
||||||
|
|
||||||
messageRouter, err := grpcclient.NewMessageRouterClient(routerAddr)
|
messageRouter, err := grpcclient.NewMessageRouterClient(routerAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal("Failed to connect to message router", zap.Error(err))
|
logger.Fatal("Failed to connect to message router", zap.Error(err))
|
||||||
|
|
@ -107,16 +99,17 @@ func main() {
|
||||||
// Initialize repositories
|
// Initialize repositories
|
||||||
keyShareRepo := postgres.NewKeySharePostgresRepo(db)
|
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(
|
participateKeygenUC := use_cases.NewParticipateKeygenUseCase(
|
||||||
keyShareRepo,
|
keyShareRepo,
|
||||||
sessionClient,
|
messageRouter, // MessageRouterClient implements SessionCoordinatorClient interface
|
||||||
messageRouter,
|
messageRouter,
|
||||||
cryptoService,
|
cryptoService,
|
||||||
)
|
)
|
||||||
participateSigningUC := use_cases.NewParticipateSigningUseCase(
|
participateSigningUC := use_cases.NewParticipateSigningUseCase(
|
||||||
keyShareRepo,
|
keyShareRepo,
|
||||||
sessionClient,
|
messageRouter, // MessageRouterClient implements SessionCoordinatorClient interface
|
||||||
messageRouter,
|
messageRouter,
|
||||||
cryptoService,
|
cryptoService,
|
||||||
)
|
)
|
||||||
|
|
@ -192,7 +185,6 @@ func main() {
|
||||||
partyID,
|
partyID,
|
||||||
participateKeygenUC,
|
participateKeygenUC,
|
||||||
participateSigningUC,
|
participateSigningUC,
|
||||||
sessionClient,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := messageRouter.SubscribeSessionEvents(ctx, partyID, eventHandler); err != nil {
|
if err := messageRouter.SubscribeSessionEvents(ctx, partyID, eventHandler); err != nil {
|
||||||
|
|
@ -535,7 +527,6 @@ func createSessionEventHandler(
|
||||||
partyID string,
|
partyID string,
|
||||||
participateKeygenUC *use_cases.ParticipateKeygenUseCase,
|
participateKeygenUC *use_cases.ParticipateKeygenUseCase,
|
||||||
participateSigningUC *use_cases.ParticipateSigningUseCase,
|
participateSigningUC *use_cases.ParticipateSigningUseCase,
|
||||||
sessionClient *grpcclient.SessionCoordinatorClient,
|
|
||||||
) func(*router.SessionEvent) {
|
) func(*router.SessionEvent) {
|
||||||
return func(event *router.SessionEvent) {
|
return func(event *router.SessionEvent) {
|
||||||
// Check if this party is selected for the session
|
// Check if this party is selected for the session
|
||||||
|
|
@ -617,6 +608,15 @@ func createSessionEventHandler(
|
||||||
MessageHash: event.MessageHash,
|
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)
|
result, err := participateSigningUC.Execute(ctx, input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Signing participation failed",
|
logger.Error("Signing participation failed",
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package grpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
@ -17,6 +18,79 @@ import (
|
||||||
"google.golang.org/grpc/status"
|
"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
|
// SessionCoordinatorServer implements the gRPC SessionCoordinator service
|
||||||
type SessionCoordinatorServer struct {
|
type SessionCoordinatorServer struct {
|
||||||
pb.UnimplementedSessionCoordinatorServer
|
pb.UnimplementedSessionCoordinatorServer
|
||||||
|
|
@ -86,6 +160,15 @@ func (s *SessionCoordinatorServer) CreateSession(
|
||||||
ExpiresIn: time.Duration(req.ExpiresInSeconds) * time.Second,
|
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
|
// Execute use case
|
||||||
output, err := s.createSessionUC.Execute(ctx, inputData)
|
output, err := s.createSessionUC.Execute(ctx, inputData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -194,13 +277,34 @@ func (s *SessionCoordinatorServer) GetSessionStatus(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &pb.GetSessionStatusResponse{
|
resp := &pb.GetSessionStatusResponse{
|
||||||
Status: output.Status,
|
Status: output.Status,
|
||||||
CompletedParties: int32(completedParties),
|
CompletedParties: int32(completedParties),
|
||||||
TotalParties: int32(len(output.Participants)),
|
TotalParties: int32(len(output.Participants)),
|
||||||
|
SessionType: output.SessionType,
|
||||||
PublicKey: output.PublicKey,
|
PublicKey: output.PublicKey,
|
||||||
Signature: output.Signature,
|
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
|
// ReportCompletion reports that a participant has completed
|
||||||
|
|
@ -323,6 +427,40 @@ func (s *SessionCoordinatorServer) StartSession(
|
||||||
}, nil
|
}, 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
|
// toGRPCError converts domain errors to gRPC errors
|
||||||
func toGRPCError(err error) error {
|
func toGRPCError(err error) error {
|
||||||
switch err {
|
switch err {
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ func (h *SessionHTTPHandler) CreateSession(c *gin.Context) {
|
||||||
|
|
||||||
expiresIn := time.Duration(req.ExpiresIn) * time.Second
|
expiresIn := time.Duration(req.ExpiresIn) * time.Second
|
||||||
if expiresIn == 0 {
|
if expiresIn == 0 {
|
||||||
expiresIn = 10 * time.Minute // Default
|
expiresIn = 24 * time.Hour // Default: 24-hour session validity
|
||||||
}
|
}
|
||||||
|
|
||||||
inputData := input.CreateSessionInput{
|
inputData := input.CreateSessionInput{
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/rwadurian/mpc-system/pkg/grpcutil"
|
"github.com/rwadurian/mpc-system/pkg/grpcutil"
|
||||||
"github.com/rwadurian/mpc-system/pkg/logger"
|
"github.com/rwadurian/mpc-system/pkg/logger"
|
||||||
"github.com/rwadurian/mpc-system/pkg/retry"
|
"github.com/rwadurian/mpc-system/pkg/retry"
|
||||||
|
"github.com/rwadurian/mpc-system/services/session-coordinator/application/use_cases"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -120,6 +121,7 @@ func (c *MessageRouterClient) PublishSessionCreated(
|
||||||
messageHash []byte,
|
messageHash []byte,
|
||||||
createdAt int64,
|
createdAt int64,
|
||||||
expiresAt int64,
|
expiresAt int64,
|
||||||
|
delegateUserShare *use_cases.DelegateUserShareInfo,
|
||||||
) error {
|
) error {
|
||||||
event := &router.SessionEvent{
|
event := &router.SessionEvent{
|
||||||
EventId: uuid.New().String(),
|
EventId: uuid.New().String(),
|
||||||
|
|
@ -134,5 +136,14 @@ func (c *MessageRouterClient) PublishSessionCreated(
|
||||||
ExpiresAt: expiresAt,
|
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)
|
return c.PublishSessionEvent(ctx, event)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,13 @@ type PartyComposition struct {
|
||||||
CustomFilters []output.PartySelectionFilter // Custom party selection filters
|
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
|
// CreateSessionInput contains input for creating a session
|
||||||
type CreateSessionInput struct {
|
type CreateSessionInput struct {
|
||||||
InitiatorID string
|
InitiatorID string
|
||||||
|
|
@ -43,9 +50,10 @@ type CreateSessionInput struct {
|
||||||
ThresholdN int
|
ThresholdN int
|
||||||
ThresholdT int
|
ThresholdT int
|
||||||
Participants []ParticipantInfo
|
Participants []ParticipantInfo
|
||||||
PartyComposition *PartyComposition // Optional: specify party composition by role
|
PartyComposition *PartyComposition // Optional: specify party composition by role
|
||||||
MessageHash []byte // For sign sessions
|
MessageHash []byte // For sign sessions
|
||||||
ExpiresIn time.Duration
|
ExpiresIn time.Duration
|
||||||
|
DelegateUserShare *DelegateUserShare // For sign sessions with delegate party
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParticipantInfo contains information about a participant
|
// ParticipantInfo contains information about a participant
|
||||||
|
|
@ -96,13 +104,16 @@ type PartyInfo struct {
|
||||||
|
|
||||||
// SessionStatusOutput contains session status information
|
// SessionStatusOutput contains session status information
|
||||||
type SessionStatusOutput struct {
|
type SessionStatusOutput struct {
|
||||||
SessionID uuid.UUID
|
SessionID uuid.UUID
|
||||||
Status string
|
Status string
|
||||||
ThresholdT int
|
SessionType string // "keygen" or "sign"
|
||||||
ThresholdN int
|
ThresholdT int
|
||||||
Participants []ParticipantStatus
|
ThresholdN int
|
||||||
PublicKey []byte // For completed keygen
|
Participants []ParticipantStatus
|
||||||
Signature []byte // For completed sign
|
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
|
// ParticipantStatus contains participant status information
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,13 @@ import (
|
||||||
"go.uber.org/zap"
|
"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
|
// MessageRouterClient interface for publishing session events
|
||||||
type MessageRouterClient interface {
|
type MessageRouterClient interface {
|
||||||
PublishSessionCreated(
|
PublishSessionCreated(
|
||||||
|
|
@ -28,6 +35,7 @@ type MessageRouterClient interface {
|
||||||
messageHash []byte,
|
messageHash []byte,
|
||||||
createdAt int64,
|
createdAt int64,
|
||||||
expiresAt int64,
|
expiresAt int64,
|
||||||
|
delegateUserShare *DelegateUserShareInfo,
|
||||||
) error
|
) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -286,6 +294,17 @@ func (uc *CreateSessionUseCase) Execute(
|
||||||
// Only publish if parties were selected from pool
|
// Only publish if parties were selected from pool
|
||||||
if len(session.GetPartyIDs()) > 0 && uc.messageRouterClient != nil {
|
if len(session.GetPartyIDs()) > 0 && uc.messageRouterClient != nil {
|
||||||
selectedParties := session.GetPartyIDs()
|
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(
|
err := uc.messageRouterClient.PublishSessionCreated(
|
||||||
ctx,
|
ctx,
|
||||||
session.ID.UUID(),
|
session.ID.UUID(),
|
||||||
|
|
@ -296,6 +315,7 @@ func (uc *CreateSessionUseCase) Execute(
|
||||||
session.MessageHash,
|
session.MessageHash,
|
||||||
session.CreatedAt.UnixMilli(),
|
session.CreatedAt.UnixMilli(),
|
||||||
session.ExpiresAt.UnixMilli(),
|
session.ExpiresAt.UnixMilli(),
|
||||||
|
delegateUserShare,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Log error but don't fail the operation
|
// Log error but don't fail the operation
|
||||||
|
|
|
||||||
|
|
@ -46,12 +46,17 @@ func (uc *GetSessionStatusUseCase) Execute(
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Build response
|
// 3. Build response
|
||||||
|
// has_delegate is only meaningful for keygen sessions
|
||||||
|
hasDelegate := session.DelegatePartyID != "" && string(session.SessionType) == "keygen"
|
||||||
return &input.SessionStatusOutput{
|
return &input.SessionStatusOutput{
|
||||||
SessionID: session.ID.UUID(),
|
SessionID: session.ID.UUID(),
|
||||||
Status: session.Status.String(),
|
Status: session.Status.String(),
|
||||||
ThresholdT: session.Threshold.T(),
|
SessionType: string(session.SessionType),
|
||||||
ThresholdN: session.Threshold.N(),
|
ThresholdT: session.Threshold.T(),
|
||||||
Participants: participants,
|
ThresholdN: session.Threshold.N(),
|
||||||
PublicKey: session.PublicKey,
|
Participants: participants,
|
||||||
|
PublicKey: session.PublicKey,
|
||||||
|
HasDelegate: hasDelegate,
|
||||||
|
DelegatePartyID: session.DelegatePartyID,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,15 +84,11 @@ func (s *SessionCoordinatorService) ShouldExpireSession(session *entities.MPCSes
|
||||||
}
|
}
|
||||||
|
|
||||||
// CalculateSessionTimeout calculates the timeout for a session type
|
// 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 {
|
func (s *SessionCoordinatorService) CalculateSessionTimeout(sessionType entities.SessionType) time.Duration {
|
||||||
switch sessionType {
|
// All session types have a 24-hour validity period
|
||||||
case entities.SessionTypeKeygen:
|
return 24 * time.Hour
|
||||||
return 10 * time.Minute
|
|
||||||
case entities.SessionTypeSign:
|
|
||||||
return 5 * time.Minute
|
|
||||||
default:
|
|
||||||
return 10 * time.Minute
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateMessageRouting validates if a message can be routed
|
// ValidateMessageRouting validates if a message can be routed
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue