rwadurian/backend/mpc-system/MPC-Distributed-Signature-S...

86 KiB
Raw Permalink Blame History

MPC分布式签名系统 - 完整技术规范

真正的去中心化MPC架构对等参与、零信任、Share物理隔离

目录

  1. 系统概述
  2. 核心架构
  3. 技术栈
  4. 领域模型设计
  5. 核心服务实现
  6. 数据库设计
  7. 客户端SDK
  8. API接口
  9. 部署方案
  10. 安全设计

1. 系统概述

1.1 核心理念

真正的分布式MPC签名

  • 私钥从未在任何地方完整存在
  • 所有参与方Party地位完全对等
  • 客户端和服务器都运行完整的tss-lib
  • Coordinator只负责协调,不参与计算
  • Share物理隔离存储,互不可见

1.2 业务场景

场景 阈值方案 参与方
账号注册 2-of-3 用户设备 + 服务器 + 恢复密钥
多人审核 3-of-5 5个审核员需3人同意
高安全审批 4-of-7 7个高管需4人同意
数据签名 2-of-3 应用服务器 + HSM + 备份

1.3 关键特性

  • 🔐 零信任架构:无需信任任何单一节点
  • 🚀 跨平台支持Android、iOS、PC、Server
  • 📱 硬件安全Android KeyStore、Secure Enclave、HSM
  • 高可用任意t个Party即可完成签名
  • 🔄 可恢复通过MPC协议安全恢复丢失的share
  • 🏗️ 微服务架构DDD + Hexagonal + 独立部署

2. 核心架构

2.1 整体架构图

┌─────────────────────────── MPC 参与方层(对等架构)───────────────────────────┐
│                                                                              │
│   ┌──────────────────┐      ┌──────────────────┐      ┌──────────────────┐ │
│   │   Party 1        │      │   Party 2        │      │   Party 3        │ │
│   │  (用户手机)      │      │  (服务器节点)    │      │  (恢复密钥)      │ │
│   │                  │      │                  │      │                  │ │
│   │ ┌──────────────┐ │      │ ┌──────────────┐ │      │ ┌──────────────┐ │ │
│   │ │  tss-lib     │ │      │ │  tss-lib     │ │      │ │  tss-lib     │ │ │
│   │ │  (Go Mobile) │ │      │ │  (Go Native) │ │      │ │  (Go Native) │ │ │
│   │ └──────┬───────┘ │      │ └──────┬───────┘ │      │ └──────┬───────┘ │ │
│   │        │         │      │        │         │      │        │         │ │
│   │ ┌──────▼───────┐ │      │ ┌──────▼───────┐ │      │ ┌──────▼───────┐ │ │
│   │ │ Share 1      │ │      │ │ Share 2      │ │      │ │ Share 3      │ │ │
│   │ │ (KeyStore)   │ │      │ │ (HSM/PG)     │ │      │ │ (Cold Store) │ │ │
│   │ └──────────────┘ │      │ └──────────────┘ │      │ └──────────────┘ │ │
│   └──────────┬───────┘      └──────────┬───────┘      └──────────┬───────┘ │
│              │                         │                         │         │
│              └─────────────────────────┼─────────────────────────┘         │
│                                        │                                   │
│                         P2P MPC 消息交换(端到端加密)                     │
│                                        │                                   │
└────────────────────────────────────────┼───────────────────────────────────┘
                                         │
                                         │
┌────────────────────────────────────────▼───────────────────────────────────┐
│                        协调服务层不参与MPC计算                          │
│                                                                             │
│  ┌──────────────────────┐              ┌──────────────────────┐           │
│  │ Session Coordinator  │              │   Message Router     │           │
│  │                      │              │                      │           │
│  │ • 创建MPC会话        │              │ • P2P消息中继        │           │
│  │ • 管理参与方列表     │◄────────────►│ • 消息持久化         │           │
│  │ • 会话状态追踪       │              │ • 离线消息缓存       │           │
│  │ • 超时控制           │              │ • 消息去重排序       │           │
│  │ • 参与方认证         │              │                      │           │
│  │                      │              │ ❌ 不解密MPC消息     │           │
│  │ ❌ 不存储Share       │              │ ❌ 不参与MPC计算     │           │
│  │ ❌ 不参与MPC计算     │              │                      │           │
│  └──────────────────────┘              └──────────────────────┘           │
│                                                                             │
└────────────────────────────────────────┬───────────────────────────────────┘
                                         │
                                         │
┌────────────────────────────────────────▼───────────────────────────────────┐
│                           业务服务层                                        │
│                                                                             │
│  ┌──────────────┐   ┌──────────────┐   ┌──────────────┐                  │
│  │   Account    │   │    Audit     │   │    Data      │                  │
│  │   Service    │   │   Service    │   │  Integrity   │                  │
│  │              │   │              │   │   Service    │                  │
│  │ • 用户管理   │   │ • 审核工作流 │   │ • 数据签名   │                  │
│  │ • 账号创建   │   │ • 多签管理   │   │ • 签名验证   │                  │
│  │ • 恢复流程   │   │ • 审批追踪   │   │ • 防篡改     │                  │
│  └──────────────┘   └──────────────┘   └──────────────┘                  │
│                                                                             │
└────────────────────────────────────────┬───────────────────────────────────┘
                                         │
┌────────────────────────────────────────▼───────────────────────────────────┐
│                           基础设施层                                        │
│                                                                             │
│  ┌────────────┐   ┌────────────┐   ┌────────────┐   ┌────────────┐       │
│  │ PostgreSQL │   │   Redis    │   │  RabbitMQ  │   │   Consul   │       │
│  │            │   │            │   │            │   │            │       │
│  │ • 会话状态 │   │ • 临时缓存 │   │ • 消息队列 │   │ • 服务发现 │       │
│  │ • 元数据   │   │ • 分布式锁 │   │ • 事件总线 │   │ • 配置中心 │       │
│  │ • 审计日志 │   │            │   │            │   │            │       │
│  └────────────┘   └────────────┘   └────────────┘   └────────────┘       │
└─────────────────────────────────────────────────────────────────────────────┘

2.2 MPC消息流2-of-3 账号创建)

时序图用户注册账号2-of-3 Keygen

┌─────────┐         ┌─────────┐         ┌─────────┐         ┌──────────┐
│ Android │         │ Server  │         │Recovery │         │Coordinator│
│ Party   │         │ Party   │         │ Party   │         │          │
└────┬────┘         └────┬────┘         └────┬────┘         └────┬─────┘
     │                   │                   │                   │
     │ 1. Request Create Account             │                   │
     ├──────────────────────────────────────────────────────────>│
     │                   │                   │                   │
     │ 2. Create Keygen Session (3 parties, t=2)                 │
     │<──────────────────────────────────────────────────────────┤
     │   SessionID: abc-123                  │                   │
     │   JoinTokens: {party1, party2, party3}│                   │
     │                   │                   │                   │
     │ 3. Join Session   │                   │                   │
     ├──────────────────────────────────────────────────────────>│
     │                   │                   │   ✓ Party1 Joined │
     │                   │ 4. Join Session   │                   │
     │                   ├──────────────────────────────────────>│
     │                   │                   │   ✓ Party2 Joined │
     │                   │                   │ 5. Join Session   │
     │                   │                   ├──────────────────>│
     │                   │                   │   ✓ Party3 Joined │
     │                   │                   │                   │
     │ 6. 所有Party就绪开始 TSS Keygen Protocol                 │
     │                   │                   │                   │
     │ Round 1: 生成随机commitment            │                   │
     ├──────────────────►│◄─────────────────►│                   │
     │       (通过Message Router中继消息)     │                   │
     │                   │                   │                   │
     │ Round 2: Decommitment & Secret Share  │                   │
     ├──────────────────►│◄─────────────────►│                   │
     │                   │                   │                   │
     │ Round 3: VSS Verification             │                   │
     ├──────────────────►│◄─────────────────►│                   │
     │                   │                   │                   │
     │ 7. Keygen完成各方获得自己的Share      │                   │
     │ ✓ Share1         │ ✓ Share2          │ ✓ Share3          │
     │ (存KeyStore)      │ (存HSM/DB)         │ (离线存储)         │
     │                   │                   │                   │
     │ 8. 上报完成状态,返回群公钥             │                   │
     ├──────────────────────────────────────────────────────────>│
     │                   │                   │                   │
     │ 9. PublicKey: 0x1a2b3c...             │                   │
     │<──────────────────────────────────────────────────────────┤
     │                   │                   │                   │
     
注意:
- Coordinator只负责会话管理不参与MPC计算
- 各Party直接通过Message Router交换加密消息
- 无任何节点知道完整私钥
- 各Party的Share完全物理隔离

2.3 架构设计原则

原则 说明 实现方式
对等参与 所有Party地位平等无主从关系 客户端和服务器都运行tss-lib
零信任 不信任任何单一节点 需要t个Party协同才能签名
物理隔离 Share分布在不同物理位置 Android KeyStore / HSM / Cold Storage
协调不计算 Coordinator只管理流程 Session Coordinator不参与MPC
端到端加密 MPC消息加密传输 Message Router不解密消息内容
可审计 所有操作可追溯 完整的审计日志

3. 技术栈

3.1 核心技术选型

组件 技术 版本 说明
MPC库 Binance tss-lib latest ECDSA阈值签名
后端语言 Go 1.21+ 高性能、并发友好
移动端 Go Mobile + Kotlin/Swift - 跨平台MPC实现
数据库 PostgreSQL 15+ 关系型存储
缓存 Redis 7+ 会话缓存、分布式锁
消息队列 RabbitMQ 3.12+ 异步消息、事件总线
服务发现 Consul 1.16+ 服务注册、健康检查
API协议 gRPC + REST - 高性能RPC
容器化 Docker + K8s - 微服务部署

