背景图

BIP-65 OP_CHECKLOCKTIMEVERIFY

抽象

该BIP为比特币脚本系统描述了一个新的操作码(OP_CHECKLOCKTIMEVERIFY),该操作码允许交易输出在未来的某个点之前变得不可花费。

概要

CHECKLOCKTIMEVERIFY重新定义了现有的NOP2操作码(其实就是OP_CHECKLOCKTIMEVERIFY命令替换了OP_NOP2命令)。执行时,如果以下任何一个条件成立,则脚本解释器将以错误终止:

  • 堆栈是空的;
  • 堆栈中的顶层项目小于0;
  • 顶层堆栈项的锁定时间类型(高度vs.时间戳)与nLockTime字段不同;
  • 顶部堆栈项大于事务的nLockTime字段;
  • txin(交易输入)的nSequence字段是0xffffffff;

否则,脚本执行将继续,如同NOP执行一样。

交易中的nLockTime字段可防止事务被挖掘,直到达到某个块高度或块时间为止。通过将传给CHECKLOCKTIMEVERIFY的参数与nLockTime字段进行比较,我们间接验证是否已达到所需的块高度或块时间; 直到该块高度或块时间已经达到,交易输出仍然不可花费。

动机

交易中的nLockTime字段可用于证明 将来可以花费这笔交易输出,方法是构造一个有效的交易开销,并对nLockTime字段进行设置。

然而,nLockTime字段不能证明在未来的某个时间内不可能花费交易输出,因为无法知道是否创建了支出该输出的其他交易的有效签名。

第三方托管

如果Alice和Bob联合经营一家企业,他们可能希望确保所有资金都保存在需要双方合作支出的二分之二的多重(多签名的交易)交易输出中。但是,他们认识到,在特殊情况下,例如任何一方受到“巴士撞击”,他们都需要备份计划来检索资金。因此,他们任命他们的律师Lenny担任第三方。

在任何时候,Lenny都可以与Alice或Bob合谋窃取资金,这是一个标准的2分之3 多签名。同样,Lenny可能宁愿不立即获得资金,以阻止居心不良的人试图暴力获取他的密钥。

但是,使用CHECKLOCKTIMEVERIFY可以将资金存储在以下格式的scriptPubKeys中:

1
2
3
4
5
6
7
8
IF
<now + 3 months> CHECKLOCKTIMEVERIFY DROP
<Lenny's pubkey> CHECKSIGVERIFY
1
ELSE
2
ENDIF
<Alice's pubkey> <Bob's pubkey> 2 CHECKMULTISIG

在任何时候,资金都可以用下面的脚本来支付:

1
0 <Alice's signature> <Bob's signature> 0

Lenny经过3个月后,Alice或Bob中的一个可以用以下脚本支付资金:

1
0 <Alice/Bob's signature> <Lenny's signature> 1

非交互式时间锁定退款

存在许多协议,其中创建交易输出,这需要双方的合作来花费输出。为确保一方的失败不会导致资金损失,退款交易使用nLockTime提前设置。这些退款交易需要交互式创建,此外,目前易受交易延展性影响。CHECKLOCKTIMEVERIFY可用于这些协议,用非交互式设置取代交互式设置,另外,使交易延展性不成问题。

双因素钱包

诸如GreenAddress之类的服务将比特币存储为2比2的多签名脚本ScriptPubKey,使得一个密钥对由用户控制,另一个密钥对由服务控制。为了花费资金,用户使用本地安装的生成所需签名之一的钱包软件,然后使用第二因素身份验证方法来授权该服务创建第二个SIGHASH_NONE签名,该签名在将来的某个时间被锁定,并向用户发送该存储签名。如果用户需要花费资金并且服务不可用,他们会等到nLockTime过期。

问题是,在许多情况下,用户将不会拥有一些或全部交易输出的有效签名。使用CHECKLOCKTIMEVERIFY而不是按需创建退款签名而是使用以下形式的scriptPubKeys:

1
2
3
4
5
6
IF
<service pubkey> CHECKSIGVERIFY
ELSE
<expiry time> CHECKLOCKTIMEVERIFY DROP
ENDIF
<user pubkey> CHECKSIG

现在,用户总是能够在没有服务合作的情况下花费他们的资金,等待到期时间的到来。

付款渠道

Jeremy Spilman风格的支付渠道首先设置一个存款,由2-of-2的多签名,tx1控制的存款,然后调整第二个交易tx2,将tx1的输出用于支付者和收款者。在发布tx1之前,创建一个退款交易tx3,以确保收款人是否可以清除付款人的存款。创建退款交易的过程目前易受交易延展性攻击的影响,此外还要求付款人存储退款。使用与双因子钱包示例中相同的scriptPubKey形式解决了这两个问题。

用于发布数据的无信任支付

PayPub协议可以通过首先证明加密文件包含所需数据,然后制作用于付款的scriptPubKeys来支付以无信任方式付款的信息,以便花费它们显示数据的加密密钥。然而,现有的实现有一个重大缺陷:发布者可以无限期地推迟密钥的发布。

这个问题可以用退款交易技术交互地解决; 使用CHECKLOCKTIMEVERIFY,可以使用以下形式的scriptPubKeys以非交互方式解决问题:

1
2
3
4
5
6
7
IF
HASH160 <Hash160(encryption key)> EQUALVERIFY
<publisher pubkey> CHECKSIG
ELSE
<expiry time> CHECKLOCKTIMEVERIFY DROP
<buyer pubkey> CHECKSIG
ENDIF

数据的买家现在正在提供一个有效期限的安全报价。如果发行商在到期时间到期之前未能接受报价,买家可以通过消费输出来取消报价。

证明牺牲矿工的收费

证明牺牲一些有限的资源是各种密码协议中的常见技术。已经提出将硬币的牺牲证明为采矿费,作为牺牲可以指向的普遍公共物品,而不是简单地摧毁硬币。然而,这样做并非微不足道,即使是最好的现有技术 - 宣布 - 承诺 - 也会鼓励矿业集中。CHECKLOCKTIMEVERIFY可用于创建任何人都可以花费的产出(因此,假设矿工的行为是理想的和理性的,那么开采费),但只有在未来足够远的时间,大型矿工才能以折扣销售牺牲品。

冻结资金

除了使用冷存储,硬件钱包和P2SH multisig输出来控制资金之外,现在资金可以直接在区块链中冻结在UTXO中。使用下面的scriptPubKey,在提供的失效时间之前,没有人能够使用安全输出。这种可靠地冻结资金的能力在需要减少胁迫或没收风险的情况下可能会有用。

1
<expiry time> CHECKLOCKTIMEVERIFY DROP DUP HASH160 <pubKeyHash> EQUALVERIFY CHECKSIG

完全替换nLockTime字段

另外,请注意如果SignatureHash()算法可以选择覆盖脚本的一部分,那么签名可能会要求脚本Sig包含CHECKLOCKTIMEVERIFY操作码,并且还需要执行它们。(CODESEPARATOR操作码非常接近于在比特币的v0.1中实现这一点)。这种每签名功能可以完全取代每个交易的nLockTime字段,因为有效签名现在可以证明交易输出可以花费。

详细规则

参考下面转载的参考实现,了解这些语义的精确语义和详细基本原理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
case OP_NOP2:
{
// CHECKLOCKTIMEVERIFY
//
// (nLockTime -- nLockTime )

if (!(flags & SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY))
break; // not enabled; treat as a NOP

if (stack.size() < 1)
return false;

// Note that elsewhere numeric opcodes are limited to
// operands in the range -2**31+1 to 2**31-1, however it is
// legal for opcodes to produce results exceeding that
// range. This limitation is implemented by CScriptNum's
// default 4-byte limit.
//
// If we kept to that limit we'd have a year 2038 problem,
// even though the nLockTime field in transactions
// themselves is uint32 which only becomes meaningless
// after the year 2106.
//
// Thus as a special case we tell CScriptNum to accept up
// to 5-byte bignums, which are good until 2**32-1, the
// same limit as the nLockTime field itself.
const CScriptNum nLockTime(stacktop(-1), 5);

// In the rare event that the argument may be < 0 due to
// some arithmetic being done first, you can always use
// 0 MAX CHECKLOCKTIMEVERIFY.
if (nLockTime < 0)
return false;

// There are two types of nLockTime: lock-by-blockheight
// and lock-by-blocktime, distinguished by whether
// nLockTime < LOCKTIME_THRESHOLD.
//
// We want to compare apples to apples, so fail the script
// unless the type of nLockTime being tested is the same as
// the nLockTime in the transaction.
if (!(
(txTo.nLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) ||
(txTo.nLockTime >= LOCKTIME_THRESHOLD && nLockTime >= LOCKTIME_THRESHOLD)
))
return false;

// Now that we know we're comparing apples-to-apples, the
// comparison is a simple numeric one.
if (nLockTime > (int64_t)txTo.nLockTime)
return false;

// Finally the nLockTime feature can be disabled and thus
// CHECKLOCKTIMEVERIFY bypassed if every txin has been
// finalized by setting nSequence to maxint. The
// transaction would be allowed into the blockchain, making
// the opcode ineffective.
//
// Testing if this vin is not final is sufficient to
// prevent this condition. Alternatively we could test all
// inputs, but testing just this input minimizes the data
// required to prove correct CHECKLOCKTIMEVERIFY execution.
if (txTo.vin[nIn].IsFinal())
return false;

break;

}

https://github.com/petertodd/bitcoin/commit/ab0f54f38e08ee1e50ff72f801680ee84d0f1bf4

部署

我们重用BIP66中使用的双阈值IsSuperMajority()切换机制,其阈值相同,但nVersion=4。新规则对于nVersion=4的每个块(高度为H)有效,并且至少有750之前的块(高度为H-1000..H-1)的nVersion>=4。此外,当块之前的1000个块中的950个具有nVersion>=4时,nVersion<4块将变为无效,并且全部进一步的阻止执行新的规则。

应该注意的是,BIP9涉及永久性地将高位设置为1,这导致nVersion>=所有先前的IsSuperMajority()软分叉,因此nVersion中的位不会永久丢失。

SPV客户

尽管SPV客户端(目前)无法验证块,但相信矿工为他们进行验证,但他们能够验证块头并因此可以验证部署规则的子集。如果达到95%的阈值,如果1000个前面的块中的950个nVersion>=4,则SPV客户端应拒绝nVersion<4块,以防止未升级的矿工剩余的5%发生虚假确认。

积分

感谢格雷戈里麦克斯韦提出将参数与每个交易的nLockTime进行比较,而不是当前块的高度和时间。

参考

PayPub

  • https://github.com/unsystem/paypub

Jeremy Spilman付款渠道

  • https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2013-April/002433.html

实现

Python / python-bitcoinlib

  • https://github.com/petertodd/checklocktimeverify-demos

JavaScript / Node.js / bitcore

  • https://github.com/mruddy/bip65-demos

版权

该文件置于公共领域。

引用和参考

BIP65:检查锁定时间验证
bips/bip-0065.mediawiki

0%