惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - cac2020

QA之三 - 变异测试 -- PITest QA之二 - 单元测试-- JaCoCo QA之二 - 单元测试-- JUnit5 + Mockito QA之二 - 单元测试--Mockito QA之二 - 单元测试--JUnit5 QA之二 -- 测试用例 QA之一 -- 常见测试类型和测试环境 密码学之二 —— GPG、SSH、KMS 密码学之一 —— 三类算法 记录一次在线word转PDF的问题处理过程 分析Java堆Dump Java GC日志分析 Java线程Dump分析(TDA-Thread Dump Analyzer) Java可视化故障处理工具-VisualVM Java基础故障处理工具 记录Arthas在一次性能调优过程中实践 中小型系统必要可行的性能测试实践--ArtHas调优实战 中小型系统必要可行的性能测试实践--jmeter落地实践 中小型系统必要可行的性能测试实践--性能测试理论基础
密码学之三 —— MPC(TSS)、多签、时间锁
cac2020 · 2026-02-03 · via 博客园 - cac2020

一、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);
    • 上链验证:将签名后的交易发送到链上,链上验证签名有效(和普通私钥签名结果一致)。
  • 主流 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 无 Gas 费,Multisig 需支付合约 Gas 链上兼容性 100% 兼容所有链 需部署多签合约,部分链不支持 TSS 适配所有公链,Multisig 依赖链上功能 门限调整 链下动态修改,无成本 需重新部署合约,高成本 TSS 灵活性远高于 Multisig 隐私性 参与方身份不暴露 签名方地址公开,可追踪 TSS 隐私性更好 签名效率 链下秒级完成 链上验证,延迟高 TSS 适合高频交易
对比维度 TSS(门限签名) 链上多签(Multisig) 核心差异

关键结论
个人 / 高频场景:首选 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 {}
}

学习技术不是用来写HelloWorld和Demo的,而是要用来解决线上系统的真实问题的.