3.2 安全组件

组件 用途 实现
Android KeyStore 移动端Share存储 硬件级加密
Secure Enclave iOS Share存储 硬件级加密
HSM 服务器Share存储 硬件安全模块
TLS 1.3 通信加密 强制启用
JWT 身份认证 Token-based
AES-256-GCM 数据加密 Share加密

4. 领域模型设计

4.1 Session Coordinator ServiceDDD+Hexagonal

session-coordinator/
├── domain/                          # 领域层(核心业务逻辑)
│   ├── entities/
│   │   ├── mpc_session.go          # MPC会话实体
│   │   ├── participant.go          # 参与方实体
│   │   └── session_message.go      # 会话消息实体
│   ├── value_objects/
│   │   ├── session_id.go
│   │   ├── party_id.go
│   │   ├── threshold.go
│   │   └── session_status.go
│   ├── aggregates/
│   │   └── session_aggregate.go    # 会话聚合根
│   ├── repositories/               # 仓储接口(端口)
│   │   ├── session_repository.go
│   │   └── message_repository.go
│   └── services/                   # 领域服务
│       ├── session_coordinator.go  # 会话协调器
│       └── message_router.go       # 消息路由器
│
├── application/                     # 应用层(用例编排)
│   ├── use_cases/
│   │   ├── create_session.go       # 创建会话用例
│   │   ├── join_session.go         # 加入会话用例
│   │   ├── get_session_status.go   # 查询会话状态
│   │   ├── route_message.go        # 路由消息用例
│   │   └── close_session.go        # 关闭会话用例
│   └── ports/                      # 端口定义
│       ├── input/
│       │   └── session_management_port.go
│       └── output/
│           ├── session_storage_port.go
│           ├── message_broker_port.go
│           └── notification_port.go
│
├── adapters/                        # 适配器层(技术实现)
│   ├── input/                      # 入站适配器
│   │   ├── grpc/
│   │   │   ├── session_grpc_handler.go
│   │   │   └── message_grpc_handler.go
│   │   └── http/
│   │       └── session_http_handler.go
│   └── output/                     # 出站适配器
│       ├── postgres/
│       │   ├── session_postgres_repo.go
│       │   └── message_postgres_repo.go
│       ├── redis/
│       │   └── session_cache_adapter.go
│       └── rabbitmq/
│           └── event_publisher_adapter.go
│
└── pkg/                            # 通用包
    ├── crypto/
    ├── errors/
    └── utils/

4.2 核心领域模型代码

// domain/entities/mpc_session.go
package entities

import (
    "time"
    "github.com/google/uuid"
)

// MPCSession 代表一个MPC会话
// Coordinator只管理会话元数据不参与MPC计算
type MPCSession struct {
    ID           uuid.UUID
    SessionType  SessionType     // keygen 或 sign
    ThresholdN   int             // 总参与方数
    ThresholdT   int             // 所需参与方数
    Participants []Participant
    Status       SessionStatus
    MessageHash  []byte          // Sign会话使用
    PublicKey    []byte          // Keygen完成后的群公钥
    CreatedBy    string
    CreatedAt    time.Time
    UpdatedAt    time.Time
    ExpiresAt    time.Time
    CompletedAt  *time.Time
}

type SessionType string

const (
    SessionTypeKeygen SessionType = "keygen"
    SessionTypeSign   SessionType = "sign"
)

type SessionStatus string

const (
    SessionCreated    SessionStatus = "created"
    SessionInProgress SessionStatus = "in_progress"
    SessionCompleted  SessionStatus = "completed"
    SessionFailed     SessionStatus = "failed"
    SessionExpired    SessionStatus = "expired"
)

// Participant 参与方
type Participant struct {
    PartyID     string
    PartyIndex  int
    Status      ParticipantStatus
    DeviceInfo  DeviceInfo
    PublicKey   []byte          // Party的身份公钥用于认证
    JoinedAt    time.Time
    CompletedAt *time.Time
}

type ParticipantStatus string

const (
    ParticipantInvited   ParticipantStatus = "invited"
    ParticipantJoined    ParticipantStatus = "joined"
    ParticipantReady     ParticipantStatus = "ready"
    ParticipantCompleted ParticipantStatus = "completed"
    ParticipantFailed    ParticipantStatus = "failed"
)

type DeviceInfo struct {
    DeviceType string  // android, ios, pc, server
    DeviceID   string
    Platform   string
    AppVersion string
}

// SessionMessage MPC消息加密Coordinator不解密
type SessionMessage struct {
    ID          uuid.UUID
    SessionID   uuid.UUID
    FromParty   string
    ToParties   []string      // nil表示广播
    RoundNumber int
    MessageType string
    Payload     []byte        // 加密的MPC协议消息
    CreatedAt   time.Time
    DeliveredAt *time.Time
}

// 业务方法
func (s *MPCSession) CanStart() bool {
    // 检查是否所有参与方都已加入
    joinedCount := 0
    for _, p := range s.Participants {
        if p.Status == ParticipantJoined || p.Status == ParticipantReady {
            joinedCount++
        }
    }
    return joinedCount == s.ThresholdN
}

func (s *MPCSession) AddParticipant(p Participant) error {
    if len(s.Participants) >= s.ThresholdN {
        return errors.New("session is full")
    }
    s.Participants = append(s.Participants, p)
    return nil
}

func (s *MPCSession) UpdateParticipantStatus(partyID string, status ParticipantStatus) error {
    for i, p := range s.Participants {
        if p.PartyID == partyID {
            s.Participants[i].Status = status
            if status == ParticipantCompleted {
                now := time.Now()
                s.Participants[i].CompletedAt = &now
            }
            return nil
        }
    }
    return errors.New("participant not found")
}

func (s *MPCSession) IsExpired() bool {
    return time.Now().After(s.ExpiresAt)
}

func (s *MPCSession) AllCompleted() bool {
    for _, p := range s.Participants {
        if p.Status != ParticipantCompleted {
            return false
        }
    }
    return true
}
// domain/repositories/session_repository.go
package repositories

import (
    "context"
    "github.com/google/uuid"
    "yourorg/mpc/domain/entities"
)

// SessionRepository 会话仓储接口(端口)
type SessionRepository interface {
    Save(ctx context.Context, session *entities.MPCSession) error
    FindByID(ctx context.Context, id uuid.UUID) (*entities.MPCSession, error)
    FindByStatus(ctx context.Context, status entities.SessionStatus) ([]*entities.MPCSession, error)
    Update(ctx context.Context, session *entities.MPCSession) error
    Delete(ctx context.Context, id uuid.UUID) error
}

// MessageRepository 消息仓储接口
type MessageRepository interface {
    SaveMessage(ctx context.Context, msg *entities.SessionMessage) error
    GetMessages(ctx context.Context, sessionID uuid.UUID, partyID string, afterTime time.Time) ([]*entities.SessionMessage, error)
    MarkDelivered(ctx context.Context, messageID uuid.UUID) error
}

4.3 应用层用例实现

// application/use_cases/create_session.go
package use_cases

import (
    "context"
    "time"
    "github.com/google/uuid"
    "yourorg/mpc/domain/entities"
    "yourorg/mpc/domain/repositories"
)

type CreateSessionInput struct {
    InitiatorID  string
    SessionType  string              // "keygen" or "sign"
    ThresholdN   int
    ThresholdT   int
    Participants []ParticipantInfo
    MessageHash  []byte              // Sign会话需要
    ExpiresIn    time.Duration
}

type ParticipantInfo struct {
    PartyID    string
    DeviceInfo entities.DeviceInfo
}

type CreateSessionOutput struct {
    SessionID  uuid.UUID
    JoinTokens map[string]string   // PartyID -> JoinToken
    ExpiresAt  time.Time
}

type CreateSessionUseCase struct {
    sessionRepo   repositories.SessionRepository
    tokenGen      TokenGenerator
    eventPublisher EventPublisher
}

func NewCreateSessionUseCase(
    sessionRepo repositories.SessionRepository,
    tokenGen TokenGenerator,
    eventPublisher EventPublisher,
) *CreateSessionUseCase {
    return &CreateSessionUseCase{
        sessionRepo:   sessionRepo,
        tokenGen:      tokenGen,
        eventPublisher: eventPublisher,
    }
}

func (uc *CreateSessionUseCase) Execute(
    ctx context.Context,
    input CreateSessionInput,
) (*CreateSessionOutput, error) {
    // 1. 验证输入
    if input.ThresholdT > input.ThresholdN {
        return nil, errors.New("threshold t cannot exceed n")
    }
    if len(input.Participants) != input.ThresholdN {
        return nil, errors.New("participant count must equal n")
    }
    
    // 2. 创建会话实体
    session := &entities.MPCSession{
        ID:          uuid.New(),
        SessionType: entities.SessionType(input.SessionType),
        ThresholdN:  input.ThresholdN,
        ThresholdT:  input.ThresholdT,
        Status:      entities.SessionCreated,
        MessageHash: input.MessageHash,
        CreatedBy:   input.InitiatorID,
        CreatedAt:   time.Now(),
        UpdatedAt:   time.Now(),
        ExpiresAt:   time.Now().Add(input.ExpiresIn),
    }
    
    // 3. 添加参与方并生成加入令牌
    tokens := make(map[string]string)
    for i, pInfo := range input.Participants {
        participant := entities.Participant{
            PartyID:    pInfo.PartyID,
            PartyIndex: i,
            Status:     entities.ParticipantInvited,
            DeviceInfo: pInfo.DeviceInfo,
            JoinedAt:   time.Now(),
        }
        
        if err := session.AddParticipant(participant); err != nil {
            return nil, err
        }
        
        // 生成安全的加入令牌JWT
        token, err := uc.tokenGen.Generate(session.ID, pInfo.PartyID, input.ExpiresIn)
        if err != nil {
            return nil, err
        }
        tokens[pInfo.PartyID] = token
    }
    
    // 4. 保存会话
    if err := uc.sessionRepo.Save(ctx, session); err != nil {
        return nil, err
    }
    
    // 5. 发布会话创建事件
    event := SessionCreatedEvent{
        SessionID:   session.ID,
        SessionType: string(session.SessionType),
        ThresholdN:  session.ThresholdN,
        ThresholdT:  session.ThresholdT,
        Participants: extractPartyIDs(input.Participants),
        CreatedAt:   session.CreatedAt,
    }
    if err := uc.eventPublisher.Publish(ctx, "mpc.session.created", event); err != nil {
        // Log error but don't fail the operation
        log.Error("failed to publish event", "error", err)
    }
    
    return &CreateSessionOutput{
        SessionID:  session.ID,
        JoinTokens: tokens,
        ExpiresAt:  session.ExpiresAt,
    }, nil
}
// application/use_cases/join_session.go
package use_cases

