BCH分叉时一些现象的观察

"重放攻击 && checkpoint && 难度调整算法"

Posted by gambol on November 19, 2018

概述

BCH沸沸扬扬的分叉事件, 出现了许多名词, 本文对一些名词作解释

重放保护

最近听的最多的名词, 就是重放保护. 什么是重放保护?

什么是重放保护

重放保护,就是让交易只在当前链中有效,并且在其它链中无效。通常在一条链分裂成两条链时,为了让两条链的交易互不影响,需要在生成交易信息的时候,加入不同的标识或者使用不同的交易结构,这样,另一条链的矿工就会验证该交易无效,从而不打包该交易。

重放保护解决什么问题?

由于硬分叉的两条链,它们的地址和私钥生产的算法相同,交易格式也完全相同,因此导致在其中一条链上的交易在另一条链上很可能是完全合法的。

所以你在其中一条链上发起的交易,就可以到另一条链上去重新广播,可能也会得到确认,这就是“重放攻击”。

譬如: 在ETC分叉的初期,没有重放保护,交易所以为ETC不会出现,也没有做分离处理,用户在提取ETH的时候,同时也收到了ETC,然后用户自已把ETH和ETC分离,向交易所充值ETH,再提出ETH,这样就多收到了ETC。

重放保护怎么实现的?

2017年8月1号BCH分叉时,主动添加了重放保护,简单的说,就是在交易签名的数据中,添加了SIGHASH_FORKID=0x40这个资料, 详情参考 github

BSV如果要添加重放保护,只要把0x40改为其它值就可以了。


checkpoint

什么是checkpoint

checkpoint的目的, 是防止重组.

什么是重组

假设一个币, 硬分叉后, 变成了A, B两条链. 在时间1时, A链是较长的链, B较短.
在时间2时, B的链突然比A长了, 这个时候, 原来的很多交易会挪到B那条链上去. 这个过程就叫重组.

checkpoint 怎么解决的?

checkpoint的实现思路很简单, 就是矿工回退计算最长链时, 必须包含checkpoint对应的高度.

对于BCHABC来说, 最近增加了 高度556767, 这个高度是bchabc分叉后挖出来的第一个块.

代码请看这里


BCH 的难度调整算法

引子

bchabc 和 bchsv分叉 , 双方在进行算力比拼. 在这个过程中, bchabc和bchsv爆块的数量有了比较大的差别. 但是据我所知, bch每个block都会调整难度. 既然每个block都会调整难度, 那为什么两条链的长度会有比较大的差别呢?

难度调整算法解释

根据2017-11-1这篇文章 的文章, 显示这个DAA算法的目的是

  1. 如果算力发生变化时, 能很快调整难度
  2. 保证平均600s产生一个block

简单来说, 假设目前高度是1000, 这个调整方案是

  1. 找到最顶端的3个块(高度 998,999, 100), 选择时间在中间的那个块. 正常情况下是999. (疑问: 为什么需要这个操作呢? 莫非999有可能不是中间的时间吗?)
  2. 找到高度在144之前的block(856, 857, 858), 找到时间在中间的那个块. 正常情况下是857.
  3. 计算857 和 999 高度之间的所有work之和, 记为W
  4. 计算857 和 999 高度之间的时间差, 记为 T
  5. Wn = W * 期望的时间(600s) / T
  6. 下一个block的难度 G = (2^ 256 / Wn) - 1

对现在情况的解释

  1. BCH的难度调整的依据是之前的144个block的难度和时间. 正常情况下, 一天产生144个块
  2. 刚开始时, 猛然增加算力, 会短时间产生比较多的块
  3. 如果想继续加快生成的块的速度, 所需的增加更多算力.不值当, 预计这也是为什么bchabc 和bchsv的block 数接近了.

参考代码

  1. 这个整体的代码在 这里.

  2. 选择block的算法

 /**
 * To reduce the impact of timestamp manipulation, we select the block we are
 * basing our computation on via a median of 3.
 */
static const CBlockIndex *GetSuitableBlock(const CBlockIndex *pindex) {
    assert(pindex->nHeight >= 3);
     /**
     * In order to avoid a block is a very skewed timestamp to have too much
     * influence, we select the median of the 3 top most blocks as a starting
     * point.
     */
    const CBlockIndex *blocks[3];
    blocks[2] = pindex;
    blocks[1] = pindex->pprev;
    blocks[0] = blocks[1]->pprev;
     // Sorting network.
    if (blocks[0]->nTime > blocks[2]->nTime) {
        std::swap(blocks[0], blocks[2]);
    }
     if (blocks[0]->nTime > blocks[1]->nTime) {
        std::swap(blocks[0], blocks[1]);
    }
     if (blocks[1]->nTime > blocks[2]->nTime) {
        std::swap(blocks[1], blocks[2]);
    }
     // We should have our candidate in the middle now.
    return blocks[1];
}