比特幣pow算法介紹

Proof Of Work 工做量證實ios

借鑑了 哈希現金(Hashcash)-1997年 英國密碼學專家亞當.貝克(Adam Back)算法

用工做量證實系統解決了互聯網垃圾郵件問題,它要求計算機在得到發送信息權限以前作必定的計算工做,這對正常的信息傳播來說,幾乎很難察覺,可是對向全網大量散步垃圾信息的計算機來講,就成爲了巨大的工做量和負擔。網絡

經過進行必定的運算和消耗必定的時間來計算一個符合規則的值,並提供給服務方快速作驗證。分佈式

 

比特幣中的POW共識函數

比特幣 - 去中心化的點對點電子交易系統 :維護分佈式去中心化的帳本oop

分佈式無信任條件下的帳本一致 ---》共識ui

POW解決的是拜占庭下的共識,保證分佈式帳本的最終一致性,解決雙花攻擊;同時也創建和維護了一個分佈式的時鐘spa

 

PoW系統的主要特徵是計算的不對稱性。(SHA256)orm

工做端須要作必定難度的工做得出一個結果,驗證方卻很容易經過結果來檢查工做端是否是作了相應的工做。blog

做弊行爲的前提在於花費大量的資源,一旦某人沒法成功達成惡意目標就意味着其付出了巨大的且不可挽回的沉沒成本。(這也是pow的優點所在,做惡有代價)

 

核心技術:散列函數 SHA256

 

比特幣節點pow大體流程:

  1. 生成coinbase交易,並與其餘全部準備打包進區塊的交易組成交易列表,經過Merkle樹算法生成Merkle根哈希;
  2. 把Merkle根哈希及其餘相關字段組裝成區塊頭,將區塊頭的80字節數據做爲工做量證實的輸入;
  3. 不停地變動區塊頭中的隨機數,即nonce的數值,並對每次變動後的區塊頭作雙重SHA256運算,將結果值與當前網絡的目標值作對比,若是小於目標值則解題成功,工做量證實完成。
----------------------------------------------------------
 
Bitcoin Core 0.17.1 挖礦部分源碼的解析:

比特幣區塊頭結構

class CBlockHeader
{
public:
    // header
    int32_t nVersion;
    uint256 hashPrevBlock;
    uint256 hashMerkleRoot;
    uint32_t nTime;
    uint32_t nBits;
    uint32_t nNonce;
    //...     
};
// 代碼位置src/primitives/block.h

  

比特幣區塊結構:

class CBlock : public CBlockHeader
{
public:
    // 交易的列表
    std::vector<CTransactionRef> vtx;
    //...
}

//代碼位置src/primitives/block.h

  

如上區塊頭長度爲80字節,所以執行SHA256算法,分割成 64B和16B+填充的48B兩段進行運算

挖礦的過程就是尋找符合規則的 nNonce ,使以下等式成立:

SHA256(SHA256(version + prev_hash + merkle_root + ntime + nbits + nNonce + 填充 )) < TARGET

nNonce的範圍爲 0~2^32,當 nNonce 溢出仍然沒有符合的值時,修改區塊 coinbase 裏面的 ExtraNonce 

 

pow算法中生成coinbase交易以及建立區塊:

