Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

多签(Multisig)

本节讲解 Sui 上的多重签名(Multisig)机制。多签允许多个密钥共同控制一个地址,通过设置权重和阈值来实现灵活的资产管理和权限控制。

多签概述

多签地址由多个公钥和一个阈值(threshold)定义。只有当签名的权重之和达到或超过阈值时,交易才会被执行。

┌────────────────────────────────────────────┐
│            多签 2-of-3 示例                  │
├────────────────────────────────────────────┤
│                                              │
│  密钥 A(权重 1)  ──┐                        │
│  密钥 B(权重 1)  ──┼── 阈值 = 2 ──► 执行    │
│  密钥 C(权重 1)  ──┘                        │
│                                              │
│  任意 2 个密钥签名即可执行交易                  │
│                                              │
└────────────────────────────────────────────┘

创建多签地址

使用 CLI 创建

# 生成三个密钥对
sui keytool generate ed25519
sui keytool generate ed25519
sui keytool generate ed25519

# 获取公钥
sui keytool list

# 创建多签地址(阈值=2,三个公钥各权重1)
sui keytool multi-sig-address \
  --pks <PK_A> <PK_B> <PK_C> \
  --weights 1 1 1 \
  --threshold 2

使用 TypeScript SDK

import { MultiSigPublicKey } from '@mysten/sui/multisig';
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';

// 创建三个密钥对
const keypairA = new Ed25519Keypair();
const keypairB = new Ed25519Keypair();
const keypairC = new Ed25519Keypair();

// 创建多签公钥
const multiSigPublicKey = MultiSigPublicKey.fromPublicKeys({
  threshold: 2,
  publicKeys: [
    { publicKey: keypairA.getPublicKey(), weight: 1 },
    { publicKey: keypairB.getPublicKey(), weight: 1 },
    { publicKey: keypairC.getPublicKey(), weight: 1 },
  ],
});

// 获取多签地址
const multiSigAddress = multiSigPublicKey.toSuiAddress();
console.log('MultiSig Address:', multiSigAddress);

权重设置策略

等权模式

// 3-of-5 等权多签
const multiSig = MultiSigPublicKey.fromPublicKeys({
  threshold: 3,
  publicKeys: [
    { publicKey: pk1, weight: 1 },
    { publicKey: pk2, weight: 1 },
    { publicKey: pk3, weight: 1 },
    { publicKey: pk4, weight: 1 },
    { publicKey: pk5, weight: 1 },
  ],
});

加权模式

// 加权多签:CEO 有更高权重
const multiSig = MultiSigPublicKey.fromPublicKeys({
  threshold: 3,
  publicKeys: [
    { publicKey: ceoPk, weight: 2 },    // CEO: 权重 2
    { publicKey: ctoPk, weight: 1 },    // CTO: 权重 1
    { publicKey: cfoPk, weight: 1 },    // CFO: 权重 1
    { publicKey: cooFk, weight: 1 },    // COO: 权重 1
  ],
});
// CEO + 任意一人 = 3 ≥ 阈值
// 或 CTO + CFO + COO = 3 ≥ 阈值

交易签名与执行

构造交易

import { Transaction } from '@mysten/sui/transactions';
import { SuiGrpcClient } from '@mysten/sui/grpc';

const client = new SuiGrpcClient({
  network: 'testnet',
  baseUrl: 'https://fullnode.testnet.sui.io:443',
});

const tx = new Transaction();
tx.setSender(multiSigAddress);
tx.setGasOwner(multiSigAddress);

// 添加交易命令
tx.transferObjects(
  [tx.object('0x...')],
  tx.pure.address('0x...')
);

// 构建交易字节
const txBytes = await tx.build({ client });

收集签名

// 签名者 A 签名
const sigA = await keypairA.signTransaction(txBytes);

// 签名者 B 签名
const sigB = await keypairB.signTransaction(txBytes);

组合并执行

// 组合多签签名
const multiSigSignature = multiSigPublicKey.combinePartialSignatures([
  sigA.signature,
  sigB.signature,
]);

// 执行交易
const result = await client.core.executeTransaction({
  transaction: txBytes,
  signatures: [multiSigSignature],
  include: { effects: true },
});

if (result.$kind === 'FailedTransaction') {
  throw new Error(result.FailedTransaction.status.error?.message ?? 'Transaction failed');
}
await client.waitForTransaction({ digest: result.Transaction.digest });

使用 CLI 签名

# 各方分别签名
sui keytool sign \
  --address <SIGNER_A_ADDRESS> \
  --data <TX_BYTES_BASE64>

sui keytool sign \
  --address <SIGNER_B_ADDRESS> \
  --data <TX_BYTES_BASE64>

# 组合多签
sui keytool multi-sig-combine-partial-sig \
  --pks <PK_A> <PK_B> <PK_C> \
  --weights 1 1 1 \
  --threshold 2 \
  --sigs <SIG_A> <SIG_B>

# 执行交易
sui client execute-signed-tx \
  --tx-bytes <TX_BYTES_BASE64> \
  --signatures <MULTI_SIG>

多签管理 UpgradeCap

多签是管理包升级权限的理想方式:

// 将 UpgradeCap 转移到多签地址
const tx = new Transaction();
tx.transferObjects(
  [tx.object(UPGRADE_CAP_ID)],
  tx.pure.address(multiSigAddress),
);

// 后续升级需要多签授权
async function upgradeWithMultisig() {
  const upgradeTx = new Transaction();
  upgradeTx.setSender(multiSigAddress);
  // ... 升级逻辑

  const txBytes = await upgradeTx.build({ client });

  // 收集足够的签名
  const sig1 = await keypairA.signTransaction(txBytes);
  const sig2 = await keypairB.signTransaction(txBytes);

  const multiSig = multiSigPublicKey.combinePartialSignatures([
    sig1.signature,
    sig2.signature,
  ]);

  const result = await client.core.executeTransaction({
    transaction: txBytes,
    signatures: [multiSig],
  });
  if (result.$kind === 'FailedTransaction') {
    throw new Error(result.FailedTransaction.status.error?.message ?? 'Transaction failed');
  }
  await client.waitForTransaction({ digest: result.Transaction.digest });
  return result;
}

应用场景

场景推荐配置说明
团队金库3-of-5 等权任意三人授权资金移动
包升级2-of-3 等权防止单点失败
DAO 治理加权投票按持股比例分配权重
冷存储2-of-3 不同设备一个密钥离线存储
紧急操作CEO 高权重CEO 可快速响应

小结

  • 多签通过多个密钥的组合签名来控制地址,提高安全性
  • 权重和阈值机制支持灵活的签名策略
  • 支持 Ed25519、Secp256k1 和 Secp256r1 多种密钥类型混合
  • 多签特别适合管理 UpgradeCap、金库和关键权限
  • 使用 CLI 或 TypeScript SDK 都可以创建和管理多签
  • 交易签名可以异步收集,适合分布式团队