比特币源码研读(5)数据结构-共识(Consensus)

写在前面

最近有点懒散,竟然有一周没有读源码了。想来惭愧,今天重拾bitcoin源码,来看看比特币的共识机制。

我们都知道比特币采用的共识机制是工作量证明(POW),将区块链的记账的权利通过一个数学问题的解决来决定。前面,在用python搭建简单的区块链框架中,我们简单地通过一个简单的🌰模拟了POW机制的挖矿原理。

源码初窥

Consensus

  • 源码路径:.../src/consensus/params.h
namespace Consensus {

enum DeploymentPos
{
    DEPLOYMENT_TESTDUMMY,
    DEPLOYMENT_CSV, // Deployment of BIP68, BIP112, and BIP113.
    DEPLOYMENT_SEGWIT, // Deployment of BIP141, BIP143, and BIP147.
    // NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp
    MAX_VERSION_BITS_DEPLOYMENTS
};

/**
 * Struct for each individual consensus rule change using BIP9.
 *
 **使用bip9改变共识规则的结构体
 */
struct BIP9Deployment {
    /** Bit position to select the particular bit in nVersion. */
    /**用来标志nVersion中特定位的bit*/
    int bit;
    /** Start MedianTime for version bits miner confirmation. Can be a date in the past */
    /** 矿工确认版本位的平均开始时间。这个时间可以设置在过去*/
    int64_t nStartTime;
    /** Timeout/expiry MedianTime for the deployment attempt. */
    /** 尝试部署的平均超时时间*/
    int64_t nTimeout;

    /** Constant for nTimeout very far in the future. */
    static constexpr int64_t NO_TIMEOUT = std::numeric_limits<int64_t>::max();

    /** Special value for nStartTime indicating that the deployment is always active.
     *  This is useful for testing, as it means tests don't need to deal with the activation
     *  process (which takes at least 3 BIP9 intervals). Only tests that specifically test the
     *  behaviour during activation cannot use this. */
    static constexpr int64_t ALWAYS_ACTIVE = -1;
};

/**
 * Parameters that influence chain consensus.
 *
 **影响链条共识的参数
 */
struct Params {
    uint256 hashGenesisBlock;               //创世区块哈希
    int nSubsidyHalvingInterval;            //区块奖励减半的时间间隔
    /** Block height at which BIP16 becomes active */
    int BIP16Height;                        //区块高度
    /** Block height and hash at which BIP34 becomes active */
    int BIP34Height;                        
    uint256 BIP34Hash;                      //区块重量
    /** Block height at which BIP65 becomes active */
    int BIP65Height;
    /** Block height at which BIP66 becomes active */
    int BIP66Height;
    /**
     * Minimum blocks including miner confirmation of the total of 2016 blocks in a retargeting period,
     * (nPowTargetTimespan / nPowTargetSpacing) which is also used for BIP9 deployments.
     * Examples: 1916 for 95%, 1512 for testchains.
     *
     **在2016个区块中至少要有多少个区块被矿工认可,规则改变才生效
     * 在BIP9部署时还是使用(nPowTargetTimespan / nPowTargetSpacing)
     * eg:1916 for 95%, 1512 for 测试链
     */
    uint32_t nRuleChangeActivationThreshold;
    uint32_t nMinerConfirmationWindow;
    BIP9Deployment vDeployments[MAX_VERSION_BITS_DEPLOYMENTS];
    /** Proof of work parameters */
    /**POW参数*/
    uint256 powLimit;                       //难度
    bool fPowAllowMinDifficultyBlocks;      //是否允许最低难度
    bool fPowNoRetargeting;                 //不调整难度
    int64_t nPowTargetSpacing;              //区块产生平均时间
    int64_t nPowTargetTimespan;             //难度调整时间  10
    //难度调整的比例
    int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; }
    uint256 nMinimumChainWork;              //当前难度调整最小值
    uint256 defaultAssumeValid;             //再次区块之前的区块都认为是有效的
};
} // namespace Consensus

