[转帖]Proof Of Work 工作量证明
Proof Of Work 工作量证明
https://www.cnblogs.com/zhang-qc/p/10451817.html
借鉴了 哈希现金(Hashcash)-1997年 英国密码学专家亚当.贝克(Adam Back)
用工作量证明系统解决了互联网垃圾邮件问题,它要求计算机在获得发送信息权限之前做一定的计算工作,这对正常的信息传播来讲,几乎很难察觉,但是对向全网大量散步垃圾信息的计算机来说,就成为了巨大的工作量和负担。
通过进行一定的运算和消耗一定的时间来计算一个符合规则的值,并提供给服务方快速做验证。
比特币中的POW共识
比特币 - 去中心化的点对点电子交易系统 :维护分布式去中心化的账本
分布式无信任条件下的账本一致 ---》共识
POW解决的是拜占庭下的共识,保证分布式账本的最终一致性,解决双花攻击;同时也建立和维护了一个分布式的时钟
PoW系统的主要特征是计算的不对称性。(SHA256)
工作端需要做一定难度的工作得出一个结果,验证方却很容易通过结果来检查工作端是不是做了相应的工作。
作弊行为的前提在于花费大量的资源,一旦某人无法成功达成恶意目标就意味着其付出了巨大的且不可挽回的沉没成本。(这也是pow的优势所在,作恶有代价)
核心技术:散列函数 SHA256

