协议演进 / BIP/比特币改进提案 / BIP-65
BIP-65
把 NOP2 重新定义为 OP_CHECKLOCKTIMEVERIFY,让一个输出在达到某个区块高度或时间之前,由脚本本身保证不可花费。本 BIP 目前处于已激活状态。
一、档案卡
| 编号 | BIP-65 |
|---|---|
| 标题 | OP_CHECKLOCKTIMEVERIFY |
| 作者 | Peter Todd <[email protected]> |
| 指定日期 | 2014-10-01 |
| 层级 | Consensus (soft fork)(共识(软分叉)) |
| 类型 | Specification(规范类) |
| 状态 | Deployed(已激活) |
| 阅读版 | https://bips.dev/65 |
| 官方原文 | https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki |
二、放在哪一族
BIP-65 是时间锁族里管“绝对时间”的那一份:锁的界限是一个具体的区块高度或时刻。交易自带的 nLockTime 字段早就存在,但它约束的是整笔交易何时能被挖出,约束不到单个输出“在某时刻之前谁都花不了”。
与它对照的是相对时间锁一族:BIP-68 给 nSequence 赋予“自所花输出被确认起算”的相对锁定语义,BIP-113 则为这类计算定义了中位过去时间(median-time-past)的取法。绝对锁看日历,相对锁看年龄,两者是互补关系。
部署机制上,它复用了 BIP-66 的双门槛 IsSuperMajority() 切换办法(见第四节)。
三、它要解决什么
nLockTime 能证明一件事:一笔花掉某输出的交易被构造出来了,因此“将来有可能花掉它”是成立的。
但 nLockTime 证明不了反过来的事:“在某时刻之前不可能花掉它”。因为无法排除有人已经在私下签好了另一笔花掉它的交易。
原文列的应用都建立在“可证明地暂时花不了”之上:
- 托管安排:Alice 与 Bob 用 2-of-2 多签共同经营,另请律师 Lenny 做例外情况下的后备。标准 2-of-3 里 Lenny 随时能与任一方合谋;用 CLTV 改成“平时只能两人联签,三个月后 Lenny 才可与任一方联签”,律师平时碰不到钱,也少了被胁迫交钥匙的动机;
- 非交互式定时退款:需要双方合作花费的协议,原先要交互式地预先 setup 带 nLockTime 的退款交易,还受交易可塑性影响;CLTV 可以非交互地在脚本里内置退款路径,可塑性也不再是问题;
- 双因素钱包:服务方联签的钱包,改用“服务方签,或到期后用户单签”的脚本,用户永远能在服务不可用时等到期自行花费;
- 支付通道:Jeremy Spilman 式通道原先要预签退款交易并由付款方保管,同样受可塑性影响;换成双因素钱包同款脚本可同时解掉这两个问题;
- 为发布数据付费、向矿工费证明牺牲、冻结资金:都靠“某个时刻之前谁都花不了、之后按某路径可花”的脚本形状实现。
四、规则本身
4.1 语义:重新定义 NOP2
CHECKLOCKTIMEVERIFY 重新定义已有的 OP_NOP2。执行时,只要下列任一条件成立,脚本解释器就报错终止:
- 栈为空;
- 栈顶元素小于 0;
- 栈顶元素与交易 nLockTime 字段的锁类型不同(一个按区块高度、一个按时间);
- 栈顶元素大于交易的 nLockTime 字段;
- 该输入的 nSequence 字段为 0xffffffff(即输入已“终局化”,会使 nLockTime 失效)。
以上都不成立,脚本才像执行了一个 NOP 一样继续。原理是间接比较:nLockTime 本就保证交易在达到某高度或时间前不能被挖出,把脚本参数与 nLockTime 对比一致,就等于验证了期望的高度或时间已经达到;未达到之前,该输出不可花费。
4.2 数值细节
原文给出的参考实现里,CLTV 的参数按 CScriptNum 读取时特别允许最长 5 字节:普通数值操作码限于 4 字节会有 2038 年问题,而 nLockTime 本身是 uint32、要到 2106 年后才失去意义,放宽到 5 字节正好与 nLockTime 的上限对齐。参数若可能因先行运算变为负数,可先用 0 MAX 兜底。
锁类型以 LOCKTIME_THRESHOLD 区分:小于该阈值按区块高度,大于等于按时间,两边类型必须相同才能比较。
4.3 部署
复用 BIP-66 的双门槛 IsSuperMajority() 机制,门槛相同,但对应 nVersion = 4:某高度 H 的区块为 nVersion = 4、且其前 1000 个区块中至少 750 个 nVersion ≥ 4 时,新规则对该区块生效;当前 1000 个区块中 950 个达到 nVersion ≥ 4 后,nVersion < 4 的区块变为无效,此后所有区块强制执行新规则。
原文还提醒 SPV 客户端:它们虽不能整体验证区块,但能验证区块头、从而验证一部分部署规则;在 95% 门槛达到后,应拒绝 nVersion < 4 的区块,以免被未升级的少数矿工给出错误确认。
五、谁必须动
普通持币人:不必动。用不用 CLTV 取决于所用钱包是否构造这类脚本;普通转账不受影响。
钱包开发者:构造带 CLTV 的脚本时,须让花费交易的 nLockTime 与脚本参数同类型且不小于参数值,并保证对应输入的 nSequence 不是 0xffffffff,否则锁定会被绕开式地失效。
全节点与矿工:这是软分叉共识改动——升级后按新语义解释 NOP2 并参与版本门槛部署;未升级节点把 NOP2 仍当无操作,在软分叉的安全模型内跟随升级多数挖出的链。
共识层:脚本语义在此扩展:原本无意义的 NOP2 自激活起成为可使脚本失败的检查操作码,这是本 BIP 的全部共识改动。
六、现在算不算数,以及一个常见误读
状态为 Deployed,站点标注“已激活”。OP_CHECKLOCKTIMEVERIFY 是今天脚本里绝对时间锁的标准做法,托管、退款、通道类脚本中“到期后备路径”的写法都源出于此。
常见误读:
“上了 CLTV 锁的资金,到期会自动解锁、自动转走。”
不对。CLTV 只做一件事:在界限达到之前,让花费该输出的脚本执行失败。它不执行任何转账;到期之后,仍须有人构造一笔 nLockTime 与脚本参数匹配、且签名齐全的花费交易并广播上链,资金才会动。
七、延伸
- 阅读版:https://bips.dev/65
- 官方原文:https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki
- 相关:BIP-66 部署机制来源、BIP-68 相对锁定时间、BIP-113 中位过去时间
- 仓库总目录:https://github.com/bitcoin/bips
