溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

PoS權(quán)益證明算法原理及其在點點幣、黑幣中的實現(xiàn)

發(fā)布時間:2020-07-01 05:15:30 來源:網(wǎng)絡(luò) 閱讀:21904 作者:莫名2013 欄目:建站服務(wù)器

  PoS,即Proof of Stake,譯為權(quán)益證明。
  無論PoW或PoS,均可以理解為“誰有資格寫區(qū)塊鏈”的問題。
  PoW通過算力證明自己有資格寫區(qū)塊鏈,而PoS則是通過擁有的幣齡來證明自己有資格寫區(qū)塊鏈。
?

PoW的優(yōu)勢和弊端

?
  PoW,優(yōu)勢為可靠,使用廣泛,是經(jīng)歷了充分的實踐檢驗的公有鏈共識算法。
  但其缺點也較為明顯:
  1、消耗了太多額外算力,即大量能源。
  2、資本大量投資礦機,導(dǎo)致算力中心化,有51%***的安全隱患。
?

PoS的提出和點點幣

?
  第一個基于PoS的虛擬幣是點點幣。
  鑒于PoW的缺陷,2012年Sunny King提出了PoS,并基于PoW和PoS的混合機制發(fā)布了點點幣PPCoin。
  前期采用PoW挖礦開采和分配貨幣,以保證公平。后期采用PoS機制,保障網(wǎng)絡(luò)安全,即擁有51%貨幣難度更大,從而防止51%***。
?
  PoS核心概念為幣齡,即持有貨幣的時間。例如有10個幣、持有90天,即擁有900幣天的幣齡。
  另外使用幣,即意味著幣齡的銷毀。
  在PoS中有一種特殊的交易稱為利息幣,即持有人可以消耗幣齡獲得利息,同時獲得為網(wǎng)絡(luò)產(chǎn)生區(qū)塊、以及PoS造幣的優(yōu)先權(quán)。
?

點點幣的PoS實現(xiàn)原理

?
  點點幣的PoS證明計算公式為:
  proofhash < 幣齡x目標(biāo)值
?
  展開如下:
  hash(nStakeModifier + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime) < bnTarget x bnCoinDayWeight
?
  其中proofhash,對應(yīng)一組數(shù)據(jù)的哈希值,即hash(nStakeModifier + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime)。
  幣齡即bnCoinDayWeight,即幣天,即持有的幣數(shù)乘以持有幣的天數(shù),此處天數(shù)最大值為90天。
  目標(biāo)值,即bnTarget,用于衡量PoS挖礦難度。目標(biāo)值與難度成反比,目標(biāo)值越大、難度越??;反之亦然。
?
  由公式可見,持有的幣天越大,挖到區(qū)塊的機會越大。
?
  peercoin-0.6.1ppc中PoS證明計算代碼如下:
?

bool CheckStakeKernelHash(unsigned int nBits, const CBlockHeader& blockFrom, unsigned int nTxPrevOffset, const CTransaction& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, bool fPrintProofOfStake)
{
    if (nTimeTx < txPrev.nTime)  // Transaction timestamp violation
        return error("CheckStakeKernelHash() : nTime violation");

    unsigned int nTimeBlockFrom = blockFrom.GetBlockTime();
    if (nTimeBlockFrom + nStakeMinAge > nTimeTx) // Min age requirement
        return error("CheckStakeKernelHash() : min age violation");

    //目標(biāo)值使用nBits
    CBigNum bnTargetPerCoinDay;
    bnTargetPerCoinDay.SetCompact(nBits);
    int64 nValueIn = txPrev.vout[prevout.n].nValue;
    // v0.3 protocol kernel hash weight starts from 0 at the 30-day min age
    // this change increases active coins participating the hash and helps
    // to secure the network when proof-of-stake difficulty is low
    int64 nTimeWeight = min((int64)nTimeTx - txPrev.nTime, (int64)STAKE_MAX_AGE) - (IsProtocolV03(nTimeTx)? nStakeMinAge : 0);
    //計算幣齡,STAKE_MAX_AGE為90天
    CBigNum bnCoinDayWeight = CBigNum(nValueIn) * nTimeWeight / COIN / (24 * 60 * 60);
    // Calculate hash
    CDataStream ss(SER_GETHASH, 0);
    //權(quán)重修正因子
    uint64 nStakeModifier = 0;
    int nStakeModifierHeight = 0;
    int64 nStakeModifierTime = 0;
    if (IsProtocolV03(nTimeTx))  // v0.3 protocol
    {
        if (!GetKernelStakeModifier(blockFrom.GetHash(), nTimeTx, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake))
            return false;
        ss << nStakeModifier;
    }
    else // v0.2 protocol
    {
        ss << nBits;
    }

    //計算proofhash
    //即計算hash(nStakeModifier + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime)
    ss << nTimeBlockFrom << nTxPrevOffset << txPrev.nTime << prevout.n << nTimeTx;
    hashProofOfStake = Hash(ss.begin(), ss.end());
    if (fPrintProofOfStake)
    {
        if (IsProtocolV03(nTimeTx))
            printf("CheckStakeKernelHash() : using modifier 0x%016" PRI64x" at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
                nStakeModifier, nStakeModifierHeight,
                DateTimeStrFormat(nStakeModifierTime).c_str(),
                mapBlockIndex[blockFrom.GetHash()]->nHeight,
                DateTimeStrFormat(blockFrom.GetBlockTime()).c_str());
        printf("CheckStakeKernelHash() : check protocol=%s modifier=0x%016" PRI64x" nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
            IsProtocolV05(nTimeTx)? "0.5" : (IsProtocolV03(nTimeTx)? "0.3" : "0.2"),
            IsProtocolV03(nTimeTx)? nStakeModifier : (uint64) nBits,
            nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx,
            hashProofOfStake.ToString().c_str());
    }

    // Now check if proof-of-stake hash meets target protocol
    //判斷是否滿足proofhash < 幣齡x目標(biāo)值
    if (CBigNum(hashProofOfStake) > bnCoinDayWeight * bnTargetPerCoinDay)
        return false;
    if (fDebug && !fPrintProofOfStake)
    {
        if (IsProtocolV03(nTimeTx))
            printf("CheckStakeKernelHash() : using modifier 0x%016" PRI64x" at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
                nStakeModifier, nStakeModifierHeight, 
                DateTimeStrFormat(nStakeModifierTime).c_str(),
                mapBlockIndex[blockFrom.GetHash()]->nHeight,
                DateTimeStrFormat(blockFrom.GetBlockTime()).c_str());
        printf("CheckStakeKernelHash() : pass protocol=%s modifier=0x%016" PRI64x" nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
            IsProtocolV03(nTimeTx)? "0.3" : "0.2",
            IsProtocolV03(nTimeTx)? nStakeModifier : (uint64) nBits,
            nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx,
            hashProofOfStake.ToString().c_str());
    }
    return true;
}
//代碼位置src/kernel.cpp

