1. 程式人生 > >比特幣學習(1)難度調整

比特幣學習(1)難度調整

難度值(difficulty)是礦工們在挖礦時候的重要參考指標,它決定了礦工大約需要經過多少次雜湊運算才能產生一個合法的區塊。比特幣的區塊大約每10分鐘生成一個,如果要在不同的全網算力條件下,新區塊的產生保持都基本這個速率,難度值必須根據全網算力的變化進行調整。簡單地說,難度值被設定在無論挖礦能力如何,新區塊產生速率都保持在10分鐘一個。

難度的調整是在每個完整節點中獨立自動發生的。每2016個區塊,所有節點都會按統一的公式自動調整難度,這個公式是由最新2016個區塊的花費時長與期望時長(期望時長為20160分鐘即兩週,是按每10分鐘一個區塊的產生速率計算出的總時長)比較得出的,根據實際時長與期望時長的比值,進行相應調整(或變難或變易)。也就是說,如果區塊產生的速率比10分鐘快則增加難度,比10分鐘慢則降低難度。

1、先上程式碼

unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast)
{
    const unsigned int nTargetTimespan = 14 * 24 * 60 * 60; // 兩週的秒數
    const unsigned int nTargetSpacing = 10 * 60;  //10分鐘
    const unsigned int nInterval = nTargetTimespan / nTargetSpacing;

    // Genesis block
    if (pindexLast == NULL)    //如果為創世區塊,則高度為0,上一區為空,返回最小難度即1
        return bnProofOfWorkLimit.GetCompact();

    // Only change once per interval   
    if ((pindexLast->nHeight+1) % nInterval != 0) //判斷是否到達改變難度的間隔
        return pindexLast->nBits;

    // Go back by what we want to be 14 days worth of blocks
    const CBlockIndex* pindexFirst = pindexLast;
    for (int i = 0; pindexFirst && i < nInterval-1; i++)
        pindexFirst = pindexFirst->pprev;
    assert(pindexFirst);

    // Limit adjustment step   
    unsigned int nActualTimespan = pindexLast->nTime - pindexFirst->nTime; //求得上一次生成2016個區塊的時間
    printf("  nActualTimespan = %d  before bounds\n", nActualTimespan);
    if (nActualTimespan < nTargetTimespan/4)  設定實際花費時間的最大最小值
        nActualTimespan = nTargetTimespan/4;
    if (nActualTimespan > nTargetTimespan*4)
        nActualTimespan = nTargetTimespan*4;

    // Retarget
    CBigNum bnNew;
    bnNew.SetCompact(pindexLast->nBits); //取舊難度值
    bnNew *= nActualTimespan; //bnNew=bnNew*(nActualTimespan(實際花費時間) /nTargetTimespan(計劃花費時間--兩週))
    bnNew /= nTargetTimespan;

    if (bnNew > bnProofOfWorkLimit) //如果新值大於最小工作證明限制值(實際上即難度更小)
        bnNew = bnProofOfWorkLimit;    //則取最小工作量證明限制值
    
    ///綜上,即根據上一次生成2016個區塊所花時間與標準兩週時間進行比較,如果小於,則證明難度需加大,bnNew是一個與難度成反比的值,至於如何利用bnNew來求得實際難度設定,則需繼續研讀程式碼。
    
    /// debug print
    printf("\n\n\nGetNextWorkRequired RETARGET *****\n");
    printf("nTargetTimespan = %d    nActualTimespan = %d\n", nTargetTimespan, nActualTimespan);
    printf("Before: %08x  %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString().c_str());
    printf("After:  %08x  %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString().c_str());

    return bnNew.GetCompact();
}

2、個人在看相關書籍時,總髮現有:

新難度值 = 舊難度值 * ( 過去2016個區塊花費時長 / 20160 分鐘 )

     個人認為,這種描述不準確,根據程式碼所述,bnNew是一個與難度調整值成反比的數,並非是難度越大,該值越大,因此應當稱為“易度值”似乎更合適。