用脚本锁一段时间:CLTV、CSV 与相对时间锁的分工 图 1
用脚本锁一段时间:CLTV、CSV 与相对时间锁的分工 · 图 1

先分清时间锁与高度锁

比特币脚本里管理”时间”的两组指令常被统称为时间锁,机制其实分两层。绝对锁:OP_CHECKLOCKTIMEVERIFY(CLTV,BIP-65)比对的是交易的 nLockTime 字段,字段里可以填区块高度或 Unix 时间戳(大于等于 5 亿的那个约定分界)。相对锁:OP_CHECKSEQUENCEVERIFY(CSV,BIP-112)比对的是输入的 nSequence,语义变成”被花的这个输出至少要成熟多少”,单位可以是块数,也可以是 512 秒一个刻度的时间。CLTV 随 2015 年的软分叉激活,CSV 与 BIP-68、BIP-113 打包以版本号位协商,从区块 419328 起生效。写脚本前先想清楚要锁”日历”还是锁”经过的块数”,这两条路径的解锁条件、可组合性完全不同。

用脚本锁一段时间:CLTV、CSV 与相对时间锁的分工 图 2
用脚本锁一段时间:CLTV、CSV 与相对时间锁的分工 · 图 2

一笔”到期才能花”的脚本

最简单的锁:输出锁定脚本写成”满足 数字 与 CLTV 与 CHECKSIG”。想花这笔钱,花费交易的 nLockTime 必须不小于锁定的那个值,否则脚本直接失败。有两个实现细节值得记住:其一,nLockTime 对高度和时间戳的刻度必须和脚本里的数字一致——脚本写高度就交易填高度,混用刻度是新手最常见的哑火原因;其二,一旦某输入要参与时间锁评估,它的 nSequence 不能是 0xffffffff 那个默认最大值,钱包默认值会静默地让整笔交易作废。于是”给公钥 A 随时可花、给公钥 B 锁到某个日期以后”这种双路脚本,就是用 IF 分支加一条 CLTV 路径拼出来的:链上花的时候只暴露真正走的那条分支。

CSV:锁的是”这个输出的年龄”

CSV 的巧妙在于它读的不是当前交易,而是被花输出的确认深度:数字与 nSequence 的相对掩码位配合,节点检查该输出已获得的确认数(或按 512 秒粒度的时间)不小于门槛。于是脚本可以表达”任何花我的交易都要证明我已经等了多久”。与 BIP-68 的输入级相对锁定配合,这就是现代钱包给 RBF 信号和相对时间锁留的统一底座,也是闪电网络里”对方偷放旧状态后,惩罚交易必须在惩罚窗口内上链”这类机制的计时器。CSV 与 CLTV 组合的直观用法是继承与分期:先用 CLTV 锁住”2035 年前只有多签能动”,再给继承人一条”从该年月起单人可取”的路径。

状态类指令与时间不是一回事

脚本里还有几个常被当成”状态锁”的操作码:OP_CHECKSIGVERIFYOP_RETURN、哈希锁 OP_HASH160/OP_SHA256 等。哈希锁与时间锁拼出哈希时间锁合约,是闪电网络 HTLC 的骨架:解锁要么给出原像,要么在超时后走退款分支。这类脚本的难点从来不在指令本身,而在超时分支的设计——退款路径若没有相对锁,等于把 HTLC 变成随时可撤;若惩罚与退款的时序没和确认深度对齐,链上拥堵时会把两条竞争路径同时打开。

与 Miniscript 的关系

手写以上逻辑要同时处理堆栈顺序、最小编码、操作码禁用和标准性规则,出错方式往往是”钱包直接拒收你的脚本”。Miniscript 把这类脚本变成有类型系统的表达式,钱包在签名前就能静态证明”这段脚本安全、可花费、可哈希”。部署侧的节奏供参考:Bitcoin Core 的 wsh() 描述符在 24 版支持 Miniscript(先只读跟踪),25 版加入签名支持,26 版起 tr() 也能使用 Miniscript 表达式——判断自己的环境要看 getdescriptorinfo 能不能解析表达式,而不是看发行公告。

自查清单

时间锁脚本上链前逐条过一遍:锁定的刻度与测试环境高度是否自洽;nSequence 默认值会不会掐死这笔交易;相对锁的 512 秒粒度是否接受这种误差;超时退款路径有没有相对锁保护;多路径脚本在两个分支各签一次,确认每条路径都能构造出有效解锁脚本。先在测试网(或自建的 regtest)把每条花费路径演练一遍再上主网。本文只做机制说明,不构成任何投资建议。