關(guān)于比特幣難度調(diào)整部分見《精通比特幣》8.7.3節(jié)
先介紹一下CBigNum凤优。
CBigNum
CBigNum是openssl庫(kù)中定義的BIGNUM的包裝類。公鑰密碼學(xué)需要能夠處理非常大的整數(shù)姨蝴。標(biāo)準(zhǔn)的數(shù)據(jù)類型無法滿足要求萝嘁。BIGNUM可以存放任意長(zhǎng)度的整型。
CBigNum類的結(jié)構(gòu)并不復(fù)雜。它是由一堆不同類型構(gòu)造BIGNUM的構(gòu)造器組成靡砌,包括char,short珊楼,int通殃,long,int64厕宗,int256画舌,它們unsigned版本和vector<unsigned char>等。它同樣重構(gòu)操作符已慢,例如加曲聂、減、乘佑惠、除朋腋、位操作等。所有的實(shí)際工作代理給了BIGNUM類行膜楷。大部分CBigNum的代碼僅僅是為BIGNUM的函數(shù)準(zhǔn)備輸入數(shù)據(jù)旭咽。
難度調(diào)整
這部分源代碼比較簡(jiǎn)單,如下:
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast)
{
const unsigned int nTargetTimespan = 14 * 24 * 60 * 60; // two weeks
const unsigned int nTargetSpacing = 10 * 60;
const unsigned int nInterval = nTargetTimespan / nTargetSpacing;
// Genesis block
if (pindexLast == NULL)
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;
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 /= nTargetTimespan;
if (bnNew > bnProofOfWorkLimit)
bnNew = bnProofOfWorkLimit;
/// 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();
}
nTargetTimespan 為兩個(gè)星期的秒數(shù)赌厅,nTargetSpacing 為10分鐘的秒數(shù)轻专。nInterval = nTargetTimespan / nTargetSpacing
;即為2016個(gè)區(qū)塊。
難度的調(diào)整是在每個(gè)完整節(jié)點(diǎn)中獨(dú)立自動(dòng)發(fā)生的察蹲。每2,016個(gè)區(qū)塊中的所有節(jié)點(diǎn)都會(huì)調(diào)整難度请垛。難度的調(diào)整公式是由最新2,016個(gè)區(qū)塊的花費(fèi)時(shí)長(zhǎng)與20,160分鐘(兩周,即這些區(qū)塊以10分鐘一個(gè)速率所期望花費(fèi)的時(shí)長(zhǎng))比較得出的洽议。難度是根據(jù)實(shí)際時(shí)長(zhǎng)與期望時(shí)長(zhǎng)的比值進(jìn)行相應(yīng)調(diào)整的(或變難或變易)宗收。簡(jiǎn)單來說,如果網(wǎng)絡(luò)發(fā)現(xiàn)區(qū)塊產(chǎn)生速率比10分鐘要快時(shí)會(huì)增加難度亚兄。如果發(fā)現(xiàn)比10分鐘慢時(shí)則降低難度混稽。
首先函數(shù)會(huì)計(jì)算當(dāng)前區(qū)塊是否到達(dá)下個(gè)難度周期,如果沒有達(dá)到审胚,則難度位即為上一區(qū)塊的難度位匈勋。return pindexLast->nBits;
如果已經(jīng)達(dá)到一個(gè)新的難度周期,則通過循環(huán):
for (int i = 0; pindexFirst && i < nInterval-1; i++)
pindexFirst = pindexFirst->pprev;
向前尋找2016個(gè)區(qū)塊膳叨,此時(shí)pindexFirst指向上一難度周期的第一個(gè)區(qū)塊洽洁。
nActualTimespan = pindexLast->nTime - pindexFirst->nTime
即利用上一難度周期的最后一個(gè)塊的時(shí)間戳減去第一個(gè)區(qū)塊的時(shí)間戳,其結(jié)果為上一難度周期總共花費(fèi)的時(shí)間nActualTimespan 菲嘴。
為了防止難度的變化過快饿自,每個(gè)周期的調(diào)整幅度必須小于一個(gè)因子(值為4)汰翠。如果要調(diào)整的幅度大于4倍,則按4倍調(diào)整昭雌。由于在下一個(gè)2,016區(qū)塊的周期不平衡的情況會(huì)繼續(xù)存在复唤,所以進(jìn)一步的難度調(diào)整會(huì)在下一周期進(jìn)行。因此平衡哈希計(jì)算能力和難度的巨大差異有可能需要花費(fèi)幾個(gè)2,016區(qū)塊周期才會(huì)完成烛卧。
這部分便是將上一難度周期消耗的時(shí)間與期望的時(shí)間作比較佛纫,并根據(jù)結(jié)果進(jìn)行調(diào)整。如果調(diào)整幅度大于4倍总放,則按4倍調(diào)整雳旅。
if (nActualTimespan < nTargetTimespan/4)
nActualTimespan = nTargetTimespan/4;
if (nActualTimespan > nTargetTimespan*4)
nActualTimespan = nTargetTimespan*4;
如果小于4倍則根據(jù)比例進(jìn)行調(diào)整。
bnNew *= nActualTimespan;
bnNew /= nTargetTimespan;
此時(shí)便完成了一個(gè)周期的難度調(diào)整间聊。