# MPC 分布式签名系统 - TSS 协议详解

## 1. 概述

本系统使用 **门限签名方案 (Threshold Signature Scheme, TSS)** 实现分布式密钥管理和签名。基于 [bnb-chain/tss-lib](https://github.com/bnb-chain/tss-lib) 库，采用 GG20 协议。

### 1.1 核心概念

| 术语 | 定义 |
|------|------|
| t-of-n | t+1 个参与方中的任意组合可以签名，需要 n 个参与方共同生成密钥 |
| DKG | 分布式密钥生成 (Distributed Key Generation) |
| TSS | 门限签名方案 (Threshold Signature Scheme) |
| Party | MPC 协议中的参与方 |
| Share | 密钥分片，每个 Party 持有一份 |

### 1.2 安全属性

- **无单点故障**: 私钥从未以完整形式存在
- **门限安全**: 需要 t+1 个分片才能签名
- **抗合谋**: t 个恶意方无法伪造签名
- **可审计**: 每次签名可追踪参与方

## 2. 阈值参数说明

### 2.1 tss-lib 参数约定

在 tss-lib 中，`threshold` 参数定义如下:
- `threshold = t` 表示需要 **t+1** 个签名者
- 例如: `threshold=1` 需要 2 个签名者

### 2.2 常见阈值方案

| 方案 | tss-lib threshold | 总参与方 (n) | 签名者数 (t+1) | 应用场景 |
|------|-------------------|-------------|---------------|---------|
| 2-of-3 | 1 | 3 | 2 | 个人钱包 + 设备 + 恢复 |
| 3-of-5 | 2 | 5 | 3 | 企业多签 |
| 4-of-7 | 3 | 7 | 4 | 机构托管 |
| 5-of-9 | 4 | 9 | 5 | 大型组织 |

### 2.3 阈值选择建议

```
安全性 vs 可用性权衡:

高安全性 ◄────────────────────────► 高可用性
  5-of-9     4-of-7     3-of-5     2-of-3

建议:
- 个人用户: 2-of-3 (设备 + 服务器 + 恢复)
- 小型企业: 3-of-5 (3 管理员 + 1 服务器 + 1 恢复)
- 大型企业: 4-of-7 或更高
```

## 3. 密钥生成协议 (Keygen)

### 3.1 协议流程

```
Round 1: 承诺分发
┌────────────┐     ┌────────────┐     ┌────────────┐
│  Party 0   │     │  Party 1   │     │  Party 2   │
└─────┬──────┘     └─────┬──────┘     └─────┬──────┘
      │                  │                  │
      │  生成随机多项式   │                  │
      │  计算承诺 Ci     │                  │
      │                  │                  │
      │◄─────────────────┼──────────────────┤ 广播承诺
      ├──────────────────►◄─────────────────┤
      │                  │                  │

Round 2: 秘密分享
      │                  │                  │
      │  计算 Shamir 分片│                  │
      │  发送 share_ij   │                  │
      │                  │                  │
      │──────────────────►                  │ 点对点
      │                  ◄──────────────────│
      ◄──────────────────│                  │
      │                  │──────────────────►
      │                  │                  │

Round 3: 验证与聚合
      │                  │                  │
      │  验证收到的分片   │                  │
      │  计算最终密钥分片 │                  │
      │  计算公钥 PK     │                  │
      │                  │                  │
      ▼                  ▼                  ▼
   Share_0            Share_1           Share_2
      │                  │                  │
      └──────────────────┼──────────────────┘
                         │
                    公钥 PK (相同)
```

### 3.2 代码实现

```go
// pkg/tss/keygen.go
func RunLocalKeygen(threshold, totalParties int) ([]*LocalKeygenResult, error) {
    // 验证参数
    if threshold < 1 || threshold > totalParties {
        return nil, ErrInvalidThreshold
    }

    // 创建 Party IDs
    partyIDs := make([]*tss.PartyID, totalParties)
    for i := 0; i < totalParties; i++ {
        partyIDs[i] = tss.NewPartyID(
            fmt.Sprintf("party-%d", i),
            fmt.Sprintf("party-%d", i),
            big.NewInt(int64(i+1)),
        )
    }
    sortedPartyIDs := tss.SortPartyIDs(partyIDs)
    peerCtx := tss.NewPeerContext(sortedPartyIDs)

    // 创建各方的通道和 Party 实例
    outChs := make([]chan tss.Message, totalParties)
    endChs := make([]chan *keygen.LocalPartySaveData, totalParties)
    parties := make([]tss.Party, totalParties)

    for i := 0; i < totalParties; i++ {
        outChs[i] = make(chan tss.Message, totalParties*10)
        endChs[i] = make(chan *keygen.LocalPartySaveData, 1)
        params := tss.NewParameters(
            tss.S256(),      // secp256k1 曲线
            peerCtx,
            sortedPartyIDs[i],
            totalParties,
            threshold,
        )
        parties[i] = keygen.NewLocalParty(params, outChs[i], endChs[i])
    }

    // 启动所有 Party
    for i := 0; i < totalParties; i++ {
        go parties[i].Start()
    }

    // 消息路由
    go routeMessages(parties, outChs, sortedPartyIDs)

    // 收集结果
    results := make([]*LocalKeygenResult, totalParties)
    for i := 0; i < totalParties; i++ {
        saveData := <-endChs[i]
        results[i] = &LocalKeygenResult{
            SaveData:   saveData,
            PublicKey:  saveData.ECDSAPub.ToECDSAPubKey(),
            PartyIndex: i,
        }
    }

    return results, nil
}
```

### 3.3 SaveData 结构

每个 Party 保存的数据:

```go
type LocalPartySaveData struct {
    // 本方的私钥分片 (xi)
    Xi *big.Int

    // 所有方的公钥分片 (Xi = xi * G)
    BigXj []*crypto.ECPoint

    // 组公钥
    ECDSAPub *crypto.ECPoint

    // Paillier 密钥对 (用于同态加密)
    PaillierSK *paillier.PrivateKey
    PaillierPKs []*paillier.PublicKey

    // 其他预计算数据...
}
```

## 4. 签名协议 (Signing)

### 4.1 协议流程

```
签名协议 (GG20 - 6 轮):

Round 1: 承诺生成
┌────────────┐     ┌────────────┐
│  Party 0   │     │  Party 1   │
└─────┬──────┘     └─────┬──────┘
      │                  │
      │  生成随机 ki     │
      │  计算 γi = ki*G  │
      │  广播 C(γi)      │
      │                  │
      │◄────────────────►│
      │                  │

Round 2: Paillier 加密
      │                  │
      │  加密 ki         │
      │  MtA 协议开始    │
      │                  │
      │◄────────────────►│
      │                  │

Round 3: MtA 响应
      │                  │
      │  计算乘法三元组   │
      │                  │
      │◄────────────────►│
      │                  │

Round 4: Delta 分享
      │                  │
      │  计算 δi         │
      │  广播           │
      │                  │
      │◄────────────────►│
      │                  │

Round 5: 重构与验证
      │                  │
      │  重构 δ = Σδi    │
      │  计算 R = δ^-1*Γ │
      │  计算 r = Rx     │
      │                  │
      │◄────────────────►│
      │                  │

Round 6: 签名聚合
      │                  │
      │  计算 si = ...   │
      │  广播 si         │
      │                  │
      │◄────────────────►│
      │                  │
      ▼                  ▼
    最终签名 (r, s)
```

### 4.2 代码实现

```go
// pkg/tss/signing.go
func RunLocalSigning(
    threshold int,
    keygenResults []*LocalKeygenResult,
    messageHash []byte,
) (*LocalSigningResult, error) {
    signerCount := len(keygenResults)
    if signerCount < threshold+1 {
        return nil, ErrInvalidSignerCount
    }

    // 创建 Party IDs (必须使用原始索引)
    partyIDs := make([]*tss.PartyID, signerCount)
    for i, result := range keygenResults {
        idx := result.PartyIndex
        partyIDs[i] = tss.NewPartyID(
            fmt.Sprintf("party-%d", idx),
            fmt.Sprintf("party-%d", idx),
            big.NewInt(int64(idx+1)),
        )
    }
    sortedPartyIDs := tss.SortPartyIDs(partyIDs)
    peerCtx := tss.NewPeerContext(sortedPartyIDs)

    // 转换消息哈希
    msgHash := new(big.Int).SetBytes(messageHash)

    // 创建签名方
    outChs := make([]chan tss.Message, signerCount)
    endChs := make([]chan *common.SignatureData, signerCount)
    parties := make([]tss.Party, signerCount)

    for i := 0; i < signerCount; i++ {
        outChs[i] = make(chan tss.Message, signerCount*10)
        endChs[i] = make(chan *common.SignatureData, 1)
        params := tss.NewParameters(tss.S256(), peerCtx, sortedPartyIDs[i], signerCount, threshold)
        parties[i] = signing.NewLocalParty(msgHash, params, *keygenResults[i].SaveData, outChs[i], endChs[i])
    }

    // 启动并路由消息
    for i := 0; i < signerCount; i++ {
        go parties[i].Start()
    }
    go routeSignMessages(parties, outChs, sortedPartyIDs)

    // 收集签名结果
    signData := <-endChs[0]
    return &LocalSigningResult{
        R:          new(big.Int).SetBytes(signData.R),
        S:          new(big.Int).SetBytes(signData.S),
        RecoveryID: int(signData.SignatureRecovery[0]),
    }, nil
}
```

### 4.3 签名验证

```go
// 验证签名
import "crypto/ecdsa"

func VerifySignature(publicKey *ecdsa.PublicKey, messageHash []byte, r, s *big.Int) bool {
    return ecdsa.Verify(publicKey, messageHash, r, s)
}

// 示例
message := []byte("Hello MPC!")
hash := sha256.Sum256(message)
valid := ecdsa.Verify(publicKey, hash[:], signResult.R, signResult.S)
```

## 5. 消息路由

### 5.1 消息类型

| 类型 | 说明 | 方向 |
|------|------|------|
| Broadcast | 发送给所有其他方 | 1 → n-1 |
| P2P | 点对点消息 | 1 → 1 |

### 5.2 消息结构

```go
type MPCMessage struct {
    SessionID   string   // 会话 ID
    FromParty   string   // 发送方
    ToParties   []string // 接收方 (空=广播)
    Round       int      // 协议轮次
    Payload     []byte   // 加密的协议消息
    IsBroadcast bool     // 是否广播
    Timestamp   int64
}
```

### 5.3 消息路由实现

```go
func routeMessages(
    parties []tss.Party,
    outChs []chan tss.Message,
    sortedPartyIDs []*tss.PartyID,
) {
    signerCount := len(parties)

    for idx := 0; idx < signerCount; idx++ {
        go func(i int) {
            for msg := range outChs[i] {
                if msg.IsBroadcast() {
                    // 广播给所有其他方
                    for j := 0; j < signerCount; j++ {
                        if j != i {
                            updateParty(parties[j], msg)
                        }
                    }
                } else {
                    // 点对点发送
                    for _, dest := range msg.GetTo() {
                        for j := 0; j < signerCount; j++ {
                            if sortedPartyIDs[j].Id == dest.Id {
                                updateParty(parties[j], msg)
                                break
                            }
                        }
                    }
                }
            }
        }(idx)
    }
}
```

## 6. 子集签名 (Subset Signing)

### 6.1 原理

在 t-of-n 方案中，任意 t+1 个 Party 的子集都可以生成有效签名。关键是使用原始的 Party 索引。

### 6.2 示例: 2-of-3 的所有组合

```go
// 3 方生成密钥
keygenResults, _ := tss.RunLocalKeygen(1, 3)  // threshold=1, n=3

// 任意 2 方可签名:
// 组合 1: Party 0 + Party 1
signers1 := []*tss.LocalKeygenResult{keygenResults[0], keygenResults[1]}
sig1, _ := tss.RunLocalSigning(1, signers1, messageHash)

// 组合 2: Party 0 + Party 2
signers2 := []*tss.LocalKeygenResult{keygenResults[0], keygenResults[2]}
sig2, _ := tss.RunLocalSigning(1, signers2, messageHash)

// 组合 3: Party 1 + Party 2
signers3 := []*tss.LocalKeygenResult{keygenResults[1], keygenResults[2]}
sig3, _ := tss.RunLocalSigning(1, signers3, messageHash)

// 所有签名都对同一公钥有效!
ecdsa.Verify(publicKey, messageHash, sig1.R, sig1.S) // true
ecdsa.Verify(publicKey, messageHash, sig2.R, sig2.S) // true
ecdsa.Verify(publicKey, messageHash, sig3.R, sig3.S) // true
```

### 6.3 注意事项

1. **Party 索引必须一致**: 签名时使用 keygen 时的原始索引
2. **不能混用不同 keygen 的分片**: 每个账户对应唯一的一组分片
3. **阈值验证**: 签名者数量 >= threshold + 1

## 7. 性能考虑

### 7.1 测试基准

| 操作 | 2-of-3 | 3-of-5 | 4-of-7 |
|------|--------|--------|--------|
| Keygen | ~93s | ~198s | ~221s |
| Signing | ~80s | ~120s | ~150s |

### 7.2 优化建议

1. **预计算**: 部分 Keygen 数据可预计算
2. **并行执行**: 多个签名请求可并行处理
3. **消息压缩**: 大消息进行压缩传输
4. **连接池**: 复用 Party 间的连接

## 8. 故障恢复

### 8.1 Keygen 失败

如果 Keygen 过程中某个 Party 离线:
- 协议超时失败
- 需要全部重新开始
- 建议设置合理的超时时间

### 8.2 Signing 失败

如果签名过程中 Party 离线:
- 当前签名失败
- 可以选择其他 Party 子集重试
- 密钥分片不受影响

### 8.3 密钥分片丢失

如果某个 Party 的分片丢失:
- 如果丢失数量 < n - t: 仍可签名
- 如果丢失数量 >= n - t: 无法签名，需要重新 Keygen
- 建议: 加密备份分片到安全存储