type JoinSessionInput struct {
    SessionID  uuid.UUID
    PartyID    string
    JoinToken  string
    DeviceInfo entities.DeviceInfo
}

type JoinSessionOutput struct {
    Success      bool
    SessionInfo  SessionInfo
    OtherParties []PartyInfo
}

type JoinSessionUseCase struct {
    sessionRepo repositories.SessionRepository
    tokenValidator TokenValidator
    eventPublisher EventPublisher
}

func (uc *JoinSessionUseCase) Execute(
    ctx context.Context,
    input JoinSessionInput,
) (*JoinSessionOutput, error) {
    // 1. 验证令牌
    claims, err := uc.tokenValidator.Validate(input.JoinToken)
    if err != nil {
        return nil, errors.New("invalid join token")
    }
    
    if claims.SessionID != input.SessionID || claims.PartyID != input.PartyID {
        return nil, errors.New("token mismatch")
    }
    
    // 2. 加载会话
    session, err := uc.sessionRepo.FindByID(ctx, input.SessionID)
    if err != nil {
        return nil, err
    }
    
    // 3. 检查会话状态
    if session.IsExpired() {
        return nil, errors.New("session expired")
    }
    
    if session.Status != entities.SessionCreated {
        return nil, errors.New("session already started or completed")
    }
    
    // 4. 更新参与方状态
    if err := session.UpdateParticipantStatus(input.PartyID, entities.ParticipantJoined); err != nil {
        return nil, err
    }
    
    // 5. 如果所有人都加入,开始会话
    if session.CanStart() {
        session.Status = entities.SessionInProgress
        session.UpdatedAt = time.Now()
    }
    
    // 6. 保存更新
    if err := uc.sessionRepo.Update(ctx, session); err != nil {
        return nil, err
    }
    
    // 7. 发布加入事件
    event := ParticipantJoinedEvent{
        SessionID: session.ID,
        PartyID:   input.PartyID,
        JoinedAt:  time.Now(),
    }
    uc.eventPublisher.Publish(ctx, "mpc.participant.joined", event)
    
    // 8. 构建返回信息
    return &JoinSessionOutput{
        Success: true,
        SessionInfo: SessionInfo{
            SessionID:   session.ID,
            SessionType: string(session.SessionType),
            ThresholdN:  session.ThresholdN,
            ThresholdT:  session.ThresholdT,
            MessageHash: session.MessageHash,
            Status:      string(session.Status),
        },
        OtherParties: buildPartyInfoList(session.Participants, input.PartyID),
    }, nil
}
// application/use_cases/route_message.go
package use_cases

type RouteMessageInput struct {
    SessionID   uuid.UUID
    FromParty   string
    ToParties   []string      // nil表示广播
    RoundNumber int
    MessageType string
    Payload     []byte        // 加密的MPC消息
}

type RouteMessageUseCase struct {
    sessionRepo repositories.SessionRepository
    messageRepo repositories.MessageRepository
    messageQueue MessageQueue
}

func (uc *RouteMessageUseCase) Execute(
    ctx context.Context,
    input RouteMessageInput,
) error {
    // 1. 验证会话存在
    session, err := uc.sessionRepo.FindByID(ctx, input.SessionID)
    if err != nil {
        return err
    }
    
    if session.Status != entities.SessionInProgress {
        return errors.New("session not in progress")
    }
    
    // 2. 验证发送方是参与方
    if !session.IsParticipant(input.FromParty) {
        return errors.New("sender is not a participant")
    }
    
    // 3. 创建消息实体
    msg := &entities.SessionMessage{
        ID:          uuid.New(),
        SessionID:   input.SessionID,
        FromParty:   input.FromParty,
        ToParties:   input.ToParties,
        RoundNumber: input.RoundNumber,
        MessageType: input.MessageType,
        Payload:     input.Payload,  // 不解密,直接转发
        CreatedAt:   time.Now(),
    }
    
    // 4. 持久化消息(用于离线场景)
    if err := uc.messageRepo.SaveMessage(ctx, msg); err != nil {
        return err
    }
    
    // 5. 通过消息队列路由到目标Party
    if input.ToParties == nil {
        // 广播到所有其他参与方
        for _, p := range session.Participants {
            if p.PartyID != input.FromParty {
                uc.messageQueue.Send(ctx, p.PartyID, msg)
            }
        }
    } else {
        // 单播到指定Party
        for _, toParty := range input.ToParties {
            uc.messageQueue.Send(ctx, toParty, msg)
        }
    }
    
    return nil
}

5. 核心服务实现

5.1 Server Party Service服务器作为MPC参与方

// server-party-service/domain/entities/party_key_share.go
package entities

type PartyKeyShare struct {
    ID         uuid.UUID
    PartyID    string
    PartyIndex int
    SessionID  uuid.UUID
    ThresholdN int
    ThresholdT int
    ShareData  []byte        // 加密的tss-lib LocalPartySaveData
    PublicKey  []byte        // 群公钥
    CreatedAt  time.Time
    LastUsedAt *time.Time
}
// server-party-service/application/use_cases/participate_in_keygen.go
package use_cases

import (
    "context"
    "math/big"
    "github.com/binance-chain/tss-lib/tss"
    "github.com/binance-chain/tss-lib/ecdsa/keygen"
)

type ParticipateInKeygenInput struct {
    SessionID  uuid.UUID
    PartyID    string
    JoinToken  string
}

type ParticipateInKeygenOutput struct {
    Success   bool
    KeyShare  *entities.PartyKeyShare
    PublicKey []byte
}

type ParticipateInKeygenUseCase struct {
    keyShareRepo  repositories.KeyShareRepository
    sessionClient SessionCoordinatorClient
    messageRouter MessageRouterClient
    crypto        CryptoService
}

func (uc *ParticipateInKeygenUseCase) Execute(
    ctx context.Context,
    input ParticipateInKeygenInput,
) (*ParticipateInKeygenOutput, error) {
    // 1. 加入会话通过Coordinator
    sessionInfo, err := uc.sessionClient.JoinSession(ctx, &JoinSessionRequest{
        SessionID: input.SessionID,
        PartyID:   input.PartyID,
        JoinToken: input.JoinToken,
    })
    if err != nil {
        return nil, err
    }
    
    // 2. 获取参与方列表构建TSS参数
    parties := make([]*tss.PartyID, len(sessionInfo.Participants))
    for i, p := range sessionInfo.Participants {
        parties[i] = tss.NewPartyID(
            p.PartyID,
            p.PartyID,
            big.NewInt(int64(p.PartyIndex)),
        )
    }
    
    // 3. 找到自己的Party
    var selfPartyID *tss.PartyID
    for _, p := range parties {
        if p.Id == input.PartyID {
            selfPartyID = p
            break
        }
    }
    
    // 4. 创建TSS参数
    tssCtx := tss.NewPeerContext(parties)
    params := tss.NewParameters(
        tss.S256(),
        tssCtx,
        selfPartyID,
        len(parties),
        sessionInfo.ThresholdT,
    )
    
    // 5. 创建通信通道
    outCh := make(chan tss.Message, len(parties)*10)
    endCh := make(chan keygen.LocalPartySaveData, 1)
    errCh := make(chan *tss.Error, 1)
    
    // 6. 创建TSS Keygen Party
    party := keygen.NewLocalParty(params, outCh, endCh).(*keygen.LocalParty)
    
    // 7. 启动消息路由goroutine
    go uc.routeOutgoingMessages(ctx, input.SessionID, input.PartyID, outCh)
    go uc.handleIncomingMessages(ctx, input.SessionID, input.PartyID, party)
    go uc.handleErrors(ctx, errCh)
    
    // 8. 启动Party
    go func() {
        if err := party.Start(); err != nil {
            errCh <- err
        }
    }()
    
    // 9. 等待Keygen完成
    select {
    case saveData := <-endCh:
        // 10. Keygen成功加密并保存Share
        encryptedShare, err := uc.crypto.EncryptShare(saveData, input.PartyID)
        if err != nil {
            return nil, err
        }
        
        keyShare := &entities.PartyKeyShare{
            ID:         uuid.New(),
            PartyID:    input.PartyID,
            PartyIndex: getPartyIndex(sessionInfo.Participants, input.PartyID),
            SessionID:  input.SessionID,
            ThresholdN: len(parties),
            ThresholdT: sessionInfo.ThresholdT,
            ShareData:  encryptedShare,
            PublicKey:  saveData.ECDSAPub.Bytes(),
            CreatedAt:  time.Now(),
        }
        
        if err := uc.keyShareRepo.Save(ctx, keyShare); err != nil {
            return nil, err
        }
        
        // 11. 通知Coordinator完成
        uc.sessionClient.ReportCompletion(ctx, &ReportCompletionRequest{
            SessionID: input.SessionID,
            PartyID:   input.PartyID,
            PublicKey: keyShare.PublicKey,
        })
        
        return &ParticipateInKeygenOutput{
            Success:   true,
            KeyShare:  keyShare,
            PublicKey: keyShare.PublicKey,
        }, nil
        
    case err := <-errCh:
        return nil, fmt.Errorf("keygen failed: %v", err)
        
    case <-time.After(10 * time.Minute):
        return nil, errors.New("keygen timeout")
    }
}

