BIP: 9
Title: Version bits with timeout and delay
Author: Pieter Wuille
Peter Todd
Greg Maxwell
Rusty Russell
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0009
Status: Final
Type: Informational
Created: 2015-10-04
License: PD
```
## 摘要
本文档规定了对比特币块中“版本”字段语义的拟议更改,允许并行部署多个向后兼容更改(进一步称为“软叉”)。 它依赖于将版本字段解释为位向量,其中每个位可用于跟踪独立变化。 这些在每个重新定位期间都被计算。 一旦共识改变成功或超时,就会有一个“休闲”暂停,之后可以将该位重新用于以后的更改。
## 动机
BIP 34引入了一种在没有预定义的标志时间戳(或标志块高度)的情况下进行软分叉更改的机制,而是依靠测量由块标题中较高版本号指示的矿工支持。因为它依赖于将版本号作为整数进行比较,它只支持一次单一的更改,需要在提案之间进行协调,并且不允许永久拒绝:只要一个软叉没有完全展开,没有未来一个可以安排。
此外,BIP 34在其95%阈值达到后,进行了整数比较(nVersion> = 2)一致性规则,从有效版本号集合中删除231 + 2个值(所有负数,因为nVersion被解释为签名整数,以及0和1)。这表明这种方法的另一缺点:每次升级会永久限制一组允许的nVersion字段值。这种方法后来在BIP 66和BIP 65中重新使用,这进一步取消了nversions 2和3作为有效选项。如将进一步显示的,这是不必要的。
## 规范
每个软叉部署由以下每个链参数指定(下面进一步阐述):
1.该**name**指定了对软叉的非常简短的描述,合理用作标识符。 对于单个BIP中描述的部署,建议使用名称“bipN”,其中N是相应的BIP号。
2.该**bit**确定块的nVersion字段中的哪个位将用于指示软叉锁定和激活。 它从集合{0,1,2,...,28}中选择。
3.**starttime**指定该位获得其含义的块的最小中值时间。
4.**timeout**指定部署被认为失败的时间。 如果块的中间时间> = timeout,并且软分支尚未锁定(包括该块的位状态),则该块的所有后代都认为部署失败。
### 选择指南
建议为软叉选择这些参数的以下准则:
1.应该选择**name**,使得没有两个软体,并发或以其他方式使用相同的名称。
2.应该选择**bit**,使得没有两个并发软核使用相同的位。
3.**starttime**应该设置在将来某个日期,大约在软件发布日期后的一个月,包括软叉。这允许一些释放延迟,同时防止由于各方运行预发布软件的触发。
4.启动后**timeout**应为1年(31536000秒)。
只要启动时间在上一次的超时或激活之后,使用相同位的后续部署是可能的,但是在必要的时候它是不鼓励的,甚至建议在其间进行暂停以检测错误的软件。
### 状态
对于每个块和软叉,我们关联一个部署状态。 可能的状态是:
1.DEFINED是每个软叉开始的第一个状态。 在这种状态下,每个部署的根源就是定义。
2.STARTED于开始时间的块。
3.LOCKED_IN在第一重新目标周期之后的一个重新定位期间,其中STARTED块至少具有nVersion中的关联位设置。
4.在LOCKED_IN重新定位期后的所有块都为ACTIVE。
5.失败超过超时时间的一个重新定位时间,如果未达到LOCKED_IN。
### 位标志
nVersion块头字段将被解释为32位小端整数(如当前),并且在该整数中选择位作为值(1 << N),其中N是位数。
在STARTED状态下的块获得位位置为1的nVersion。这些块的前3位必须为001,因此实际可能的nVersion值的范围为[0x20000000 ... 0x3FFFFFFF](包括端值)。
由于BIP 34,BIP 66和BIP 65设置的约束,我们只有0x7FFFFFFB可能的nVersion值可用。 这限制了我们至多30个独立部署。 通过将前3位限制为001,为了此提案的目的,我们得到29个,并支持两个未来升级的不同机制(最高位010和011)。 当块nVersion没有顶部位001时,为了部署的目的,它被视为所有位都为0。
矿工应该继续在LOCKED_IN阶段设定一点,所以吸收是可见的,尽管这对共识性规则没有影响。
### 新的共识规则
对于具有ACTIVE状态的每个块,强制执行每个软叉的新共识规则。
### 状态转换
![image](https://github.com/bitcoin/bips/raw/master/bip-0009/states.png)
起源块定义了每个部署,顾名思义状态。
State GetStateForBlock(block) {
if (block.height == 0) {
return DEFINED;
}
重新定位期间内的所有块都具有相同的状态。 这意味着如果`floor(block1.height / 2016)= floor(block2.height / 2016)`,则保证每个部署具有相同的状态。
if ((block.height % 2016) != 0) {
return GetStateForBlock(block.parent);
}
否则,下一个状态依赖于以前的状态:
switch (GetStateForBlock(GetAncestorAtHeight(block, block.height - 2016))) {
我们保持初始状态,直到我们通过开始时间或超时。 GetMedianTimePast在下面的代码中是一个块的中间nTime和它的10个前辈。 表达式GetMedianTimePast(block.parent)在上图中称为MTP,并被视为由链定义的单调时钟。
case DEFINED:
if (GetMedianTimePast(block.parent) >= timeout) {
return FAILED;
}
if (GetMedianTimePast(block.parent) >= starttime) {
return STARTED;
}
return DEFINED;
在STARTED状态下,如果超时,我们切换到“失败”。 如果没有,我们计算位设置,并转换到LOCKED_IN,如果过去一段时间内有足够数量的块设置其版本号的部署位。 门槛值≥1916块(2016年95%),或者testnet(2015年75%)≥1512。 过渡到失败优先,否则可能会出现歧义。 在同一位可能有两个不重叠的部署,其中第一个转移到LOCKED_IN,而另一个同时转换到STARTED,这意味着两者都需要设置该位。
请注意,块的状态不依赖于其自身的nVersion; 只有它的祖先。
case STARTED:
if (GetMedianTimePast(block.parent) >= timeout) {
return FAILED;
}
int count = 0;
walk = block;
for (i = 0; i < 2016; i++) {
walk = walk.parent;
if (walk.nVersion & 0xE0000000 == 0x20000000 && (walk.nVersion >> bit) & 1 == 1) {
count++;
}
}
if (count >= threshold) {
return LOCKED_IN;
}
return STARTED;
在重新定位期间LOCKED_IN之后,我们会自动转换到ACTIVE。
case LOCKED_IN:
return ACTIVE;
而ACTIVE和FAILED是终端状态,一旦达到部署状态就会停留。
case ACTIVE:
return ACTIVE;
case FAILED:
return FAILED;
}
}
“`
实现应该注意的是,状态是沿着块链分支来维护的,但是当重组发生时可能需要重新计算。
鉴于特定块/部署组合的状态在当前重定目标周期之前完全由其祖先确定(即,直到并包括其高度为block.height - 1 - (block.height%2016)的祖先),这是可能的 通过缓存由父节点索引的每个2016年的每个块的结果状态,来有效和安全地实现上述机制。
为了支持升级警告,使用“隐式位”mask =(block.nVersion&〜expectedVersion)!= 0来跟踪额外的“未知升级”。只要在nVersion中设置了一个意外的位,掩码将不为零。 当检测到未知升级的LOCKED_IN时,软件应该大声警告即将到来的软叉。 在下一个重新定位期间(当未知升级处于ACTIVE状态)时,应该更加警惕。
模板请求对象扩展为包含一个新项目:
| Key | 必须 | 类型 | 描述 |
| ——– | —–: | :—-: | :—-: |
| rules | No | Array of Strings | 按名称列出受支持的软件配置 |
模板对象也被扩展:
| Key | 必须 | 类型 | 描述 |
| ——– | —–: | :—-: | :—-: |
| rules | Yes | Array of Strings | 按名称列出受支持的软件配置 |
| vbavailable | Yes | Object |一组待处理,支持的软件部署; 每个使用软匙名称作为关键字,软匙位作为其值|
| vbrequired | No | Number | 软件包部署版本位的位掩码服务器需要在提交中启用 |
模板的“版本”键被保留,用于指示服务器对部署的偏好。 如果使用版本比特,“version”必须在[0x20000000 … 0x3FFFFFFF]的版本范围内。 矿工可以清除或设置块版本中的位,不带任何特殊的“可变”键,只要它们列在模板的“vbavailable”中,并且(当需要清除时)不包括在“vbrequired”中。
“规则”中列出的软键盘部署名称或“vbavailable”中的键可能会以“!”为前缀 字符。 没有这个前缀,GBT客户端可以假定规则不会影响模板的使用; 典型的例子是先前有效的交易将不再有效,例如BIP 16,65,66,68,112和113.如果客户端不了解没有前缀的规则,则它可以未修改地用于挖掘。 另一方面,当使用该前缀时,它表示对块结构或生成事务的更微妙的改变; 这样做的例子是BIP 34(因为它修改了硬币基础结构)和141(因为它修改了txid哈希,并增加了对代的交易的承诺)。 不明白以’!’为前缀的规则的客户端 不得尝试处理模板,也不得尝试将其用于未修改的挖掘。
上述机制是非常通用的,并且对于未来的软叉可以进行变化。 以下是可以考虑的一些想法。
修改的门槛 1916年的门槛(基于BIP 34的95%)不需要维护永久,但更改应该对警告系统有影响。 特别地,具有与用于警告系统的锁定阈值不相容的锁定阈值可能具有长期影响,因为警告系统不再依赖于永久可检测的状态。
冲击软叉 在某些时候,可以提出两个相互排斥的软叉。 处理这种情况的天真的方法是永远不会创建实现两者的软件,但这是打赌,至少有一方保证失去。 更好的是编码“软叉X不能被锁定”作为冲突的软叉的一致规则 - 允许支持两者但不能触发冲突变化的软件。
多级软叉 现在的软叉通常被视为布尔:它们从一个非活动状态到一个活动状态。 也许在某些时候,需要一个具有更多阶段的变化,附加的验证规则逐个启用。 上述机制可以通过将位的组合解释为整数而不是隔离位来适应这一点。 警告系统与此兼容,因为(nVersion&〜nExpectedVersion)将始终为非零以增加整数。
故障超时允许最终重用位,即使软叉从未被激活,因此很明显,该位的新用法指的是新的BIP。 考虑到合理的开发和部署延迟,故意非常细分。 不太可能有足够的失败的建议造成短缺。
在软叉尝试结束时的休闲时间允许检测到错误的客户端,并允许为成功的软叉提供警告和软件升级的时间。
这里可以找到有关部署建议的生活清单。
本文档位于公共领域。