dApp Docs/MPC 签名服务部署与密钥管理指南
Development reference. Not independently verified for production.

MPC 签名服务部署与密钥管理指南

数据来源:MSG Chain 代码库核实

主网状态: No-Go — 当前 MSGChain 主网裁决为 No-Go,以下内容反映代码实际状态,不代表生产可用。

技术栈:MSG Chain + Dilithium-5 PQC + GG18/GG20/CMP 阈值协议 + agent_mpc_v1 合约
域:msgchain.org | 链 ID:msg-chain-1 | Bech32 前缀:msg
定位:本文档聚焦 MPC 签名服务基础设施(节点部署、密钥管理、协议原理),不涉及 AI Agent MPC 钱包集成(参见 04-AI-Agent/AI Agent MPC多重签名钱包接入指南.md)


目录

  1. MPC 理论基础
  2. MPC vs 多签
  3. MSG Chain MPC 组件架构
  4. MPC 节点部署
  5. 密钥分片管理
  6. 密钥生命周期
  7. 密钥隔离与 HSM
  8. 监控与审计
  9. 与 MSG Chain 的集成
  10. 灾难恢复
  11. 实践:部署 3/5 阈值 MPC 签名集群
  12. 安全边界与限制

附录:术语表、参考协议与标准、配置文件模板


一、MPC 理论基础

1.1 多方计算概述

多方计算(Multi-Party Computation,MPC)是一种密码学协议,允许多个参与方在不泄露各自私密输入的情况下,共同计算一个函数并得到结果。在数字签名场景下,MPC 的目标是:

多个参与方各自持有私钥的一个分片,协作生成一个有效的签名,但任何一方(甚至少于阈值的多方合谋)都无法获知完整的私钥。

MPC 签名的核心数学基础包括:

1.2 GG18 协议

GG18(Garay-Gennaro 2018)是首个实用的两轮 ECDSA 阈值签名协议,由 Rosario Gennaro 和 Steven Goldfeder 在 2018 年提出。

1.2.1 协议特性

属性 描述
签名算法 ECDSA(Secp256k1)
阈值 t-of-n,任意 t <= n
轮次 DKG:2 轮,签名:2 轮
密钥生成 分布式密钥生成(DKG),无可信中心
假设 诚实多数 + 同步网络
安全性 静态安全(Static security)

1.2.2 GG18 DKG 流程

Phase 1: 各方生成 Paillier 密钥对 (pk_i, sk_i)
  用于加密同态运算

Phase 2: 各方生成秘密多项式
  方 i 生成: f_i(x) = a_{i0} + a_{i1}*x + ... + a_{i(t-1)}*x^{t-1}
  其中 a_{i0} 是方 i 的秘密贡献

Phase 3: 分片分发
  方 i 计算 s_{ij} = f_i(j),加密后发送给方 j
  方 j 收到所有 s_{ij},求和得到自己的完整分片: sk_j = sum_i s_{ij}

Phase 4: 验证
  各方广播承诺 (a_{ik}*G),用于验证 s_{ij} 的正确性
  不一致 → 投诉 → 恶意方被识别

Phase 5: 公钥聚合
  各方计算: PK = (sum_i a_{i0})*G
  得到共享公钥 PK

1.2.3 GG18 签名流程

输入: 消息 m,各参与方持有分片 sk_i,群公钥 PK

Round 1(预处理/Offline):
  - 各方生成随机数 k_i, gamma_i
  - 各方计算 R_i = k_i*G 并广播
  - 各方计算 delta_i = k_i*gamma_i(加密形式)

Round 2(签名/Online):
  - 计算 R = sum R_i
  - 计算 r = R.x
  - 使用 Paillier 同态计算 s
  - 输出签名 (r, s)

1.3 GG20 协议

GG20 是 Gennaro-Goldfeder 在 2020 年对 GG18 的改进版本。

1.3.1 GG18 已知问题

问题 描述 影响
Paillier 密钥长度攻击 如果 Paillier 模数长度不足,攻击者可恢复私钥 私钥泄露
重放攻击 签名随机数重用可导致私钥恢复 私钥泄露
非自适应安全 无法处理动态腐化(adaptive corruption) 安全模型有限
零知识证明缺失 某些中间值缺少 ZKP 验证 恶意方可以作弊

1.3.2 GG20 改进

GG20 针对性改进:

1. Paillier 模数检查
   - 增加 Paillier 模数的零知识证明
   - 要求 N_Paillier 至少有 2048 位

2. 范围证明(Range Proof)
   - 对加密值增加范围证明
   - 防止 Overflow Attack

3. MtA(Multiparty-to-Aggregator)改进
   - 安全的乘法-加法转换
   - 每步附带 ZKP 验证

4. 确定性 nonce 生成
   - RFC 6979 风格的确定性 nonce
   - 消除可重用攻击面

5. 可识别性(Identifiable Abort)
   - 协议中止时可定位恶意方
   - 支持问责机制

1.3.3 GG20 签名轮次

Round 1 - 承诺阶段:
  ─ 各方生成 Paillier 密钥对
  ─ 计算承诺值 commitment = H(nonce || pk)
  ─ 广播 commitment_i

Round 2 - 密钥分享阶段:
  ─ 各方打开承诺(揭示 nonce_i 和 pk_i)
  ─ 交换秘密分片(加密)
  ─ 验证 ZKP

Round 3 - 签名预处理:
  ─ 生成随机数 k_i
  ─ 计算 R_i = k_i*G
  ─ 广播 R_i 并附带 ZKP

Round 4 - 在线签名:
  ─ 计算 R = sum R_i
  ─ 计算消息哈希 e = H(m)
  ─ 通过 MtA 计算 s
  ─ 输出签名 (r, s)

1.4 CMP 协议

CMP(Canetti-Makriyannis-Peli)是一种支持 EdDSA(Ed25519)的阈值签名协议。

1.4.1 CMP 核心特性

属性 CMP 协议
签名算法 EdDSA (Ed25519, Ed448)
阈值 t-of-n
轮次 DKG: 4 轮,签名: 2 轮
签名聚合 支持批量验证
安全性 自适应安全(Adaptive security)
假设 异步网络模型

1.4.2 EdDSA 与 ECDSA 的协议差异

                     ECDSA (GG18/GG20)           EdDSA (CMP)
                     ----------------             ----------
  签名等式           s = k^{-1}(e + r*sk)         s = r + h*sk
  随机数需求         强:两次使用同一 k 泄露私钥   一次性私钥(Hash 派生)
  同态加密           Paillier + ZKP               不需要
  MtA 子协议         必需                          不需要
  签名大小           ~70 B                         ~64 B
  验证速度           ~1.0ms                        ~0.6ms

CMP 不需要 Paillier 同态加密和 MtA 子协议,
因此计算开销显著低于 GG18/GG20。

1.4.3 CMP DKG 流程

CMP DKG(4 轮):

Round 1: 各方生成临时密钥并广播承诺
  ephemeral_sk_i = random()
  ephemeral_pk_i = ephemeral_sk_i*G
  comm_i = H(ephemeral_pk_i || salt_i)
  广播 comm_i

Round 2: 打开承诺并广播公开密钥
  广播 (ephemeral_pk_i, salt_i)
  各方验证同步性

Round 3: 多方 DH 密钥交换
  每对 (i, j) 计算共享密钥: K_{ij}
  使用 K_{ij} 加密发送秘密分片 s_{ij}

Round 4: 分片验证与公钥聚合
  验证接收到的分片
  聚合: sk_i = sum_j s_{ji}
  计算群公钥: PK = sum_i pk_i

1.5 阈值 ECDSA vs 阈值 EdDSA