// routeOutgoingMessages 路由Party发出的消息
func (uc *ParticipateInKeygenUseCase) routeOutgoingMessages(
    ctx context.Context,
    sessionID uuid.UUID,
    partyID string,
    outCh <-chan tss.Message,
) {
    for {
        select {
        case msg := <-outCh:
            // 序列化TSS消息
            msgBytes, err := msg.WireBytes()
            if err != nil {
                log.Error("failed to serialize message", "error", err)
                continue
            }
            
            // 确定接收方
            var toParties []string
            if msg.IsBroadcast() {
                toParties = nil  // 广播
            } else {
                for _, to := range msg.GetTo() {
                    toParties = append(toParties, to.Id)
                }
            }
            
            // 通过Message Router发送
            _, err = uc.messageRouter.RouteMessage(ctx, &RouteMessageRequest{
                SessionID:   sessionID,
                FromParty:   partyID,
                ToParties:   toParties,
                RoundNumber: int(msg.GetRound()),
                MessageType: msg.Type(),
                Payload:     msgBytes,
            })
            if err != nil {
                log.Error("failed to route message", "error", err)
            }
            
        case <-ctx.Done():
            return
        }
    }
}

// handleIncomingMessages 处理收到的消息并传递给Party
func (uc *ParticipateInKeygenUseCase) handleIncomingMessages(
    ctx context.Context,
    sessionID uuid.UUID,
    partyID string,
    party tss.Party,
) {
    // 订阅自己的消息
    stream, err := uc.messageRouter.SubscribeMessages(ctx, &SubscribeMessagesRequest{
        SessionID: sessionID,
        PartyID:   partyID,
    })
    if err != nil {
        log.Error("failed to subscribe messages", "error", err)
        return
    }
    
    for {
        msg, err := stream.Recv()
        if err != nil {
            if err == io.EOF {
                return
            }
            log.Error("failed to receive message", "error", err)
            continue
        }
        
        // 反序列化并传递给Party
        // 注意tss-lib会自动处理消息的验证和状态更新
        if _, err := party.UpdateFromBytes(msg.Payload, msg.FromParty, msg.IsBroadcast); err != nil {
            log.Error("failed to update party from message", "error", err)
        }
    }
}
// server-party-service/application/use_cases/participate_in_signing.go
package use_cases

import (
    "github.com/binance-chain/tss-lib/ecdsa/signing"
)

type ParticipateInSigningInput struct {
    SessionID  uuid.UUID
    PartyID    string
    JoinToken  string
    MessageHash []byte
}

type ParticipateInSigningOutput struct {
    Success   bool
    Signature []byte
    R         *big.Int
    S         *big.Int
}

type ParticipateInSigningUseCase struct {
    keyShareRepo  repositories.KeyShareRepository
    sessionClient SessionCoordinatorClient
    messageRouter MessageRouterClient
    crypto        CryptoService
}

func (uc *ParticipateInSigningUseCase) Execute(
    ctx context.Context,
    input ParticipateInSigningInput,
) (*ParticipateInSigningOutput, error) {
    // 1. 加入签名会话
    sessionInfo, err := uc.sessionClient.JoinSession(ctx, &JoinSessionRequest{
        SessionID: input.SessionID,
        PartyID:   input.PartyID,
        JoinToken: input.JoinToken,
    })
    if err != nil {
        return nil, err
    }
    
    // 2. 加载自己的KeyShare
    keyShare, err := uc.keyShareRepo.FindBySessionAndParty(ctx, sessionInfo.KeygenSessionID, input.PartyID)
    if err != nil {
        return nil, errors.New("key share not found")
    }
    
    // 3. 解密Share
    saveData, err := uc.crypto.DecryptShare(keyShare.ShareData, input.PartyID)
    if err != nil {
        return nil, err
    }
    
    // 4. 构建TSS参数与Keygen类似
    parties := buildPartyList(sessionInfo.Participants)
    selfPartyID := findSelfParty(parties, input.PartyID)
    tssCtx := tss.NewPeerContext(parties)
    params := tss.NewParameters(
        tss.S256(),
        tssCtx,
        selfPartyID,
        len(parties),
        sessionInfo.ThresholdT,
    )
    
    // 5. 创建通信通道
    outCh := make(chan tss.Message, len(parties)*10)
    endCh := make(chan *common.SignatureData, 1)
    
    // 6. 创建TSS Signing Party
    msgHash := new(big.Int).SetBytes(input.MessageHash)
    party := signing.NewLocalParty(msgHash, params, saveData, outCh, endCh).(*signing.LocalParty)
    
    // 7. 启动消息路由
    go uc.routeSigningMessages(ctx, input.SessionID, input.PartyID, outCh)
    go uc.handleSigningMessages(ctx, input.SessionID, input.PartyID, party)
    
    // 8. 启动Party
    go func() {
        if err := party.Start(); err != nil {
            log.Error("signing party error", "error", err)
        }
    }()
    
    // 9. 等待签名完成
    select {
    case signData := <-endCh:
        // 签名成功
        signature := append(signData.R, signData.S...)
        
        // 更新KeyShare的最后使用时间
        now := time.Now()
        keyShare.LastUsedAt = &now
        uc.keyShareRepo.Update(ctx, keyShare)
        
        // 通知Coordinator完成
        uc.sessionClient.ReportCompletion(ctx, &ReportCompletionRequest{
            SessionID: input.SessionID,
            PartyID:   input.PartyID,
            Signature: signature,
        })
        
        return &ParticipateInSigningOutput{
            Success:   true,
            Signature: signature,
            R:         signData.R,
            S:         signData.S,
        }, nil
        
    case <-time.After(5 * time.Minute):
        return nil, errors.New("signing timeout")
    }
}

6. 数据库设计

6.1 Session Coordinator Schema

-- 会话表
CREATE TABLE mpc_sessions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    session_type VARCHAR(20) NOT NULL,      -- 'keygen' or 'sign'
    threshold_n INTEGER NOT NULL,
    threshold_t INTEGER NOT NULL,
    status VARCHAR(20) NOT NULL,
    message_hash BYTEA,                     -- Sign会话使用
    public_key BYTEA,                       -- Keygen完成后的群公钥
    created_by VARCHAR(255) NOT NULL,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
    expires_at TIMESTAMP NOT NULL,
    completed_at TIMESTAMP,
    CONSTRAINT chk_threshold CHECK (threshold_t <= threshold_n AND threshold_t > 0),
    CONSTRAINT chk_session_type CHECK (session_type IN ('keygen', 'sign')),
    CONSTRAINT chk_status CHECK (status IN ('created', 'in_progress', 'completed', 'failed', 'expired'))
);

CREATE INDEX idx_mpc_sessions_status ON mpc_sessions(status);
CREATE INDEX idx_mpc_sessions_created_at ON mpc_sessions(created_at);
CREATE INDEX idx_mpc_sessions_expires_at ON mpc_sessions(expires_at);

-- 参与方表
CREATE TABLE participants (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    session_id UUID NOT NULL REFERENCES mpc_sessions(id) ON DELETE CASCADE,
    party_id VARCHAR(255) NOT NULL,
    party_index INTEGER NOT NULL,
    status VARCHAR(20) NOT NULL,
    device_type VARCHAR(50),
    device_id VARCHAR(255),
    platform VARCHAR(50),
    app_version VARCHAR(50),
    public_key BYTEA,                       -- Party身份公钥用于认证
    joined_at TIMESTAMP NOT NULL DEFAULT NOW(),
    completed_at TIMESTAMP,
    CONSTRAINT chk_participant_status CHECK (status IN ('invited', 'joined', 'ready', 'completed', 'failed')),
    UNIQUE(session_id, party_id),
    UNIQUE(session_id, party_index)
);

CREATE INDEX idx_participants_session_id ON participants(session_id);
CREATE INDEX idx_participants_party_id ON participants(party_id);
CREATE INDEX idx_participants_status ON participants(status);

-- MPC消息表用于离线消息缓存
CREATE TABLE mpc_messages (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    session_id UUID NOT NULL REFERENCES mpc_sessions(id) ON DELETE CASCADE,
    from_party VARCHAR(255) NOT NULL,
    to_parties TEXT[],                      -- NULL表示广播
    round_number INTEGER NOT NULL,
    message_type VARCHAR(50) NOT NULL,
    payload BYTEA NOT NULL,                 -- 加密的MPC消息
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    delivered_at TIMESTAMP,
    CONSTRAINT chk_round_number CHECK (round_number >= 0)
);

CREATE INDEX idx_mpc_messages_session_id ON mpc_messages(session_id);
CREATE INDEX idx_mpc_messages_to_parties ON mpc_messages USING GIN(to_parties);
CREATE INDEX idx_mpc_messages_delivered_at ON mpc_messages(delivered_at) WHERE delivered_at IS NULL;
CREATE INDEX idx_mpc_messages_created_at ON mpc_messages(created_at);

6.2 Server Party Service Schema

