MPCium:高弹性、安全的 MPC 引擎

Mpcium 是一个用 Go 编写的高性能框架,使用多方计算(MPC)实现安全的分布式门限签名方案。与将私钥存放在单一位置的传统密码系统不同,Mpcium 将私钥分片分布在多个独立节点上,只有达到门限数量的节点协同配合,才能执行交易签名等密码学操作。

这一架构带来:

  • 更强的安全性: 不存在单点泄露风险
  • 分布式信任: 无需信任任何单一实体
  • 容错能力: 即使部分节点故障,系统仍能正常运行
  • 隐私保护: 没有任何单个节点持有完整密钥

在密码学核心层面,Mpcium 集成了 tss-lib,这是由 Binance 开发的生产级门限签名方案库。它支持:

  • ECDSA (secp256k1):Bitcoin、Ethereum、BNB、Polygon 以及 EVM 兼容的 L2 链

  • EdDSA (Ed25519):用于 Solana、Polkadot、Cardano 及其他现代区块链

架构文档

MPCium 集群架构
Click to open the full-size diagram

👉 详细架构文档

门限与节点

Mpcium 使用 t-of-n 门限方案来安全地生成私钥并使用私钥签名。

  • n = MPC 节点(密钥分片)总数
  • t = 签名所需的最少节点数

n 个节点中只需 t 个参与即可,完整私钥永远不会被重建。

为了在节点被攻破时仍能保持安全,Mpcium 强制要求:

t ≥ ⌊n / 2⌋ + 1

示例:2-of-3 门限

  • ✅ node0 + node1 → 签名成功
  • ✅ node1 + node2 → 签名成功
  • ❌ 仅 node0 → 分片数量不足

这可以确保:

  • 不存在单点泄露风险
  • 部分节点离线时仍具备容错能力
  • 可通过调整 t 和 n 配置安全级别

架构

概述

每个 Mpcium 节点:

  • 在本地 AES-256 加密存储(基于 Badger KV)中保存一个密钥分片
  • 使用 tss-lib 参与门限签名
  • 通过基于 NATS 的弹性消息层进行通信
  • 向 Consul 注册自身,用于服务发现和健康检查
  • 使用基于 Ed25519 的双向认证校验收到的消息

消息流与签名校验

  1. 签名请求作为经过认证的事件,通过 NATS 广播到 MPC 集群。每个节点在处理请求前都会校验发送方的 Ed25519 签名。
  2. NATS 将请求广播给各 MPC 节点。
  3. 每个参与节点都会校验:
    • 发送方的签名(Ed25519)
    • 消息的真实性(不可重放、会话唯一)
  4. 如果节点健康且属于法定数量(t)之内,它会:
    • 使用其分片计算部分签名
    • 通过 NATS 发布计算结果
  5. 收到 t 个部分签名后,将其聚合为完整签名。

特性

  • 无单点泄露风险:密钥永远不会被完整组装
  • 拜占庭容错:只需 n 个节点中的 t 个即可继续执行
  • 可扩展、可插拔:可轻松扩展集群或集成其他工具
  • 安全的节点认证:所有节点间消息都使用 Ed25519 签名和校验

安装与运行

使用预览

启动节点

$ mpcium start -n node0
$ mpcium start -n node1
$ mpcium start -n node2

客户端实现

客户端


import (
    "github.com/fystack/mpcium/client"
    "github.com/nats-io/nats.go"
)


func main () {
	natsConn, err := nats.Connect(natsURL)
	if err != nil {
		logger.Fatal("Failed to connect to NATS", err)
	}
	defer natsConn.Close()
	mpcClient := client.NewMPCClient(client.Options{
		NatsConn: natsConn,
		KeyPath:  "./event_initiator.key",
	})
	err = mpcClient.OnWalletCreationResult(func(event mpc.KeygenSuccessEvent) {
		logger.Info("Received wallet creation result", "event", event)
	})
	if err != nil {
		logger.Fatal("Failed to subscribe to wallet-creation results", err)
	}

	walletID := uuid.New().String()
	if err := mpcClient.CreateWallet(walletID); err != nil {
		logger.Fatal("CreateWallet failed", err)
	}
	logger.Info("CreateWallet sent, awaiting result...", "walletID", walletID)
}