对比维度 阈值 ECDSA (GG18/GG20) 阈值 EdDSA (CMP)
数学基础 Secp256k1 椭圆曲线 Curve25519 椭圆曲线
协议复杂度 高(需 Paillier + MtA + ZKP) 低(无需同态加密)
签名轮次 Offline + Online(2 轮签名) 2 轮
计算开销 高 低
通信开销 中等 低
量子抗性 否(需 Dilithium 扩展) 否(需 Dilithium 扩展)
实现成熟度 高 中等
MSG Chain 策略 Dilithium-5 阈值扩展 未来考虑

1.6 MSG Chain 的改进:阈值 Dilithium-5

MSG Chain 采用的阈值签名方案是将 GG20 框架扩展至 Dilithium-5。

1.6.1 阈值 Dilithium-5 的挑战

  Dilithium-5 签名结构:
    私钥: (s1, s2)
    公钥: (t1 = A * s1)
    签名: sigma = (z, h)
    其中:
      z = y + c * s1
      c = H(m || w)
      w = HighBits(A * y)

  挑战 1: Dilithium-5 使用拒绝采样(Rejection Sampling)
          阈值版本需要在多方间协调拒绝逻辑

  挑战 2: Dilithium-5 使用掩码向量 y 而非随机标量 k
          分片需要扩展到向量运算

  挑战 3: 签名过程中 w = HighBits(A*y) 的非线性
          多方同态计算困难

1.6.2 当前实现策略

    阈值 Dilithium-5 策略

    策略 A: 秘密共享 + 本地签名(当前)
      - 使用 Shamir 秘密共享拆分 Dilithium-5 私钥
      - 各方独立生成部分签名
      - 聚合器组合部分签名为完整签名
      - 部分实现,适合本地沙箱

    策略 B: 完整 GG20 风格阈值签名(未来)
      - 实现 DKG 生成 Dilithium-5 密钥分片
      - 完整的 MtA 风格签名轮次
      - 需要 Dilithium-5 的 Paillier-like 同态方案
      - 路线图中

二、MPC vs 多签

2.1 阈值签名 vs 多签

维度 阈值签名(MPC) 传统多签(Multisig)
链上签名体积 单个签名(固定大小) 多个签名(线性增长)
Gas 开销 单次验证 多次验证
签名者数量对链的影响 无影响 每增加一个签名者,签名体积增加
隐私性 聚合签名不暴露参与方 链上可见所有签名者
密钥存储 分片存储,私钥从未完整出现 完整私钥各自存储
恢复机制 分片丢失可重新分发 私钥丢失不可恢复
适配现有合约 无需修改(签名格式一致) 需修改合约验证逻辑

2.2 签名体积对比

传统多签体积 (2-of-3, Ed25519):
  ─ 3 个公钥:    3 x 32 B  = 96 B
  ─ 2 个签名:    2 x 64 B  = 128 B
  ─ 总计:        ~224 B

传统多签体积 (3-of-5, Ed25519):
  ─ 5 个公钥:    5 x 32 B  = 160 B
  ─ 3 个签名:    3 x 64 B  = 192 B
  ─ 总计:        ~352 B

MPC 阈值签名 (2-of-3, Dilithium-5):
  ─ 单个签名:    ~4,595 B
  ─ 不暴露参与方: 1 个签名包含所有信息

2.3 安全模型对比

传统多签安全主要依赖于:
  ─ 各参与方独立保管完整私钥
  ─ 链上合约验证签名数量是否达到阈值

安全弱点:
  1. 私钥单点泄露
  2. 侧信道攻击(完整私钥在签名过程中全程在内存中)
  3. 量子威胁(Ed25519/Secp256k1 均无量子抗性)

MPC 安全模型的核心差异:
  1. 私钥从不存在(分片形式,从未完整重构)
  2. 签名过程安全(私钥分片不离开本地)
  3. 前向安全性(长期密钥泄露不影响历史签名)
  4. 可识别中止(可定位恶意参与方)
  5. 量子安全(Dilithium-5 分片继承量子抗性)

2.4 适用场景对比

场景 推荐方案 理由
DAO 金库管理 MPC 阈值签名 单签名 Gas 低,不暴露治理成员
小额多签钱包 传统多签 实现简单,Gas 差异不大
高价值资产管理 MPC + HSM 最高安全等级,分片 + 硬件隔离
AI Agent 自主签名 MPC Agent 不能持有完整私钥
跨链资产桥 MPC + 门限预言机 签名者分布在多条链
企业财务管理 MPC 审批流配置灵活,审计合规

三、MSG Chain MPC 组件架构

3.1 架构总览

                    MSG Chain MPC 服务架构

  外部调用方 (AI Agent / dApp / DAO / 企业应用)
        |
        v
  Agent API 网关层
  POST /agent/v1/mpc/wallet              [Stub]
  POST /agent/v1/mpc/wallet/{id}/sign    [Stub]
  GET  /agent/v1/mpc/wallet/{id}        [Implemented]
  GET  /agent/v1/mpc/wallet/{id}/sign/{session} [Impl]
        |
        v
  MPC Orchestrator(协调器)
  [会话管理] [节点发现] [状态机] [审计日志] [限流器]
        |
        v
  MPC P2P 网络层
  节点 A --- P2P --- 节点 B --- P2P --- 节点 C
  libp2p / Noise / Kademlia DHT
        |
        v
  密码学协议层
  [GG20 框架 (Paillier + MtA + ZKP)]
  [Dilithium-5 签名引擎]
  [Shamir 秘密共享]
        |
        v
  链集成层
  Cosmos SDK Tx Builder --> agent_mpc_v1 合约
        |
        v
  msg-chain-1 (RPC: https://rpc.msgchain.org)

3.2.2 MPC 会话状态机

                    MPC 会话状态转换图

                         CREATED
                            |
                            v
                    PENDING_SIGNATURES
                      /              \
                     v                v
               COLLECTING        TIME_LIMIT_EXCEEDED
                     |                |
                     v                v
               FINALIZING          TIMEOUT
                     |
              /------+------\
             v               v
        COMPLETED          FAILED

状态定义:
  CREATED: 会话刚创建,等待签名者加入
  PENDING_SIGNATURES: 签名者正在生成部分签名
  COLLECTING: 部分签名正在收集中
  FINALIZING: 已收集足够部分签名,正在聚合计算
  COMPLETED: 签名完成,可获取完整签名
  FAILED: 签名失败(阈值不足/验证失败/协议错误)
  TIMEOUT: 超时未完成

会话超时机制:
  - session_timeout: 可配置(默认 300 秒)
  - 预处理超时: 60 秒
  - 每轮通信超时: 30 秒
  - 聚合超时: 15 秒

3.3 当前实现状态

重要声明:MSG Chain 的 MPC 签名服务目前处于部分实现 / Stub 状态。以下组件的状态分类帮助开发者准确评估生产可用性。

组件 子系统 状态 生产可用 备注
Agent API GET 查询路径 已实现 是 公开端点,无认证要求
Agent API POST/DELETE 写路径 Stub 否 返回 X-MSG-Stub=true,仅沙箱可用
agent_mpc_v1 合约 合约骨架 部分实现 否 基本状态存储实现,阈值签名逻辑未完成
Dilithium-5 签名 标准签名 已实现 是 生产中用于区块/交易签名
阈值 Dilithium-5 秘密共享分片 部分实现 否 Shamir 分片可用,完整 GG20 未实现
MPC P2P 网络 节点发现和通信 原型 否 基础 libp2p 框架搭建中
MPC Orchestrator 会话管理 原型 否 本地单节点可模拟
密钥存储 分片加密存储 部分实现 否 本地文件存储可用,HSM 集成未完成
监控指标 Prometheus 暴露 已实现 是 节点基础指标可用
灾难恢复 分片备份恢复 设计阶段 否 恢复协议设计未完成

3.4 组件通信图

组件间通信序列(创建钱包 + 签名):

Agent API                      Orchestrator                   MPC 节点群组
   |                               |                               |
   |-- POST /wallet (Stub) ------->|                               |
   |                               |-- DKGInit ------------------->|
   |                               |<-- Ack -----------------------|
   |                               |-- DKGCommit ----------------->|
   |                               |<-- Commitments ---------------|
   |                               |-- DKGShare ------------------>|
   |                               |<-- EncryptedShares -----------|
   |                               |-- DKGVerify ----------------->|
   |                               |<-- VerificationResult --------|
   |                               |-- DKGComplete --------------->|
   |                               |<-- Share + PK ----------------|
   |<-- wallet_created ------------|                               |
   |                               |                               |
   |-- POST /sign (Stub) -------->|                               |
   |                               |-- SignPrepare (e, signers) -->|
   |                               |-- Node_A: SignRound1 -------->|
   |                               |-- Node_B: SignRound1 -------->|
   |                               |-- Node_C: SignRound1 -------->|
   |                               |<-- R_i, Gamma_i, proofs -----|
   |                               |-- Node_A: SignRound2 -------->|
   |                               |<-- partial_sig_A ------------|
   |                               |-- Node_B: SignRound2 -------->|
   |                               |<-- partial_sig_B ------------|
   |                               |-- Node_C: SignRound2 -------->|
   |                               |<-- partial_sig_C ------------|
   |                               |-- Aggregate verification --->|
   |<-- full_signature ------------|                               |

3.5 Stub 端点行为

当调用 Stub 端点时,Agent API 返回如下特征:

{
  "wallet_id": "mpc-wallet-stub-xxxx",
  "status": "simulated",
  "simulation": {
    "note": "This endpoint is a stub for local development only.",
    "production_readiness": false
  }
}

HTTP 响应头中包含 X-MSG-Stub: true 标识。客户端应始终检查此头。


四、MPC 节点部署

4.1 节点内部架构

MPC Sign Node
  [协议引擎] DKG 状态机 / 签名状态机 / 消息序列化
  [密码学引擎] Dilithium-5 / Shamir 秘密共享 / Paillier / ZKP
  [密钥存储] 节点标识密钥 / MPC 分片存储 / HSM 接口 (PKCS#11)
  [P2P 通信层] libp2p / Noise 加密 / Kademlia DHT
  [API 接口层] gRPC / HTTP/REST / Prometheus 指标

4.2 节点发现

三层发现机制:

Layer 1: 静态种子节点
  - 配置文件指定种子节点 multiaddress
  - 启动时连接种子节点
  - 种子节点提供对等列表

Layer 2: Kademlia DHT
  - 使用 libp2p Kademlia 协议
  - 网络内自动发现对等节点
  - 支持 NAT 穿透

Layer 3: 链上注册
  - 节点在 agent_mpc_v1 合约注册
  - 合约存储节点 multiaddress
  - 新节点可通过合约事件发现

4.3 P2P 通信

传输层安全栈: 应用层 (/mpc/dkg/1.0.0) -> libp2p Stream -> Noise XX 握手 -> Yamux mux -> TCP/QUIC

所有 MPC 协议消息使用 Protocol Buffers 序列化,包含两层加密保护:
Layer 1: 传输层加密(Noise XX)- 端到端加密和身份认证
Layer 2: 应用层加密 - DKG 分片值使用接收方公钥加密

4.4 DKG 流程

DKG 流程(以 3/5 阈值为例):

Step 1: 参数协商
  - 协调器发送 DKGInit 消息给所有签名者
  - 各方确认参与 -> 返回 ACK

Step 2: 各方生成密钥材料
  - 每个节点 i: 生成 Paillier 密钥对、多项式、承诺、ZKP

Step 3: 广播承诺
  - 广播 (C_i, proof_i, comm_i)
  - 等待所有参与方到达

Step 4: 广播 Paillier 公钥
  - 各节点打开承诺,验证一致性

Step 5: 交换分片
  - 使用接收方 Paillier 公钥加密发送分片

Step 6: 分片验证
  - 解密后验证分片正确性,不匹配则投诉

Step 7: 投诉处理
  - 被投诉节点自证,如果证实恶意则移除

Step 8: 分片聚合
  - 节点 j 计算: sk_j = sum_i s_{ij}
  - 计算: PK = sum_i a_{i0}*G

4.5 签名轮次

参与签名: Node_A, Node_B, Node_C, 阈值: 3

Pre-signing Phase (预处理,可离线):
  - 各方生成随机数 k_i, chi_i
  - 计算 R_i = k_i*G, 广播 R_i
  - 计算 R = sum R_i, r = R.x mod q

Online Signing Phase (需在线通信):
  - 计算消息哈希 e = H(m)
  - 各方通过 MtA 子协议交换乘积值
  - 各方计算部分签名
  - 发送部分签名给聚合器

Aggregation Phase:
  - Coordinator 聚合所有部分签名
  - 验证完整签名的有效性
  - 返回完整 Dilithium-5 签名

4.6 节点配置

# mpc-node-config.yaml
node:
  identity:
    key_path: "/etc/mpc-node/keys/node-identity.dilithium"
    peer_id_file: "/etc/mpc-node/keys/peer_id"
  network:
    listen_addresses:
      - "/ip4/0.0.0.0/tcp/9001"
      - "/ip4/0.0.0.0/udp/9001/quic-v1"
    bootstrap_peers:
      - "/ip4/seed1.msgchain.org/tcp/9001/p2p/12D3KooW..."
  protocol:
    dkg_timeout_seconds: 300
    sign_timeout_seconds: 60
    max_concurrent_sessions: 10
    max_parties: 50
    min_threshold: 2
  crypto:
    paillier_key_bits: 2048
    dilithium_security_level: 5
    use_tee: false
  storage:
    type: "filesystem"
    path: "/var/lib/mpc-node/shares"
    encryption_key: ""
    backup_path: "/var/backups/mpc-node"
  api:
    grpc_port: 9002
    http_port: 9003
    metrics_port: 9004
  logging:
    level: "info"
    format: "json"
  monitoring:
    prometheus_enabled: true
    health_check_interval: 10
    heartbeat_interval: 30

五、密钥分片管理

5.1 分片生成

5.1.1 Shamir 秘密共享原理

给定: 秘密 s(Dilithium-5 私钥的标量分量)
阈值: t
总参与方: n

构造多项式: f(x) = s + a_1*x + a_2*x^2 + ... + a_{t-1}*x^{t-1}

分片计算: share_i = f(i) for i = 1, 2, ..., n

重构(拉格朗日插值):
  s = f(0) = sum_{i in S} share_i * L_i(0)
  L_i(0) = prod_{j in S, j != i} (j / (j - i))

5.1.2 DKG 分片数据结构

share_metadata:
  scheme: GG20-Dilithium5
  threshold: 3
  total_parties: 5
  party_index: 1
  session_id: dkg-session-uuid-xxx
share_data:
  sk_share: { s1: 0x..., s2: 0x... }
  chain_code: 0x...
  public_key: dilithium5:...
  polynomial_commitments: [0x..., 0x..., 0x...]

5.2 分片存储

5.2.1 四层存储层级

Level 0: 内存 - 使用 mlock() 防止交换到磁盘,签名完成后立即擦除
Level 1: 本地加密存储 - AES-256-GCM 加密,文件系统或数据库
Level 2: HSM 硬件安全模块 - PKCS#11 接口,分片永不离开 HSM
Level 3: 离线备份 - 加密导出到冷存储,地理分布式

5.2.2 加密存储实现

import os
import hashlib
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

class SecureShareStorage:
    def __init__(self, master_key, storage_path):
        assert len(master_key) == 32
        self.master_key = master_key
        self.storage_path = storage_path

    def _derive_share_key(self, wallet_id, party_index):
        context = f"mpc-share-{wallet_id}-party-{party_index}"
        return hashlib.sha256(self.master_key + context.encode()).digest()

    def encrypt_share(self, share_data, wallet_id, party_index):
        key = self._derive_share_key(wallet_id, party_index)
        aesgcm = AESGCM(key)
        nonce = os.urandom(12)
        ad = f"{wallet_id}:{party_index}".encode()
        ct = aesgcm.encrypt(nonce, share_data, ad)
        return nonce + ct

    def decrypt_share(self, encrypted_data, wallet_id, party_index):
        key = self._derive_share_key(wallet_id, party_index)
        aesgcm = AESGCM(key)
        nonce, ct = encrypted_data[:12], encrypted_data[12:]
        ad = f"{wallet_id}:{party_index}".encode()
        return aesgcm.decrypt(nonce, ct, ad)

    def store_share(self, share_data, wallet_id, party_index):
        enc = self.encrypt_share(share_data, wallet_id, party_index)
        path = os.path.join(self.storage_path, f"{wallet_id}_{party_index}.enc")
        with open(path, "wb") as f:
            f.write(enc)

    def load_share(self, wallet_id, party_index):
        path = os.path.join(self.storage_path, f"{wallet_id}_{party_index}.enc")
        with open(path, "rb") as f:
            return self.decrypt_share(f.read(), wallet_id, party_index)

5.3 分片备份

5.3.1 三层次备份策略

Layer A: 热备份 - 加密分区副本,延迟 < 5 分钟
Layer B: 温备份 - S3 兼容存储 + KMS 加密密钥,保留 30 天
Layer C: 冷备份 - 硬件安全模块 + 纸质二维码,地理分散

备份加密要求:
  - 所有备份使用独立于节点主密钥的备份密钥加密
  - 备份密钥分开存储(密钥管理服务 + 阈值保护)
  - 每次备份包含完整性校验和

5.4 分片恢复

5.4.1 单节点分片恢复

触发条件: 节点宕机超过 24h / 存储损坏 / 需要替换节点

恢复步骤:
Phase 1: 验证恢复资格(检查活节点 >= t)
Phase 2: 分片重新分发(协作刷新协议,保持公钥不变)
Phase 3: 新节点初始化(生成身份密钥,加入 P2P 网络)
Phase 4: 验证(测试签名,更新分片版本号)

5.4.2 协作刷新协议

当节点 C 的存储损坏时,剩余节点生成零和偏移多项式
(常数项为 0,因此公钥不变),分发偏移分片给节点 C
新分片 = 旧分片 + 收到的偏移总和

六、密钥生命周期

6.1 生命周期模型

Phase 1: 创建 (Create) - DKG 分布式密钥生成,分片分发,公钥验证
Phase 2: 活跃 (Active) - 阈值签名服务,分片版本管理,监控审计
Phase 3a: 轮换 (Rotate) - 生成新密钥,递增版本号
Phase 3b: 归档 (Archive) - 锁定密钥,只读状态,停止签名
Phase 3c: 销毁 (Destroy) - 永久删除分片,符合 NIST SP 800-88

6.2 密钥创建验证

def verify_new_mpc_key(wallet_id, public_key, threshold, total_parties):
    """验证新创建的 MPC 密钥对"""
    # 1. 验证聚合公钥格式
    if len(public_key) != 2592:  # Dilithium-5 公钥大小
        return False

    # 2. 执行测试签名
    test_msg = b"MPC_KEY_VERIFICATION_" + wallet_id.encode()
    signature = perform_test_sign(test_msg)
    if signature is None:
        return False

    # 3. 验证签名
    if not verify_dilithium5_signature(public_key, test_msg, signature):
        return False

    # 4. 检查分片数量
    if load_share_count(wallet_id) != total_parties:
        return False

    # 5. 验证所有参与方的承诺一致性
    commitments = load_dkg_commitments(wallet_id)
    if not verify_polynomial_commitments(commitments, public_key):
        return False

    return True

6.3 密钥轮换

定期轮换: 每个季度(90 天)轮换一次,高安全要求每月轮换
事件驱动轮换: 签名者变更、安全事件、节点被攻破

零停机轮换协议:
Phase 0: 准备工作(通知、健康检查、记录活跃会话)
Phase 1: 分片刷新(执行协作刷新协议)
Phase 2: 并行运行(旧/新分片同时可用)
Phase 3: 旧分片销毁(等待进行中会话完成)
Phase 4: 验证与切换(测试签名,更新版本号)

轮换状态机:

```python
from enum import Enum

class RotationPhase(Enum):
    INITIATED = "initiated"
    PREPARING = "preparing"
    REFRESHING = "refreshing"
    PARALLEL_RUN = "parallel_run"
    DESTROYING_OLD = "destroying_old"
    VERIFYING = "verifying"
    COMPLETED = "completed"
    FAILED = "failed"
    ROLLED_BACK = "rolled_back"

class KeyRotationManager:
    def __init__(self, node_registry):
        self.node_registry = node_registry
        self.active_rotations = {}

    def initiate_rotation(self, wallet_id):
        session = {
            "rotation_id": f"rot-{wallet_id}-{int(time.time())}",
            "phase": RotationPhase.INITIATED,
            "old_version": self._get_current_version(wallet_id),
            "new_version": self._get_current_version(wallet_id) + 1,
            "participant_status": {nid: "pending" for nid in self.node_registry},
        }
        self.active_rotations[session["rotation_id"]] = session
        return session

    def advance_phase(self, rotation_id):
        session = self.active_rotations.get(rotation_id)
        if session["phase"] == RotationPhase.INITIATED:
            self._prepare_rotation(session)
            session["phase"] = RotationPhase.PREPARING
        elif session["phase"] == RotationPhase.PREPARING:
            self._execute_refresh(session)
            session["phase"] = RotationPhase.REFRESHING
        elif session["phase"] == RotationPhase.REFRESHING:
            session["phase"] = RotationPhase.PARALLEL_RUN
        elif session["phase"] == RotationPhase.PARALLEL_RUN:
            self._destroy_old_shares(session)
            session["phase"] = RotationPhase.DESTROYING_OLD
        elif session["phase"] == RotationPhase.DESTROYING_OLD:
            if self._verify_new_shares(session):
                session["phase"] = RotationPhase.COMPLETED
            else:
                session["phase"] = RotationPhase.ROLLED_BACK

    def _destroy_old_shares(self, session):
        for node_id in self.node_registry:
            self._secure_erase(node_id, session["wallet_id"], session["old_version"])
        logger.info(f"Old shares destroyed for {session['wallet_id']}")

    def _secure_erase(self, node_id, wallet_id, version):
        share_path = f"/var/lib/mpc-node/shares/{wallet_id}_v{version}_*.enc"
        # 使用 NIST SP 800-88 标准覆写 3 次
        for pattern in [b"\\x00", b"\\xff", os.urandom(4096)]:
            self._overwrite(share_path, pattern)
        os.remove(share_path)

6.3 密钥归档

自动归档: 密钥连续 180 天未使用 / 钱包余额为 0 超 30 天 / 签名者低于阈值超 90 天
手动归档: 合规要求 / 调查隔离 / 迁移后

归档操作: 活性密钥 -> archived,加密打包,停止签名请求,保留审计日志

6.4 密钥销毁

合规销毁: 保留期限到期(7 年),监管批准
安全销毁: 密钥被攻破,节点被物理控制

Type A: 软销毁 - 删除加密分片,标记为 destroyed,保留元数据
Type B: 硬销毁 - 物理销毁介质,NIST SP 800-88 安全擦除

6.5 合规要求

密钥生成: DKG 无需可信中心,CSPRNG,完整审计
密钥存储: AES-256-GCM 加密,最小权限,<= 90 天轮换
密钥使用: >= t 个签名者,审计日志,速率限制
密钥备份: 地理分布式,密钥分离,年度演练
密钥归档: 7 年保留,加密防篡改,不可用于签名
密钥销毁: 书面批准,NIST SP 800-88,销毁证书

七、密钥隔离与 HSM

7.1 分片存储威胁模型

Threat 1: 操作系统级入侵 - 缓解: 全盘加密 + HSM + 最小权限
Threat 2: 内存转储 - 缓解: mlock() + 内存加密 + 使用后立即擦除
Threat 3: 供应链攻击 - 缓解: 依赖审计 + 签名验证 + TEE 隔离
Threat 4: 侧信道攻击 - 缓解: 恒定时间实现 + 噪声注入 + HSM
Threat 5: 物理访问 - 缓解: 全盘加密 + TPM 绑定 + 物理安全

7.2 TEE 架构

TEE(可信执行环境)集成:

操作系统 (不可信)
  [TEE Enclave (可信环境)]
    MPC Protocol Engine (GG20)
    Crypto Engine (Dilithium-5)
    Share Store (加密分片)
    远程证明 (Remote Attestation)
  [P2P 通信层 libp2p (不可信)]

支持的 TEE 后端: Intel SGX/TDX, AMD SEV-SNP, ARM TrustZone

远程证明流程:
  Verifier -> Challenge -> Enclave 生成 Quote -> 返回 Quote+公钥
  -> IAS/DCAP 验证签名 -> 确认可信代码 -> 信任公钥用于加密通信

7.3 HSM 集成

HSM 部署模式:

模式 A: 每节点独立 HSM - 最高安全,成本高,适用高价值资产
模式 B: 共享云 HSM - 成本低,弹性扩展,适用标准部署
模式 C: 软件 HSM + TEE - 部署灵活,适用开发测试
模式 D: 混合模式 - 平衡安全与成本,适用大多数生产场景

支持的 HSM: YubiHSM 2, Nitrokey HSM, AWS/Azure/GCP CloudHSM, Ledger

八、监控与审计

8.1 监控架构

MPC 节点监控架构:

  Prometheus <--scrape-- MPC Node (port 9004)
      |
      v
  Alertmanager --> 告警通知 (Slack/PagerDuty/Email)
      |
      v
  Grafana --> 可视化仪表盘

每节点暴露的监控维度:
  系统层: CPU/内存/磁盘/网络 IO
  协议层: DKG/签名延迟/成功率
  网络层: P2P 连接数/消息吞吐量
  安全层: 异常检测/验证失败/投诉
  密钥层: 分片数量/版本/备份状态

8.2 Prometheus 指标

mpc_node_info            - 节点元数据
mpc_node_uptime_seconds  - 节点运行时间
mpc_node_peers_connected - 连接对等节点数
mpc_dkg_total            - DKG 会话计数(按状态分类)
mpc_sign_total           - 签名会话计数(按状态分类)
mpc_sign_duration_seconds - 签名延迟直方图
mpc_node_health          - 节点健康状态
mpc_node_shares_count    - 本地分片数量
mpc_p2p_messages_sent    - P2P 消息计数(按类型分类)
mpc_share_verification_failures - 分片验证失败计数
mpc_complaints_filed     - DKG 投诉计数

8.3 告警规则

# mpc-alert-rules.yml
groups:
  - name: mpc-node
    rules:
      - alert: NodeDown
        expr: mpc_node_health == 0
        for: 5m
        labels: { severity: critical }
        annotations:
          summary: "MPC node {{ $labels.node_id }} is down"

      - alert: HighSignFailureRate
        expr: rate(mpc_sign_total{status="failed"}[5m]) / rate(mpc_sign_total{status="completed"}[5m]) > 0.05
        for: 10m
        labels: { severity: warning }
        annotations:
          summary: "Signature failure rate > 5%"

      - alert: LowPeerCount
        expr: mpc_node_peers_connected < 3
        for: 2m
        labels: { severity: warning }
        annotations:
          summary: "Node has fewer than 3 connected peers"

      - alert: DkgFailure
        expr: rate(mpc_dkg_total{status="failed"}[30m]) > 0
        for: 1m
        labels: { severity: critical }
        annotations:
          summary: "DKG failure detected"

      - alert: ShareVerificationFailure
        expr: rate(mpc_share_verification_failures[15m]) > 0
        for: 5m
        labels: { severity: critical }
        annotations:
          summary: "Share verification failures detected"

8.4 Grafana 仪表盘建议

Row 1: 集群概览 - 健康状态 / 活跃会话 / 成功率 / 延迟 P50/P95/P99
Row 2: 节点详情 - 连接数 / 分片数 / CPU内存 / 网络 IO
Row 3: 协议指标 - DKG 成功率 / 签名轮次延迟 / MtA 时间 / ZKP 时间
Row 4: P2P 网络 - 连接状态 / 消息吞吐量 / 消息延迟
Row 5: 安全 - 异常检测 / 失败尝试 / 投诉频率 / 轮换状态

8.5 防篡改审计日志

import json, hashlib, os
from datetime import datetime

class MPCAuditLogger:
    """哈希链审计日志系统"""

    def __init__(self, log_path):
        self.log_path = log_path
        self._chain_head = self._load_chain_head()

    def _load_chain_head(self):
        try:
            with open(f"{self.log_path}.chain", "r") as f:
                return f.read().strip()
        except FileNotFoundError:
            return None

    def _update_chain_head(self, entry_hash):
        with open(f"{self.log_path}.chain", "w") as f:
            f.write(entry_hash)

    def log_event(self, event_type, severity, context, result=None):
        entry = {
            "timestamp": datetime.utcnow().isoformat() + "Z",
            "event_type": event_type,
            "severity": severity,
            "source": {"node_id": os.uname().nodename, "pid": os.getpid()},
            "context": context,
            "result": result or {"status": "success"},
            "previous_hash": self._chain_head or "",
        }
        entry_json = json.dumps(entry, sort_keys=True, default=str)
        entry_hash = hashlib.sha256(entry_json.encode()).hexdigest()
        entry["entry_hash"] = entry_hash
        with open(self.log_path, "a") as f:
            f.write(json.dumps(entry, default=str) + "\\n")
        self._update_chain_head(entry_hash)
        return entry_hash

    def verify_chain_integrity(self):
        prev = ""
        with open(self.log_path, "r") as f:
            for line in f:
                entry = json.loads(line)
                entry_copy = dict(entry)
                del entry_copy["entry_hash"]
                computed = hashlib.sha256(
                    json.dumps(entry_copy, sort_keys=True, default=str).encode()
                ).hexdigest()
                if computed != entry["entry_hash"]:
                    return False
                if entry["previous_hash"] != prev:
                    return False
                prev = entry["entry_hash"]
        return True

8.6 审计事件分类

KEY_MGMT: KEY_CREATED / KEY_ROTATED / KEY_ARCHIVED / KEY_DESTROYED / KEY_BACKUP / KEY_RESTORED
SIGN: SIGN_REQUESTED / SIGN_PARTIAL / SIGN_COMPLETED / SIGN_FAILED / SIGN_TIMEOUT
NODE: NODE_JOINED / NODE_LEFT / NODE_SUSPENDED / NODE_BLACKLISTED
PROTOCOL: DKG_PHASE_STARTED / DKG_COMPLAINT / DKG_ABORTED
ACCESS: API_KEY_CREATED / UNAUTHORIZED_ACCESS / RATE_LIMIT_HIT

8.7 异常签名检测引擎

from collections import defaultdict
from dataclasses import dataclass
import time

@dataclass
class AnomalyAlert:
    alert_id: str
    severity: str
    rule_name: str
    description: str
    affected_nodes: list
    timestamp: float

class AnomalyDetector:
    def __init__(self):
        self.sign_rate_tracker = defaultdict(list)
        self.alert_handlers = []

    def register_alert_handler(self, handler):
        self.alert_handlers.append(handler)

    def _emit_alert(self, alert):
        for handler in self.alert_handlers:
            handler(alert)

    def check_sign_rate(self, wallet_id, node_id):
        now = time.time()
        window = 60
        key = f"{wallet_id}:{node_id}"
        self.sign_rate_tracker[key] = [
            t for t in self.sign_rate_tracker[key] if now - t < window
        ]
        self.sign_rate_tracker[key].append(now)
        rate = len(self.sign_rate_tracker[key])
        if rate > 60:
            return AnomalyAlert(
                alert_id=f"anom-{int(now)}",
                severity="critical",
                rule_name="sign_rate_exceeded",
                description=f"Node {node_id} signing rate: {rate}/min",
                affected_nodes=[node_id],
                timestamp=now,
            )
        return None

    def check_signer_combo(self, wallet_id, signers, history):
        combo = frozenset(signers)
        if combo not in history.get(wallet_id, set()):
            return AnomalyAlert(
                alert_id=f"anom-combo-{int(time.time())}",
                severity="warning",
                rule_name="unusual_signer_combo",
                description=f"New signer combination: {signers}",
                affected_nodes=signers,
                timestamp=time.time(),
            )
        return None

    def check_transaction_value(self, wallet_id, amount, historical_stats):
        stats = historical_stats.get(wallet_id)
        if not stats:
            return None
        if amount > stats["mean"] + 5 * stats["std"]:
            return AnomalyAlert(
                alert_id=f"anom-value-{int(time.time())}",
                severity="critical",
                rule_name="unusual_value",
                description=f"Amount {amount} exceeds 5sigma threshold",
                affected_nodes=["all"],
                timestamp=time.time(),
            )
        return None

九、与 MSG Chain 的集成

9.1 集成架构

  MPC 签名服务 -> Agent API 网关 -> MSG Chain 区块链
  [签名会话] [交易构建器] [状态机管理器] -> [POST/GET MPC 端点] -> [agent_mpc_v1 合约]

  Chain ID: msg-chain-1
  Bech32: msg
  RPC: https://rpc.msgchain.org
  REST: https://api.msgchain.org

9.2 MPC Gateway 客户端

import httpx

class MPCGatewayClient:
    def __init__(self, gateway_url="https://api.msgchain.org",
                 api_key=None, chain_id="msg-chain-1"):
        self.gateway_url = gateway_url
        headers = {"Content-Type": "application/json"}
        if api_key:
            headers["X-API-Key"] = api_key
        self._client = httpx.Client(base_url=gateway_url, headers=headers, timeout=60)

    def request_signature(self, wallet_id, tx_body, signer_order, timeout=300, memo=""):
        payload = {"tx_body": tx_body, "signer_order": signer_order,
                   "session_timeout": timeout, "memo": memo, "chain_id": "msg-chain-1"}
        resp = self._client.post(f"/agent/v1/mpc/wallet/{wallet_id}/sign", json=payload)
        if resp.headers.get("X-MSG-Stub") == "true":
            print("[WARN] MPC sign endpoint is Stub")
        resp.raise_for_status()
        return resp.json()

    def query_session(self, wallet_id, session_id):
        resp = self._client.get(f"/agent/v1/mpc/wallet/{wallet_id}/sign/{session_id}")
        resp.raise_for_status()
        return resp.json()

    def poll_signature(self, wallet_id, session_id, interval=2.0, timeout=300.0):
        import time
        start = time.time()
        while True:
            if time.time() - start > timeout:
                raise TimeoutError(f"Session {session_id} timed out")
            state = self.query_session(wallet_id, session_id)
            if state["status"] == "COMPLETED":
                return state
            elif state["status"] in ("FAILED", "TIMEOUT"):
                raise RuntimeError(state.get("error", {}).get("message", "unknown"))
            time.sleep(interval)

9.3 交易构建与广播

def build_cosmos_tx(messages, signature, pubkey, tx_config, memo="", fee_gas=350_000):
    import base64, json
    gas_price_num = int(float(tx_config["gas_price"]) * 10**18)
    fee_amt = str(gas_price_num * fee_gas)
    tx = {
        "body": {"messages": messages, "memo": memo},
        "auth_info": {
            "signer_infos": [{
                "public_key": {"@type": "/cosmos.crypto.dilithium.PubKey",
                               "key": base64.b64encode(pubkey).decode()},
                "mode_info": {"single": {"mode": "SIGN_MODE_DIRECT"}},
                "sequence": str(tx_config["sequence"]),
            }],
            "fee": {"amount": [{"denom": "umsg", "amount": fee_amt}],
                    "gas_limit": str(fee_gas)},
        },
        "signatures": [base64.b64encode(signature).decode()],
    }
    return json.dumps(tx).encode("utf-8")

def broadcast_tx(rpc_endpoint, signed_tx_bytes):
    import base64, httpx
    tx_b64 = base64.b64encode(signed_tx_bytes).decode("ascii")
    resp = httpx.post(rpc_endpoint, json={
        "jsonrpc": "2.0", "id": 1, "method": "broadcast_tx_sync",
        "params": {"tx": tx_b64},
    }, timeout=30)
    resp.raise_for_status()
    return resp.json().get("result", {})

def wait_for_tx(rpc_endpoint, tx_hash, timeout=60):
    import time, httpx
    start = time.time()
    while time.time() - start < timeout:
        resp = httpx.post(rpc_endpoint, json={
            "jsonrpc": "2.0", "id": 1, "method": "tx",
            "params": {"hash": f"0x{tx_hash}"},
        })
        if resp.status_code == 200:
            tx = resp.json().get("result", {})
            if tx.get("height") and int(tx["height"]) > 0:
                return {"height": int(tx["height"]), "tx_hash": tx_hash}
        time.sleep(2)
    raise TimeoutError(f"Tx {tx_hash} not confirmed")

9.4 agent_mpc_v1 合约接口状态

ExecuteMsg:
  RegisterWallet { wallet_id, threshold, signers, public_key } - 已实现
  UpdateSigners { wallet_id, signers } - 部分实现
  VerifySignature { wallet_id, message, signature } - 已实现 (Dilithium-5 验证)
  ArchiveWallet { wallet_id } - 部分实现

QueryMsg:
  GetWallet { wallet_id } -> WalletInfo - 已实现
  ListWallets { owner } -> Vec<WalletInfo> - 已实现
  GetSigners { wallet_id } -> Vec<SignerInfo> - 已实现

未实现: 阈值签名逻辑(链上聚合/阈值检查)

9.5 完整集成时序

1. 构建 Unsigned Tx - 创建 Cosmos SDK 交易消息
2. 发起 MPC 签名会话 - POST /agent/v1/mpc/wallet/{id}/sign
3. 收集部分签名 - 各签名者提交部分签名,协调器聚合
4. 组装完整交易 - 将签名和公钥嵌入交易
5. 广播至 MSG Chain - broadcast_tx_sync
6. 等待确认 - 轮询 tx 查询端点

预计耗时: MPC 签名 1-5s + 广播上链 5-10s = 总计约 6-15s

十、灾难恢复

10.1 灾难场景分类

Level 1: 单节点故障 - 宕机/网络分区/进程崩溃 - 自动重启恢复
Level 2: 多节点故障 - 2+ 节点宕机/数据中心故障 - 紧急恢复协议
Level 3: 密钥分片丢失 - 存储损坏/备份不可用 - 备份还原/社交恢复
Level 4: 全网级灾难 - 所有节点故障 - 冷备份启动/新链部署

10.2 单节点恢复

自动恢复流程:
Step 1: 健康检查(进程/磁盘/网络/分片完整性)
Step 2: 状态恢复(加载分片,连接 P2P,同步状态)
Step 3: 节点验证(其他节点验证身份和分片完整性)
Step 4: 恢复签名服务(OFFLINE -> ACTIVE)

10.3 多节点故障恢复

条件: 丢失节点数量 >= t,签名能力丧失

紧急恢复:
Phase 1: 灾难评估(剩余节点、数据完整性)
Phase 2: 选择策略(剩余节点/备份恢复/社交恢复/钱包迁移)
Phase 3: 执行恢复(启动备用节点,分配分片,运行恢复协议)
Phase 4: 验证切换(测试签名,更新注册,通知依赖方)

10.4 恢复演练计划

Q1: 单节点故障恢复演练 - 目标 < 5 分钟恢复签名能力
Q2: 多节点故障恢复演练 - 目标 < 30 分钟恢复签名能力
Q3: 备份恢复演练 - 目标 100% 备份可用性
Q4: 全网级灾难演练 - 目标 < 4 小时恢复完整服务

十一、实践:部署 3/5 阈值 MPC 签名集群

11.1 环境准备

硬件要求(每个节点):
  CPU: 4 核以上(推荐 8 核)
  内存: 8 GB 以上(推荐 16 GB)
  磁盘: 50 GB SSD(加密分区)
  网络: 100 Mbps 以上,低延迟

软件要求: Ubuntu 22.04 / Debian 12, Docker 24.0+, Docker Compose 2.20+

网络要求:
  TCP 9001: P2P 节点通信
  TCP 9002: gRPC 内部通信
  TCP 9003: HTTP API
  TCP 9004: Prometheus 指标

11.2 初始化节点身份

# 初始化 5 个节点的身份密钥
for node in bootstrap node-a node-b node-c node-d node-e; do
    mkdir -p "./keys/$node"
    msg-chain-devkit keys generate --algorithm dilithium5 --output "./keys/$node/node-identity.dilithium"
    msg-chain-devkit p2p keygen --output "./keys/$node/peer_id"
done

11.3 Docker Compose 部署

version: "3.9"
networks:
  mpc-net:
    driver: bridge
    ipam:
      config:
        - subnet: "172.20.0.0/16"

services:
  bootstrap:
    image: msgchain/mpc-node:latest
    container_name: mpc-bootstrap
    ports: ["9001:9001", "9004:9004"]
    networks: {mpc-net: {ipv4_address: "172.20.0.100"}}

  node-a:
    image: msgchain/mpc-node:latest
    container_name: mpc-node-a
    depends_on: [bootstrap]
    networks: {mpc-net: {ipv4_address: "172.20.0.1"}}
    volumes:
      - ./keys/node-a:/etc/mpc-node/keys
      - ./data/node-a:/var/lib/mpc-node

  node-b:
    image: msgchain/mpc-node:latest
    container_name: mpc-node-b
    depends_on: [bootstrap]
    networks: {mpc-net: {ipv4_address: "172.20.0.2"}}
    volumes:
      - ./keys/node-b:/etc/mpc-node/keys
      - ./data/node-b:/var/lib/mpc-node

  node-c:
    image: msgchain/mpc-node:latest
    container_name: mpc-node-c
    depends_on: [bootstrap]
    networks: {mpc-net: {ipv4_address: "172.20.0.3"}}
    volumes:
      - ./keys/node-c:/etc/mpc-node/keys
      - ./data/node-c:/var/lib/mpc-node

  node-d:
    image: msgchain/mpc-node:latest
    container_name: mpc-node-d
    depends_on: [bootstrap]
    networks: {mpc-net: {ipv4_address: "172.20.0.4"}}
    volumes:
      - ./keys/node-d:/etc/mpc-node/keys
      - ./data/node-d:/var/lib/mpc-node

  node-e:
    image: msgchain/mpc-node:latest
    container_name: mpc-node-e
    depends_on: [bootstrap]
    networks: {mpc-net: {ipv4_address: "172.20.0.5"}}
    volumes:
      - ./keys/node-e:/etc/mpc-node/keys
      - ./data/node-e:/var/lib/mpc-node

  orchestrator:
    image: msgchain/mpc-orchestrator:latest
    container_name: mpc-orchestrator
    depends_on: [node-a, node-b, node-c, node-d, node-e]
    ports: ["8080:8080", "9090:9090"]
    environment:
      - ORCHESTRATOR_CHAIN_ID=msg-chain-1
      - ORCHESTRATOR_RPC_ENDPOINT=https://rpc.msgchain.org
    networks: {mpc-net: {ipv4_address: "172.20.0.200"}}

11.4 启动与管理

# 启动集群
docker compose up -d

# 查看节点状态
curl -s http://localhost:9004/health | jq .

# 查看 P2P 对等节点
curl -s http://localhost:9003/api/v1/peers | jq .

# 查看本地分片
curl -s http://localhost:9003/api/v1/shares | jq .

# 导出备份
curl -X POST http://localhost:9003/api/v1/shares/export     -H "Content-Type: application/json"     -d '{"backup_key_id": "backup-key-v2", "output_dir": "/backup"}'

# Prometheus 指标
curl -s http://localhost:9004/metrics | grep mpc_

11.5 执行 DKG 和测试签名

import httpx, time, json

def run_dkg(orchestrator_url, wallet_name, threshold, signer_nodes):
    """通过 Orchestrator 发起 DKG"""
    client = httpx.Client(base_url=orchestrator_url, timeout=300)

    payload = {
        "name": wallet_name,
        "threshold": threshold,
        "total_signers": len(signer_nodes),
        "signers": [{"node_id": n["node_id"], "pubkey": n["pubkey"]} for n in signer_nodes],
        "chain_id": "msg-chain-1",
    }

    resp = client.post("/agent/v1/mpc/wallet", json=payload)
    is_stub = resp.headers.get("X-MSG-Stub") == "true"
    if is_stub:
        print("[Stub] Wallet creation is simulated - sandbox only")

    wallet = resp.json()
    print(f"Wallet created: {wallet['wallet_id']}")
    print(f"Address: {wallet.get('address', 'N/A')}")
    print(f"Threshold: {threshold}/{len(signer_nodes)}")
    return wallet["wallet_id"]


def test_mpc_signature(orchestrator_url, wallet_id, signer_order):
    """执行测试签名验证 DKG 成功"""
    client = httpx.Client(base_url=orchestrator_url, timeout=60)
    test_payload = {"test_message": "MPC_CLUSTER_VERIFICATION"}

    resp = client.post(
        f"/agent/v1/mpc/wallet/{wallet_id}/sign",
        json={"payload": test_payload, "signer_order": signer_order, "chain_id": "msg-chain-1"},
    )
    session = resp.json()
    session_id = session["session_id"]
    print(f"Sign session: {session_id}")

    # 轮询直到完成
    for i in range(30):
        state = client.get(
            f"/agent/v1/mpc/wallet/{wallet_id}/sign/{session_id}"
        ).json()
        status = state["status"]
        if status == "COMPLETED":
            sig = state.get("full_signature", "")
            print(f"Signature obtained: {sig[:64]}...")
            print(f"Size: {state.get('signature_size_bytes', 'N/A')} bytes")
            return True
        elif status in ("FAILED", "TIMEOUT"):
            print(f"Signing failed: {state.get('error', {})}")
            return False
        print(f"  Status: {status} (attempt {i+1})")
        time.sleep(2)
    return False


# 使用示例
nodes = [
    {"node_id": "node-a", "pubkey": "dilithium5:pubkey_a_here..."},
    {"node_id": "node-b", "pubkey": "dilithium5:pubkey_b_here..."},
    {"node_id": "node-c", "pubkey": "dilithium5:pubkey_c_here..."},
    {"node_id": "node-d", "pubkey": "dilithium5:pubkey_d_here..."},
    {"node_id": "node-e", "pubkey": "dilithium5:pubkey_e_here..."},
]
wallet_id = run_dkg("http://localhost:8080", "my-3-5-wallet", 3, nodes)
test_mpc_signature("http://localhost:8080", wallet_id, ["node-a", "node-b", "node-c"])

11.6 节点管理操作

# 查看节点健康状态
curl -s http://localhost:9004/health | jq .

# 查看 P2P 对等节点列表
curl -s http://localhost:9003/api/v1/peers | jq .

# 查看本地存储的分片列表
curl -s http://localhost:9003/api/v1/shares | jq .

# 查看活跃签名会话
curl -s http://localhost:8080/api/v1/sessions/active | jq .

# 查看节点日志 (错误级别)
docker logs mpc-node-a --tail 100 2>&1 | grep -i error

# 导出加密分片备份
curl -X POST http://localhost:9003/api/v1/shares/export \
    -H "Content-Type: application/json" \
    -d '{"backup_key_id": "backup-key-v2", "output_dir": "/backup"}'

# 暂停节点签名 (优雅维护)
curl -X POST http://localhost:9003/api/v1/pause

# 恢复节点签名
curl -X POST http://localhost:9003/api/v1/resume

# 触发密钥轮换
curl -X POST http://localhost:8080/api/v1/keys/rotate \
    -H "Content-Type: application/json" \
    -d '{"wallet_id": "mpc-wallet-uuid-xxx"}'

# 查看 Prometheus 指标
curl -s http://localhost:9004/metrics | grep mpc_

11.7 集群健康检查

#!/bin/bash
NODES=("node-a" "node-b" "node-c" "node-d" "node-e")
ALL_HEALTHY=true
for node in "${NODES[@]}"; do
    PORT=9004  # In production, each node has a different port
    STATUS=$(curl -sf "http://localhost:$PORT/health" 2>/dev/null | jq -r '.status' || echo "unreachable")
    if [ "$STATUS" = "healthy" ]; then echo "[PASS] $node"; else echo "[FAIL] $node"; ALL_HEALTHY=false; fi
done
if [ "$ALL_HEALTHY" = true ]; then echo "Cluster HEALTHY"; else echo "Cluster UNHEALTHY"; fi

十二、安全边界与限制

12.1 当前实现限制

诚实陈述:MSG Chain 的 MPC 签名服务当前处于 partial/Stub 状态。以下限制必须在评估生产使用前仔细审查。

限制领域 具体问题 影响程度 预计解决
API 写路径 所有 POST/DELETE 端点返回 Stub 响应 不可生产 Q4 2026
agent_mpc_v1 合约 阈值签名逻辑未实现 链上无法验证 待定
阈值 Dilithium-5 仅 Shamir 分片可用,完整 GG20 未实现 安全性不足 路线图中
P2P 网络 基础框架搭建中 无法部署多节点 Q4 2026
HSM 集成 PKCS#11 接口实现中 无法硬件存储 Q1 2027
灾难恢复 恢复协议设计阶段 无开发参考级恢复 Q4 2026

12.2 风险缓解策略

短期(当前 - 2026 Q4):
  - 仅在沙箱/测试环境使用 MPC 写路径
  - 使用标准 Dilithium-5 签名(非阈值)进行生产签名
  - 通过传统 cw3 multisig 合约实现多签需求

中期(2026 Q4 - 2027 Q2):
  - MPC 写路径解除 Stub 后升级到阈值签名
  - 实施分片备份策略,开始 HSM 集成评估

长期(2027 Q2+):
  - 完整 GG20-Dilithium5 协议上线
  - TEE + HSM 双保险架构
  - 开发参考级灾难恢复能力

12.3 未来路线图

v0.1 (当前 - 原型)
  Shamir 秘密共享 + 基本存储
  Dilithium-5 标准签名(已实现)
  Agent API GET 端点(已实现)
  Agent API POST/DELETE 端点(Stub)

v0.2 (2026 Q4 - 早期可用)
  完整 GG20 框架集成
  agent_mpc_v1 合约阈值逻辑
  MPC P2P 网络 alpha 版本
  测试网可用

v0.3 (2027 Q1 - Beta)
  阈值 Dilithium-5 协议完整
  HSM PKCS#11 集成
  灾难恢复协议
  监控仪表盘

v1.0 (2027 Q2+ - GA)
  TEE 集成(SGX/TDX)
  完整审计和合规
  地理冗余部署
  性能优化(~500 TPS 签名)

12.4 总结

优势:
  - 后量子安全(Dilithium-5)
  - 单签名大小固定
  - 与 Cosmos SDK 交易格式兼容
  - Agent API 架构支持无缝升级
  - 支持分层安全(MPC + 多签 + 社交恢复)

当前限制:
  - 写路径端点为 Stub,不可用于生产
  - agent_mpc_v1 阈值逻辑未实现
  - 完整 GG20 框架未集成
  - HSM/TEE 部署尚在路线图中

建议:
  - 生产部署使用标准 Dilithium-5 签名 + cw3 多签
  - 沙箱环境中探索 MPC 阈值签名能力
  - 关注路线图时间节点,规划升级迁移

附录一:术语表

术语 英文 定义
MPC Multi-Party Computation 多方计算,多个参与方协同计算但不泄露各自私密输入
DKG Distributed Key Generation 分布式密钥生成,无可信中心的密钥对生成协议
GG18 Garay-Gennaro 2018 首个实用两轮 ECDSA 阈值签名协议
GG20 Garay-Gennaro 2020 改进版阈值签名协议
CMP Canetti-Makriyannis-Peli 支持 EdDSA 的阈值签名协议
MtA Multiplication-to-Addition 乘法转加法子协议
ZKP Zero-Knowledge Proof 零知识证明
Paillier Paillier Cryptosystem 加法同态加密方案
Threshold Signature 阈值签名 t-of-n 签名方案
Shamir SSS Shamir's Secret Sharing 基于拉格朗日插值的秘密共享方案
HSM Hardware Security Module 硬件安全模块
TEE Trusted Execution Environment 可信执行环境
Dilithium-5 ML-DSA NIST FIPS 204 后量子签名方案
Enclave - TEE 中的安全执行区域
PKCS#11 Cryptoki 密码学令牌接口标准
AEAD Authenticated Encryption with AD 认证加密
libp2p - 模块化 P2P 网络协议栈
Refreshing 分片刷新 保持公钥不变但更新分片
Stub 桩 端点已定义但返回模拟响应

附录二:参考协议与标准

参考协议:
  GG18: "Fast Multiparty Threshold ECDSA with Fast Trustless Setup" (2018)
  GG20: "One Round Threshold ECDSA with Identifiable Abort" (2020)
  CMP: "Threshold Signatures in the Asynchronous Setting" (Canetti et al.)
  FIPS 204: "Module-Lattice-Based Digital Signature Standard" (NIST, 2024)

参考标准:
  NIST SP 800-88: "Guidelines for Media Sanitization"
  PKCS#11: "Cryptographic Token Interface Standard"
  FIPS 140-2/3: "Security Requirements for Cryptographic Modules"
  RFC 6979: "Deterministic Usage of DSA and ECDSA"

附录三:配置参考

# orchestrator.yaml - MPC 协调器配置
orchestrator:
  chain:
    chain_id: "msg-chain-1"
    rpc_endpoint: "https://rpc.msgchain.org"
    rest_endpoint: "https://api.msgchain.org"
    bech32_prefix: "msg"
  api:
    listen: ":8080"
    rate_limit: 100
    rate_burst: 200
  session:
    default_timeout: 300
    max_sessions: 100
  mpc:
    supported_protocols: ["GG20-Dilithium5"]
    default_threshold: 3
    min_threshold: 2
    max_parties: 50
  monitoring:
    metrics_enabled: true
    metrics_port: 9090
    health_check_interval: 10

MSG Chain Whitepaper | https://msgchain.org/whitepaper | 代码库实际状态,不代表生产可用