std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn)
{
    int64_t nTimeStart = GetTimeMicros();

    resetBlock();

    pblocktemplate.reset(new CBlockTemplate());

    if(!pblocktemplate.get())
        return nullptr;
    pblock = &pblocktemplate->block; // pointer for convenience

    // Add dummy coinbase tx as first transaction
    pblock->vtx.emplace_back();
    pblocktemplate->vTxFees.push_back(-1); // updated at end
    pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end

    LOCK2(cs_main, mempool.cs);
    CBlockIndex* pindexPrev = chainActive.Tip();
    assert(pindexPrev != nullptr);
    nHeight = pindexPrev->nHeight + 1;
//版本號 pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); // -regtest only: allow overriding block.nVersion with // -blockversion=N to test forking scenarios if (chainparams.MineBlocksOnDemand()) pblock->nVersion = gArgs.GetArg("-blockversion", pblock->nVersion);
//時間戳 pblock->nTime = GetAdjustedTime(); const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) ? nMedianTimePast : pblock->GetBlockTime(); fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus()); int nPackagesSelected = 0; int nDescendantsUpdated = 0; addPackageTxs(nPackagesSelected, nDescendantsUpdated); int64_t nTime1 = GetTimeMicros(); m_last_block_num_txs = nBlockTx; m_last_block_weight = nBlockWeight; // Create coinbase transaction. 建立coinbase交易 CMutableTransaction coinbaseTx; coinbaseTx.vin.resize(1); coinbaseTx.vin[0].prevout.SetNull(); coinbaseTx.vout.resize(1);
//挖礦獎勵 GetBlockSubsidy()和手續費 coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn; coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;
//第一筆交易即爲礦工得到獎勵和手續費的特殊交易 pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx)); pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus()); pblocktemplate->vTxFees[0] = -nFees; LogPrintf("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost); // Fill in header 將區塊頭數據補齊 pblock->hashPrevBlock = pindexPrev->GetBlockHash(); UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
//隨機數 nNonce 先重置爲0 pblock->nNonce = 0; pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]); CValidationState state; if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) { throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state))); } int64_t nTime2 = GetTimeMicros(); LogPrint(BCLog::BENCH, "CreateNewBlock() packages: %.2fms (%d packages, %d updated descendants), validity: %.2fms (total %.2fms)\n", 0.001 * (nTime1 - nTimeStart), nPackagesSelected, nDescendantsUpdated, 0.001 * (nTime2 - nTime1), 0.001 * (nTime2 - nTimeStart)); return std::move(pblocktemplate); } //代碼位置 src/miner.cpp

  

POW的實現

UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript)
{
    static const int nInnerLoopCount = 0x10000;
    int nHeightEnd = 0;
    int nHeight = 0;

    {   // Don't keep cs_main locked
        LOCK(cs_main);
        nHeight = chainActive.Height();
        nHeightEnd = nHeight+nGenerate;
    }
    unsigned int nExtraNonce = 0;
    UniValue blockHashes(UniValue::VARR);
    while (nHeight < nHeightEnd && !ShutdownRequested())
    {
        std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript));
        if (!pblocktemplate.get())
            throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
        CBlock *pblock = &pblocktemplate->block;
        {
            LOCK(cs_main);
       //用於更改 coinbase交易中的 ExtraNonce IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce); } //不斷變動區塊頭中的隨機數 Nonce //對變動後的區塊頭作雙重SHA256哈希運算 //CheckProofOfWork 函數 與當前難度的目標值作比對,若是小於目標難度,即Pow完成 //uint64_t nMaxTries = 1000000;即重試100萬次
while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) { ++pblock->nNonce; --nMaxTries; } if (nMaxTries == 0) { break; } if (pblock->nNonce == nInnerLoopCount) { continue; } std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock); //ProcessNewBlock 函數驗證合法性 if (!ProcessNewBlock(Params(), shared_pblock, true, nullptr)) throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted"); ++nHeight; blockHashes.push_back(pblock->GetHash().GetHex()); //mark script as important because it was used at least for one coinbase output if the script came from the wallet if (keepScript) { coinbaseScript->KeepScript(); } } return blockHashes; } //代碼位置src/rpc/mining.cpp

  

 雙SHA256驗證過程

區塊頭長度爲80字節,所以執行SHA256算法,分割成 64B和16B+填充的48B兩段進行運算

挖礦的過程就是尋找符合規則的 nNonce ,使以下等式成立:

SHA256(SHA256(version + prev_hash + merkle_root + ntime + nbits + nNonce + 填充 )) < TARGET

源碼:

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

    bnTarget.SetCompact(nBits, &fNegative, &fOverflow);

    // Check range
    if (fNegative || bnTarget == 0 || fOverflow || bnTarget > UintToArith256(params.powLimit))
        return false;

    // Check proof of work matches claimed amount
    if (UintToArith256(hash) > bnTarget)
        return false;

    return true;
}
//代碼位置 src/pow.cpp

  

nNonce的範圍爲 0~2^32,當 nNonce 溢出仍然沒有符合的值時,使用IncrementExtraNonce()函數修改區塊 coinbase 裏面的 ExtraNonce

 

void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
{
    // Update nExtraNonce 更新
    static uint256 hashPrevBlock;
    if (hashPrevBlock != pblock->hashPrevBlock)
    {
        nExtraNonce = 0;
        hashPrevBlock = pblock->hashPrevBlock;
    }
    ++nExtraNonce; //加 1
    unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2
    CMutableTransaction txCoinbase(*pblock->vtx[0]);
    //從新生成簽名
    txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS;
    assert(txCoinbase.vin[0].scriptSig.size() <= 100);
    //從新計算 pBlock 區塊頭中的 hashMerkleRoot
    pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
    pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
}

//代碼位置 src/miner.cpp  

 

難度值計算 - 源碼見 GetNextWorkRequired 函數,位置 src/pow.cpp

規則大體爲每建立2016個塊後將計算新的難度,此後的2016個塊使用新的難度。計算步驟以下: 

  1. 找到前2016個塊的第一個塊,計算生成這2016個塊花費的時間。即最後一個塊的時間與第一個塊的時間差。時間差不小於3.5天,不大於56天。
  2. 計算前2016個塊的難度總和,即單個塊的難度x總時間。
  3. 計算新的難度,即2016個塊的難度總和/14天的秒數,獲得每秒的難度值。
  4. 要求新的難度,難度不低於參數定義的最小難度。

 

POW算法被批評的點

PoW機制形成了巨大的能源浪費;

算力集中致使的中心化問題(礦池)。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息