比特币节点pow大致流程:
- 生成coinbase交易,并与其他所有准备打包进区块的交易组成交易列表,通过Merkle树算法生成Merkle根哈希;
- 把Merkle根哈希及其他相关字段组装成区块头,将区块头的80字节数据作为工作量证明的输入;
- 不停地变更区块头中的随机数,即nonce的数值,并对每次变更后的区块头做双重SHA256运算,将结果值与当前网络的目标值做对比,如果小于目标值则解题成功,工作量证明完成。
比特币区块头结构
| 1 2 3 4 5 6 7 8 9 10 11 12 | classCBlockHeader{public:    // header    int32_t nVersion;    uint256 hashPrevBlock;    uint256 hashMerkleRoot;    uint32_t nTime;    uint32_t nBits;    uint32_t nNonce;    //...    };<br>// 代码位置src/primitives/block.h<br> | 
比特币区块结构:
| 1 2 3 4 5 6 7 8 9 | classCBlock : publicCBlockHeader{public:    // 交易的列表    std::vector<CTransactionRef> vtx;    //...}//代码位置src/primitives/block.h | 
如上区块头长度为80字节,因此执行SHA256算法,分割成 64B和16B+填充的48B两段进行运算
挖矿的过程就是寻找符合规则的 nNonce ,使如下等式成立:
| 1 | SHA256(SHA256(version + prev_hash + merkle_root + ntime + nbits + nNonce + 填充 )) < TARGET | 
nNonce的范围为 0~2^32,当 nNonce 溢出仍然没有符合的值时,修改区块 coinbase 里面的 ExtraNonce
pow算法中生成coinbase交易以及创建区块:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(constCScript& scriptPubKeyIn){    int64_t nTimeStart = GetTimeMicros();    resetBlock();    pblocktemplate.reset(newCBlockTemplate());    if(!pblocktemplate.get())        returnnullptr;    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;<br>    //版本号    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);<br>    //时间戳    pblock->nTime = GetAdjustedTime();    constint64_t nMedianTimePast = pindexPrev->GetMedianTimePast();    nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST)                       ? nMedianTimePast                       : pblock->GetBlockTime();    fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus());    intnPackagesSelected = 0;    intnDescendantsUpdated = 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);<br>    //挖矿奖励 GetBlockSubsidy()和手续费    coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn;    coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus());    coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;<br>   //第一笔交易即为矿工获得奖励和手续费的特殊交易    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());   <br>    //随机数 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)) {        throwstd::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));    returnstd::move(pblocktemplate);}//代码位置 src/miner.cpp | 
POW的实现
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, intnGenerate, uint64_t nMaxTries, boolkeepScript){    staticconstintnInnerLoopCount = 0x10000;    intnHeightEnd = 0;    intnHeight = 0;    {   // Don't keep cs_main locked        LOCK(cs_main);        nHeight = chainActive.Height();        nHeightEnd = nHeight+nGenerate;    }    unsigned intnExtraNonce = 0;    UniValue blockHashes(UniValue::VARR);    while(nHeight < nHeightEnd && !ShutdownRequested())    {        std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript));        if(!pblocktemplate.get())            throwJSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");        CBlock *pblock = &pblocktemplate->block;        {            LOCK(cs_main);<br>       //用于更改 coinbase交易中的 ExtraNonce            <strong>IncrementExtraNonce</strong>(pblock, chainActive.Tip(), nExtraNonce);        }      //不断变更区块头中的随机数 Nonce      //对变更后的区块头做双重SHA256哈希运算      //CheckProofOfWork 函数 与当前难度的目标值做比对,如果小于目标难度,即Pow完成      //uint64_t nMaxTries = 1000000;即重试100万次<br>        while(nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !<strong>CheckProofOfWork(</strong>pblock->GetHash(), pblock->nBits, Params().GetConsensus())) {            ++pblock->nNonce;            --nMaxTries;        }        if(nMaxTries == 0) {            break;        }        if(pblock->nNonce == nInnerLoopCount) {            continue;        }        std::shared_ptr<constCBlock> shared_pblock = std::make_shared<constCBlock>(*pblock);        //<strong>ProcessNewBlock</strong> 函数验证合法性        if(!ProcessNewBlock(Params(), shared_pblock, true, nullptr))            throwJSONRPCError(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();        }    }    returnblockHashes;}//代码位置src/rpc/mining.cpp | 
双SHA256验证过程:
区块头长度为80字节,因此执行SHA256算法,分割成 64B和16B+填充的48B两段进行运算
挖矿的过程就是寻找符合规则的 nNonce ,使如下等式成立:
| 1 | SHA256(SHA256(version + prev_hash + merkle_root + ntime + nbits + nNonce + 填充 )) < TARGET | 
源码:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | boolCheckProofOfWork(uint256 hash, unsigned intnBits, constConsensus::Params& params){    boolfNegative;    boolfOverflow;    arith_uint256 bnTarget;    bnTarget.SetCompact(nBits, &fNegative, &fOverflow);    // Check range    if(fNegative || bnTarget == 0 || fOverflow || bnTarget > UintToArith256(params.powLimit))        returnfalse;    // Check proof of work matches claimed amount    if(UintToArith256(hash) > bnTarget)        returnfalse;    returntrue;}//代码位置 src/pow.cpp | 
nNonce的范围为 0~2^32,当 nNonce 溢出仍然没有符合的值时,使用IncrementExtraNonce()函数修改区块 coinbase 里面的 ExtraNonce
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | voidIncrementExtraNonce(CBlock* pblock, constCBlockIndex* pindexPrev, unsigned int& nExtraNonce){    // Update nExtraNonce 更新    staticuint256 hashPrevBlock;    if(hashPrevBlock != pblock->hashPrevBlock)    {        nExtraNonce = 0;        hashPrevBlock = pblock->hashPrevBlock;    }    ++nExtraNonce; //加 1    unsigned intnHeight = 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个块使用新的难度。计算步骤如下:
- 找到前2016个块的第一个块,计算生成这2016个块花费的时间。即最后一个块的时间与第一个块的时间差。时间差不小于3.5天,不大于56天。
- 计算前2016个块的难度总和,即单个块的难度x总时间。
- 计算新的难度,即2016个块的难度总和/14天的秒数,得到每秒的难度值。
- 要求新的难度,难度不低于参数定义的最小难度。
POW算法被批评的点:
PoW机制造成了巨大的能源浪费;
算力集中导致的中心化问题(矿池)。
[转帖]Proof Of Work 工作量证明的更多相关文章
- 区块链共识机制之工作量证明(POW)
		像比特币.以太坊.NXT.Bitshares等这些区块链系统,其本质上是一种加密经济组织,它建立在点对点网络上,是去中心化.无管辖的,由密码学.经济学和社会共识来共同维护.这些加密网络因各种原因有着多 ... 
- 用python阐释工作量证明(proof of work)
		了解比特币的都知道挖矿非常耗电,这是由于比特币用到了工作量证明. 工作量证明是指系统为达到某目标而设置的工作度量方法.一開始是用在网络攻防上,大大提高攻击者的计算量,攻击成本也就上去了. 工作量证明须 ... 
- cpp 区块链模拟示例(四) 区块链工作量证明
		本文主要在之前的区块链原形上添加了工作量证明,并且为后继的交易功能做好准备. 上一个章节我们已经创建了区块链的基本原形,但是区块的哈希计算和加入太过于简单,如果按照这种速度添加区块那么区块链估计一个小 ... 
- Go实现Pow工作量证明
		之前使用python编写了一段代码实现了工作量证明机制,近期由于参与以太坊智能合约开发钱包的工作接触到golang语言,所以借此以go来实现Pow(Proof of work). 实现代码如下: // ... 
- 详解POW工作量证明原理
		原文地址 来自 微信公众号 区块链大师 POW工作量证明(英文全称为Proof of Work)早在比特币出现之前就已经有人探索,常见的是利用HASH运算的复杂度进行CPU运算实现工作量确定,当然你 ... 
- 【区块链Go语言实现】Part 2:工作量证明机制POW
		0x00 介绍 在上一篇文章中,我们建立了一个非常简单的数据结构,它是区块链数据库的本质.并且,我们实现了以类似链条关系的方式向其中添加区块的功能:每个区块都会链接到前一区块.然而,我们实现的区块链有 ... 
- [GO]使用go语言实现比特币的工作量证明
		之前的博文已经实现了区块连的基本的工作原理,但在比特币系统中有一个很重要的概念:工作量证明POW,在比特币系统中它的作用就是在十分钟左右的时间内只有一个有能够记帐并得到奖励 在之前的博文中,区块的哈希 ... 
- [转]工作量证明(PoW)权益证明(PoS)和委任权益证明(DPoS)区别
		原文链接 Both in the glossary and in some of our previous posts we've touched on mining and the two main ... 
- Python实现一条基于POS算法的区块链
		区块链中的共识算法 在比特币公链架构解析中,就曾提到过为了实现去中介化的设计,比特币设计了一套共识协议,并通过此协议来保证系统的稳定性和防攻击性. 并且我们知道,截止目前使用最广泛,也是最被大家接受的 ... 
随机推荐
- [转载]sql server死锁
			SQL Server Deadlocks by Examplehttps://www.red-gate.com/simple-talk/sql/performance/sql-server-deadl ... 
- JAVA基础--环境搭建
			概况 系统:win10 企业版 IDE:Eclipse 4.7.3 JDK:jdk1.8.0_171 数据库:SQLServer2012 Oracle,未安装MySQL 安装 JDK与开发工具(Ecl ... 
- myeclipse的安装与破解
			myeclipe安装和破解一直困扰我很长时间,我又是尴尬症的人,不破解就是不行,花费一天时间终于搞定是怎么破解的. 一:首先myeclipse的官方下载网站www.myeclipsecn.com/do ... 
- js svg转图片格式
			1.情景展示 闲来无事的时候,发现chrome扩展程序里面有图像,本想下载下来,却发现文件格式是svg格式,如何将svg文件改成图片格式? chrome-extension://jlgkpaici ... 
- eclipse Target runtime com.genuitec.runtime.generic.jee50 is not defined
			1.情景展示 报错信息如下: 2.原因分析 使用eclipse导入myeclipse时,唯独这个报错信息改不了. 需要通过修改这个项目的配置文件才行. 3.解决方案 第一步:切换到导航视图: 第二 ... 
- jQuery实现列表框双向选择操作
			对列表框的操作经常碰到过这样的应用:从左侧的列表框中选中要选的项添加到右侧列表框中,然后提交最终选择的项,对误操作而选中的项还可以执行移除操作.在很多系统中应用比如说求职网站的选择意向工作地区,QQ好 ... 
- 二叉树 B-树B+树
			聚集索引和非聚集索引结构参考:http://blog.csdn.net/cangchen/article/details/44818623 前两天有位朋友邀请我回答个问题,为什么 MongoDB (索 ... 
- 解决:Could not load type 'System.ServiceModel.Activation.HttpModule' from assembly 'System.ServiceMode
			版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/Eric_K1m/article/deta ... 
- A Full Hardware Guide to Deep Learning深度学习电脑配置
			https://study.163.com/provider/400000000398149/index.htm?share=2&shareId=400000000398149( 欢迎关注博 ... 
- 【转】SQL2008 链接Oracle 调用存储过程
			1. SQL链接ORACLE 都是可视化的操作 如下图: 红色框选的是oracle的数据驱动,如果没有这个驱动 那需要单独安装oracle的client端 装完以后就有了. 2.在创建之前,在SQLS ... 
