密码学之三 —— MPC(TSS)、多签、时间锁
一、MPC
MPC 全称 Secure Multi-Party Computation(多方安全计算),让多个参与方在「不泄露各自私密数据」的前提下,共同完成计算并得到正确结果,全程数据不共享、不落地、不暴露原始信息。在区块链 / 智能合约开发场景中,MPC 最核心的落地应用是MPC钱包(多方签名),解决了「私钥单点泄露」的致命问题。
-
官方定义
MPC 是一种密码学协议,允许多个互不信任的参与方,各自持有私密数据,在不向任何第三方(包括其他参与方)泄露自身数据的情况下,协同完成指定计算,最终仅输出计算结果,全程原始数据始终处于加密 / 分片状态。 -
核心本质:「数据可用不可见,计算协同不共享」
- 数据:每个参与方的私密信息(如区块链私钥、交易金额、个人数据)绝不暴露给任何人;
- 计算:多个参与方通过密码学协议协作,完成计算(如签名、求和、验证);
- 结果:仅输出最终计算结果(如签名后的交易、总和),无任何原始数据泄露。
-
MPC 核心工作原理
MPC 的核心底层是秘密分享(Secret Sharing) + 同态加密 / 混淆电路,以区块链最常用的MPC 签名(如 ECDSA/Ed25519 签名)为例,原理极简拆解:- 核心前提:私钥分片(Sharding)
MPC 不会生成「完整私钥」,而是将私钥拆分成 N 个加密分片(Shard),分发给 N 个参与方(如你的手机、电脑、平板、信任的朋友),任何单个分片都无法还原完整私钥,也无法单独签名。 - 签名流程(多方协同,无完整私钥)
以「3 个参与方,2 个即可签名(2-of-3 MPC)」为例: - 发起签名:你发起一笔区块链交易(如合约部署、转账),生成待签名的哈希值;
- 分片协同计算:
- 参与方 1、2(任意 2 个)各自用自己的私钥分片,对哈希值进行局部签名计算;
- 计算过程中,仅交换「加密的中间结果」,不交换私钥分片,也不暴露原始数据;
- 汇总签名:两个参与方的局部计算结果汇总,生成完整的交易签名(RSV);
- 上链验证:将签名后的交易发送到链上,链上验证签名有效(和普通私钥签名结果一致)。
- 核心前提:私钥分片(Sharding)
-
主流 MPC 签名协议(区块链专用)
区块链 MPC 签名有成熟的标准化协议,目前主流的是:- GG18/GG20:最早的 ECDSA-MPC 协议,支持 2-of-N 多签,兼容以太坊 / 比特币;
- FROST:新一代轻量 MPC 协议,效率更高、开销更小,支持 Ed25519/ECDSA,是当前个人 / 企业首选;
- CMP:针对企业级的 MPC 协议,支持更多参与方,安全性更强。
-
个人 / 智能合约开发者适用的 MPC 方案
-
个人 MPC 钱包(直接使用,无需开发)
适合个人管理资产、合约签名,开箱即用:
Coinbase Wallet MPC:Coinbase 推出的 MPC 钱包,支持 EVM/Solana,2-of-3 多签(手机 + 邮箱 + 信任联系人),社交恢复,无硬件,适合新手 / 开发者;
Argent X:以太坊生态主流 MPC 钱包,2-of-2 多签(手机 + guardian),支持合约部署、DApp 交互,Gas 优化;
Safe {Wallet}(原 Gnosis Safe)MPC 版:多签钱包的 MPC 升级,支持 1-of-1 到 N-of-N,适合开发者 / 团队管理合约部署私钥;
Web3Auth MPC:支持社交登录(谷歌 / 邮箱)+ MPC 分片,私钥分片在用户设备 + Web3Auth 节点,易用性极高,适合 DApp 用户。 -
开发者 MPC SDK(集成到项目 / 脚本)
适合智能合约开发者,将 MPC 签名集成到部署脚本、DApp 中:
Coinbase MPC SDK:开源 SDK,支持 ECDSA/Ed25519,可自定义分片数量,集成到合约部署脚本;
Fireblocks MPC SDK:企业级 MPC SDK,支持多链,适合批量合约部署、资产操作;
FROST SDK:开源 FROST 协议实现,轻量高效,适合个人开发者自定义 MPC 方案;
Web3Auth Core Kit:开源 MPC SDK,支持社交登录 + 分片管理,适合 DApp 前端集成。 -
开源 MPC 库(自主搭建,极客 / 开发者)
适合想深入研究、自主搭建 MPC 方案的开发者:
mpc-crypto:开源 MPC 密码学库,支持 ECDSA/Ed25519 签名;
frost-ed25519:Ed25519 的 FROST 协议实现,适配区块链签名;
gg20-ecdsa:GG20 协议的 ECDSA 实现,兼容以太坊 / 比特币。
-
总结:硬件钱包是「离线保险柜」,MPC 钱包是「分布式密码箱」,KMS 是「云保险柜」,三者各有侧重。
二、TSS
是什么?
门限签名(TSS)是一种分布式数字签名方案,核心是将单个私钥拆分成多个密钥分片,由多个参与方分别持有;只有当至少达到指定门限数量(t-of-n) 的参与方协同合作时,才能生成有效签名,单个 / 不足门限数量的参与方无法单独签名,且全程不会出现完整私钥。
TSS 是MPC(多方安全计算)在数字签名领域的核心落地应用(属于 MPC 子集),也是当前区块链、交易所、钱包解决「私钥单点泄露」的主流技术,和传统「链上多签(Multisig)」是完全不同的技术路径。
-
TSS 遵循t-of-n门限规则:
- n:总参与方数量(如 3 个设备 / 服务器);
- t:签名所需的最小参与方数量(如 2 个);
- 规则:≥t 个参与方协同 → 生成有效签名;<t 个参与方 → 无法签名;
- 核心:完整私钥永不生成、永不落地,仅以「分片」形式存在,签名通过多方密码学协同完成。
-
关键特性(区块链 / 交易所核心价值)
- 无完整私钥:私钥始终分片存储,任何参与方都无法获取完整私钥,杜绝单点泄露;
- 链下签名:签名过程在链下完成,链上仅验证签名结果,无额外 Gas 费;
- 链上兼容:生成的签名和「普通单私钥签名」完全一致,兼容所有区块链(无需修改合约 / 链上逻辑);
- 门限灵活:可动态调整 t/n(如 2-of-3 → 3-of-5),无需链上操作;
- 隐私保护:参与方身份、分片信息不暴露,比链上多签更隐私;
- 容错性:部分参与方离线 / 失效,只要达到门限数量,仍可正常签名。
-
TSS 核心工作原理
以区块链最常用的 ECDSA-secp256k1 TSS(适配以太坊 / 比特币),2-of-3为例-
阶段 1:分布式密钥生成(DKG)
- TSS 不会生成「完整私钥」,而是通过分布式密钥生成(DKG) 协议,由 3 个参与方协同生成私钥分片:
- 3 个参与方各自生成随机数,通过 MPC 协议交换加密的中间值;
- 协同计算出私钥分片(Share1、Share2、Share3),分别由 3 个参与方持有;
- 同时生成唯一公钥(和普通私钥的公钥格式一致,可公开);
- 关键:完整私钥仅存在于数学逻辑中,从未被任何一方生成或存储。
-
阶段 2:分布式签名(链下协同)
- 发起交易 / 合约部署时,需 2 个参与方协同签名(满足 2-of-3 门限):
- 参与方 A、B 各自用自己的私钥分片,对交易哈希值进行局部签名计算;
- 双方交换「加密的局部签名结果」(不暴露私钥分片);
- 协同汇总局部结果,生成完整的 ECDSA 签名(RSV);
- 关键:签名过程中,完整私钥仍未出现,仅输出签名结果。
-
阶段 3:链上验证
- 将签名后的交易发送到区块链,链上验证逻辑和普通签名完全一致:
- 用公钥验证签名(RSV)是否有效;
- 验证通过 → 交易上链;验证失败 → 拒绝交易;
- 关键:区块链无法区分是 TSS 签名还是普通单私钥签名,完全兼容。
-
-
TSS分类
1)按签名算法分类(区块链主流)
| 算法类型 | 适配场景 | 主流协议 | 代表应用 |
|---|---|---|---|
| ECDSA TSS | 以太坊 / 比特币 / BSC 等 EVM 链 | GG18/GG20、CMP、FROST-ECDSA | 交易所热钱包、硬件钱包、MPC 钱包 |
| EdDSA TSS(Ed25519) | Solana/Aptos 等非 EVM 链 | FROST-Ed25519 | 新公链钱包、分布式节点签名 |
| Schnorr TSS | 比特币 Taproot、新公链 | FROST、MuSig2 | 比特币多签、轻量级签名场景 |
2)按门限类型分类
固定门限 TSS:t/n 提前设定(如 2-of-3),签名前无法修改,最常用;
动态门限 TSS:可实时调整 t/n(如 2-of-3 → 3-of-5),适合企业级灵活权限控制;
分层门限 TSS:多层门限嵌套(如核心层 3-of-5 + 操作层 2-of-3),交易所大额资产专用。
-
主流TSS协议
-
GG18/GG20(最成熟的 ECDSA TSS)
开发者:Goldfeder 等密码学家,2018/2020 年发布;
特点:抗恶意节点、兼容 ECDSA,支持 2-of-n 门限;
应用:Coinbase、Kraken 等交易所,早期 MPC 钱包;
局限:签名需 2-3 轮通信,效率略低。 -
FROST(新一代轻量 TSS)
全称:Flexible Round-Optimized Schnorr Threshold Signatures;
特点:单轮签名、效率极高、支持 Ed25519/ECDSA,容错性强;
应用:新公链、去中心化交易所、个人 MPC 钱包(如 Argent X);
优势:比 GG20 快 50%+,适合移动端 / 轻量级设备。 -
MPC-CMP(Fireblocks 自研 TSS)
全称:MPC-Coordinated Multiparty Protocol;
特点:单轮签名、速度提升 800%,支持冷 / 热钱包混合;
应用:Bitfinex、中小交易所、金融机构;
优势:开源免费,适配高频交易场景。 -
MuSig2(比特币专用 Schnorr TSS)
特点:适配比特币 Taproot,签名结果和单签一致,隐私性极强;
应用:比特币多签钱包、冷存储方案。
-
-
TSS 核心应用场景
-
交易所资产安全(核心场景)
热钱包:采用 2-of-3/3-of-5 TSS,私钥分片在多服务器,防止单点泄露;
冷钱包:离线设备 + 在线服务器 TSS 混合,大额资产离线保护,小额资产 TSS 快速流转;
代表:Binance、Coinbase、Fireblocks 客户均采用 TSS 作为核心安全方案。 -
个人 / 开发者 MPC 钱包
2-of-3 门限:私钥分片在手机、电脑、云备份,任意 2 个设备即可签名;
无助记词:通过分片恢复,替代传统助记词,避免备份泄露;
代表:Argent X、Coinbase Wallet MPC、Web3Auth。 -
智能合约开发与部署
合约部署私钥:采用 TSS 分片管理,防止部署私钥泄露导致合约被攻击;
DApp 交互:用户用 TSS 钱包签名,无需暴露私钥,提升 DApp 安全性;
批量签名:TSS 支持批量交易签名,适合合约批量部署 / 调用。 -
企业级多签管理
团队权限:2-of-3 门限(2 个员工 + 1 个管理员),防止单人操作风险;
审计合规:签名操作全程可追溯,满足金融 / 政务监管要求。
-
-
TSS vs 链上多签(Multisig):核心区别(必懂)
这是最易混淆的点,TSS 是链下分布式签名,Multisig 是链上合约多签,二者目标一致(多方授权),但技术路径、成本、兼容性差异极大:
| 对比维度 | TSS(门限签名) | 链上多签(Multisig) | 核心差异 |
|---|---|---|---|
| 私钥状态 | 永不完整(分片) | 各签名方持有完整私钥 | TSS 无单点泄露风险,Multisig 有 |
| 签名位置 | 链下多方协同 | 链上合约验证 | TSS 无 Gas 费,Multisig 需支付合约 Gas |
| 链上兼容性 | 100% 兼容所有链 | 需部署多签合约,部分链不支持 | TSS 适配所有公链,Multisig 依赖链上功能 |
| 门限调整 | 链下动态修改,无成本 | 需重新部署合约,高成本 | TSS 灵活性远高于 Multisig |
| 隐私性 | 参与方身份不暴露 | 签名方地址公开,可追踪 | TSS 隐私性更好 |
| 签名效率 | 链下秒级完成 | 链上验证,延迟高 | TSS 适合高频交易 |
关键结论
个人 / 高频场景:首选 TSS(无 Gas、便捷、兼容所有链);
大额资产 / 强监管场景:可 TSS + Multisig 混合(如交易所热钱包 TSS + 冷钱包 Multisig)。
简单说:链上多签是「合约管权限」,TSS 是「密码学管私钥」,TSS 更安全、更灵活、更适配所有区块链场景。
三、多签(Multisig)
-
多签(Multisignature)是一种需要多个私钥持有者共同授权才能完成交易 / 操作的安全机制,核心遵循「t-of-n 门限规则」:
- n:授权人总数量(如 3 个管理员);
- t:完成操作所需的最小授权数量(如 2 个);
- 只有收集到≥t 个授权人的签名,交易 / 操作才能执行;单个 / 不足 t 个授权人无法单独完成操作。
-
多签的核心类型(链上 vs 链下)
区块链多签分两类,核心差异在于「签名验证位置」:- 链上多签(Multisig):智能合约实现授权逻辑,区块链上(合约验证),Gnosis Safe、ERC-20 多签合约,中高(依赖合约安全),大额资产管理、合约权限控制;
- 链下多签(TSS):MPC/TSS 密码学实现,区块链下(链上仅验证最终签名),Fireblocks MPC、Coinbase MPC,高(无完整私钥),高频交易、交易所热钱包
关键区别: - 链上多签:多个私钥分别签名,合约收集签名后执行(有完整私钥,合约管权限);
- 链下多签(TSS):私钥分片,多方协同生成一个签名(无完整私钥,密码学管私钥);
为什么?
-
链上多签核心工作原理(以太坊 EVM 链举例)
以「2-of-2 多签合约」为例,核心流程:- 部署多签合约:指定 2 个授权人地址(如 AddrA、AddrB),门限设为 2;
- 发起交易:任意授权人提交交易(如转账 ETH、调用合约)到多签合约;
- 收集签名:2 个授权人分别用自己的私钥对该交易签名,将签名提交到合约;
- 验证执行:合约验证签名数量≥门限(2 个),且签名合法,执行交易;
- 失败场景:仅 1 个授权人签名→合约拒绝执行。
-
多签合约示例代码
点击查看代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title Simple2of2Multisig
* @dev 简化版2-of-2链上多签合约,支持ETH转账和合约调用
* 核心功能:提交交易→收集2个签名→执行交易
*/
contract Simple2of2Multisig {
// 授权人地址(2个)
address public immutable owner1;
address public immutable owner2;
// 交易结构体:存储待执行的交易信息
struct Transaction {
address to; // 接收方地址
uint256 value; // 转账ETH金额(wei)
bytes data; // 调用合约的calldata(空则为纯ETH转账)
bool executed; // 是否已执行
uint8 confirmCount; // 已收集的签名数量
}
// 交易ID → 交易信息
mapping(uint256 => Transaction) public transactions;
// 交易ID → 授权人地址 → 是否已签名
mapping(uint256 => mapping(address => bool)) public isConfirmed;
// 交易计数器(生成唯一交易ID)
uint256 public txCount;
// 事件:提交交易
event TransactionSubmitted(uint256 indexed txId, address indexed submitter, address to, uint256 value);
// 事件:确认交易(签名)
event TransactionConfirmed(uint256 indexed txId, address indexed confirmer);
// 事件:执行交易
event TransactionExecuted(uint256 indexed txId, address indexed executor);
// 修饰器:仅授权人可调用
modifier onlyOwner() {
require(msg.sender == owner1 || msg.sender == owner2, "Not an owner");
_;
}
// 修饰器:交易未执行
modifier notExecuted(uint256 txId) {
require(!transactions[txId].executed, "Transaction already executed");
_;
}
// 构造函数:初始化2个授权人
constructor(address _owner1, address _owner2) {
require(_owner1 != address(0) && _owner2 != address(0) && _owner1 != _owner2, "Invalid owners");
owner1 = _owner1;
owner2 = _owner2;
}
/**
* @dev 提交交易(任意授权人可提交)
* @param _to 接收方地址
* @param _value 转账ETH金额(wei)
* @param _data 合约调用数据(空则为纯ETH转账)
* @return txId 生成的交易ID
*/
function submitTransaction(
address _to,
uint256 _value,
bytes calldata _data
) external onlyOwner returns (uint256 txId) {
require(_to != address(0), "Invalid to address");
txId = txCount;
transactions[txId] = Transaction({
to: _to,
value: _value,
data: _data,
executed: false,
confirmCount: 0
});
txCount++;
emit TransactionSubmitted(txId, msg.sender, _to, _value);
}
/**
* @dev 确认交易(签名,任意授权人可确认)
* @param txId 交易ID
*/
function confirmTransaction(uint256 txId) external onlyOwner notExecuted(txId) {
Transaction storage txn = transactions[txId];
require(!isConfirmed[txId][msg.sender], "Already confirmed");
isConfirmed[txId][msg.sender] = true;
txn.confirmCount++;
emit TransactionConfirmed(txId, msg.sender);
}
/**
* @dev 执行交易(需收集2个签名,任意授权人可执行)
* @param txId 交易ID
*/
function executeTransaction(uint256 txId) external onlyOwner notExecuted(txId) {
Transaction storage txn = transactions[txId];
// 验证签名数量≥2(2-of-2门限)
require(txn.confirmCount >= 2, "Not enough confirmations");
// 标记交易已执行
txn.executed = true;
// 执行交易:转账ETH或调用合约
(bool success, ) = txn.to.call{value: txn.value}(txn.data);
require(success, "Transaction execution failed");
emit TransactionExecuted(txId, msg.sender);
}
/**
* @dev 接收ETH(合约可存储ETH)
*/
receive() external payable {}
}
- 调用流程:
-
步骤 1:部署合约
输入2个授权人地址(如 Owner1: 0xAAA...,Owner2: 0xBBB...);
部署合约(执行Constructor初始化合约 校验授权人地址合法性,存储 2 个授权人),记录合约地址(如 0xCCC...); -
步骤 2:提交交易
用Owner1的地址调用submitTransaction,提交交易,生成交易ID,存储交易信息,触发提交事件;
记录返回的txId(如 0); -
步骤 3:确认交易(收集签名)
confirmTransaction 确认交易(签名) 校验授权人身份→校验未重复签名→增加签名数→触发确认事件
用 Owner1 的地址调用confirmTransaction(0)(第一个签名);
用 Owner2 的地址调用confirmTransaction(0)(第二个签名,达到 2-of-2 门限)。 -
步骤 4:执行交易
用任意授权人(如 Owner1)调用executeTransaction(0)执行交易;
查看区块链浏览器:合约向 0xDDD... 转账 0.005 ETH,交易执行成功。
-
四、时间锁(Timelock)核心定义
是什么?
时间锁是区块链 / 智能合约中强制延迟执行操作的安全机制,核心逻辑是:当用户提交一笔操作(如转账、合约升级、多签交易)后,必须等待预设的延迟时间(如 24 小时),且当前区块时间超过该延迟时间阈值时,操作才能被执行;未到时间则无法执行。
本质是给关键操作增加「冷却期 / 反悔期」,防止恶意操作、误操作或内部攻击,是智能合约安全的核心组件(尤其适配多签、DAO、合约升级场景)。
-
关键术语
- 提交时间(proposalTime):操作被提交到合约的区块时间;
- 延迟时间(delay):预设的最小等待时间(如 86400 秒 = 24 小时);
- 可执行时间(executionTime):proposalTime + delay,只有≥该时间才能执行操作;
- 过期时间(expirationTime):可选,超过该时间操作失效(防止长期未执行的废操作)。
-
时间锁的核心作用
- 防误操作 / 恶意操作(多签 + 时间锁核心价值)
多签合约中添加时间锁:授权人提交大额转账后,有 24 小时反悔期 —— 若发现是误操作 / 恶意提交,可及时撤销,避免资产损失。 - 防闪电贷攻击 / 套利
DeFi 合约中,关键操作(如调整费率、清算规则)加时间锁,防止黑客利用闪电贷在操作生效前套利。 - DAO 提案生效延迟
DAO 投票通过提案后,需等待时间锁到期才执行(如 7 天),给社区成员足够时间理解提案、撤离风险资产。 - 合约升级安全
合约升级前,时间锁延迟生效(如 48 小时),用户可在窗口期将资产转出,避免升级漏洞导致损失。 - 合规与审计
延迟期内可对操作进行审计,确认无漏洞后再执行,满足金融 / 监管合规要求。
- 防误操作 / 恶意操作(多签 + 时间锁核心价值)
-
基础时间锁合约示例
点击查看代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title GenericTimelock
* @dev 通用型相对时间锁合约
* 核心逻辑:提交操作→等待delay秒→执行操作(支持ETH转账/合约调用)
*/
contract GenericTimelock {
// 管理员:有权提交/执行/撤销操作(生产环境可替换为多签地址)
address public immutable admin;
// 延迟时间(秒):提交后需等待该时间才能执行
uint256 public immutable delay;
// 操作结构体:存储待执行的操作信息
struct Operation {
address to; // 接收方地址
uint256 value; // 转账ETH金额(wei)
bytes data; // 合约调用calldata
uint256 proposalTime;// 提交时间(区块时间)
bool executed; // 是否已执行
bool canceled; // 是否已撤销
}
// 操作ID → 操作信息(操作ID=keccak256(操作关键参数),避免重复)
mapping(bytes32 => Operation) public operations;
// 事件:提交操作
event OperationProposed(bytes32 indexed opId, address indexed proposer, address to, uint256 value);
// 事件:执行操作
event OperationExecuted(bytes32 indexed opId, address indexed executor);
// 事件:撤销操作
event OperationCanceled(bytes32 indexed opId, address indexed canceler);
// 修饰器:仅管理员可调用
modifier onlyAdmin() {
require(msg.sender == admin, "Not admin");
_;
}
// 修饰器:操作未执行且未撤销
modifier validOperation(bytes32 opId) {
Operation storage op = operations[opId];
require(op.proposalTime > 0, "Operation does not exist");
require(!op.executed, "Operation already executed");
require(!op.canceled, "Operation canceled");
_;
}
// 修饰器:操作已到可执行时间
modifier timeLockPassed(bytes32 opId) {
Operation storage op = operations[opId];
require(block.timestamp >= op.proposalTime + delay, "Time lock not passed");
_;
}
/**
* @dev 构造函数
* @param _admin 管理员地址(生产环境建议设为多签合约地址)
* @param _delay 延迟时间(秒),建议≥86400(24小时)
*/
constructor(address _admin, uint256 _delay) {
require(_admin != address(0), "Invalid admin");
require(_delay > 0, "Delay must be > 0");
admin = _admin;
delay = _delay;
}
/**
* @dev 生成操作ID(唯一标识,防止重复提交)
* @param to 接收方
* @param value 转账金额
* @param data 调用数据
* @return opId 操作ID
*/
function getOperationId(
address to,
uint256 value,
bytes calldata data
) public pure returns (bytes32 opId) {
opId = keccak256(abi.encodePacked(to, value, data));
}
/**
* @dev 提交操作(提案)
* @param to 接收方地址
* @param value 转账ETH金额
* @param data 合约调用数据(空则为纯ETH转账)
* @return opId 操作ID
*/
function proposeOperation(
address to,
uint256 value,
bytes calldata data
) external onlyAdmin returns (bytes32 opId) {
require(to != address(0), "Invalid to address");
opId = getOperationId(to, value, data);
require(operations[opId].proposalTime == 0, "Operation already proposed");
operations[opId] = Operation({
to: to,
value: value,
data: data,
proposalTime: block.timestamp,
executed: false,
canceled: false
});
emit OperationProposed(opId, msg.sender, to, value);
return opId;
}
/**
* @dev 执行操作(需等待时间锁到期)
* @param to 接收方地址(需与提交时一致)
* @param value 转账金额(需与提交时一致)
* @param data 调用数据(需与提交时一致)
*/
function executeOperation(
address to,
uint256 value,
bytes calldata data
) external onlyAdmin validOperation(getOperationId(to, value, data)) timeLockPassed(getOperationId(to, value, data)) {
bytes32 opId = getOperationId(to, value, data);
Operation storage op = operations[opId];
// 标记操作已执行
op.executed = true;
// 执行转账/合约调用
(bool success, ) = op.to.call{value: op.value}(op.data);
require(success, "Operation execution failed");
emit OperationExecuted(opId, msg.sender);
}
/**
* @dev 撤销操作(仅未执行/未到期时可撤销)
* @param to 接收方地址
* @param value 转账金额
* @param data 调用数据
*/
function cancelOperation(
address to,
uint256 value,
bytes calldata data
) external onlyAdmin validOperation(getOperationId(to, value, data)) {
bytes32 opId = getOperationId(to, value, data);
operations[opId].canceled = true;
emit OperationCanceled(opId, msg.sender);
}
/**
* @dev 接收ETH(合约可存储ETH)
*/
receive() external payable {}
}
-
调用流程
- 部署合约:管理员地址填你的钱包地址,延迟时间设为3600(1 小时);
- 提交操作:调用proposeOperation,to 填测试地址,value 填1000000000000000(0.001 ETH),data 留空;
- 等待 1 小时:期间调用executeOperation会提示 “Time lock not passed”;
- 执行操作:1 小时后调用executeOperation,参数与提交时一致,合约向测试地址转账 0.001 ETH;
- 撤销测试:若提交后未到 1 小时,调用cancelOperation可撤销操作,撤销后无法执行。
-
多签 + 时间锁整合合约(生产级核心场景)
多签合约仅解决 “多人授权”,但缺少 “反悔期”;时间锁仅解决 “延迟执行”,但缺少 “多人授权”—— 二者结合是生产环境资产安全的黄金组合(如 Gnosis Safe 的时间锁模块)。
点击查看代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title MultisigWithTimelock
* @dev 2-of-2多签 + 时间锁整合合约
* 核心流程:提交交易→收集2个签名→等待delay秒→执行交易
*/
contract MultisigWithTimelock {
// 多签授权人
address public immutable owner1;
address public immutable owner2;
// 时间锁延迟(秒)
uint256 public immutable delay;
// 交易结构体:整合多签+时间锁
struct Transaction {
address to;
uint256 value;
bytes data;
uint256 proposalTime; // 提交时间(时间锁起点)
bool executed;
bool canceled;
uint8 confirmCount; // 多签签名数
}
mapping(uint256 => Transaction) public transactions;
mapping(uint256 => mapping(address => bool)) public isConfirmed;
uint256 public txCount;
// 事件
event TransactionProposed(uint256 indexed txId, address indexed proposer);
event TransactionConfirmed(uint256 indexed txId, address indexed confirmer);
event TransactionExecuted(uint256 indexed txId, address indexed executor);
event TransactionCanceled(uint256 indexed txId, address indexed canceler);
// 修饰器:仅授权人
modifier onlyOwner() {
require(msg.sender == owner1 || msg.sender == owner2, "Not owner");
_;
}
// 修饰器:交易有效(未执行/未撤销)
modifier validTx(uint256 txId) {
Transaction storage txn = transactions[txId];
require(txn.proposalTime > 0, "Tx does not exist");
require(!txn.executed, "Tx executed");
require(!txn.canceled, "Tx canceled");
_;
}
// 修饰器:时间锁到期
modifier timelockPassed(uint256 txId) {
Transaction storage txn = transactions[txId];
require(block.timestamp >= txn.proposalTime + delay, "Timelock not passed");
_;
}
// 修饰器:多签签名数≥2
modifier enoughConfirmations(uint256 txId) {
require(transactions[txId].confirmCount >= 2, "Not enough confirmations");
_;
}
constructor(address _owner1, address _owner2, uint256 _delay) {
require(_owner1 != _owner2 && _owner1 != address(0), "Invalid owners");
require(_delay > 0, "Delay > 0");
owner1 = _owner1;
owner2 = _owner2;
delay = _delay;
}
/**
* @dev 提交交易(多签授权人提交,启动时间锁)
*/
function proposeTx(address to, uint256 value, bytes calldata data) external onlyOwner returns (uint256 txId) {
require(to != address(0), "Invalid to");
txId = txCount;
transactions[txId] = Transaction({
to: to,
value: value,
data: data,
proposalTime: block.timestamp,
executed: false,
canceled: false,
confirmCount: 0
});
txCount++;
emit TransactionProposed(txId, msg.sender);
}
/**
* @dev 确认交易(多签签名)
*/
function confirmTx(uint256 txId) external onlyOwner validTx(txId) {
Transaction storage txn = transactions[txId];
require(!isConfirmed[txId][msg.sender], "Already confirmed");
isConfirmed[txId][msg.sender] = true;
txn.confirmCount++;
emit TransactionConfirmed(txId, msg.sender);
}
/**
* @dev 执行交易(需:2个签名+时间锁到期)
*/
function executeTx(uint256 txId) external onlyOwner validTx(txId) enoughConfirmations(txId) timelockPassed(txId) {
Transaction storage txn = transactions[txId];
txn.executed = true;
(bool success, ) = txn.to.call{value: txn.value}(txn.data);
require(success, "Tx failed");
emit TransactionExecuted(txId, msg.sender);
}
/**
* @dev 撤销交易(仅未执行/未到期时)
*/
function cancelTx(uint256 txId) external onlyOwner validTx(txId) {
transactions[txId].canceled = true;
emit TransactionCanceled(txId, msg.sender);
}
receive() external payable {}
}

浙公网安备 33010602011771号