Pow

  • 源码路径:.../src/pow.cpp
//获取下一次需要的工作量量
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
{
    assert(pindexLast != nullptr);
    //工作量证明的限制值
    unsigned int nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact();

    // Only change once per difficulty adjustment interval
    // 难度发生调整时才有所改变
    if ((pindexLast->nHeight+1) % params.DifficultyAdjustmentInterval() != 0)
    {
        // 判断是否允许最小难度值
        if (params.fPowAllowMinDifficultyBlocks)
        {
            // Special difficulty rule for testnet:
            // If the new block's timestamp is more than 2* 10 minutes
            // then allow mining of a min-difficulty block.
            /**测试网络的特殊难度规则:
             * 如果新区块的时间戳超过两个区块的产生时间20min,那么返回限制挖矿难度
            */
            if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + params.nPowTargetSpacing*2)
                return nProofOfWorkLimit;
            else
            {
                // Return the last non-special-min-difficulty-rules-block
                const CBlockIndex* pindex = pindexLast;
                while (pindex->pprev && pindex->nHeight % params.DifficultyAdjustmentInterval() != 0 && pindex->nBits == nProofOfWorkLimit)
                    pindex = pindex->pprev;
                return pindex->nBits;
            }
        }
        //返回新区快的nBits值
        return pindexLast->nBits;
    }

    // Go back by what we want to be 14 days worth of blocks
    int nHeightFirst = pindexLast->nHeight - (params.DifficultyAdjustmentInterval()-1);
    assert(nHeightFirst >= 0);
    const CBlockIndex* pindexFirst = pindexLast->GetAncestor(nHeightFirst);
    assert(pindexFirst);

    //计算工作量证明
    return CalculateNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params);
}

//计算工作量证明
unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params)
{   
    //如果不调整难度值
    if (params.fPowNoRetargeting)
        return pindexLast->nBits;

    // Limit adjustment step
    // 难度调整的限制
    int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
    if (nActualTimespan < params.nPowTargetTimespan/4)
        nActualTimespan = params.nPowTargetTimespan/4;
    if (nActualTimespan > params.nPowTargetTimespan*4)
        nActualTimespan = params.nPowTargetTimespan*4;

    // Retarget
    //计算调整后的难度值
    const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
    arith_uint256 bnNew;
    bnNew.SetCompact(pindexLast->nBits);
    bnNew *= nActualTimespan;
    bnNew /= params.nPowTargetTimespan;

    if (bnNew > bnPowLimit)
        bnNew = bnPowLimit;

    return bnNew.GetCompact();
}

//检验工作量证明
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;
}

比特币的共识机制(Pow)

每一个链上的区块在加入区块链之前都做了大量的计算,因此要想修改某一块的某些交易数据的成本是极其大的。这也是比特币不可篡改特性的一个重要技术依据。

工作量证明本质是在计算一个符合系统设定条件的哈希值,这个哈希值是通过对区块头信息进行哈希得到的。为了得到这个符合条件的哈希值,必须创建一个不超过特定值的区块块头的哈希。例如,如果最大可能的散列值是2 256  - 1,则可以通过产生小于2 255的散列值来证明您尝试了两种组合。

如果新的区块的哈希值符合共识协议期望的目标难度值条件,那么只会将新区块添加到区块链中。每2016 个块网络使用存储在每个区块头中的时间戳 来计算在生成最后2016 个区块的第一个和最后一个之间所经过的秒数。理想值是1209600秒(两周)。

  • 如果生成2016 个区块的时间少于两个星期,则预期难度值会按比例增加(最多达300%),以便下一个2016 [个块]应该花费两周时间,以便以相同速率检查哈希值。

  • 如果花费两个多星期来生成这些区块,出于同样的原因,预期的 难度值会成比例地下降(最高达75%)。

.
.
.
.

互联网颠覆世界,区块链颠覆互联网!

--------------------------------------------------20180502 23:03
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容