?

點點幣的PoS挖礦難度

?
  點點幣使用目標(biāo)值來衡量挖礦難度,目標(biāo)值與難度成反比,目標(biāo)值越大、難度越?。环粗嗳?。
  當(dāng)前區(qū)塊的目標(biāo)值與前一個區(qū)塊目標(biāo)值、前兩個區(qū)塊的時間間隔有關(guān)。
?
  計算公式如下:
  當(dāng)前區(qū)塊目標(biāo)值 = 前一個區(qū)塊目標(biāo)值 x (1007x10x60 + 2x前兩個區(qū)塊時間間隔) / (1009x10x60)
?
  由公式可見,兩個區(qū)塊目標(biāo)間隔時間即為10分鐘。
  如果前兩個區(qū)塊時間間隔大于10分鐘,目標(biāo)值會提高,即當(dāng)前區(qū)塊難度會降低。
  反之,如果前兩個區(qū)塊時間間隔小于10分鐘,目標(biāo)值會降低,即當(dāng)前區(qū)塊難度會提高。
?
  peercoin-0.6.1ppc中目標(biāo)值計算代碼如下:
?

unsigned int static GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake)
{
    if (pindexLast == NULL)
        return bnProofOfWorkLimit.GetCompact(); // genesis block

    const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake);
    if (pindexPrev->pprev == NULL)
        return bnInitialHashTarget.GetCompact(); // first block
    const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake);
    if (pindexPrevPrev->pprev == NULL)
        return bnInitialHashTarget.GetCompact(); // second block

    int64 nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime();

    // ppcoin: target change every block
    // ppcoin: retarget with exponential moving toward target spacing
    CBigNum bnNew;
    bnNew.SetCompact(pindexPrev->nBits);
    //STAKE_TARGET_SPACING為10分鐘,即10 * 60
    //兩個區(qū)塊目標(biāo)間隔時間即為10分鐘
    int64 nTargetSpacing = fProofOfStake? STAKE_TARGET_SPACING : min(nTargetSpacingWorkMax, (int64) STAKE_TARGET_SPACING * (1 + pindexLast->nHeight - pindexPrev->nHeight));
    //nTargetTimespan為1周,即7 * 24 * 60 * 60
    //nInterval為1008,即區(qū)塊間隔為10分鐘時,1周產(chǎn)生1008個區(qū)塊
    int64 nInterval = nTargetTimespan / nTargetSpacing;
    //計算當(dāng)前區(qū)塊目標(biāo)值
    bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing);
    bnNew /= ((nInterval + 1) * nTargetSpacing);

    if (bnNew > bnProofOfWorkLimit)
        bnNew = bnProofOfWorkLimit;

    return bnNew.GetCompact();
}
//代碼位置src/kernel.cpp

?

PoS 2.0的提出和黑幣

?
  為了進一步鞏固PoS的安全,2014年rat4(Pavel Vasin)提出了PoS 2.0,并發(fā)布了黑幣。
  黑幣前5000個塊,為純PoW階段;第5001個塊到第10000個塊為PoW與PoS并存階段,從第10001個塊及以后為純PoS階段。
  黑幣首創(chuàng)快速挖礦+低股息發(fā)行模式,發(fā)行階段采用POW方式,通過算法改進在短時間內(nèi)無法制造出專用的GPU和AISC礦機,解決分配不公平的問題。
