该BIP (Bitcoin Improvement Proposal) 描述了比特币脚本系统中的一个新的操作码 OP_CHECKLOCKTIMEVERIFY,允许交易输出在未来的某个时间点之前保持不可花费状态。它重新定义了现有的 NOP2 操作码,并通过与交易的 nLockTime 字段进行比较,验证所需的块高度或块时间是否已达到。
forked from bitcoin/bips
bip-anyprevout
搜索此仓库
/
复制路径
Blame 更多文件操作
Blame 更多文件操作
2018年3月5日
12a9819 · 2018年3月5日
打开提交详情
338 行 (247 loc) · 12.9 KB
/
顶部
预览
代码
Blame
338 行 (247 loc) · 12.9 KB
复制 raw 文件
下载 raw 文件
Outline
编辑和 raw 操作
BIP: 65
Layer: Consensus (soft fork)
Title: OP_CHECKLOCKTIMEVERIFY
Author: Peter Todd <pete@petertodd.org>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0065
Status: Final
Type: Standards Track
Created: 2014-10-01
License: PD
## 目录<br>Permalink: 目录<br>- 摘要<br>- 概要<br>- 动机 <br> - Escrow<br> - 非交互式时间锁定退款 <br> - 双因素钱包<br> - 支付通道<br> - 用于发布数据的无需信任的支付<br> - 证明对矿工费用的牺牲<br> - 冻结资金<br> - 完全替换 nLockTime 字段<br>- 详细规范<br>- 部署 <br> - SPV 客户端<br>- 致谢<br>- 参考文献<br>- 实现<br>- 版权 |
此 BIP 描述了比特币脚本系统的一个新操作码 (OP_CHECKLOCKTIMEVERIFY),它允许将交易输出设置为在未来的某个时间点之前不可花费。
CHECKLOCKTIMEVERIFY 重新定义了现有的 NOP2 操作码。执行时,如果以下任何条件为真,脚本解释器将终止并报错:
否则,脚本执行将继续,就像执行了 NOP 一样。
交易中的 nLockTime 字段阻止交易被挖掘,直到达到某个区块高度或区块时间。通过将 CHECKLOCKTIMEVERIFY 的参数与 nLockTime 字段进行比较,我们间接验证是否已达到所需的区块高度或区块时间;在达到该区块高度或区块时间之前,交易输出仍然不可花费。
交易中的 nLockTime 字段可用于证明可能在未来花费一个交易输出,通过构造一个有效的交易,设置 nLockTime 字段来花费该输出。
然而,nLockTime 字段无法证明在未来的某个时间之前不可能花费一个交易输出,因为无法知道是否已经创建了用于花费该输出的不同交易的有效签名。
如果 Alice 和 Bob 共同经营一家企业,他们可能希望确保所有资金都保存在 2-of-2 多重签名交易输出中,这些输出需要双方合作才能花费。但是,他们认识到,在特殊情况下,例如任何一方“被公共汽车撞倒”,他们需要一个备用计划来取回资金。因此,他们指定他们的律师 Lenny 作为第三方。
在任何时候,使用标准的 2-of-3 CHECKMULTISIG,Lenny 都可以与 Alice 或 Bob 合谋,非法窃取资金。同样,Lenny 可能不希望立即访问这些资金,以阻止不良行为者试图通过武力从他那里获取密钥。
但是,使用 CHECKLOCKTIMEVERIFY,资金可以存储在以下形式的 scriptPubKey 中:
IF
<现在 + 3 个月> CHECKLOCKTIMEVERIFY DROP
<Lenny 的公钥> CHECKSIGVERIFY
1
ELSE
2
ENDIF
<Alice 的公钥> <Bob 的公钥> 2 CHECKMULTISIG
在任何时候,都可以使用以下 scriptSig 花费资金:
0 <Alice 的签名> <Bob 的签名> 0
3 个月后,Lenny 和 Alice 或 Bob 中的任何一个都可以使用以下 scriptSig 花费资金:
0 <Alice/Bob 的签名> <Lenny 的签名> 1
存在许多协议,在这些协议中,创建了一个交易输出,需要双方合作才能花费该输出。为了确保一方的失败不会导致资金丢失,提前使用 nLockTime 设置了退款交易。这些退款交易需要以交互方式创建,此外,目前容易受到交易可延展性的影响。CHECKLOCKTIMEVERIFY 可用于这些协议中,将交互式设置替换为非交互式设置,此外,使交易可延展性不再成为问题。
像 GreenAddress 这样的服务使用 2-of-2 多重签名 scriptPubKey 存储比特币,这样一来,一个密钥对由用户控制,另一个密钥对由服务控制。要花费资金,用户使用本地安装的钱包软件生成所需的签名之一,然后使用第二因素身份验证方法授权服务创建第二个 SIGHASH_NONE 签名,该签名被锁定到未来的某个时间,并将该签名发送给用户进行存储。如果用户需要花费他们的资金且服务不可用,他们会等到 nLockTime 过期。
问题是,在许多情况下,用户将没有一些或所有交易输出的有效签名。使用 CHECKLOCKTIMEVERIFY,而不是按需创建退款签名,而是使用以下形式的 scriptPubKey:
IF
<服务公钥> CHECKSIGVERIFY
ELSE
<到期时间> CHECKLOCKTIMEVERIFY DROP
ENDIF
<用户公钥> CHECKSIG
现在,用户始终能够在无需服务合作的情况下花费他们的资金,只需等待到期时间即可。
Jeremy Spilman 样式的支付通道首先设置一个由 2-of-2 多重签名控制的存款,tx1,然后调整第二个交易,tx2,该交易花费 tx1 的输出以支付给付款人和收款人。在发布 tx1 之前,会创建一个退款交易,tx3,以确保如果收款人消失,付款人可以取回他们的存款。创建退款交易的过程目前容易受到交易可延展性攻击,此外,还需要付款人存储退款。使用与双因素钱包示例中相同的 scriptPubKey 形式可以解决这两个问题。
PayPub 协议可以通过首先证明加密文件包含所需数据,其次设计用于支付的 scriptPubKey,使得花费它们会揭示数据的加密密钥,从而以无需信任的方式支付信息。但是,现有的实现存在一个重大缺陷:发布者可以无限期地延迟密钥的发布。
这个问题可以通过退款交易技术以交互方式解决; 使用 CHECKLOCKTIMEVERIFY,可以使用以下形式的 scriptPubKey 非交互式地解决该问题:
IF
HASH160 <Hash160(加密密钥)> EQUALVERIFY
<发布者公钥> CHECKSIG
ELSE
<到期时间> CHECKLOCKTIMEVERIFY DROP
<购买者公钥> CHECKSIG
ENDIF
数据的购买者现在正在发出具有到期时间的担保要约。如果发布者未能在到期时间之前接受要约,则购买者可以通过花费输出来取消要约。
证明对某些有限资源的牺牲是各种密码学协议中常用的技术。已经有人提出证明将代币牺牲给矿工费用是一种普遍的公共物品,可以将牺牲导向该公共物品,而不是简单地销毁代币。然而,这样做并非易事,即使是现有的最佳技术 - 宣布-提交牺牲 - 也可能鼓励挖矿中心化。CHECKLOCKTIMEVERIFY 可用于创建可证明可由任何人花费(因此对挖矿费用而言,假设矿工以最佳和理性方式行事)的输出,但只有在未来足够远的时间,以至于大型矿工无法以折扣价出售牺牲品。
除了使用冷存储、硬件钱包和 P2SH 多重签名输出来控制资金外,现在资金可以直接在区块链上的 UTXO 中冻结。使用以下 scriptPubKey,在提供的到期时间之前,没有人能够花费受约束的输出。这种可靠地冻结资金的能力在需要降低胁迫或没收风险的情况下可能很有用。
<到期时间> CHECKLOCKTIMEVERIFY DROP DUP HASH160 <pubKeyHash> EQUALVERIFY CHECKSIG
顺便提一下,请注意,如果 SignatureHash() 算法可以选择覆盖 scriptSig 的一部分,则签名可能需要 scriptSig 包含 CHECKLOCKTIMEVERIFY 操作码,此外,还需要执行它们。(CODESEPARATOR 操作码非常接近于使这在比特币的 v0.1 中成为可能)这种每个签名的能力可以完全替换每个交易的 nLockTime 字段,因为有效的签名现在将是交易输出可以花费的证明。
有关这些语义的精确语义和详细原理,请参阅参考实现,如下所示。
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.
// 请注意,在其他地方,数字操作码限制为 -2**31+1 到 2**31-1 范围内的操作数,但是,操作码可以合法地产生超过该范围的结果。 此限制由 CScriptNum 的默认 4 字节限制实现。
// 如果我们保持在该限制范围内,我们将遇到 2038 年问题,即使事务中的 nLockTime 字段本身是 uint32,仅在 2106 年之后才变得毫无意义。
// 因此,作为一种特殊情况,我们告诉 CScriptNum 接受最多 5 字节的 bignum,这对于达到 2**32-1 之前是有效的,这与 nLockTime 字段本身的限制相同。
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.
// 在极少数情况下,由于首先完成了一些算术运算,参数可能 < 0,你始终可以使用 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.
// nLockTime 有两种类型:按区块高度锁定和按区块时间锁定,区别在于 nLockTime < LOCKTIME_THRESHOLD。
// 我们希望比较苹果与苹果,因此除非被测试的 nLockTime 类型与交易中的 nLockTime 相同,否则脚本将失败。
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.
// 最后,如果每个 txin 都通过将其 nSequence 设置为 maxint 来最终确定,则可以禁用 nLockTime 功能,从而绕过 CHECKLOCKTIMEVERIFY。 该交易将被允许进入区块链,从而使操作码无效。
// 测试此 vin 是否不是最终版本足以防止这种情况。 或者,我们可以测试所有输入,但是仅测试此输入可以最大程度地减少证明正确 CHECKLOCKTIMEVERIFY 执行所需的数据。
if (txTo.vin[nIn].IsFinal())
return false;
break;
}
https://github.com/petertodd/bitcoin/commit/ab0f54f38e08ee1e50ff72f801680ee84d0f1bf4
我们重用 BIP66 中使用的双阈值 IsSuperMajority() 切换机制,具有相同的阈值,但对于 nVersion = 4。新规则对每个区块(高度为 H)都有效,其中 nVersion = 4,并且其前面的 1000 个区块(高度为 H-1000..H-1)中至少有 750 个也具有 nVersion >= 4。此外,当一个区块前面的 1000 个区块中有 950 个确实具有 nVersion >= 4 时,nVersion < 4 的区块将变为无效,并且所有后续区块都将强制执行新规则。
应该注意的是,BIP9 涉及永久性地将高阶位设置为 1,这导致 nVersion >= 所有先前的 IsSuperMajority() 软分叉,因此 nVersion 中没有位永久丢失。
虽然 SPV 客户端(目前)通常无法验证区块,而是信任矿工进行验证,但他们能够验证区块头,因此可以验证部署规则的子集。如果 1000 个前面的区块中有 950 个具有 nVersion >= 4,则 SPV 客户端应拒绝 nVersion < 4 的区块,以防止在达到 95% 的阈值时,来自剩余 5% 的未升级矿工的虚假确认。
感谢 Gregory Maxwell 建议将参数与每个交易的 nLockTime 进行比较,而不是与当前的区块高度和时间进行比较。
PayPub
Jeremy Spilman 支付通道
Python / python-bitcoinlib
JavaScript / Node.js / bitcore
本文档已置于公共领域。
- 原文链接: github.com/ajtowns/bips/...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!