内容简介:时间锁功能让比特币交易拥有了时间维度,这篇文章介绍时间锁的详细内容本文也是介绍比特币交易内幕细节的最后一篇文章为了理解时间锁,需要提前介绍一点区块的概念
时间锁功能让比特币交易拥有了时间维度,这篇文章介绍时间锁的详细内容
本文也是介绍比特币交易内幕细节的最后一篇文章
区块
为了理解时间锁,需要提前介绍一点区块的概念
你可以把区块(Block)想象成一个箱子,里面装着交易
这个“箱子”
- 可以按先来后到的顺序,用 高度 标识,第一个箱子的高度为
0
- 也可以用哈希标识,相当于这个箱子的标签
- 每个箱子,都记录了它 前一个 箱子的标签(哈希)
网络不断产生新的交易,人们根据规则,将这些交易“塞到”新箱子里,随后将它摞在之前整理好的最后一个箱子上
容易想象,网络中的交易,被不断整理到箱子中,这些依次摞起来的箱子,形成了一条 不断延长 的“箱子” 链
比特币网络的总帐本,就是这样的一条链,交易填充区块,一个个区块 前向引用 ,形成 区块链
关于区块和区块链更详细的介绍,放到之后的文章
nLocktime
回忆一下比特币交易的结构
长度(字节) | 描述 |
---|---|
4 | 交易结构的版本 |
1~9 VarInt | 交易包含几个输入,非零正整数 |
变长 | 输入数组 |
1~9 VarInt | 交易包含几个输出,非零正整数 |
变长 | 输出数组 |
4 | nLockTime |
通过最后 4
字节的 nLockTime
字段,可以实现 交易 粒度的时间锁
-
nLockTime = 0
,表示这笔交易没有时间锁定,可以被“随时”写入账本,“即时生效” -
nLockTime < 500000000
,指示块 高度 ,这笔交易在块高度nLockTime
之后,才可以被写入账本 -
nLockTime >= 500000000
,指示具体的 Unix时间戳 ,这笔交易在Unix时间戳nLockTime
之后,才可以被写入账本
Unix时间戳是一种时间表示法,它的值,表示从 1970年1月1日 0时0分0秒 UTC
开始,经历的秒数
你可以用这个 工具 转换, 1546434313
表示的时间为 2019年1月2日 13时5分13秒 UTC
,即北京时间 2019年1月2日 21时5分13秒 UTC+8
设想如下的需求
父亲想立个遗嘱,在自己去世后,儿子可以拥有自己所有的比特币
需要保证
- 父亲在世时,可以随时修改遗嘱
- 父亲过世后,儿子确定可以拿到币
设置交易的 nLockTime
字段,可以实现这样的遗嘱需求
- 父亲计算出自己
80
岁时的Unix时间戳,值为T
- 父亲构造一笔
P2PKH
交易,将自己所有的比特币,付款到儿子的公钥哈希,并设置交易nLockTime
字段的值为T
- 父亲用自己的私钥,对这笔交易签名(设置正确的解锁脚本),将签名后的交易数据交给儿子
这笔交易是合法的,但因为时间锁的设置,即使向网络“展示”这笔交易,它也不会被提前写入账本,转账不会在父亲 80
岁前发生,即儿子不会在父亲 80
岁前,拿到这笔钱
如果父亲去世时没到 80
岁,儿子也可以在未来,在父亲 80
岁这天之后,向网络“展示”交易,拿到这笔钱
如果父亲想修改遗嘱,只需要
nLockTime
完成操作 1
后,原来那些签名过的交易,都会 变得无效 ,因为对应的 UTXO
已经被消费
一般的,Alice签名了一笔交易,付款到Bob的公钥哈希,并将交易的 nLocktime
设为三个月
Alice把这笔交易发送给Bob,此时,两人都知道
UTXO
这正是 nLocktime
的局限性
nLocktime
唯一能保证的,是这笔 交易在时间锁释放之前 , 无法被写入账本 ,即收款人无法在时间锁释放之前,收到资金
交易粒度的 nLocktime
时间锁,只在下列情况满足时,才会释放
-
nLocktime = 0
,没有时间锁 -
nLockTime < 500000000
,且当前的区块高度,已经超过了nLockTime
的值 -
nLockTime >= 500000000
,且当前的Unix时间戳,已经超过了nLockTime
的值
OP_CHECKLOCKTIMEVERIFY
为了改善交易 nLocktime
时间锁的局限性,有更细粒度的控制,时间锁需要跟 UTXO
关联,即放到锁定脚本中
2015年12月, BIP-65 引入了全新的操作码 OP_CHECKLOCKTIMEVERIFY
( CLTV
,Check Lock Time Verify),来实现 UTXO
粒度的时间锁定
对一般的 P2PKH
,其锁定脚本为
OP_DUP OP_HASH160 [公钥哈希] OP_EQUALVERIFY OP_CHECKSIG
如果一个 UTXO
的锁定脚本是如下的形式
[过期时间] OP_CHECKLOCKTIMEVERIFY OP_DROP OP_DUP OP_HASH160 [公钥哈希] OP_EQUALVERIFY OP_CHECKSIG |<--------------CLTV时间锁--------------->|
我们说,这是一个被 CLTV
锁定的 UTXO
,只能在锁定脚本中的 CLTV
时间锁释放后才可以被消费
其中, [过期时间]
与交易的 nLockTime
字段有相同的格式,指示一个区块高度( < 500000000
),或一个Unix时间戳( >= 500000000
)
即,只有在当前区块高度超过 [过期时间]
,或当前Unix时间戳超过 [过期时间]
时, CLTV
时间锁才会释放,这个 UTXO
才可以被消费
CLTV的幕后细节
逻辑上 CLTV
很好理解,但其工作方式稍显复杂,具体在 BIP-65 里定义,这里也做个说明
回忆一下交易输入的结构,最后 4
字节的 nSequence
字段,这里会用到
长度(字节) | 描述 |
---|---|
32 | 引用的交易哈希,UTXO来自哪笔交易 |
4 | 引用的输出序号,UTXO是那笔交易的第几个输出,从 0 开始计数 |
1~9 VarInt | 后面紧跟的解锁脚本,有多少字节 |
变长 | 解锁脚本的内容 |
4 | nSequence |
如果一笔交易要消费 CLTV
锁定的 UTXO
,需要同时满足下列所有条件
- 输入
nSequence
字段的值,必须小于0xffffffff
- 锁定脚本中
[过期时间]
的值,必须大于等于0
- 这笔交易的
nLockTime
和锁定脚本中的[过期时间]
,必须同时大于等于500000000
或同时小于500000000
,即要么都指示Unix时间戳,要么都指示区块高度 - 这笔交易
nLockTime
字段的值,必须大于等于[过期时间]
的值
你能看到,交易要消费 CLTV
锁定的 UTXO
,需要配合使用 nLockTime
字段
这些条件组合有些复杂,与当前区块高度或当前Unix时间戳,好像并没有什么关系
让我们换个角度看,着重关注最后一个条件
对一笔交易
- 为了保证交易能“即时生效”,你需要将
nLockTime
的值,设置为小于等于当前的区块高度或小于等于当前的Unix时间戳,否则这笔交易不会被写入账本 - 为了满足上述最后一个条件,你需要将
nLockTime
的值,设置为大于等于锁定脚本中的[过期时间]
的值,否则时间锁不会释放
画个图,直观的看一下
如果当前时间 Tc
,未到 CLTV
锁定的过期时间 Tb
那么,你找不到这么一个 nLockTime
值 T
,同时满足 T <= Tc
(交易即时生效)且 T >= Tb
(释放时间锁)
如果当前时间 Tc
,超过了 CLTV
锁定的过期时间 Tb
那么, nLockTime
可以被设置为大于等于 Tb
且小于等于 Tc
的任意值,一般的,都直接设置为当前的区块高度或当前的Unix时间戳 Tc
对于
- 使用
nLockTime
字段的交易时间锁 - 使用
CLTV
操作码的UTXO
时间锁
这两种时间锁都是 指定某个具体的绝对时间点 为过期时间的 绝对时间 锁
nSequence
nSequence
字段最初被设计为,标识某些还未被写入账本(仍在内存池中)的交易,允许它们在之后被更新
- 如果某个交易的输入,其
nSequence
字段的值,小于0xffffffff
,表示这笔交易尚未“确定”,还不是最终版本 - 这笔交易会被暂时搁置,直到被另一个消耗了同样输入的,并且有一个更大
nSequence
值的交易替换 - 直到收到
nSequence
值为0xffffffff
的交易,才认为这笔交易已经准备就绪,可以随时被写入账本
但这个功能没有实现,从未在系统中使用过
BIP-68 通过复用交易输入的 nSequence
字段,实现 交易 粒度的 相对时间 锁
相对时间的意思是,从 被引用的交易写入账本 后,经过的时间
- 高度
10000
的区块中有一笔交易,创建了一个UTXO
- 你现在创建一笔新交易消费这个
UTXO
,并设置新交易输入nSequence
字段的值为100
个区块
那么,这笔新交易不会被写入账本,除非当前区块高度已经达到或超过 10100
nSequence
是输入的一个字段,交易可以包含多个输入
只有在满足了 所有输入 上的 nSequence
相对时间锁(如果有)要求后, 交易 才被认为合法,才会被写入账本
根据 BIP-68 的设计,如果 nSequence
字段的值小于 2^31
( 0x80000000
),表示这是一笔激活了 nSequence
相对时间锁的交易
用 nSequence
表示相对时间锁的过期时间,格式上与 nLockTime
和 CLTV
略有不同
对于这个 4
字节的值
- 最高位第
31
位,作为开关,为1
表示 禁用 相对时间锁 - 第
22
位作为类型标志,为1
指示 多少个512
秒 ,为0
指示 多少个区块 - 低
16
位,作为值
如果 nSequence
的值为 0x0040005a
31 22 15 0 | | | | 0000 0000 0100 0000 0000 0000 0101 1010
可以知道
- 第
31
位是0
,启用了相对时间锁 - 第
22
位是1
,指示多少个512
秒 - 低
16
位是0x005a
,值为90
- 超时时间是
90 * 512 = 46080
秒
整理下 nSequence
的不同情况
- 等于
2^32 - 1
,即等于0xffffffff
,表示没有设置任何时间锁 - 小于
2^32 - 1
,表示启用了nLockTime
或CLTV
绝对时间锁,一般都设置为0xfffffffe
- 小于
2^31
,即小于0x80000000
,表示启用了nSequence
相对时间锁
OP_CHECKSEQUENCEVERIFY
通过 BIP-112 引入的全新操作码 OP_CHECKSEQUENCEVERIFY
( CSV
,Check Sequence Verify),可以实现 UTXO
粒度的相对时间锁定
锁定脚本,形如
[过期时间] OP_CHECKSEQUENCEVERIFY OP_DROP OP_DUP OP_HASH160 [公钥哈希] OP_EQUALVERIFY OP_CHECKSIG |<---------------CSV时间锁--------------->|
简单的说,被 CSV
锁定的 UTXO
,从 创建它的交易被写入账本 开始计时,只有在经过一定的秒数或一定的区块数后,这个 UTXO
才可以被消费
与 CLTV
类似,在消费一个 CSV
锁定的 UTXO
时,要求
- 交易的
nVersion
字段的值,必须大于等于2
- 输入
nSequence
字段的值,必须小于0x800000000
- 锁定脚本中
[过期时间]
的值,必须大于等于0
且小于0x800000000
- 输入
nSequence
和锁定脚本中的[过期时间]
,要么都指示秒数,要么都指示区块数 - 输入
nSequence
的值,必须大于等于[过期时间]
的值
总结
这篇文章介绍了四种比特币交易时间锁
- 基于交易粒度的
nLockTime
绝对时间锁,在达到指定的区块高度或具体的Unix时间戳前,这笔交易不会被写入账本 - 基于交易粒度的
nSequence
相对时间锁,这笔交易不会被写入账本,除非其输入引用的那笔已经被写入账本的交易,经过了指定的时间或区块数 - 基于
UTXO
粒度的CLTV
绝对时间锁,在达到指定的区块高度或具体的Unix时间戳前,这笔UTXO
无法被消费 - 基于
UTXO
粒度的CSV
相对时间锁,在创建这个UTXO
的交易写入帐本后,除非经过了指定的时间或区块数,否则这个UTXO
无法被消费
关于 Median-Time-Past
和 Fee Sniping
,因为需要理解区块和挖矿的相关内容,所以我把他们放在之后的文章中再介绍
时间锁的概念非常好理解,但细节繁多略显晦涩,建议你在阅读本文的同时,也看看下面列出的资料
以上所述就是小编给大家介绍的《[学习笔记] 比特币交易的时间锁》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- golang学习笔记6:时间和日期
- php 学习笔记之日期时间操作一箩筐
- 剑指offer题解笔记:时间效率和空间效率的平衡
- Flink 源码阅读笔记(十二):时间、定时器和窗口
- 《Java8实战》-第十二章笔记(新的日期和时间API)
- 解惑3:时间频度,算法时间复杂度
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。