该BIP(Bitcoin Improvement Proposal)描述了一种新的比特币脚本操作码CHECKSEQUENCEVERIFY,结合BIP68,允许基于输出的年龄限制脚本的执行路径。它通过重新定义NOP3操作码,使得可以根据交易的nSequence字段来限制脚本的执行,从而实现诸如带过期时间的合约、追溯失效、HTLCs、双向支付通道和闪电网络等应用。
forked from bitcoin/bips
bip-anyprevout
搜索此仓库
/
复制路径
BlameMore 文件操作
BlameMore 文件操作
May 16, 2018
a5c5c76 · May 16, 2018
打开提交详情
400 行 (296 loc) · 16.4 KB
/
顶部
预览
代码
Blame
400 行 (296 loc) · 16.4 KB
复制原始文件
下载原始文件
大纲
编辑和原始操作
BIP: 112
Layer: Consensus (soft fork)
Title: CHECKSEQUENCEVERIFY
Author: BtcDrak <btcdrak@gmail.com>
Mark Friedenbach <mark@friedenbach.org>
Eric Lombrozo <elombrozo@gmail.com>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0112
Status: Final
Type: Standards Track
Created: 2015-08-10
License: PD
## 目录<br>Permalink: 目录<br>- 摘要<br>- 概要<br>- 动机 <br> - 具有到期截止日期的合约 <br> - 带超时的托管<br> - 追溯失效 <br> - 哈希时间锁定合约<br> - 双向支付通道<br> - 闪电网络<br> - 双向锚定侧链<br>- 规范<br>- 参考实现<br>- 部署<br>- 鸣谢<br>- 参考<br>- 版权 |
这个BIP描述了比特币脚本系统的一个新的操作码(CHECKSEQUENCEVERIFY),结合BIP 68,允许基于被花费的输出的年龄来限制脚本的执行路径。
CHECKSEQUENCEVERIFY 重新定义了现有的 NOP3 操作码。 当执行时,如果以下任何一个条件为真,脚本解释器将以错误终止:
否则,脚本执行将继续,就像执行了 NOP 一样。
BIP 68阻止非最终交易被选择包含在一个区块中,直到相应的输入达到指定的年龄,以区块高度或区块时间衡量。通过将CHECKSEQUENCEVERIFY的参数与nSequence字段进行比较,我们间接验证了被花费的输出的期望的最小年龄;在达到相对年龄之前,任何包括CHECKSEQUENCEVERIFY的脚本执行路径都将无法验证,导致交易无法被选择包含在一个区块中。
BIP 68 重新定义了交易 nSequence 字段的含义,通过赋予 序列号作为相对锁定时间的共识强制语义。然而,没有办法构建比特币脚本来基于这个字段做出决策。
通过使 nSequence 字段可被脚本访问,就有可能构建代码路径,这些路径只有在发布证明后的某些最短时间内才能访问。这使得在分阶段协议中实现广泛的应用,例如托管、支付通道或双向锚定。
一个在资金投入后 30 天自动超时的托管可以通过以下方式建立。Alice、Bob 和 Escrow 创建一个 2-of-3 地址,其 redeemscript 如下。
IF
2 <Alice's pubkey> <Bob's pubkey> <Escrow's pubkey> 3 CHECKMULTISIG
ELSE
"30d" CHECKSEQUENCEVERIFY DROP
<Alice's pubkey> CHECKSIG
ENDIF
在任何时候,都可以使用 Alice、Bob 或 Escrow 中任意两位的签名来花费资金。
30 天后,Alice 可以单独签名。
时钟在支付到托管地址确认后才开始计时。
在许多情况下,我们希望创建在未来事件发生时可以撤销的合约。然而,鉴于区块链的不可变性,实际上不可能追溯地使先前已经确认的承诺无效。我们真正拥有的追溯失效的唯一机制是区块链重组,出于基本安全原因,这种重组被设计得非常困难且成本非常高。
尽管有这个限制,我们确实有一种方法可以提供在功能上类似于追溯失效的东西,同时使用 CHECKSEQUENCEVERIFY 保持过去承诺的不可逆性。通过构建具有多个执行分支的脚本,其中一个或多个分支被延迟,我们提供了一个时间窗口,在此时间窗口内,有人可以提供一个失效条件,允许花费输出,从而有效地使本应延迟的分支失效,并可能阻止另一方首先广播该交易。如果失效条件在超时之前没有发生,则延迟的分支变为可花费的,从而兑现了原始合约。
这个想法的一些更具体的应用:
哈希时间锁定合约 (HTLC) 提供了一种用于链下合约协商的通用机制。可以使执行路径需要知道可以在失效时间窗口内呈现的密钥(哈希原像)。通过共享密钥,可以向交易对手保证交易永远不会被广播,因为这将允许交易对手立即索取输出,而一方必须等待时间窗口过去。如果密钥未被共享,则交易对手将无法使用即时路径,而必须使用延迟路径。
可脚本化的相对锁定时间提供了一个可预测的响应时间,以防交易对手广播已撤销的交易:绝对锁定时间需要关闭通道并在接近超时时重新打开它,而使用相对锁定时间,时钟在交易在一个区块中确认的那一刻开始计时。它还提供了一种手段来精确地知道在非合作交易对手的情况下,在可以将资金从通道中取出之前需要等待多长时间(以区块数计)。
闪电网络扩展了双向支付通道的想法,允许通过多个双向支付通道跳跃来路由支付。
这些通道基于一个锚交易,该交易需要 Alice 和 Bob 的 2-of-2 多重签名,以及一系列可撤销的承诺交易,这些交易花费锚交易。承诺交易将来自锚的资金在 Alice 和 Bob 之间分配,任何一方可以随时发布最新的承诺交易,从而完成通道。
理想情况下,已撤销的承诺交易将永远无法成功花费;并且最新的承诺交易应该能够非常快速地花费。
为了允许有效地撤销承诺交易,Alice 和 Bob 拥有略有不同的最新承诺交易版本。在 Alice 的版本中,承诺交易中支付给 Alice 的任何输出还包括强制延迟,以及一个允许 Bob 在知道该交易的撤销代码的情况下花费该输出的替代分支。在 Bob 的版本中,支付给 Bob 的款项也受到类似的限制。当 Alice 和 Bob 协商新的余额和新的承诺交易时,他们还会透露旧的撤销代码,从而承诺不转发旧的交易。
一个简单的输出,支付给 Alice,可能看起来像这样:
HASH160 <revokehash> EQUAL
IF
<Bob's pubkey>
ELSE
"24h" CHECKSEQUENCEVERIFY DROP
<Alice's pubkey>
ENDIF
CHECKSIG
这允许 Alice 随时发布最新的承诺交易并在 24 小时后花费资金,但同时也确保如果 Alice 转发已撤销的交易,Bob 有 24 小时的时间来索取资金。
使用 CHECKLOCKTIMEVERIFY,这将如下所示:
HASH160 <revokehash> EQUAL
IF
<Bob's pubkey>
ELSE
"2015/12/15" CHECKLOCKTIMEVERIFY DROP
<Alice's pubkey>
ENDIF
CHECKSIG
这种形式的交易意味着,如果锚在 2015/12/16 仍未花费,Alice 即使已被撤销也可以使用此承诺,只需立即花费它,从而不给 Bob 任何时间来索取它。
这意味着通道有一个截止日期,如果不访问区块链就无法将其推迟;而且资金可能要到截止日期才能使用。CHECKSEQUENCEVERIFY 允许你避免做出这样的权衡。
哈希时间锁定合约 (HTLC) 使这稍微复杂一些,因为原则上它们可以支付给 Alice 或 Bob,具体取决于 Alice 是否发现了密钥 R 或是否达到超时,但同样的原则适用 - Alice 的承诺交易中支付给 Alice 的分支会有一个延迟,如果知道撤销密钥,则另一方可以索取整个输出。使用 CHECKSEQUENCEVERIFY,在 Alice 的承诺交易中,可支付给 Alice 的 HTLC 可能如下所示:
HASH160 DUP <R-HASH> EQUAL
IF
"24h" CHECKSEQUENCEVERIFY
2DROP
<Alice's pubkey>
ELSE
<Commit-Revocation-Hash> EQUAL
NOTIF
"2015/10/20 10:33" CHECKLOCKTIMEVERIFY DROP
ENDIF
<Bob's pubkey>
ENDIF
CHECKSIG
相应地,在 Bob 的承诺交易中:
HASH160 DUP <R-HASH> EQUAL
SWAP <Commit-Revocation-Hash> EQUAL ADD
IF
<Alice's pubkey>
ELSE
"2015/10/20 10:33" CHECKLOCKTIMEVERIFY
"24h" CHECKSEQUENCEVERIFY
2DROP
<Bob's pubkey>
ENDIF
CHECKSIG
请注意,CHECKSEQUENCEVERIFY 和 CHECKLOCKTIMEVERIFY 都用于上述的最后一个分支中,以确保 Bob 无法花费输出,直到超时完成并且 Alice 有时间揭示撤销密钥。
请参阅 可部署的闪电网络 论文。
双向锚定侧链需要一个新的 REORGPROOFVERIFY 操作码,其语义超出了本 BIP 的范围。CHECKSEQUENCEVERIFY 用于确保自发布返回锚定以来有足够的时间来发布重组证明:
IF
lockTxHeight <lockTxHash> nlocktxOut [<workAmount>] reorgBounty Hash160(<...>) <genesisHash> REORGPROOFVERIFY
ELSE
withdrawLockTime CHECKSEQUENCEVERIFY DROP HASH160 p2shWithdrawDest EQUAL
ENDIF
有关精确的语义和这些语义的详细原理,请参阅参考实现,如下所示。
/* Below flags apply in the context of BIP 68 */
/* If this flag set, CTxIn::nSequence is NOT interpreted as a
* relative lock-time. */
static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 << 31);
/* If CTxIn::nSequence encodes a relative lock-time and this flag
* is set, the relative lock-time has units of 512 seconds,
* otherwise it specifies blocks with a granularity of 1. */
static const uint32_t SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22);
/* If CTxIn::nSequence encodes a relative lock-time, this mask is
* applied to extract that lock-time from the sequence field. */
static const uint32_t SEQUENCE_LOCKTIME_MASK = 0x0000ffff;
case OP_NOP3:
{
if (!(flags & SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)) {
// not enabled; treat as a NOP3
// 未启用; 视为 NOP3
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) {
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS);
}
break;
}
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
// 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.
// 请注意,其他地方的数字操作码仅限于 -2**31+1 到 2**31-1 范围内的操作数,但是
// 操作码产生超出该范围的结果是合法的。 这种限制由 CScriptNum 的
// 默认 4 字节限制实现。
//
// Thus as a special case we tell CScriptNum to accept up
// to 5-byte bignums, which are good until 2**39-1, well
// beyond the 2**32-1 limit of the nSequence field itself.
// 因此,作为一种特殊情况,我们告诉 CScriptNum 接受最多 5 字节的 bignum,它很好用,直到 2**39-1,远远
// 超过 nSequence 字段本身的 2**32-1 限制。
const CScriptNum nSequence(stacktop(-1), fRequireMinimal, 5);
// In the rare event that the argument may be < 0 due to
// some arithmetic being done first, you can always use
// 0 MAX CHECKSEQUENCEVERIFY.
// 在极少数情况下,由于首先完成了一些算术运算,参数可能 < 0,你始终可以使用
// 0 MAX CHECKSEQUENCEVERIFY。
if (nSequence < 0)
return set_error(serror, SCRIPT_ERR_NEGATIVE_LOCKTIME);
// To provide for future soft-fork extensibility, if the
// operand has the disabled lock-time flag set,
// CHECKSEQUENCEVERIFY behaves as a NOP.
// 为了提供未来的软分叉可扩展性,如果
// 操作数已设置禁用的锁定时间标志,
// CHECKSEQUENCEVERIFY 的行为类似于 NOP。
if ((nSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0)
break;
// Compare the specified sequence number with the input.
// 将指定的序列号与输入进行比较。
if (!checker.CheckSequence(nSequence))
return set_error(serror, SCRIPT_ERR_UNSATISFIED_LOCKTIME);
break;
}
bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) const
{
// Relative lock times are supported by comparing the passed
// in operand to the sequence number of the input.
// 通过将传入的操作数与输入的序列号进行比较来支持相对锁定时间。
const int64_t txToSequence = (int64_t)txTo->vin[nIn].nSequence;
// Fail if the transaction's version number is not set high
// enough to trigger BIP 68 rules.
// 如果交易的版本号设置得不够高而无法触发 BIP 68 规则,则失败。
if (static_cast<uint32_t>(txTo->nVersion) < 2)
return false;
// Sequence numbers with their most significant bit set are not
// consensus constrained. Testing that the transaction's sequence
// number do not have this bit set prevents using this property
// to get around a CHECKSEQUENCEVERIFY check.
// 最高有效位已设置的序列号不受共识约束。 测试交易的序列
// 编号没有设置此位可防止使用此属性
// 绕过 CHECKSEQUENCEVERIFY 检查。
if (txToSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG)
return false;
// Mask off any bits that do not have consensus-enforced meaning
// before doing the integer comparisons
// 在进行整数比较之前,屏蔽掉任何不具有共识强制含义的位
const uint32_t nLockTimeMask = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | CTxIn::SEQUENCE_LOCKTIME_MASK;
const int64_t txToSequenceMasked = txToSequence & nLockTimeMask;
const CScriptNum nSequenceMasked = nSequence & nLockTimeMask;
// There are two kinds of nSequence: lock-by-blockheight
// and lock-by-blocktime, distinguished by whether
// nSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG.
// 有两种 nSequence:按块高度锁定 和按块时间锁定,区别在于
// nSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG。
//
// We want to compare apples to apples, so fail the script
// unless the type of nSequenceMasked being tested is the same as
// the nSequenceMasked in the transaction.
// 我们想比较苹果和苹果,所以如果测试的 nSequenceMasked 的类型与
// 交易中的 nSequenceMasked 相同,则脚本会失败。
if (!(
(txToSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) ||
(txToSequenceMasked >= CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked >= CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG)
))
return false;
// Now that we know we're comparing apples-to-apples, the
// comparison is a simple numeric one.
// 既然我们知道我们正在比较苹果对苹果,那么
// 比较是一个简单的数值比较。
if (nSequenceMasked > txToSequenceMasked)
return false;
return true;
}
以下拉取请求提供了参考实现:
这个 BIP 将使用 bit 0 通过 "versionbits" BIP9 部署。
对于比特币 mainnet,BIP9 的 starttime 将是 2016 年 5 月 1 日午夜 UTC(Epoch 时间戳 1462060800),BIP9 的 timeout 将是 2017 年 5 月 1 日午夜 UTC(Epoch 时间戳 1493596800)。
对于比特币 testnet,BIP9 的 starttime 将是 2016 年 3 月 1 日午夜 UTC(Epoch 时间戳 1456790400),BIP9 的 timeout 将是 2017 年 5 月 1 日午夜 UTC(Epoch 时间戳 1493596800)。
这个 BIP 必须与 BIP68 和 BIP113 同时部署,使用相同的部署机制。
Mark Friedenbach 发明了将序列号应用于实现相对锁定时间的方法,并编写了 CHECKSEQUENCEVERIFY 的参考实现。
参考实现和这个 BIP 很大程度上基于 Peter Todd 为密切相关的 BIP 65 所做的工作。
BtcDrak 编写了这个 BIP 文档。
感谢 Eric Lombrozo 和 Anthony Towns 贡献了示例用例。
BIP 9 Versionbits
BIP 68 通过共识强制序列号实现的相对锁定时间
BIP 65 OP_CHECKLOCKTIMEVERIFY
BIP 113 用于时间锁定约束的中间过去区块时间
使用 OP_CHECKSEQUENCEVERIFY/OP_LOCKTIMEVERIFY 和撤销哈希的 HTLC
本文档置于公共领域。
- 原文链接: github.com/ajtowns/bips/...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!