?
  PoS2.0相比PoS的改進:
?
  1、將幣齡從等式中拿掉。新系統(tǒng)采用如下公式計算權(quán)益證明:
  proofhash < 幣數(shù)x目標(biāo)值
?
  點點幣中,部分節(jié)點平時保持離線,只在積累了可觀的幣齡以后才連線獲取利息,然后再次離線。
  PoS 2.0中拿掉幣齡,使得積攢幣齡的方法不再有效,所有節(jié)點必須更多的保持在線,以進行權(quán)益累積。
  越多的節(jié)點在線進行權(quán)益累積,系統(tǒng)遭遇51%***的可能性就越低。
?
  2、為了防范預(yù)先計算***,權(quán)益修正因子每次均改變。
  3、改變時間戳規(guī)則,以及哈希算法改用SHA256。
?

黑幣的PoS實現(xiàn)原理

?
  黑幣的PoS證明計算公式為:
  proofhash < 幣數(shù)x目標(biāo)值
?
  展開如下:
  hash(nStakeModifier + txPrev.block.nTime + txPrev.nTime + txPrev.vout.hash + txPrev.vout.n + nTime) < bnTarget * nWeight
?
  其中proofhash,對應(yīng)一組數(shù)據(jù)的哈希值,即hash(nStakeModifier + txPrev.block.nTime + txPrev.nTime + txPrev.vout.hash + txPrev.vout.n + nTime)。
  幣數(shù)即nWeight,目標(biāo)值即bnTarget。
?
  blackcoin-1.2.4中PoS證明計算代碼如下:
?

static bool CheckStakeKernelHashV2(CBlockIndex* pindexPrev, unsigned int nBits, unsigned int nTimeBlockFrom, const CTransaction& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, uint256& targetProofOfStake, bool fPrintProofOfStake)
{
    if (nTimeTx < txPrev.nTime)  // Transaction timestamp violation
        return error("CheckStakeKernelHash() : nTime violation");

    //目標(biāo)值使用nBits
    CBigNum bnTarget;
    bnTarget.SetCompact(nBits);

    //計算幣數(shù)x目標(biāo)值
    int64_t nValueIn = txPrev.vout[prevout.n].nValue;
    CBigNum bnWeight = CBigNum(nValueIn);
    bnTarget *= bnWeight;

    targetProofOfStake = bnTarget.getuint256();

    //權(quán)重修正因子
    uint64_t nStakeModifier = pindexPrev->nStakeModifier;
    uint256 bnStakeModifierV2 = pindexPrev->bnStakeModifierV2;
    int nStakeModifierHeight = pindexPrev->nHeight;
    int64_t nStakeModifierTime = pindexPrev->nTime;

    //計算哈希值
    //即計算hash(nStakeModifier + txPrev.block.nTime + txPrev.nTime + txPrev.vout.hash + txPrev.vout.n + nTime)
    CDataStream ss(SER_GETHASH, 0);
    if (IsProtocolV3(nTimeTx))
        ss << bnStakeModifierV2;
    else
        ss << nStakeModifier << nTimeBlockFrom;
    ss << txPrev.nTime << prevout.hash << prevout.n << nTimeTx;
    hashProofOfStake = Hash(ss.begin(), ss.end());

    if (fPrintProofOfStake)
    {
        LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from timestamp=%s\n",
            nStakeModifier, nStakeModifierHeight,
            DateTimeStrFormat(nStakeModifierTime),
            DateTimeStrFormat(nTimeBlockFrom));
        LogPrintf("CheckStakeKernelHash() : check modifier=0x%016x nTimeBlockFrom=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
            nStakeModifier,
            nTimeBlockFrom, txPrev.nTime, prevout.n, nTimeTx,
            hashProofOfStake.ToString());
    }

    // Now check if proof-of-stake hash meets target protocol
    //判斷是否滿足proofhash < 幣數(shù)x目標(biāo)值
    if (CBigNum(hashProofOfStake) > bnTarget)
        return false;

    if (fDebug && !fPrintProofOfStake)
    {
        LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from timestamp=%s\n",
            nStakeModifier, nStakeModifierHeight,
            DateTimeStrFormat(nStakeModifierTime),
            DateTimeStrFormat(nTimeBlockFrom));
        LogPrintf("CheckStakeKernelHash() : pass modifier=0x%016x nTimeBlockFrom=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
            nStakeModifier,
            nTimeBlockFrom, txPrev.nTime, prevout.n, nTimeTx,
            hashProofOfStake.ToString());
    }

    return true;
}

?

后記

?
  PoS有種種優(yōu)點,但也有所缺陷。
  即因為PoS并不消耗更多的算力,因此如果出現(xiàn)分叉,理性節(jié)點會在所有鏈上同時PoS挖礦。
  以至于每次分叉都會形成新的山寨幣,即PoS無法很好的應(yīng)對分叉。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI