OP_CHECKSEQUENCEVERIFY 相对时间锁:延迟从何时起算 图 1
OP_CHECKSEQUENCEVERIFY 相对时间锁:延迟从何时起算 · 图 1

从“到某天才给”到“等满 N 天”

上一篇讲绝对时间锁的文章里提到,OP_CHECKLOCKTIMEVERIFY 锁的是日历上的一个坐标。相对时间锁解决另一类问题:不关心世界时间到了几号,只关心“这笔钱本身安静了多久”。比特币用 OP_CHECKSEQUENCEVERIFY(CSV)实现这个语义,由 BIP112 定义,并依赖 BIP68 给输入序列号字段 nSequence 规定的解释规则,与 BIP113 一起在区块高度 419328 处作为软分叉获得共识层强制,今天已永久生效。

延迟的起算点是被花费的输出

CSV 的基准不是今天,而是“当前这个输入所引用的那笔交易的确认高度”加上脚本要求的延迟。假设某个输出被一笔交易确认后,花它的那笔交易在输入里声明“再等 144 个块”,那么这笔花费交易最早只能在输出确认高度加 144 之后生效。这个“从被花费输出起算”的特性,正是闪电网络所有延迟逻辑的地基:状态更新链上落定后,对手方或惩罚分支必须等满延迟才能动用资金,机制背景见 比特币闪电网络是什么?

nSequence 里怎么编码时间

nSequence 是一个 4 字节字段。最低位决定单位:不开启标志位时,剩余位按“块数”计数;开启时间标志位后,按 512 秒为一个刻度换算成“时长”。所以 CSV 的延迟粒度是“一个块”或“约 8 分半”,更细的秒级精度不存在。脚本里的门槛值也用同一套位编码,两个值做无符号比较,输入编码的单位必须与门槛一致,否则脚本失败。写工具时最容易犯的错就是把“秒”直接塞进块单位里,导致等待时间被放大上千倍。

和 CLTV 的本质区别

两者对照记忆:CLTV 比较的是链的绝对高度或时间戳,任何花费交易都要等同一个日历点;CSV 比较的是这笔 UTXO 自己的历史年龄,同一脚本模板下,早确认的输出比晚确认的输出更早可用。闪电通道里两者都用:惩罚与超时分支用 CSV 保证“对方状态落链后我方有足够时间反应”,而整个通道的绝对兜底可能再叠一层 CLTV,见 比特币离线签名流程是什么?不联网怎么完成一次转账 中通道资金与链上交易的对应关系。相对时间锁的等待以“确认后的链”为准,遇到重组起算点可能后移,对时限敏感的合约应把延迟设得更保守。

工程上要注意什么

第一,nSequence 参与全替代信号判定:按 BIP68/BIP125 的约定,默认序列号会声明交易可被替代,想让某笔花费不可被自己替换时要把序列号设为最大值,哪怕交易里根本没有时间锁。第二,验证 CSV 条件的节点必须先知道被引用输出的确认高度,这意味着节点要维护区块高度索引,未同步完成的节点不能参与判定,同步状态判断见相关节点文档。第三,测试网演练时注意 testnet 出块节奏不稳定,块延迟会真实感受为忽长忽短。

小结与风险提示

CSV 把“延迟”从日历变成账龄,是比特币合约里最常用的等待原语。理解三件事就够开始读闪电脚本了:起算点是被花费输出的确认高度,单位是块或 512 秒刻度,比较是无符号的门槛检查。使用涉及时间锁的资金流程存在构造错误导致资金延迟或不可用的风险,上线前务必完整演练,本文不构成投资建议。

一个帮助建立直觉的思想实验

设想你收到一笔带“等 144 个块”条件的付款。明天世界是否改朝换代与你无关,重要的是承载这笔输出的那笔交易何时被写进某个区块:如果它赶上了一次六块重组后重新确认,起算高度就向后推了六个块,你的可用时间随之顺延。这就是相对时间锁对重组天然敏感的原因,也是为什么关键合约宁可把延迟设得更长。另一个常被问到的问题:CSV 会不会被“时间旅行”绕过?不会——验证比较的输入是节点自己维护的链上高度与被引用交易所在区块的高度,两者都来自全量验证过的数据,作恶者无法在不组织大规模重组的前提下伪造起算点。把“等待”理解为账龄而非墙钟,闪电的惩罚窗口、Vault 的冷静期这些设计就不再神秘。