-- Party密钥分片表Server Party自己的Share
CREATE TABLE party_key_shares (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    party_id VARCHAR(255) NOT NULL,
    party_index INTEGER NOT NULL,
    session_id UUID NOT NULL,               -- Keygen会话ID
    threshold_n INTEGER NOT NULL,
    threshold_t INTEGER NOT NULL,
    share_data BYTEA NOT NULL,              -- 加密的tss-lib LocalPartySaveData
    public_key BYTEA NOT NULL,              -- 群公钥
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    last_used_at TIMESTAMP,
    CONSTRAINT chk_threshold CHECK (threshold_t <= threshold_n)
);

CREATE INDEX idx_party_key_shares_party_id ON party_key_shares(party_id);
CREATE INDEX idx_party_key_shares_session_id ON party_key_shares(session_id);
CREATE INDEX idx_party_key_shares_public_key ON party_key_shares(public_key);
CREATE UNIQUE INDEX idx_party_key_shares_unique ON party_key_shares(party_id, session_id);

6.3 Account Service Schema

-- 账户表
CREATE TABLE accounts (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    username VARCHAR(255) UNIQUE NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    phone VARCHAR(50),
    public_key BYTEA NOT NULL,              -- MPC群公钥
    keygen_session_id UUID NOT NULL,        -- 关联的Keygen会话
    threshold_n INTEGER NOT NULL,
    threshold_t INTEGER NOT NULL,
    status VARCHAR(20) NOT NULL,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
    last_login_at TIMESTAMP,
    CONSTRAINT chk_status CHECK (status IN ('active', 'suspended', 'locked', 'recovering'))
);

CREATE INDEX idx_accounts_username ON accounts(username);
CREATE INDEX idx_accounts_email ON accounts(email);
CREATE INDEX idx_accounts_public_key ON accounts(public_key);
CREATE INDEX idx_accounts_status ON accounts(status);

-- 账户Share映射表记录各个Share的位置不存储Share内容
CREATE TABLE account_shares (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    account_id UUID NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
    share_type VARCHAR(20) NOT NULL,        -- 'user_device', 'server', 'recovery'
    party_id VARCHAR(255) NOT NULL,
    party_index INTEGER NOT NULL,
    device_type VARCHAR(50),
    device_id VARCHAR(255),
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    last_used_at TIMESTAMP,
    is_active BOOLEAN DEFAULT TRUE,
    CONSTRAINT chk_share_type CHECK (share_type IN ('user_device', 'server', 'recovery')),
    UNIQUE(account_id, share_type, is_active)
);

CREATE INDEX idx_account_shares_account_id ON account_shares(account_id);
CREATE INDEX idx_account_shares_party_id ON account_shares(party_id);

-- 账户恢复记录表
CREATE TABLE account_recovery_sessions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    account_id UUID NOT NULL REFERENCES accounts(id),
    recovery_type VARCHAR(20) NOT NULL,     -- 'device_lost', 'share_rotation'
    old_share_type VARCHAR(20),
    new_keygen_session_id UUID,
    status VARCHAR(20) NOT NULL,
    requested_at TIMESTAMP NOT NULL DEFAULT NOW(),
    completed_at TIMESTAMP,
    CONSTRAINT chk_recovery_status CHECK (status IN ('requested', 'in_progress', 'completed', 'failed'))
);

CREATE INDEX idx_account_recovery_account_id ON account_recovery_sessions(account_id);
CREATE INDEX idx_account_recovery_status ON account_recovery_sessions(status);

6.4 Audit Service Schema

-- 审核工作流表
CREATE TABLE audit_workflows (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    workflow_name VARCHAR(255) NOT NULL,
    workflow_type VARCHAR(50) NOT NULL,
    data_hash BYTEA NOT NULL,
    threshold_n INTEGER NOT NULL,
    threshold_t INTEGER NOT NULL,
    sign_session_id UUID,                   -- 关联的签名会话
    signature BYTEA,
    status VARCHAR(20) NOT NULL,
    created_by VARCHAR(255) NOT NULL,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
    expires_at TIMESTAMP,
    completed_at TIMESTAMP,
    metadata JSONB,
    CONSTRAINT chk_status CHECK (status IN ('pending', 'in_progress', 'approved', 'rejected', 'expired'))
);

CREATE INDEX idx_audit_workflows_status ON audit_workflows(status);
CREATE INDEX idx_audit_workflows_created_at ON audit_workflows(created_at);
CREATE INDEX idx_audit_workflows_workflow_type ON audit_workflows(workflow_type);

-- 审批人表
CREATE TABLE audit_approvers (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    workflow_id UUID NOT NULL REFERENCES audit_workflows(id) ON DELETE CASCADE,
    approver_id VARCHAR(255) NOT NULL,
    party_id VARCHAR(255) NOT NULL,
    party_index INTEGER NOT NULL,
    status VARCHAR(20) NOT NULL,
    approved_at TIMESTAMP,
    comments TEXT,
    CONSTRAINT chk_approver_status CHECK (status IN ('pending', 'approved', 'rejected')),
    UNIQUE(workflow_id, approver_id)
);

CREATE INDEX idx_audit_approvers_workflow_id ON audit_approvers(workflow_id);
CREATE INDEX idx_audit_approvers_approver_id ON audit_approvers(approver_id);
CREATE INDEX idx_audit_approvers_status ON audit_approvers(status);

6.5 审计日志表(所有服务共享)

-- 审计日志表
CREATE TABLE audit_logs (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    service_name VARCHAR(100) NOT NULL,
    action_type VARCHAR(100) NOT NULL,
    user_id VARCHAR(255),
    resource_type VARCHAR(100),
    resource_id VARCHAR(255),
    session_id UUID,
    ip_address INET,
    user_agent TEXT,
    request_data JSONB,
    response_data JSONB,
    status VARCHAR(20) NOT NULL,
    error_message TEXT,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    CONSTRAINT chk_audit_status CHECK (status IN ('success', 'failure', 'pending'))
);

CREATE INDEX idx_audit_logs_created_at ON audit_logs(created_at);
CREATE INDEX idx_audit_logs_user_id ON audit_logs(user_id);
CREATE INDEX idx_audit_logs_session_id ON audit_logs(session_id);
CREATE INDEX idx_audit_logs_action_type ON audit_logs(action_type);
CREATE INDEX idx_audit_logs_service_name ON audit_logs(service_name);

7. 客户端SDK

7.1 Go SDK核心实现

// sdk/mpc_client.go
package mpcsdk

import (
    "context"
    "crypto/ecdsa"
    "crypto/elliptic"
    "math/big"
    "github.com/binance-chain/tss-lib/tss"
    "github.com/binance-chain/tss-lib/ecdsa/keygen"
    "github.com/binance-chain/tss-lib/ecdsa/signing"
)

// MPCClient 是MPC客户端SDK
type MPCClient struct {
    config          *Config
    coordinatorClient CoordinatorClient
    messageRouter   MessageRouterClient
    localStorage    LocalStorage
    crypto          CryptoService
}

type Config struct {
    CoordinatorEndpoint string
    MessageRouterEndpoint string
    PartyID            string
    Timeout            time.Duration
}

func NewMPCClient(config *Config) *MPCClient {
    return &MPCClient{
        config:          config,
        coordinatorClient: NewCoordinatorClient(config.CoordinatorEndpoint),
        messageRouter:   NewMessageRouterClient(config.MessageRouterEndpoint),
        localStorage:    NewLocalStorage(),
        crypto:          NewCryptoService(),
    }
}

// CreateAccount 创建账号2-of-3 Keygen
func (c *MPCClient) CreateAccount(
    ctx context.Context,
    username string,
) (*Account, error) {
    // 1. 请求创建Keygen会话
    createResp, err := c.coordinatorClient.CreateSession(ctx, &CreateSessionRequest{
        SessionType: "keygen",
        ThresholdN:  3,
        ThresholdT:  2,
        Participants: []ParticipantInfo{
            {PartyID: username + "-device", DeviceInfo: getDeviceInfo()},
            {PartyID: username + "-server", DeviceInfo: DeviceInfo{DeviceType: "server"}},
            {PartyID: username + "-recovery", DeviceInfo: DeviceInfo{DeviceType: "recovery"}},
        },
        ExpiresIn: 10 * time.Minute,
    })
    if err != nil {
        return nil, err
    }
    
    // 2. 参与Keygen作为user-device party
    keyShare, publicKey, err := c.participateInKeygen(
        ctx,
        createResp.SessionID,
        username+"-device",
        createResp.JoinTokens[username+"-device"],
    )
    if err != nil {
        return nil, err
    }
    
    // 3. 保存KeyShare到本地安全存储
    if err := c.localStorage.SaveKeyShare(keyShare); err != nil {
        return nil, err
    }
    
    // 4. 返回账户信息
    return &Account{
        ID:          uuid.New().String(),
        Username:    username,
        PublicKey:   publicKey,
        KeyShareID:  keyShare.ID,
        ThresholdN:  3,
        ThresholdT:  2,
    }, nil
}

