比特币探究之工作量证明

比特币采用工作量证明(POW)的方式来确保取得共识。在解析之前,先看一下比特币定义的arith_uint256类,它可以按照预定规则,将32位无符号整数,以类似浮点数的方式转换为一个256位无符号整数:

最前面8位是以256为底的指数,用e表示;
从前面数第9位是符号位,代表正负,用s表示;
尾23位是尾数,用m表示。

那么它代表的数字实际上为:

来看源码src/arith_uint256.cpp中的SetCompact函数,就容易理解了:

//把uint32_t转换为uint256
arith_uint256& arith_uint256::SetCompact(uint32_t nCompact, bool* pfNegative, bool* pfOverflow)
{
    int nSize = nCompact >> 24;  //指数e
    uint32_t nWord = nCompact & 0x007fffff;  //尾数m
    if (nSize <= 3) {
        nWord >>= 8 * (3 - nSize);
        *this = nWord;
    } else {
        *this = nWord;
        *this <<= 8 * (nSize - 3);
    }
    if (pfNegative)
        *pfNegative = nWord != 0 && (nCompact & 0x00800000) != 0;  //是负值吗
    if (pfOverflow)
        *pfOverflow = nWord != 0 && ((nSize > 34) ||
                                     (nWord > 0xff && nSize > 33) ||
                                     (nWord > 0xffff && nSize > 32));  //有没有溢出
    return *this;
}

如何证明完成工作量证明?比特币规定,只有当区块的hash值小于一个预定的难度值时,才被视为完成POW。这个预定难度值用一个uint256表示,最低难度powLimit被设定为0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff,即至少前32位是0。本文写作时,刚刚生成的第532871块的hash值为000000000000000000032d35bd3f1f7ec4417d7e35698cf76daba0f2a5d61db6,前78位都是0,可见当前挖矿难度之高。

现在可以看看CheckProofOfWork函数了,它在src/pow.cpp文件中:

bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params& params)
{
    bool fNegative;
    bool fOverflow;
    arith_uint256 bnTarget;

    //nBits就是当前的难度值,调用SetCompact把它转换成uint256,一个表示难度的hash值
    bnTarget.SetCompact(nBits, &fNegative, &fOverflow);

    //如果是负值,或者溢出,或者小于最低难度,那么检查失败
    if (fNegative || bnTarget == 0 || fOverflow || bnTarget > UintToArith256(params.powLimit))
        return false;

    //大于当前的难度hash,检查失败
    if (UintToArith256(hash) > bnTarget)
        return false;

    //执行到这里,说明hash值比预定的难度hash还要小,说明已完成POW
    return true;
}

上面的函数中,nBits代表挖矿难度,它是从哪里来的呢?回头再看看之前比特币探究之挖矿一文中的CreateNewBlock函数,其中有如下一行:

pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());

GetNextWorkRequired函数同样定义在src/pow.cpp中:

unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
{
    assert(pindexLast != nullptr);
    unsigned int nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact();

    //检查是否到了难度调整周期。比特币规定,每14天(2周)调整一次难度
    //按照平均10分钟的区块生成速度,14天可生成2016个,即每过2016个块调整一次难度
    if ((pindexLast->nHeight+1) % params.DifficultyAdjustmentInterval() != 0)
    {
        if (params.fPowAllowMinDifficultyBlocks)
        {
            //如果新块比上一个块晚了20分钟,那么允许最低难度挖块
            //这个主要用在测试网络里,主网里是不允许的
            if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + params.nPowTargetSpacing*2)
                return nProofOfWorkLimit;
            else
            {
                //返回上一个正常难度
                const CBlockIndex* pindex = pindexLast;
                while (pindex->pprev && pindex->nHeight % params.DifficultyAdjustmentInterval() != 0 && pindex->nBits == nProofOfWorkLimit)
                    pindex = pindex->pprev;
                return pindex->nBits;
            }
        }
        //没到难度调整时间,直接返回上一个块的难度
        return pindexLast->nBits;
    }

    //如果已经到了调整时间,向上回溯找到这一组2016块的首个区块
    int nHeightFirst = pindexLast->nHeight - (params.DifficultyAdjustmentInterval()-1);
    assert(nHeightFirst >= 0);
    const CBlockIndex* pindexFirst = pindexLast->GetAncestor(nHeightFirst);
    assert(pindexFirst);
    //计算新的难度值
    return CalculateNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params);
}

当区块生成难度需要调整时,CalculateNextWorkRequired将被调用,以计算下一组区块的生成难度:

unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params)
{
    if (params.fPowNoRetargeting)
        return pindexLast->nBits;

    //限定调整节奏,正常为14天,最小3.5天,最大56天
    int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
    if (nActualTimespan < params.nPowTargetTimespan/4)
        nActualTimespan = params.nPowTargetTimespan/4;
    if (nActualTimespan > params.nPowTargetTimespan*4)
        nActualTimespan = params.nPowTargetTimespan*4;

    const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
    arith_uint256 bnNew;
    bnNew.SetCompact(pindexLast->nBits);
    //新难度 = 旧难度 × 实际生成时间 / 预定生成时间
    //如果旧难度低了,生成快了,实际生成时间短了,那么新难度自然就会提升
    //所以,随着硬件越来越快,挖矿难度自然越来越大
    bnNew *= nActualTimespan;
    bnNew /= params.nPowTargetTimespan;
    //难度也是有范围的,起码不能比最低难度还容易
    if (bnNew > bnPowLimit)
        bnNew = bnPowLimit;

    return bnNew.GetCompact();
}

写完了发现居然没有图。好吧,最后贴一张矿场图镇个场子、撑撑门面(图片来自百度,如有侵权烦请告知)。
轰鸣的矿场

欢迎转载,请注明出处。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341