协议演进 / BIP/比特币改进提案 / BIP-112
BIP-112
把 NOP3 重定义为 CHECKSEQUENCEVERIFY,让脚本能结合 BIP-68 的 nSequence 相对锁定时间,限制某条花费路径必须等被花输出达到最小年龄后才可用。本 BIP 目前处于已激活状态。
一、档案卡
| 编号 | BIP-112 |
|---|---|
| 标题 | CHECKSEQUENCEVERIFY |
| 作者 | BtcDrak <[email protected]>;Mark Friedenbach <[email protected]>;Eric Lombrozo <[email protected]> |
| 指定日期 | 2015-08-10 |
| 层级 | Consensus (soft fork)(共识(软分叉)) |
| 类型 | Specification(规范类) |
| 状态 | Deployed(已激活) |
| 阅读版 | https://bips.dev/112 |
| 官方原文 | https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki |
二、放在哪一族
时间锁族的核心成员,与两份文档不可分开读:
BIP-68:用共识强制的序列号实现相对锁定时间——先给交易的 nSequence 字段赋予“相对锁定”的共识语义
BIP-112:再给脚本一个能检查这个字段的操作码
BIP-113:把锁定时间的判断端点改为过去时间中位数——三份文件在部署一节中互相写明,须用同一机制同时部署
它的前辈是 BIP-65(OP_CHECKLOCKTIMEVERIFY):那是绝对时间锁,检查“现在是否已过某个时刻/高度”;BIP-112 是相对时间锁,检查“被花的输出是否已经够老”。原文的参考实现与文档大量建立在 Peter Todd 为 BIP-65 所做的工作之上;相对锁定的想法由 Mark Friedenbach 提出,他发明了用序列号实现相对锁定的方法并写了 CHECKSEQUENCEVERIFY 的参考实现。
在编号相邻的这一组里,BIP-110(数年后)与 BIP-114 反复引用本页的托管与 HTLC 例题——114 的例子正是把 112 的脚本拆成 MAST 分支。
三、它要解决什么
3.1 BIP-68 给了语义,脚本却够不着
BIP-68 把 nSequence 重新用作共识强制的相对锁定时间:未到龄的非最终(non-final)交易不能被选进区块。但脚本本身没有办法根据这个字段作决定——合约无法写出“这条路径要等输出确认满 N 个区块/一段时间之后才走得通”。
BIP-112 把 nSequence 交到脚本手里。由此可以构造这样的执行路径:时钟从证明已发布(proof-of-publication,即资金交易上链确认)那一刻才开始走,而不是在某个历法时刻预先定死。这为分阶段协议打开了空间:托管、支付通道、双向锚定(bidirectional pegs)等。
3.2 典型场景
- 带超时的托管:Alice、Bob 与托管方建 2-of-3 地址,赎回脚本一条支路是任意两人签名(随时可花),另一条支路是 “30d” CHECKSEQUENCEVERIFY 之后 Alice 单独签名——资金确认 30 天后,Alice 可独自取回。时钟从付款确认时才开始。
- 追溯撤销(功能等价):区块链不可篡改,确认过的承诺无法真正撤回。但可以让一条路径被延迟、另一条路径在窗口期内凭“撤销条件”立即花费,从而在功能上模拟撤销:撤销条件迟迟不出现,延迟路径到期后按原合约执行。
- 哈希时间锁定合约(HTLC):一条路径要求在撤销窗口内出示哈希原像,出示秘密即向对方保证该交易永不会被广播——因为对方可立即凭秘密 claim,而自己还要等窗口过去。
- 双向支付通道与闪电网络:相对锁定让一方在对方广播已撤销交易时,有一段可预期长度的响应时间;时钟从承诺交易确认那刻起算,通道不必像绝对锁定那样临近到期就关闭重开。具体构造见第四节末与原文例题。
- 双向锚定侧链:延时分支配合(不在本 BIP 范围内的)REORGPROOFVERIFY,让转回主链的提取在重组证明期过后才生效。
四、规则本身
4.1 重定义 NOP3
CHECKSEQUENCEVERIFY 重定义既有的 NOP3 操作码。执行时,只要下列任一条件为真,脚本解释器即终止并报错:
- 栈为空;或
- 栈顶元素小于 0;或
- 栈顶元素的禁用标志(disable flag,1 « 31)未置起,且同时有下列情形之一:
- 交易版本小于 2;或
- 该交易输入的序列号禁用标志(1 « 31)已置起;或
- 相对锁定的类型不一致(一方按区块高度计、另一方按区块时间计);或
- 经 BIP-68 掩码处理后,栈顶元素大于该交易输入的序列号。
以上都不成立时,脚本继续执行,如同执行了一个 NOP。它本身不弹出栈顶元素——原文例题里通常写成 CHECKSEQUENCEVERIFY DROP,或利用 VERIFY 形态的变体把检查与签名合并。
4.2 与 BIP-68 共用的位定义
- SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 « 31):置起时,nSequence 不按相对锁定解释;
- SEQUENCE_LOCKTIME_TYPE_FLAG = (1 « 22):置起时相对锁定以 512 秒为单位计时间,否则以 1 个区块为粒度计高度;
- SEQUENCE_LOCKTIME_MASK = 0x0000ffff:从序列号字段中提取锁定值的掩码。
比较时先用类型位与掩码把两边对齐,“苹果对苹果”再比大小:操作数经掩码后大于输入序列号经掩码后的值,即失败。特别地:若操作数本身置起了禁用标志,CHECKSEQUENCEVERIFY 直接表现为 NOP——这是为未来软分叉扩展预留的退出口。
4.3 为什么间接比较的是“年龄”
BIP-68 保证:一笔交易只有在其每个输入都达到所声明的相对年龄(按块高或块时间计)后,才可能被选进区块。脚本把自己的参数与该输入的 nSequence 比较,等于间接核验了“被花输出的最小年龄”——年龄未到时,任何包含这条 CHECKSEQUENCEVERIFY 的执行路径都会验证失败,交易也就进不了区块。
4.4 部署
以 BIP-9(versionbits)的第 0 位部署,且原文写明须与 BIP-68、BIP-113 用同一机制同时部署:
- 主网:starttime 为 2016 年 5 月 1 日 00:00 UTC(Unix 时间戳 1462060800),timeout 为 2017 年 5 月 1 日 00:00 UTC(1493596800);
- 测试网:starttime 为 2016 年 3 月 1 日 00:00 UTC(1456790400),timeout 同为 2017 年 5 月 1 日 00:00 UTC。
五、谁必须动
普通持币人:不必改动钱包或交易习惯。不使用相对锁定脚本的资金完全不受影响。
钱包开发者:要构造带相对延时的合约(托管、通道、HTLC)时,须正确设置交易版本(≥2)、各输入的 nSequence(类型与数值、不得误置禁用标志),并在赎回脚本中按掩码语义写 CHECKSEQUENCEVERIFY 参数。
全节点与矿工:升级节点按新语义执行 NOP3 并执行 BIP-68 的选块约束;未升级节点把 NOP3 仍当无操作——这是软分叉的典型形态,新规则下能花的交易在旧节点眼里更宽松。
共识层:新增一条脚本级的相对时间锁检查。它收紧的是“哪些脚本执行路径此刻有效”,不改变货币发行、区块大小等其他规则。
六、现在算不算数,以及一个常见误读
档案状态写作 Deployed,站点状态为已激活:它与 BIP-68、BIP-113 同批部署,是今天仍在生效的共识规则。支付通道与闪电网络式的“先延时、给对方留响应窗口”的构造,其脚本地基就是这个操作码。注意区分档案用词:preamble 的 Status 是 Deployed,描述的是这套规则已部署落地,并非一份还在征求意见的草案。
常见误读:
“CHECKSEQUENCEVERIFY 检查的是交易自己的等待时间,谁广播都一样要等。”
不准确。它检查的是被花费的那个输出自确认以来达到的年龄,且只作用于包含它的那条脚本路径:同一输出的不同支路可以有不同的延时,另一条满足其他条件的支路可以立即花费。延时的起算点是资金交易确认,而不是脚本写就或交易签名的时刻——这正是它区别于 BIP-65 绝对锁定的地方。
七、延伸
- 阅读版:https://bips.dev/112
- 官方原文:https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki
- 相关:BIP-65 OP_CHECKLOCKTIMEVERIFY、BIP-68 相对锁定时间、BIP-113 中位过去时间、BIP-9 version bits
- 仓库总目录:https://github.com/bitcoin/bips