// participateInKeygen 参与Keygen协议
func (c *MPCClient) participateInKeygen(
    ctx context.Context,
    sessionID uuid.UUID,
    partyID string,
    joinToken string,
) (*KeyShare, []byte, error) {
    // 1. 加入会话
    joinResp, err := c.coordinatorClient.JoinSession(ctx, &JoinSessionRequest{
        SessionID:  sessionID,
        PartyID:    partyID,
        JoinToken:  joinToken,
        DeviceInfo: getDeviceInfo(),
    })
    if err != nil {
        return nil, nil, err
    }
    
    // 2. 构建TSS Party列表
    parties := make([]*tss.PartyID, len(joinResp.OtherParties)+1)
    for i, p := range joinResp.OtherParties {
        parties[i] = tss.NewPartyID(
            p.PartyID,
            p.PartyID,
            big.NewInt(int64(p.PartyIndex)),
        )
    }
    // 添加自己
    selfIndex := findSelfIndex(joinResp.SessionInfo, partyID)
    selfPartyID := tss.NewPartyID(partyID, partyID, big.NewInt(int64(selfIndex)))
    parties[selfIndex] = selfPartyID
    
    // 3. 创建TSS参数
    tssCtx := tss.NewPeerContext(parties)
    params := tss.NewParameters(
        tss.S256(),
        tssCtx,
        selfPartyID,
        joinResp.SessionInfo.ThresholdN,
        joinResp.SessionInfo.ThresholdT,
    )
    
    // 4. 创建通信通道
    outCh := make(chan tss.Message, len(parties)*10)
    endCh := make(chan keygen.LocalPartySaveData, 1)
    
    // 5. 创建TSS Keygen Party
    party := keygen.NewLocalParty(params, outCh, endCh).(*keygen.LocalParty)
    
    // 6. 启动消息处理
    ctx, cancel := context.WithTimeout(ctx, 10*time.Minute)
    defer cancel()
    
    errCh := make(chan error, 1)
    
    go c.handleOutgoingMessages(ctx, sessionID, partyID, outCh, errCh)
    go c.handleIncomingMessages(ctx, sessionID, partyID, party, errCh)
    
    // 7. 启动Party
    go func() {
        if err := party.Start(); err != nil {
            errCh <- err
        }
    }()
    
    // 8. 等待完成或超时
    select {
    case saveData := <-endCh:
        // Keygen成功
        encryptedShare, err := c.crypto.EncryptShare(saveData.Bytes(), partyID)
        if err != nil {
            return nil, nil, err
        }
        
        keyShare := &KeyShare{
            ID:         uuid.New(),
            PartyID:    partyID,
            SessionID:  sessionID,
            ShareData:  encryptedShare,
            PublicKey:  saveData.ECDSAPub.Bytes(),
            ThresholdN: joinResp.SessionInfo.ThresholdN,
            ThresholdT: joinResp.SessionInfo.ThresholdT,
            CreatedAt:  time.Now(),
        }
        
        // 通知Coordinator完成
        c.coordinatorClient.ReportCompletion(ctx, &ReportCompletionRequest{
            SessionID: sessionID,
            PartyID:   partyID,
            PublicKey: keyShare.PublicKey,
        })
        
        return keyShare, keyShare.PublicKey, nil
        
    case err := <-errCh:
        return nil, nil, fmt.Errorf("keygen failed: %v", err)
        
    case <-ctx.Done():
        return nil, nil, errors.New("keygen timeout")
    }
}

// SignMessage 使用MPC签名消息
func (c *MPCClient) SignMessage(
    ctx context.Context,
    account *Account,
    messageHash []byte,
) ([]byte, error) {
    // 1. 加载本地KeyShare
    keyShare, err := c.localStorage.LoadKeyShare(account.KeyShareID)
    if err != nil {
        return nil, err
    }
    
    // 2. 请求创建Sign会话2-of-3使用device+server
    createResp, err := c.coordinatorClient.CreateSession(ctx, &CreateSessionRequest{
        SessionType: "sign",
        ThresholdN:  2,
        ThresholdT:  2,
        Participants: []ParticipantInfo{
            {PartyID: account.Username + "-device", DeviceInfo: getDeviceInfo()},
            {PartyID: account.Username + "-server", DeviceInfo: DeviceInfo{DeviceType: "server"}},
        },
        MessageHash: messageHash,
        ExpiresIn:   5 * time.Minute,
    })
    if err != nil {
        return nil, err
    }
    
    // 3. 参与Signing
    signature, err := c.participateInSigning(
        ctx,
        createResp.SessionID,
        account.Username+"-device",
        keyShare,
        messageHash,
        createResp.JoinTokens[account.Username+"-device"],
    )
    if err != nil {
        return nil, err
    }
    
    return signature, nil
}

// participateInSigning 参与Signing协议
func (c *MPCClient) participateInSigning(
    ctx context.Context,
    sessionID uuid.UUID,
    partyID string,
    keyShare *KeyShare,
    messageHash []byte,
    joinToken string,
) ([]byte, error) {
    // 1. 加入会话
    joinResp, err := c.coordinatorClient.JoinSession(ctx, &JoinSessionRequest{
        SessionID: sessionID,
        PartyID:   partyID,
        JoinToken: joinToken,
    })
    if err != nil {
        return nil, err
    }
    
    // 2. 解密KeyShare
    saveDataBytes, err := c.crypto.DecryptShare(keyShare.ShareData, partyID)
    if err != nil {
        return nil, err
    }
    
    var saveData keygen.LocalPartySaveData
    if err := saveData.UnmarshalBinary(saveDataBytes); err != nil {
        return nil, err
    }
    
    // 3. 构建TSS参数
    parties := buildPartyList(joinResp.OtherParties, partyID, findSelfIndex(joinResp.SessionInfo, partyID))
    selfPartyID := parties[findSelfIndex(joinResp.SessionInfo, partyID)]
    tssCtx := tss.NewPeerContext(parties)
    params := tss.NewParameters(
        tss.S256(),
        tssCtx,
        selfPartyID,
        len(parties),
        joinResp.SessionInfo.ThresholdT,
    )
    
    // 4. 创建通信通道
    outCh := make(chan tss.Message, len(parties)*10)
    endCh := make(chan *common.SignatureData, 1)
    
    // 5. 创建TSS Signing Party
    msgHash := new(big.Int).SetBytes(messageHash)
    party := signing.NewLocalParty(msgHash, params, saveData, outCh, endCh).(*signing.LocalParty)
    
    // 6. 启动消息处理
    ctx, cancel := context.WithTimeout(ctx, 5*time.Minute)
    defer cancel()
    
    errCh := make(chan error, 1)
    
    go c.handleOutgoingMessages(ctx, sessionID, partyID, outCh, errCh)
    go c.handleIncomingMessages(ctx, sessionID, partyID, party, errCh)
    
    // 7. 启动Party
    go func() {
        if err := party.Start(); err != nil {
            errCh <- err
        }
    }()
    
    // 8. 等待完成
    select {
    case signData := <-endCh:
        // 签名成功
        signature := append(signData.R, signData.S...)
        
        // 通知Coordinator完成
        c.coordinatorClient.ReportCompletion(ctx, &ReportCompletionRequest{
            SessionID: sessionID,
            PartyID:   partyID,
            Signature: signature,
        })
        
        return signature, nil
        
    case err := <-errCh:
        return nil, fmt.Errorf("signing failed: %v", err)
        
    case <-ctx.Done():
        return nil, errors.New("signing timeout")
    }
}

// handleOutgoingMessages 处理Party发出的消息
func (c *MPCClient) handleOutgoingMessages(
    ctx context.Context,
    sessionID uuid.UUID,
    partyID string,
    outCh <-chan tss.Message,
    errCh chan<- error,
) {
    for {
        select {
        case msg := <-outCh:
            msgBytes, err := msg.WireBytes()
            if err != nil {
                errCh <- err
                return
            }
            
            var toParties []string
            if !msg.IsBroadcast() {
                for _, to := range msg.GetTo() {
                    toParties = append(toParties, to.Id)
                }
            }
            
            _, err = c.messageRouter.RouteMessage(ctx, &RouteMessageRequest{
                SessionID:   sessionID,
                FromParty:   partyID,
                ToParties:   toParties,
                RoundNumber: int(msg.GetRound()),
                Payload:     msgBytes,
            })
            if err != nil {
                errCh <- err
                return
            }
            
        case <-ctx.Done():
            return
        }
    }
}

// handleIncomingMessages 处理接收到的消息
func (c *MPCClient) handleIncomingMessages(
    ctx context.Context,
    sessionID uuid.UUID,
    partyID string,
    party tss.Party,
    errCh chan<- error,
) {
    stream, err := c.messageRouter.SubscribeMessages(ctx, &SubscribeMessagesRequest{
        SessionID: sessionID,
        PartyID:   partyID,
    })
    if err != nil {
        errCh <- err
        return
    }
    
    for {
        msg, err := stream.Recv()
        if err != nil {
            if err == io.EOF {
                return
            }
            errCh <- err
            return
        }
        
        if _, err := party.UpdateFromBytes(msg.Payload, msg.FromParty, msg.IsBroadcast); err != nil {
            log.Error("failed to update party", "error", err)
        }
    }
}

// VerifySignature 验证ECDSA签名
func (c *MPCClient) VerifySignature(
    messageHash []byte,
    signature []byte,
    publicKey []byte,
) (bool, error) {
    // 解析公钥
    x, y := elliptic.Unmarshal(elliptic.P256(), publicKey)
    if x == nil {
        return false, errors.New("invalid public key")
    }
    
    pubKey := &ecdsa.PublicKey{
        Curve: elliptic.P256(),
        X:     x,
        Y:     y,
    }
    
    // 解析签名 (r, s)
    if len(signature) != 64 {
        return false, errors.New("invalid signature length")
    }
    
    r := new(big.Int).SetBytes(signature[:32])
    s := new(big.Int).SetBytes(signature[32:])
    
    // 验证
    msgHashInt := new(big.Int).SetBytes(messageHash)
    valid := ecdsa.Verify(pubKey, msgHashInt.Bytes(), r, s)
    
    return valid, nil
}

7.2 Android SDKKotlin + Go Mobile

// android-sdk/src/main/java/com/yourorg/mpcsdk/MPCAndroidClient.kt
package com.yourorg.mpcsdk

import android.content.Context
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import androidx.biometric.BiometricPrompt
import androidx.fragment.app.FragmentActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import mpcsdk.Mpcsdk  // Generated by gomobile bind
import java.security.KeyStore
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.spec.GCMParameterSpec
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

/**
 * MPC Android客户端SDK
 * 在Android设备上运行完整的tss-lib
 */
class MPCAndroidClient(
    private val context: Context,
    private val config: MPCConfig
) {
    private val goMPCClient: Mpcsdk.MPCClient
    private val keyStore: KeyStore
    private val secureStorage: SecureStorage
    
    init {
        // 初始化Go MPC客户端
        val goConfig = Mpcsdk.NewConfig()
        goConfig.coordinatorEndpoint = config.coordinatorEndpoint
        goConfig.messageRouterEndpoint = config.messageRouterEndpoint
        goConfig.timeout = config.timeout
        
        goMPCClient = Mpcsdk.NewMPCClient(goConfig)
        
        // 初始化Android KeyStore
        keyStore = KeyStore.getInstance("AndroidKeyStore")
        keyStore.load(null)
        
        // 初始化安全存储
        secureStorage = SecureStorage(context, keyStore)
    }
    
    /**
     * 创建账号2-of-3 Keygen
     */
    suspend fun createAccount(username: String): Account = withContext(Dispatchers.IO) {
        try {
            // 调用Go SDK执行Keygen
            val goAccount = goMPCClient.createAccount(username)
            
            // 安全存储KeyShare到Android KeyStore
            secureStorage.saveKeyShare(
                keyShareID = goAccount.keyShareID,
                shareData = goAccount.shareData
            )
            
            // 返回账户信息
            Account(
                id = goAccount.id,
                username = goAccount.username,
                publicKey = goAccount.publicKey,
                keyShareID = goAccount.keyShareID,
                thresholdN = goAccount.thresholdN.toInt(),
                thresholdT = goAccount.thresholdT.toInt()
            )
        } catch (e: Exception) {
            throw MPCException("Failed to create account: ${e.message}", e)
        }
    }
    
    /**
     * 签名消息(需要生物识别认证)
     */
    suspend fun signMessage(
        activity: FragmentActivity,
        account: Account,
        messageHash: ByteArray
    ): ByteArray = withContext(Dispatchers.IO) {
        // 1. 生物识别认证
        authenticateUser(activity)
        
        // 2. 从安全存储加载KeyShare
        val shareData = secureStorage.loadKeyShare(account.keyShareID)
        
        // 3. 调用Go SDK执行Signing
        try {
            goMPCClient.signMessage(
                account.toGoAccount(shareData),
                messageHash
            )
        } catch (e: Exception) {
            throw MPCException("Failed to sign message: ${e.message}", e)
        }
    }
    
    /**
     * 验证签名
     */
    fun verifySignature(
        messageHash: ByteArray,
        signature: ByteArray,
        publicKey: ByteArray
    ): Boolean {
        return goMPCClient.verifySignature(messageHash, signature, publicKey)
    }
    
    /**
     * 生物识别认证
     */
    private suspend fun authenticateUser(activity: FragmentActivity) {
        return suspendCancellableCoroutine { continuation ->
            val biometricPrompt = BiometricPrompt(
                activity,
                ContextCompat.getMainExecutor(context),
                object : BiometricPrompt.AuthenticationCallback() {
                    override fun onAuthenticationSucceeded(
                        result: BiometricPrompt.AuthenticationResult
                    ) {
                        continuation.resume(Unit)
                    }
                    
                    override fun onAuthenticationFailed() {
                        continuation.resumeWithException(
                            MPCException("Biometric authentication failed")
                        )
                    }
                    
                    override fun onAuthenticationError(
                        errorCode: Int,
                        errString: CharSequence
                    ) {
                        continuation.resumeWithException(
                            MPCException("Authentication error: $errString")
                        )
                    }
                }
            )
            
            val promptInfo = BiometricPrompt.PromptInfo.Builder()
                .setTitle("MPC Signature Required")
                .setSubtitle("Authenticate to sign with your key share")
                .setNegativeButtonText("Cancel")
                .build()
            
            biometricPrompt.authenticate(promptInfo)
        }
    }
}

/**
 * 安全存储使用Android KeyStore
 */
class SecureStorage(
    private val context: Context,
    private val keyStore: KeyStore
) {
    private val prefs = context.getSharedPreferences("mpc_shares", Context.MODE_PRIVATE)
    
    fun saveKeyShare(keyShareID: String, shareData: ByteArray) {
        // 1. 获取或创建AES密钥
        val secretKey = getOrCreateSecretKey()
        
        // 2. 加密Share数据
        val cipher = Cipher.getInstance(TRANSFORMATION)
        cipher.init(Cipher.ENCRYPT_MODE, secretKey)
        
        val encryptedData = cipher.doFinal(shareData)
        val iv = cipher.iv
        
        // 3. 存储到SharedPreferences
        prefs.edit()
            .putString("share_$keyShareID", Base64.encodeToString(encryptedData, Base64.DEFAULT))
            .putString("iv_$keyShareID", Base64.encodeToString(iv, Base64.DEFAULT))
            .apply()
    }
    
    fun loadKeyShare(keyShareID: String): ByteArray {
        // 1. 从SharedPreferences加载
        val encryptedDataStr = prefs.getString("share_$keyShareID", null)
            ?: throw MPCException("Key share not found")
        val ivStr = prefs.getString("iv_$keyShareID", null)
            ?: throw MPCException("IV not found")
        
        val encryptedData = Base64.decode(encryptedDataStr, Base64.DEFAULT)
        val iv = Base64.decode(ivStr, Base64.DEFAULT)
        
        // 2. 解密
        val secretKey = getOrCreateSecretKey()
        val cipher = Cipher.getInstance(TRANSFORMATION)
        val spec = GCMParameterSpec(128, iv)
        cipher.init(Cipher.DECRYPT_MODE, secretKey, spec)
        
        return cipher.doFinal(encryptedData)
    }
    
    private fun getOrCreateSecretKey(): SecretKey {
        val keyAlias = "mpc_share_key"
        
        return if (keyStore.containsAlias(keyAlias)) {
            (keyStore.getEntry(keyAlias, null) as KeyStore.SecretKeyEntry).secretKey
        } else {
            val keyGenerator = KeyGenerator.getInstance(
                KeyProperties.KEY_ALGORITHM_AES,
                "AndroidKeyStore"
            )
            
            val spec = KeyGenParameterSpec.Builder(
                keyAlias,
                KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
            )
                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                .setUserAuthenticationRequired(true)
                .setUserAuthenticationValidityDurationSeconds(30)
                .build()
            
            keyGenerator.init(spec)
            keyGenerator.generateKey()
        }
    }
    
    companion object {
        private const val TRANSFORMATION = "AES/GCM/NoPadding"
    }
}

// 数据类
data class MPCConfig(
    val coordinatorEndpoint: String,
    val messageRouterEndpoint: String,
    val timeout: Long = 60000
)

data class Account(
    val id: String,
    val username: String,
    val publicKey: ByteArray,
    val keyShareID: String,
    val thresholdN: Int,
    val thresholdT: Int
)

class MPCException(message: String, cause: Throwable? = null) : Exception(message, cause)

7.3 编译移动SDK

#!/bin/bash
# build-mobile-sdk.sh

# 1. 安装gomobile
go install golang.org/x/mobile/cmd/gomobile@latest
gomobile init

# 2. 编译Android SDK
echo "Building Android SDK..."
cd sdk/go
gomobile bind -target=android -o ../android/libs/mpcsdk.aar .

# 3. 编译iOS SDK
echo "Building iOS SDK..."
gomobile bind -target=ios -o ../ios/Mpcsdk.xcframework .

echo "Mobile SDKs built successfully!"

8. API接口

8.1 gRPC API定义

// api/proto/session_coordinator.proto
syntax = "proto3";

package mpc.coordinator.v1;

option go_package = "github.com/yourorg/mpc-system/api/grpc/coordinator/v1;coordinator";

service SessionCoordinator {
  // 会话管理
  rpc CreateSession(CreateSessionRequest) returns (CreateSessionResponse);
  rpc JoinSession(JoinSessionRequest) returns (JoinSessionResponse);
  rpc GetSessionStatus(GetSessionStatusRequest) returns (GetSessionStatusResponse);
  rpc ReportCompletion(ReportCompletionRequest) returns (ReportCompletionResponse);
  rpc CloseSession(CloseSessionRequest) returns (CloseSessionResponse);
}

message CreateSessionRequest {
  string session_type = 1;               // "keygen" or "sign"
  int32 threshold_n = 2;
  int32 threshold_t = 3;
  repeated ParticipantInfo participants = 4;
  bytes message_hash = 5;                // For sign sessions
  int64 expires_in_seconds = 6;
}

message ParticipantInfo {
  string party_id = 1;
  DeviceInfo device_info = 2;
}

message DeviceInfo {
  string device_type = 1;                // android, ios, pc, server
  string device_id = 2;
  string platform = 3;
  string app_version = 4;
}

message CreateSessionResponse {
  string session_id = 1;
  map<string, string> join_tokens = 2;   // party_id -> join_token
  int64 expires_at = 3;
}

message JoinSessionRequest {
  string session_id = 1;
  string party_id = 2;
  string join_token = 3;
  DeviceInfo device_info = 4;
}

message JoinSessionResponse {
  bool success = 1;
  SessionInfo session_info = 2;
  repeated PartyInfo other_parties = 3;
}

message SessionInfo {
  string session_id = 1;
  string session_type = 2;
  int32 threshold_n = 3;
  int32 threshold_t = 4;
  bytes message_hash = 5;
  string status = 6;
}

message PartyInfo {
  string party_id = 1;
  int32 party_index = 2;
  DeviceInfo device_info = 3;
}

message GetSessionStatusRequest {
  string session_id = 1;
}

message GetSessionStatusResponse {
  string status = 1;
  int32 completed_parties = 2;
  int32 total_parties = 3;
  bytes public_key = 4;                  // For completed keygen
  bytes signature = 5;                   // For completed sign
}

message ReportCompletionRequest {
  string session_id = 1;
  string party_id = 2;
  bytes public_key = 3;                  // For keygen
  bytes signature = 4;                   // For sign
}

message ReportCompletionResponse {
  bool success = 1;
  bool all_completed = 2;
}

message CloseSessionRequest {
  string session_id = 1;
}

message CloseSessionResponse {
  bool success = 1;
}
// api/proto/message_router.proto
syntax = "proto3";

package mpc.router.v1;

option go_package = "github.com/yourorg/mpc-system/api/grpc/router/v1;router";

service MessageRouter {
  // 消息路由
  rpc RouteMessage(RouteMessageRequest) returns (RouteMessageResponse);
  rpc SubscribeMessages(SubscribeMessagesRequest) returns (stream MPCMessage);
}

message RouteMessageRequest {
  string session_id = 1;
  string from_party = 2;
  repeated string to_parties = 3;        // empty for broadcast
  int32 round_number = 4;
  string message_type = 5;
  bytes payload = 6;                     // Encrypted MPC message
}

message RouteMessageResponse {
  bool success = 1;
}

message SubscribeMessagesRequest {
  string session_id = 1;
  string party_id = 2;
}

message MPCMessage {
  string message_id = 1;
  string from_party = 2;
  bool is_broadcast = 3;
  int32 round_number = 4;
  bytes payload = 5;
  int64 created_at = 6;
}

9. 部署方案

9.1 Docker Compose开发环境

version: '3.8'

services:
  # PostgreSQL
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: mpc_system
      POSTGRES_USER: mpc_user
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    ports:
      - "5432:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data
      - ./migrations:/docker-entrypoint-initdb.d
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U mpc_user"]
      interval: 10s
      timeout: 5s
      retries: 5
  
  # Redis
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    command: redis-server --appendonly yes
  
  # RabbitMQ
  rabbitmq:
    image: rabbitmq:3-management-alpine
    ports:
      - "5672:5672"
      - "15672:15672"
    environment:
      RABBITMQ_DEFAULT_USER: mpc_user
      RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD}
    volumes:
      - rabbitmq-data:/var/lib/rabbitmq
  
  # Consul
  consul:
    image: consul:latest
    ports:
      - "8500:8500"
    command: agent -server -ui -bootstrap-expect=1 -client=0.0.0.0
    volumes:
      - consul-data:/consul/data
  
  # Session Coordinator Service
  session-coordinator:
    build:
      context: ./services/session-coordinator
    ports:
      - "50051:50051"  # gRPC
      - "8080:8080"    # HTTP
    environment:
      DATABASE_URL: postgres://mpc_user:${POSTGRES_PASSWORD}@postgres:5432/mpc_system
      REDIS_URL: redis://redis:6379/0
      RABBITMQ_URL: amqp://mpc_user:${RABBITMQ_PASSWORD}@rabbitmq:5672/
      CONSUL_URL: consul:8500
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
      rabbitmq:
        condition: service_started
  
  # Message Router Service
  message-router:
    build:
      context: ./services/message-router
    ports:
      - "50052:50051"
      - "8081:8080"
    environment:
      DATABASE_URL: postgres://mpc_user:${POSTGRES_PASSWORD}@postgres:5432/mpc_system
      RABBITMQ_URL: amqp://mpc_user:${RABBITMQ_PASSWORD}@rabbitmq:5672/
      REDIS_URL: redis://redis:6379/1
    depends_on:
      - postgres
      - rabbitmq
      - redis
  
  # Server Party Service
  server-party:
    build:
      context: ./services/server-party
    ports:
      - "50053:50051"
      - "8082:8080"
    environment:
      DATABASE_URL: postgres://mpc_user:${POSTGRES_PASSWORD}@postgres:5432/mpc_system
      COORDINATOR_URL: session-coordinator:50051
      ROUTER_URL: message-router:50051
      HSM_CONFIG: ${HSM_CONFIG}
    depends_on:
      - postgres
      - session-coordinator
      - message-router
  
  # Account Service
  account-service:
    build:
      context: ./services/account
    ports:
      - "50054:50051"
      - "8083:8080"
    environment:
      DATABASE_URL: postgres://mpc_user:${POSTGRES_PASSWORD}@postgres:5432/mpc_system
      COORDINATOR_URL: session-coordinator:50051
    depends_on:
      - postgres
      - session-coordinator

volumes:
  postgres-data:
  redis-data:
  rabbitmq-data:
  consul-data:

9.2 Kubernetes部署生产环境

# k8s/session-coordinator-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: session-coordinator
  namespace: mpc-system
spec:
  replicas: 3
  selector:
    matchLabels:
      app: session-coordinator
  template:
    metadata:
      labels:
        app: session-coordinator
    spec:
      containers:
      - name: session-coordinator
        image: yourorg/session-coordinator:latest
        ports:
        - containerPort: 50051
          name: grpc
        - containerPort: 8080
          name: http
        env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: database-credentials
              key: url
        - name: REDIS_URL
          value: redis://redis:6379/0
        - name: RABBITMQ_URL
          valueFrom:
            secretKeyRef:
              name: rabbitmq-credentials
              key: url
        resources:
          requests:
            memory: "512Mi"
            cpu: "500m"
          limits:
            memory: "1Gi"
            cpu: "1000m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: session-coordinator
  namespace: mpc-system
spec:
  selector:
    app: session-coordinator
  ports:
  - name: grpc
    port: 50051
    targetPort: 50051
  - name: http
    port: 8080
    targetPort: 8080
  type: ClusterIP
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: session-coordinator-hpa
  namespace: mpc-system
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: session-coordinator
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

9.3 Makefile

.PHONY: help proto build test docker-build docker-up deploy-k8s

help: ## Show this help
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'

proto: ## Generate protobuf code
	@echo "Generating protobuf..."
	protoc --go_out=. --go-grpc_out=. api/proto/*.proto

build: ## Build all services
	@echo "Building services..."
	cd services/session-coordinator && go build -o ../../bin/session-coordinator cmd/server/main.go
	cd services/message-router && go build -o ../../bin/message-router cmd/server/main.go
	cd services/server-party && go build -o ../../bin/server-party cmd/server/main.go
	cd services/account && go build -o ../../bin/account cmd/server/main.go

test: ## Run tests
	go test -v ./...

docker-build: ## Build Docker images
	docker-compose build

docker-up: ## Start all services
	docker-compose up -d

docker-down: ## Stop all services
	docker-compose down

build-android-sdk: ## Build Android SDK
	@echo "Building Android SDK..."
	cd sdk/go && gomobile bind -target=android -o ../android/libs/mpcsdk.aar .

build-ios-sdk: ## Build iOS SDK
	@echo "Building iOS SDK..."
	cd sdk/go && gomobile bind -target=ios -o ../ios/Mpcsdk.xcframework .

deploy-k8s: ## Deploy to Kubernetes
	kubectl apply -f k8s/

10. 安全设计

10.1 Share存储安全

Party类型 存储位置 加密方式 访问控制
Android客户端 Android KeyStore AES-256-GCM硬件支持 生物识别/PIN
iOS客户端 Secure Enclave 硬件加密 Face ID/Touch ID
PC客户端 OS Keychain 系统级加密 用户密码
服务器 HSM或PostgreSQL AES-256-GCM IAM + 审计
恢复密钥 冷存储 离线加密 物理隔离

10.2 通信安全

// TLS 1.3配置
func setupTLS() (*tls.Config, error) {
    cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
    if err != nil {
        return nil, err
    }
    
    return &tls.Config{
        Certificates: []tls.Certificate{cert},
        MinVersion:   tls.VersionTLS13,
        CipherSuites: []uint16{
            tls.TLS_AES_256_GCM_SHA384,
            tls.TLS_CHACHA20_POLY1305_SHA256,
        },
    }, nil
}

10.3 认证与授权

// JWT认证
type JWTAuth struct {
    secretKey []byte
    issuer    string
}

func (a *JWTAuth) GenerateToken(partyID string, sessionID uuid.UUID, expiresIn time.Duration) (string, error) {
    claims := jwt.MapClaims{
        "party_id":   partyID,
        "session_id": sessionID.String(),
        "iss":        a.issuer,
        "iat":        time.Now().Unix(),
        "exp":        time.Now().Add(expiresIn).Unix(),
    }
    
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(a.secretKey)
}

快速开始

# 1. Clone项目
git clone https://github.com/yourorg/mpc-distributed-signature-system.git
cd mpc-distributed-signature-system

# 2. 配置环境变量
cp .env.example .env
# 编辑 .env 文件

# 3. 生成protobuf代码
make proto

# 4. 启动所有服务
make docker-up

# 5. 编译Android SDK
make build-android-sdk

# 6. 运行测试
make test

总结

这是一份真正的去中心化MPC分布式签名系统完整技术规范,核心特点:

对等参与客户端和服务器都运行tss-lib地位平等
零信任架构:无需信任任何单一节点
Share物理隔离各Party的share完全独立存储
Coordinator不参与计算只负责协调不参与MPC
跨平台支持Android、iOS、PC、Server
DDD+Hexagonal架构:清晰的领域模型和六边形设计
生产级实现:完整的数据库设计、部署方案、安全措施

可直接用于Claude Code自动化开发。


版本: 2.0(修正版)
最后更新: 2024-11-27
作者: Your Organization